• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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