• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2017 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 #include "PostWorker.h"
17 
18 #include <string.h>
19 
20 #include <chrono>
21 
22 #include "ColorBuffer.h"
23 #include "FrameBuffer.h"
24 #include "RenderThreadInfo.h"
25 #include "aemu/base/Tracing.h"
26 #include "gl/DisplayGl.h"
27 #include "host-common/GfxstreamFatalError.h"
28 #include "host-common/logging.h"
29 #include "host-common/misc.h"
30 #include "vulkan/DisplayVk.h"
31 #include "vulkan/VkCommonOperations.h"
32 
33 #define POST_DEBUG 0
34 #if POST_DEBUG >= 1
35 #define DD(fmt, ...) \
36     fprintf(stderr, "%s:%d| " fmt, __func__, __LINE__, ##__VA_ARGS__)
37 #else
38 #define DD(fmt, ...) (void)0
39 #endif
40 
41 #define POST_ERROR(fmt, ...)                                                  \
42     do {                                                                      \
43         fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, \
44                 ##__VA_ARGS__);                                               \
45         fflush(stderr);                                                       \
46     } while (0)
47 
sDefaultRunOnUiThread(UiUpdateFunc f,void * data,bool wait)48 static void sDefaultRunOnUiThread(UiUpdateFunc f, void* data, bool wait) {
49     (void)f;
50     (void)data;
51     (void)wait;
52 }
53 
54 namespace gfxstream {
55 namespace {
56 
57 using emugl::ABORT_REASON_OTHER;
58 using emugl::FatalError;
59 using gl::DisplayGl;
60 using vk::DisplayVk;
61 
getTransformFromRotation(int rotation)62 hwc_transform_t getTransformFromRotation(int rotation) {
63     switch (static_cast<int>(rotation / 90)) {
64         case 1:
65             return HWC_TRANSFORM_ROT_270;
66         case 2:
67             return HWC_TRANSFORM_ROT_180;
68         case 3:
69             return HWC_TRANSFORM_ROT_90;
70         default:
71             return HWC_TRANSFORM_NONE;
72     }
73 }
74 
75 }  // namespace
76 
PostWorker(bool mainThreadPostingOnly,Compositor * compositor,DisplayGl * displayGl,DisplayVk * displayVk)77 PostWorker::PostWorker(bool mainThreadPostingOnly, Compositor* compositor,
78                        DisplayGl* displayGl, DisplayVk* displayVk)
79     : mFb(FrameBuffer::getFB()),
80       m_mainThreadPostingOnly(mainThreadPostingOnly),
81       m_runOnUiThread(m_mainThreadPostingOnly ? emugl::get_emugl_window_operations().runOnUiThread
82                                               : sDefaultRunOnUiThread),
83       m_compositor(compositor),
84       m_displayGl(displayGl),
85       m_displayVk(displayVk) {}
86 
postWithOverlay(ColorBuffer * cb)87 DisplayGl::PostLayer PostWorker::postWithOverlay(ColorBuffer* cb) {
88     float dpr = mFb->getDpr();
89     int windowWidth = mFb->windowWidth();
90     int windowHeight = mFb->windowHeight();
91     float px = mFb->getPx();
92     float py = mFb->getPy();
93     int zRot = mFb->getZrot();
94     hwc_transform_t rotation = (hwc_transform_t)0;
95 
96     // Find the x and y values at the origin when "fully scrolled."
97     // Multiply by 2 because the texture goes from -1 to 1, not 0 to 1.
98     // Multiply the windowing coordinates by DPR because they ignore
99     // DPR, but the viewport includes DPR.
100     float fx = 2.f * (m_viewportWidth - windowWidth * dpr) / (float)m_viewportWidth;
101     float fy = 2.f * (m_viewportHeight - windowHeight * dpr) / (float)m_viewportHeight;
102 
103     // finally, compute translation values
104     float dx = px * fx;
105     float dy = py * fy;
106 
107     return DisplayGl::PostLayer{
108         .colorBuffer = cb,
109         .overlayOptions =
110             DisplayGl::PostLayer::OverlayOptions{
111                 .rotation = static_cast<float>(zRot),
112                 .dx = dx,
113                 .dy = dy,
114             },
115     };
116 }
117 
postImpl(ColorBuffer * cb)118 std::shared_future<void> PostWorker::postImpl(ColorBuffer* cb) {
119     std::shared_future<void> completedFuture =
120         std::async(std::launch::deferred, [] {}).share();
121     completedFuture.wait();
122 
123     if (m_displayVk) {
124         constexpr const int kMaxPostRetries = 2;
125         for (int i = 0; i < kMaxPostRetries; i++) {
126             const auto imageInfo = mFb->borrowColorBufferForDisplay(cb->getHndl());
127             auto result = m_displayVk->post(imageInfo.get());
128             if (result.success) {
129                 return result.postCompletedWaitable;
130             }
131         }
132 
133         ERR("Failed to post ColorBuffer after %d retries.", kMaxPostRetries);
134         return completedFuture;
135     }
136 
137     if (!m_displayGl) {
138         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
139             << "PostWorker missing DisplayGl.";
140     }
141 
142     DisplayGl::Post post = {};
143 
144     ComposeLayer postLayerOptions = {
145         .composeMode = HWC2_COMPOSITION_DEVICE,
146         .blendMode = HWC2_BLEND_MODE_NONE,
147         .alpha = 1.0f,
148         .transform = HWC_TRANSFORM_NONE,
149     };
150 
151     const auto& multiDisplay = emugl::get_emugl_multi_display_operations();
152     if (multiDisplay.isMultiDisplayEnabled()) {
153         if (multiDisplay.isMultiDisplayWindow()) {
154             int32_t previousDisplayId = -1;
155             uint32_t currentDisplayId;
156             uint32_t currentDisplayColorBufferHandle;
157             while (multiDisplay.getNextMultiDisplay(previousDisplayId, &currentDisplayId,
158                                                     /*x=*/nullptr,
159                                                     /*y=*/nullptr,
160                                                     /*w=*/nullptr,
161                                                     /*h=*/nullptr,
162                                                     /*dpi=*/nullptr,
163                                                     /*flags=*/nullptr,
164                                                     &currentDisplayColorBufferHandle)) {
165                 previousDisplayId = currentDisplayId;
166 
167                 if (currentDisplayColorBufferHandle == 0) {
168                     continue;
169                 }
170                 emugl::get_emugl_window_operations().paintMultiDisplayWindow(
171                     currentDisplayId, currentDisplayColorBufferHandle);
172             }
173             post.layers.push_back(postWithOverlay(cb));
174         } else {
175             uint32_t combinedDisplayW = 0;
176             uint32_t combinedDisplayH = 0;
177             multiDisplay.getCombinedDisplaySize(&combinedDisplayW, &combinedDisplayH);
178 
179             post.frameWidth = combinedDisplayW;
180             post.frameHeight = combinedDisplayH;
181 
182             int32_t previousDisplayId = -1;
183             uint32_t currentDisplayId;
184             int32_t currentDisplayOffsetX;
185             int32_t currentDisplayOffsetY;
186             uint32_t currentDisplayW;
187             uint32_t currentDisplayH;
188             uint32_t currentDisplayColorBufferHandle;
189             while (multiDisplay.getNextMultiDisplay(previousDisplayId,
190                                                 &currentDisplayId,
191                                                 &currentDisplayOffsetX,
192                                                 &currentDisplayOffsetY,
193                                                 &currentDisplayW,
194                                                 &currentDisplayH,
195                                                 /*dpi=*/nullptr,
196                                                 /*flags=*/nullptr,
197                                                 &currentDisplayColorBufferHandle)) {
198                 previousDisplayId = currentDisplayId;
199 
200                 if (currentDisplayW == 0 || currentDisplayH == 0 ||
201                     (currentDisplayId != 0 && currentDisplayColorBufferHandle == 0)) {
202                     continue;
203                 }
204 
205                 ColorBuffer* currentCb =
206                     currentDisplayId == 0
207                         ? cb
208                         : mFb->findColorBuffer(currentDisplayColorBufferHandle).get();
209                 if (!currentCb) {
210                     continue;
211                 }
212 
213                 postLayerOptions.displayFrame = {
214                     .left = static_cast<int>(currentDisplayOffsetX),
215                     .top = static_cast<int>(currentDisplayOffsetY),
216                     .right = static_cast<int>(currentDisplayOffsetX + currentDisplayW),
217                     .bottom = static_cast<int>(currentDisplayOffsetY + currentDisplayH),
218                 };
219                 postLayerOptions.crop = {
220                     .left = 0.0f,
221                     .top = static_cast<float>(currentCb->getHeight()),
222                     .right = static_cast<float>(currentCb->getWidth()),
223                     .bottom = 0.0f,
224                 };
225 
226                 post.layers.push_back(DisplayGl::PostLayer{
227                     .colorBuffer = currentCb,
228                     .layerOptions = postLayerOptions,
229                 });
230             }
231         }
232     } else if (emugl::get_emugl_window_operations().isFolded()) {
233         const float dpr = mFb->getDpr();
234 
235         post.frameWidth = m_viewportWidth / dpr;
236         post.frameHeight = m_viewportHeight / dpr;
237 
238         int displayOffsetX;
239         int displayOffsetY;
240         int displayW;
241         int displayH;
242         emugl::get_emugl_window_operations().getFoldedArea(&displayOffsetX,
243                                                            &displayOffsetY,
244                                                            &displayW,
245                                                            &displayH);
246 
247         postLayerOptions.displayFrame = {
248             .left = 0,
249             .top = 0,
250             .right = mFb->windowWidth(),
251             .bottom = mFb->windowHeight(),
252         };
253         postLayerOptions.crop = {
254             .left = static_cast<float>(displayOffsetX),
255             .top = static_cast<float>(displayOffsetY + displayH),
256             .right = static_cast<float>(displayOffsetX + displayW),
257             .bottom = static_cast<float>(displayOffsetY),
258         };
259         postLayerOptions.transform = getTransformFromRotation(mFb->getZrot());
260 
261         post.layers.push_back(DisplayGl::PostLayer{
262             .colorBuffer = cb,
263             .layerOptions = postLayerOptions,
264         });
265     } else {
266         post.layers.push_back(postWithOverlay(cb));
267     }
268 
269     return m_displayGl->post(post);
270 }
271 
272 // Called whenever the subwindow needs a refresh (FrameBuffer::setupSubWindow).
273 // This rebinds the subwindow context (to account for
274 // when the refresh is a display change, for instance)
275 // and resets the posting viewport.
viewportImpl(int width,int height)276 void PostWorker::viewportImpl(int width, int height) {
277     if (m_displayVk) {
278         return;
279     }
280 
281     const float dpr = mFb->getDpr();
282     m_viewportWidth = width * dpr;
283     m_viewportHeight = height * dpr;
284 
285     if (!m_displayGl) {
286         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
287             << "PostWorker missing DisplayGl.";
288     }
289     m_displayGl->viewport(m_viewportWidth, m_viewportHeight);
290 }
291 
292 // Called when the subwindow refreshes, but there is no
293 // last posted color buffer to show to the user. Instead of
294 // displaying whatever happens to be in the back buffer,
295 // clear() is useful for outputting consistent colors.
clearImpl()296 void PostWorker::clearImpl() {
297     if (m_displayVk) {
298         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
299             << "PostWorker with Vulkan doesn't support clear";
300     }
301 
302     if (!m_displayGl) {
303         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
304             << "PostWorker missing DisplayGl.";
305     }
306     m_displayGl->clear();
307 }
308 
composeImpl(const FlatComposeRequest & composeRequest)309 std::shared_future<void> PostWorker::composeImpl(const FlatComposeRequest& composeRequest) {
310     std::shared_future<void> completedFuture =
311         std::async(std::launch::deferred, [] {}).share();
312     completedFuture.wait();
313 
314     if (!isComposeTargetReady(composeRequest.targetHandle)) {
315         ERR("The last composition on the target buffer hasn't completed.");
316     }
317 
318     Compositor::CompositionRequest compositorRequest = {};
319     compositorRequest.target = mFb->borrowColorBufferForComposition(composeRequest.targetHandle,
320                                                                     /*colorBufferIsTarget=*/true);
321     if (!compositorRequest.target) {
322         ERR("Compose target is null (cb=0x%x).", composeRequest.targetHandle);
323         return completedFuture;
324     }
325 
326     for (const ComposeLayer& guestLayer : composeRequest.layers) {
327         // Skip the ColorBuffer whose id is 0.
328         if (!guestLayer.cbHandle) {
329             continue;
330         }
331         auto source = mFb->borrowColorBufferForComposition(guestLayer.cbHandle,
332                                                            /*colorBufferIsTarget=*/false);
333         if (!source) {
334             continue;
335         }
336 
337         auto& compositorLayer = compositorRequest.layers.emplace_back();
338         compositorLayer.props = guestLayer;
339         compositorLayer.source = std::move(source);
340     }
341 
342     return m_compositor->compose(compositorRequest);
343 }
344 
screenshot(ColorBuffer * cb,int width,int height,GLenum format,GLenum type,int rotation,void * pixels,Rect rect)345 void PostWorker::screenshot(ColorBuffer* cb, int width, int height, GLenum format, GLenum type,
346                             int rotation, void* pixels, Rect rect) {
347     if (m_displayVk) {
348         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
349                             "Screenshot not supported with native Vulkan swapchain enabled.";
350     }
351     cb->readToBytesScaled(width, height, format, type, rotation, rect, pixels);
352 }
353 
block(std::promise<void> scheduledSignal,std::future<void> continueSignal)354 void PostWorker::block(std::promise<void> scheduledSignal, std::future<void> continueSignal) {
355     // Do not block mainthread.
356     if (m_mainThreadPostingOnly) {
357         return;
358     }
359     // MSVC STL doesn't support not copyable std::packaged_task. As a workaround, we use the
360     // copyable std::shared_ptr here.
361     auto block = std::make_shared<Post::Block>(Post::Block{
362         .scheduledSignal = std::move(scheduledSignal),
363         .continueSignal = std::move(continueSignal),
364     });
365     runTask(std::packaged_task<void()>([block] {
366         block->scheduledSignal.set_value();
367         block->continueSignal.wait();
368     }));
369 }
370 
~PostWorker()371 PostWorker::~PostWorker() {}
372 
post(ColorBuffer * cb,std::unique_ptr<Post::CompletionCallback> postCallback)373 void PostWorker::post(ColorBuffer* cb, std::unique_ptr<Post::CompletionCallback> postCallback) {
374     auto packagedPostCallback = std::shared_ptr<Post::CompletionCallback>(std::move(postCallback));
375     runTask(
376         std::packaged_task<void()>([cb, packagedPostCallback, this] {
377             auto completedFuture = postImpl(cb);
378             (*packagedPostCallback)(completedFuture);
379         }));
380 }
381 
viewport(int width,int height)382 void PostWorker::viewport(int width, int height) {
383     runTask(std::packaged_task<void()>(
384         [width, height, this] { viewportImpl(width, height); }));
385 }
386 
compose(std::unique_ptr<FlatComposeRequest> composeRequest,std::unique_ptr<Post::CompletionCallback> composeCallback)387 void PostWorker::compose(std::unique_ptr<FlatComposeRequest> composeRequest,
388                          std::unique_ptr<Post::CompletionCallback> composeCallback) {
389     // std::shared_ptr(std::move(...)) is WA for MSFT STL implementation bug:
390     // https://developercommunity.visualstudio.com/t/unable-to-move-stdpackaged-task-into-any-stl-conta/108672
391     auto packagedComposeCallback =
392         std::shared_ptr<Post::CompletionCallback>(std::move(composeCallback));
393     auto packagedComposeRequest = std::shared_ptr<FlatComposeRequest>(std::move(composeRequest));
394     runTask(
395         std::packaged_task<void()>([packagedComposeCallback, packagedComposeRequest, this] {
396         auto completedFuture = composeImpl(*packagedComposeRequest);
397         m_composeTargetToComposeFuture.emplace(packagedComposeRequest->targetHandle,
398                                                completedFuture);
399         (*packagedComposeCallback)(completedFuture);
400         }));
401 }
402 
clear()403 void PostWorker::clear() {
404     runTask(std::packaged_task<void()>([this] { clearImpl(); }));
405 }
406 
runTask(std::packaged_task<void ()> task)407 void PostWorker::runTask(std::packaged_task<void()> task) {
408     using Task = std::packaged_task<void()>;
409     auto taskPtr = std::make_unique<Task>(std::move(task));
410     if (m_mainThreadPostingOnly) {
411         m_runOnUiThread(
412             [](void* data) {
413                 std::unique_ptr<Task> taskPtr(reinterpret_cast<Task*>(data));
414                 (*taskPtr)();
415             },
416             taskPtr.release(), false);
417     } else {
418         (*taskPtr)();
419     }
420 }
421 
isComposeTargetReady(uint32_t targetHandle)422 bool PostWorker::isComposeTargetReady(uint32_t targetHandle) {
423     // Even if the target ColorBuffer has already been destroyed, the compose future should have
424     // been waited and set to the ready state.
425     for (auto i = m_composeTargetToComposeFuture.begin();
426          i != m_composeTargetToComposeFuture.end();) {
427         auto& composeFuture = i->second;
428         if (composeFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
429             i = m_composeTargetToComposeFuture.erase(i);
430         } else {
431             i++;
432         }
433     }
434     if (m_composeTargetToComposeFuture.find(targetHandle) == m_composeTargetToComposeFuture.end()) {
435         return true;
436     }
437     return false;
438 }
439 
440 }  // namespace gfxstream
441