1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * TODO(b/384939093): PLEASE NOTE: The implemented here is in a WIP status.
19 *
20 * Currently the Composition algorithm implemented in
21 * this module has a known limitation. It uses IPC buffers in such a way where
22 * it is currently possible for frames to be simultaneously
23 * read and written from the same memory lcoation. It's therefore possible to
24 * have some display artifacts as partial frames are read. To remedy there is
25 * follow-up work (documented in b/384939093) planned.
26 */
27
28 #include "host/libs/screen_connector/composition_manager.h"
29
30 #include <android-base/parseint.h>
31 #include <android-base/strings.h>
32 #include <libyuv.h>
33
34 #include <drm/drm_fourcc.h>
35 #include "host/frontend/webrtc/display_handler.h"
36 #include "host/libs/screen_connector/ring_buffer_manager.h"
37
38 static const int kRedIdx = 0;
39 static const int kGreenIdx = 1;
40 static const int kBlueIdx = 2;
41 static const int kAlphaIdx = 3;
42
43 namespace cuttlefish {
44
alpha_blend_layer(std::uint8_t * frame_pixels,std::uint32_t h,std::uint32_t w,std::uint8_t * overlay)45 void alpha_blend_layer(std::uint8_t* frame_pixels, std::uint32_t h,
46 std::uint32_t w, std::uint8_t* overlay) {
47 std::uint8_t* dst = frame_pixels;
48 std::uint8_t* src = overlay;
49 int max = w * h;
50 for (int idx = 0; idx < max; idx++) {
51 float a = ((float)src[kAlphaIdx]) / 255.0f;
52 float a_inv = 1.0f - a;
53 dst[kRedIdx] = (std::uint8_t)((src[kRedIdx] * a) + (dst[kRedIdx] * a_inv));
54 dst[kBlueIdx] =
55 (std::uint8_t)((src[kBlueIdx] * a) + (dst[kBlueIdx] * a_inv));
56 dst[kGreenIdx] =
57 (std::uint8_t)((src[kGreenIdx] * a) + (dst[kGreenIdx] * a_inv));
58 dst[kAlphaIdx] = 255;
59 dst += 4;
60 src += 4;
61 }
62 }
63
64 std::map<int, std::vector<CompositionManager::DisplayOverlay>>
ParseOverlays(std::vector<std::string> overlay_items)65 CompositionManager::ParseOverlays(std::vector<std::string> overlay_items) {
66 std::map<int, std::vector<DisplayOverlay>> overlays;
67 // This iterates the list of overlay tuples, entries are of the form x:y
68 // where x is a vm index in the cluster, and y is a display
69 // index within that vm. Structured types are created as result.
70 for (int display_index = 0; display_index < overlay_items.size();
71 display_index++) {
72 auto overlay_item = android::base::Trim(overlay_items[display_index]);
73
74 if (overlay_item == "" || overlay_item == "_") {
75 continue;
76 }
77
78 std::vector<DisplayOverlay>& display_overlays = overlays[display_index];
79
80 std::vector<std::string> overlay_list =
81 android::base::Split(overlay_item, " ");
82
83 for (const auto& overlay_tuple_str : overlay_list) {
84 std::vector<std::string> overlay_tuple =
85 android::base::Split(overlay_tuple_str, ":");
86
87 DisplayOverlay docfg;
88
89 if (overlay_tuple.size() == 2) {
90 if (!(android::base::ParseInt(overlay_tuple[0], &docfg.src_vm_index) &&
91 android::base::ParseInt(overlay_tuple[1],
92 &docfg.src_display_index))) {
93 LOG(FATAL) << "Failed to parse display overlay directive: "
94 << overlay_tuple_str;
95 } else {
96 display_overlays.push_back(docfg);
97 }
98 } else {
99 LOG(FATAL) << "Failed to parse display overlay directive, not a tuple "
100 "of format x:y - "
101 << overlay_tuple_str;
102 }
103 }
104 }
105 return overlays;
106 }
107
CompositionManager(int cluster_index,std::string & group_uuid,std::map<int,std::vector<DisplayOverlay>> & overlays)108 CompositionManager::CompositionManager(
109 int cluster_index, std::string& group_uuid,
110 std::map<int, std::vector<DisplayOverlay>>& overlays)
111 : display_ring_buffer_manager_(cluster_index - 1, group_uuid),
112 cluster_index_(cluster_index - 1),
113 group_uuid_(group_uuid),
114 cfg_overlays_(overlays) {}
115
~CompositionManager()116 CompositionManager::~CompositionManager() {}
117
Create()118 Result<std::unique_ptr<CompositionManager>> CompositionManager::Create() {
119 auto cvd_config = CuttlefishConfig::Get();
120 auto instance = cvd_config->ForDefaultInstance();
121 // Aggregate all the display overlays into a single list per config
122 std::vector<std::string> overlays;
123 for (const auto& display : instance.display_configs()) {
124 overlays.push_back(display.overlays);
125 }
126
127 std::map<int, std::vector<CompositionManager::DisplayOverlay>> domap =
128 CompositionManager::ParseOverlays(overlays);
129 for (auto const& [display_index, display_overlays] : domap) {
130 for (auto const& display_overlay : display_overlays) {
131 CF_EXPECTF(display_overlay.src_vm_index < cvd_config->Instances().size(),
132 "Invalid source overlay VM index: {}",
133 display_overlay.src_vm_index);
134
135 const cuttlefish::CuttlefishConfig::InstanceSpecific src_instance =
136 cvd_config->Instances()[display_overlay.src_vm_index];
137
138 CF_EXPECTF(display_overlay.src_display_index <
139 src_instance.display_configs().size(),
140 "Invalid source overlay display index: {}",
141 display_overlay.src_vm_index);
142
143 const cuttlefish::CuttlefishConfig::DisplayConfig src_display =
144 src_instance.display_configs()[display_overlay.src_display_index];
145
146 const cuttlefish::CuttlefishConfig::DisplayConfig dest_display =
147 instance.display_configs()[display_index];
148
149 CF_EXPECT(src_display.width == dest_display.width &&
150 src_display.height == dest_display.height,
151 "Source and target overlay display must be of identical size.");
152 }
153 }
154
155 // Calculate the instance's position within cluster
156 // For display overlay config calculations
157 int instance_index = instance.index();
158
159 std::string group_uuid =
160 fmt::format("{}", cvd_config->ForDefaultEnvironment().group_uuid());
161
162 CF_EXPECT(group_uuid.length() > 0, "Invalid group UUID");
163
164 std::unique_ptr<CompositionManager> mgr(
165 new CompositionManager(instance_index + 1, group_uuid, domap));
166
167 return mgr;
168 }
169
170 // Whenever a display is created, a shared memory IPC ringbuffer
171 // is initialized so that other frames can obtain this display's contents
172 // for composition.
OnDisplayCreated(const DisplayCreatedEvent & e)173 void CompositionManager::OnDisplayCreated(const DisplayCreatedEvent& e) {
174 auto result = display_ring_buffer_manager_.CreateLocalDisplayBuffer(
175 cluster_index_, e.display_number, e.display_width, e.display_height);
176
177 if (!result.ok()) {
178 LOG(FATAL) << "OnDisplayCreated failed: " << result.error().FormatForEnv();
179 }
180 }
181
182 // Called every frame.
OnFrame(std::uint32_t display_number,std::uint32_t frame_width,std::uint32_t frame_height,std::uint32_t frame_fourcc_format,std::uint32_t frame_stride_bytes,std::uint8_t * frame_pixels)183 void CompositionManager::OnFrame(std::uint32_t display_number,
184 std::uint32_t frame_width,
185 std::uint32_t frame_height,
186 std::uint32_t frame_fourcc_format,
187 std::uint32_t frame_stride_bytes,
188 std::uint8_t* frame_pixels) {
189 // First step is to push the local display pixels to the shared memory region
190 // ringbuffer
191 std::uint8_t* shmem_local_display = display_ring_buffer_manager_.WriteFrame(
192 cluster_index_, display_number, frame_pixels,
193 frame_width * frame_height * 4);
194
195 // Next some upkeep, the format of the frame is needed for blending
196 // computations.
197 LastFrameInfo last_frame_info = LastFrameInfo(
198 display_number, frame_width, frame_height, frame_fourcc_format,
199 frame_stride_bytes, (std::uint8_t*)shmem_local_display);
200
201 last_frame_info_map_[display_number] = last_frame_info;
202
203 // Lastly, the pixels of the current frame are modified by blending any
204 // configured layers over the top of the current 'base layer'
205 AlphaBlendLayers(frame_pixels, display_number, frame_width, frame_height);
206 }
207
208 // This is called to 'Force a Display Composition Refresh' on a display. It is
209 // triggered by a thread to force displays to constantly update so that when
210 // layers are updated, the user will see the blended result.
ComposeFrame(int display_index,std::shared_ptr<CvdVideoFrameBuffer> buffer)211 void CompositionManager::ComposeFrame(
212 int display_index, std::shared_ptr<CvdVideoFrameBuffer> buffer) {
213 if (!last_frame_info_map_.contains(display_index)) {
214 return;
215 }
216 LastFrameInfo& last_frame_info = last_frame_info_map_[display_index];
217
218 ComposeFrame(display_index, last_frame_info.frame_width_,
219 last_frame_info.frame_height_,
220 last_frame_info.frame_fourcc_format_,
221 last_frame_info.frame_stride_bytes_, buffer);
222 }
223
AlphaBlendLayers(std::uint8_t * frame_pixels,int display_number,int frame_width,int frame_height)224 std::uint8_t* CompositionManager::AlphaBlendLayers(std::uint8_t* frame_pixels,
225 int display_number,
226 int frame_width,
227 int frame_height) {
228 if (cfg_overlays_.count(display_number) == 0) {
229 return frame_pixels;
230 }
231
232 std::vector<DisplayOverlay>& cfg_overlays = cfg_overlays_[display_number];
233 int num_overlays = cfg_overlays.size();
234
235 std::vector<void*> overlays;
236 overlays.resize(num_overlays, nullptr);
237
238 for (int i = 0; i < num_overlays; i++) {
239 if (overlays[i] != nullptr) {
240 continue;
241 }
242
243 DisplayOverlay& layer = cfg_overlays[i];
244
245 overlays[i] = display_ring_buffer_manager_.ReadFrame(
246 layer.src_vm_index, layer.src_display_index, frame_width, frame_height);
247 }
248
249 for (auto i : overlays) {
250 if (i) {
251 alpha_blend_layer(frame_pixels, frame_height, frame_width,
252 (std::uint8_t*)i);
253 }
254 }
255 return (std::uint8_t*)frame_pixels;
256 }
257
ComposeFrame(int display,int width,int height,std::uint32_t frame_fourcc_format,std::uint32_t frame_stride_bytes,std::shared_ptr<CvdVideoFrameBuffer> buffer)258 void CompositionManager::ComposeFrame(
259 int display, int width, int height, std::uint32_t frame_fourcc_format,
260 std::uint32_t frame_stride_bytes,
261 std::shared_ptr<CvdVideoFrameBuffer> buffer) {
262 std::uint8_t* shmem_local_display = display_ring_buffer_manager_.ReadFrame(
263 cluster_index_, display, width, height);
264
265 if (!frame_work_buffer_.contains(display)) {
266 frame_work_buffer_[display] = std::vector<std::uint8_t>(width * height * 4);
267 }
268 std::uint8_t* tmp_buffer = frame_work_buffer_[display].data();
269 memcpy(tmp_buffer, shmem_local_display, width * height * 4);
270
271 AlphaBlendLayers(tmp_buffer, display, width, height);
272
273 if (frame_fourcc_format == DRM_FORMAT_ARGB8888 ||
274 frame_fourcc_format == DRM_FORMAT_XRGB8888) {
275 libyuv::ARGBToI420(tmp_buffer, frame_stride_bytes, buffer->DataY(),
276 buffer->StrideY(), buffer->DataU(), buffer->StrideU(),
277 buffer->DataV(), buffer->StrideV(), width, height);
278 } else if (frame_fourcc_format == DRM_FORMAT_ABGR8888 ||
279 frame_fourcc_format == DRM_FORMAT_XBGR8888) {
280 libyuv::ABGRToI420(tmp_buffer, frame_stride_bytes, buffer->DataY(),
281 buffer->StrideY(), buffer->DataU(), buffer->StrideU(),
282 buffer->DataV(), buffer->StrideV(), width, height);
283 }
284 }
285
286 } // namespace cuttlefish