• 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 "Debug.h"
24 #include "DispatchTables.h"
25 #include "FrameBuffer.h"
26 #include "OpenGLESDispatch/EGLDispatch.h"
27 #include "OpenGLESDispatch/GLESv2Dispatch.h"
28 #include "RenderThreadInfo.h"
29 #include "base/Tracing.h"
30 #include "host-common/GfxstreamFatalError.h"
31 #include "host-common/logging.h"
32 #include "host-common/misc.h"
33 #include "vulkan/VkCommonOperations.h"
34 
35 using emugl::ABORT_REASON_OTHER;
36 using emugl::FatalError;
37 
38 #define POST_DEBUG 0
39 #if POST_DEBUG >= 1
40 #define DD(fmt, ...) \
41     fprintf(stderr, "%s:%d| " fmt, __func__, __LINE__, ##__VA_ARGS__)
42 #else
43 #define DD(fmt, ...) (void)0
44 #endif
45 
46 #define POST_ERROR(fmt, ...)                                                  \
47     do {                                                                      \
48         fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, \
49                 ##__VA_ARGS__);                                               \
50         fflush(stderr);                                                       \
51     } while (0)
52 
sDefaultRunOnUiThread(UiUpdateFunc f,void * data,bool wait)53 static void sDefaultRunOnUiThread(UiUpdateFunc f, void* data, bool wait) {
54     (void)f;
55     (void)data;
56     (void)wait;
57 }
58 
PostWorker(PostWorker::BindSubwinCallback && cb,bool mainThreadPostingOnly,EGLContext eglContext,EGLSurface,DisplayVk * displayVk)59 PostWorker::PostWorker(PostWorker::BindSubwinCallback&& cb, bool mainThreadPostingOnly,
60                        EGLContext eglContext, EGLSurface,
61                        DisplayVk* displayVk)
62     : mFb(FrameBuffer::getFB()),
63       mBindSubwin(cb),
64       m_mainThreadPostingOnly(mainThreadPostingOnly),
65       m_runOnUiThread(m_mainThreadPostingOnly ? emugl::get_emugl_window_operations().runOnUiThread
66                                               : sDefaultRunOnUiThread),
67       mContext(eglContext),
68       m_displayVk(displayVk) {}
69 
fillMultiDisplayPostStruct(ComposeLayer * l,hwc_rect_t displayArea,hwc_frect_t cropArea,hwc_transform_t transform)70 void PostWorker::fillMultiDisplayPostStruct(ComposeLayer* l,
71                                             hwc_rect_t displayArea,
72                                             hwc_frect_t cropArea,
73                                             hwc_transform_t transform) {
74     l->composeMode = HWC2_COMPOSITION_DEVICE;
75     l->blendMode = HWC2_BLEND_MODE_NONE;
76     l->transform = transform;
77     l->alpha = 1.0;
78     l->displayFrame = displayArea;
79     l->crop = cropArea;
80 }
81 
postImpl(ColorBuffer * cb)82 void PostWorker::postImpl(ColorBuffer* cb) {
83     // bind the subwindow eglSurface
84     if (!m_mainThreadPostingOnly && m_needsToRebindWindow) {
85         m_needsToRebindWindow = !mBindSubwin();
86         if (m_needsToRebindWindow) {
87             // Do not proceed if fail to bind to the window.
88             return;
89         }
90     }
91 
92     if (m_displayVk) {
93         bool shouldSkip = m_lastVkComposeColorBuffer == cb->getHndl();
94         m_lastVkComposeColorBuffer = std::nullopt;
95         if (shouldSkip) {
96             return;
97         }
98         goldfish_vk::acquireColorBuffersForHostComposing({}, cb->getHndl());
99         auto [success, waitForGpu] = m_displayVk->post(cb->getDisplayBufferVk());
100         goldfish_vk::releaseColorBufferFromHostComposing({cb->getHndl()});
101         if (success) {
102             waitForGpu.wait();
103         } else {
104             m_needsToRebindWindow = true;
105         }
106         return;
107     }
108 
109     float dpr = mFb->getDpr();
110     int windowWidth = mFb->windowWidth();
111     int windowHeight = mFb->windowHeight();
112     float px = mFb->getPx();
113     float py = mFb->getPy();
114     int zRot = mFb->getZrot();
115     hwc_transform_t rotation = (hwc_transform_t)0;
116 
117     cb->waitSync();
118 
119     // Find the x and y values at the origin when "fully scrolled."
120     // Multiply by 2 because the texture goes from -1 to 1, not 0 to 1.
121     // Multiply the windowing coordinates by DPR because they ignore
122     // DPR, but the viewport includes DPR.
123     float fx = 2.f * (m_viewportWidth  - windowWidth  * dpr) / (float)m_viewportWidth;
124     float fy = 2.f * (m_viewportHeight - windowHeight * dpr) / (float)m_viewportHeight;
125 
126     // finally, compute translation values
127     float dx = px * fx;
128     float dy = py * fy;
129 
130     if (emugl::get_emugl_multi_display_operations().isMultiDisplayEnabled()) {
131         uint32_t combinedW, combinedH;
132         emugl::get_emugl_multi_display_operations().getCombinedDisplaySize(&combinedW, &combinedH);
133         mFb->getTextureDraw()->prepareForDrawLayer();
134         int32_t start_id = -1, x, y;
135         uint32_t id, w, h, c;
136         while(emugl::get_emugl_multi_display_operations().getNextMultiDisplay(start_id, &id,
137                                                                               &x, &y, &w, &h,
138                                                                               nullptr, nullptr,
139                                                                               &c)) {
140             if ((id != 0) && (w == 0 || h == 0 || c == 0)) {
141                 start_id = id;
142                 continue;
143             }
144             ColorBuffer* multiDisplayCb = id == 0 ? cb : mFb->findColorBuffer(c).get();
145             if (multiDisplayCb == nullptr) {
146                 start_id = id;
147                 continue;
148             }
149             ComposeLayer l;
150             hwc_rect_t displayArea = { .left = (int)x,
151                                        .top = (int)y,
152                                        .right = (int)(x + w),
153                                        .bottom = (int)(y + h) };
154             hwc_frect_t cropArea = { .left = 0.0,
155                                      .top = (float)multiDisplayCb->getHeight(),
156                                      .right = (float)multiDisplayCb->getWidth(),
157                                      .bottom = 0.0 };
158             fillMultiDisplayPostStruct(&l, displayArea, cropArea, rotation);
159             multiDisplayCb->postLayer(&l, combinedW, combinedH);
160             start_id = id;
161         }
162         mFb->getTextureDraw()->cleanupForDrawLayer();
163     }
164     else if (emugl::get_emugl_window_operations().isFolded()) {
165         mFb->getTextureDraw()->prepareForDrawLayer();
166         ComposeLayer l;
167         int x, y, w, h;
168         emugl::get_emugl_window_operations().getFoldedArea(&x, &y, &w, &h);
169         hwc_rect_t displayArea = { .left = 0,
170                                    .top = 0,
171                                    .right = windowWidth,
172                                    .bottom = windowHeight };
173         hwc_frect_t cropArea = { .left = (float)x,
174                                  .top = (float)(y + h),
175                                  .right = (float)(x + w),
176                                  .bottom = (float)y };
177         switch ((int)zRot/90) {
178             case 1:
179                 rotation = HWC_TRANSFORM_ROT_270;
180                 break;
181             case 2:
182                 rotation = HWC_TRANSFORM_ROT_180;
183                 break;
184             case 3:
185                 rotation = HWC_TRANSFORM_ROT_90;
186                 break;
187             default: ;
188         }
189 
190         fillMultiDisplayPostStruct(&l, displayArea, cropArea, rotation);
191         cb->postLayer(&l, m_viewportWidth/dpr, m_viewportHeight/dpr);
192         mFb->getTextureDraw()->cleanupForDrawLayer();
193     }
194     else {
195         // render the color buffer to the window and apply the overlay
196         GLuint tex = cb->scale();
197         cb->postWithOverlay(tex, zRot, dx, dy);
198     }
199 
200     s_egl.eglSwapBuffers(mFb->getDisplay(), mFb->getWindowSurface());
201 }
202 
203 // Called whenever the subwindow needs a refresh (FrameBuffer::setupSubWindow).
204 // This rebinds the subwindow context (to account for
205 // when the refresh is a display change, for instance)
206 // and resets the posting viewport.
viewportImpl(int width,int height)207 void PostWorker::viewportImpl(int width, int height) {
208     if (!m_mainThreadPostingOnly) {
209         // For GLES, we rebind the subwindow eglSurface unconditionally: this
210         // could be from a display change, but we want to avoid binding
211         // VkSurfaceKHR too frequently, because that's too expensive.
212         if (!m_displayVk || m_needsToRebindWindow) {
213             m_needsToRebindWindow = !mBindSubwin();
214             if (m_needsToRebindWindow) {
215                 // Do not proceed if fail to bind to the window.
216                 return;
217             }
218         }
219     }
220 
221     if (m_displayVk) {
222         return;
223     }
224 
225     float dpr = mFb->getDpr();
226     m_viewportWidth = width * dpr;
227     m_viewportHeight = height * dpr;
228     s_gles2.glViewport(0, 0, m_viewportWidth, m_viewportHeight);
229 }
230 
231 // Called when the subwindow refreshes, but there is no
232 // last posted color buffer to show to the user. Instead of
233 // displaying whatever happens to be in the back buffer,
234 // clear() is useful for outputting consistent colors.
clearImpl()235 void PostWorker::clearImpl() {
236     if (m_displayVk) {
237         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
238             << "PostWorker with Vulkan doesn't support clear";
239     }
240 #ifndef __linux__
241     s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
242                     GL_STENCIL_BUFFER_BIT);
243     s_egl.eglSwapBuffers(mFb->getDisplay(), mFb->getWindowSurface());
244 #endif
245 }
246 
composeImpl(const ComposeDevice * p)247 void PostWorker::composeImpl(const ComposeDevice* p) {
248     if (m_displayVk) {
249         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
250             << "PostWorker with Vulkan doesn't support ComposeV1";
251     }
252     // bind the subwindow eglSurface
253     if (!m_mainThreadPostingOnly && m_needsToRebindWindow) {
254         m_needsToRebindWindow = !mBindSubwin();
255         if (m_needsToRebindWindow) {
256             // Do not proceed if fail to bind to the window.
257             return;
258         }
259     }
260 
261     GLint vport[4] = { 0, };
262 
263     auto cbPtr = mFb->findColorBuffer(p->targetHandle);
264     if (!cbPtr) {
265         s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
266         s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
267         return;
268     }
269 
270     GL_SCOPED_DEBUG_GROUP("PostWorker::composeImpl(into ColorBuffer{hndl:%d tex:%d})", cbPtr->getHndl(), cbPtr->getTexture());
271 
272     ComposeLayer* l = (ComposeLayer*)p->layer;
273 
274     s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
275     s_gles2.glViewport(0, 0, mFb->getWidth(),mFb->getHeight());
276     if (!m_composeFbo) {
277         s_gles2.glGenFramebuffers(1, &m_composeFbo);
278     }
279     s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, m_composeFbo);
280     s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER,
281                                    GL_COLOR_ATTACHMENT0_OES,
282                                    GL_TEXTURE_2D,
283                                    cbPtr->getTexture(),
284                                    0);
285 
286     DD("worker compose %d layers\n", p->numLayers);
287     mFb->getTextureDraw()->prepareForDrawLayer();
288     for (int i = 0; i < p->numLayers; i++, l++) {
289         DD("\tcomposeMode %d color %d %d %d %d blendMode "
290                "%d alpha %f transform %d %d %d %d %d "
291                "%f %f %f %f\n",
292                l->composeMode, l->color.r, l->color.g, l->color.b,
293                l->color.a, l->blendMode, l->alpha, l->transform,
294                l->displayFrame.left, l->displayFrame.top,
295                l->displayFrame.right, l->displayFrame.bottom,
296                l->crop.left, l->crop.top, l->crop.right,
297                l->crop.bottom);
298         glesComposeLayer(l, mFb->getWidth(), mFb->getHeight());
299     }
300 
301     cbPtr->setSync();
302 
303     s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
304     s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
305     mFb->getTextureDraw()->cleanupForDrawLayer();
306 }
307 
composev2Impl(const ComposeDevice_v2 * p)308 std::shared_future<void> PostWorker::composev2Impl(const ComposeDevice_v2* p) {
309     std::shared_future<void> completedFuture =
310         std::async(std::launch::deferred, [] {}).share();
311     completedFuture.wait();
312     // bind the subwindow eglSurface
313     if (!m_mainThreadPostingOnly && m_needsToRebindWindow) {
314         m_needsToRebindWindow = !mBindSubwin();
315         if (m_needsToRebindWindow) {
316             // Do not proceed if fail to bind to the window.
317             return completedFuture;
318         }
319     }
320     ComposeLayer* l = (ComposeLayer*)p->layer;
321     auto targetColorBufferPtr = mFb->findColorBuffer(p->targetHandle);
322 
323     if (m_displayVk) {
324         if (!targetColorBufferPtr) {
325             GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
326                                 "Failed to retrieve the composition target buffer";
327         }
328         // We don't copy the render result to the targetHandle color buffer
329         // when using the Vulkan native host swapchain, because we directly
330         // render to the swapchain image instead of rendering onto a
331         // ColorBuffer, and we don't readback from the ColorBuffer so far.
332         std::vector<ColorBufferPtr> cbs;  // Keep ColorBuffers alive
333         cbs.emplace_back(targetColorBufferPtr);
334         std::vector<std::shared_ptr<DisplayVk::DisplayBufferInfo>>
335             composeBuffers;
336         std::vector<uint32_t> layerColorBufferHandles;
337         for (int i = 0; i < p->numLayers; ++i) {
338             auto colorBufferPtr = mFb->findColorBuffer(l[i].cbHandle);
339             if (!colorBufferPtr) {
340                 composeBuffers.push_back(nullptr);
341                 continue;
342             }
343             auto db = colorBufferPtr->getDisplayBufferVk();
344             composeBuffers.push_back(db);
345             if (!db) {
346                 continue;
347             }
348             cbs.push_back(colorBufferPtr);
349             layerColorBufferHandles.emplace_back(l[i].cbHandle);
350         }
351         goldfish_vk::acquireColorBuffersForHostComposing(layerColorBufferHandles, p->targetHandle);
352         auto [success, waitForGpu] = m_displayVk->compose(
353             p->numLayers, l, composeBuffers, targetColorBufferPtr->getDisplayBufferVk());
354         goldfish_vk::setColorBufferCurrentLayout(p->targetHandle,
355                                                  VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
356         std::vector<uint32_t> colorBufferHandles(layerColorBufferHandles.begin(),
357                                                  layerColorBufferHandles.end());
358         colorBufferHandles.emplace_back(p->targetHandle);
359         goldfish_vk::releaseColorBufferFromHostComposing(colorBufferHandles);
360         if (!success) {
361             m_needsToRebindWindow = true;
362             waitForGpu = completedFuture;
363         }
364         m_lastVkComposeColorBuffer = p->targetHandle;
365         return waitForGpu;
366     }
367 
368     GLint vport[4] = { 0, };
369     s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
370     uint32_t w, h;
371     emugl::get_emugl_multi_display_operations().getMultiDisplay(p->displayId,
372                                                                 nullptr,
373                                                                 nullptr,
374                                                                 &w,
375                                                                 &h,
376                                                                 nullptr,
377                                                                 nullptr,
378                                                                 nullptr);
379     s_gles2.glViewport(0, 0, w, h);
380     if (!m_composeFbo) {
381         s_gles2.glGenFramebuffers(1, &m_composeFbo);
382     }
383     s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, m_composeFbo);
384 
385     if (!targetColorBufferPtr) {
386         s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
387         s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
388         return completedFuture;
389     }
390 
391     s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES,
392                                    GL_TEXTURE_2D,
393                                    targetColorBufferPtr->getTexture(), 0);
394 
395     DD("worker compose %d layers\n", p->numLayers);
396     mFb->getTextureDraw()->prepareForDrawLayer();
397     for (int i = 0; i < p->numLayers; i++, l++) {
398         DD("\tcomposeMode %d color %d %d %d %d blendMode "
399                "%d alpha %f transform %d %d %d %d %d "
400                "%f %f %f %f\n",
401                l->composeMode, l->color.r, l->color.g, l->color.b,
402                l->color.a, l->blendMode, l->alpha, l->transform,
403                l->displayFrame.left, l->displayFrame.top,
404                l->displayFrame.right, l->displayFrame.bottom,
405                l->crop.left, l->crop.top, l->crop.right,
406                l->crop.bottom);
407         glesComposeLayer(l, w, h);
408     }
409 
410     targetColorBufferPtr->setSync();
411     s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
412     s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
413     mFb->getTextureDraw()->cleanupForDrawLayer();
414     return completedFuture;
415 }
416 
bind()417 void PostWorker::bind() {
418     if (m_mainThreadPostingOnly && !m_displayVk) {
419         if (mFb->getDisplay() != EGL_NO_DISPLAY) {
420             EGLint res = s_egl.eglMakeCurrent(mFb->getDisplay(), mFb->getWindowSurface(), mFb->getWindowSurface(), mContext);
421             if (!res) fprintf(stderr, "%s: error in binding: 0x%x\n", __func__, s_egl.eglGetError());
422         } else {
423             fprintf(stderr, "%s: no display!\n", __func__);
424         }
425     } else {
426         mBindSubwin();
427     }
428 }
429 
unbind()430 void PostWorker::unbind() {
431     if (m_displayVk) {
432         return;
433     }
434     if (mFb->getDisplay() != EGL_NO_DISPLAY) {
435         s_egl.eglMakeCurrent(mFb->getDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE,
436                              EGL_NO_CONTEXT);
437     }
438 }
439 
glesComposeLayer(ComposeLayer * l,uint32_t w,uint32_t h)440 void PostWorker::glesComposeLayer(ComposeLayer* l, uint32_t w, uint32_t h) {
441     if (m_displayVk) {
442         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
443                             "Should not reach with native vulkan swapchain enabled.";
444     }
445     if (l->composeMode == HWC2_COMPOSITION_DEVICE) {
446         ColorBufferPtr cb = mFb->findColorBuffer(l->cbHandle);
447         if (!cb) {
448             // bad colorbuffer handle
449             // ERR("%s: fail to find colorbuffer %d\n", __FUNCTION__, l->cbHandle);
450             return;
451         }
452 
453         GL_SCOPED_DEBUG_GROUP("PostWorker::glesComposeLayer(layer ColorBuffer{hndl:%d tex:%d})", cb->getHndl(), cb->getTexture());
454         cb->postLayer(l, w, h);
455     }
456     else {
457         // no Colorbuffer associated with SOLID_COLOR mode
458         mFb->getTextureDraw()->drawLayer(l, w, h, 1, 1, 0);
459     }
460 }
461 
screenshot(ColorBuffer * cb,int width,int height,GLenum format,GLenum type,int rotation,void * pixels)462 void PostWorker::screenshot(
463     ColorBuffer* cb,
464     int width,
465     int height,
466     GLenum format,
467     GLenum type,
468     int rotation,
469     void* pixels) {
470     if (m_displayVk) {
471         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
472                             "Screenshot not supported with native Vulkan swapchain enabled.";
473     }
474     cb->readPixelsScaled(
475         width, height, format, type, rotation, pixels);
476 }
477 
~PostWorker()478 PostWorker::~PostWorker() {
479     if (mFb->getDisplay() != EGL_NO_DISPLAY) {
480         s_egl.eglMakeCurrent(mFb->getDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE,
481                              EGL_NO_CONTEXT);
482     }
483 }
484 
post(ColorBuffer * cb)485 void PostWorker::post(ColorBuffer* cb) {
486     runTask(std::packaged_task<void()>([cb, this] { postImpl(cb); }));
487 }
488 
viewport(int width,int height)489 void PostWorker::viewport(int width, int height) {
490     runTask(std::packaged_task<void()>(
491         [width, height, this] { viewportImpl(width, height); }));
492 }
493 
compose(ComposeDevice * p,uint32_t bufferSize,std::shared_ptr<Post::ComposeCallback> callback)494 void PostWorker::compose(ComposeDevice* p, uint32_t bufferSize,
495                          std::shared_ptr<Post::ComposeCallback> callback) {
496     std::vector<char> buffer(bufferSize, 0);
497     memcpy(buffer.data(), p, bufferSize);
498     runTask(std::packaged_task<void()>([buffer = std::move(buffer),
499                                         callback = std::move(callback), this] {
500         auto completedFuture = std::async(std::launch::deferred, [] {}).share();
501         auto composeDevice =
502             reinterpret_cast<const ComposeDevice*>(buffer.data());
503         if (!isComposeTargetReady(composeDevice->targetHandle)) {
504             ERR("The last composition on the target buffer hasn't completed.");
505         }
506         composeImpl(composeDevice);
507         m_composeTargetToComposeFuture.emplace(composeDevice->targetHandle, completedFuture);
508         (*callback)(completedFuture);
509     }));
510 }
511 
compose(ComposeDevice_v2 * p,uint32_t bufferSize,std::shared_ptr<Post::ComposeCallback> callback)512 void PostWorker::compose(ComposeDevice_v2* p, uint32_t bufferSize,
513                          std::shared_ptr<Post::ComposeCallback> callback) {
514     std::vector<char> buffer(bufferSize, 0);
515     memcpy(buffer.data(), p, bufferSize);
516     runTask(std::packaged_task<void()>([buffer = std::move(buffer),
517                                         callback = std::move(callback), this] {
518         auto composeDevice =
519             reinterpret_cast<const ComposeDevice_v2*>(buffer.data());
520         if (!isComposeTargetReady(composeDevice->targetHandle)) {
521             ERR("The last composition on the target buffer hasn't completed.");
522         }
523         auto completedFuture = composev2Impl(composeDevice);
524         m_composeTargetToComposeFuture.emplace(composeDevice->targetHandle, completedFuture);
525         (*callback)(completedFuture);
526     }));
527 }
528 
clear()529 void PostWorker::clear() {
530     runTask(std::packaged_task<void()>([this] { clearImpl(); }));
531 }
532 
runTask(std::packaged_task<void ()> task)533 void PostWorker::runTask(std::packaged_task<void()> task) {
534     using Task = std::packaged_task<void()>;
535     auto taskPtr = std::make_unique<Task>(std::move(task));
536     if (m_mainThreadPostingOnly) {
537         m_runOnUiThread(
538             [](void* data) {
539                 std::unique_ptr<Task> taskPtr(reinterpret_cast<Task*>(data));
540                 (*taskPtr)();
541             },
542             taskPtr.release(), false);
543     } else {
544         (*taskPtr)();
545     }
546 }
547 
isComposeTargetReady(uint32_t targetHandle)548 bool PostWorker::isComposeTargetReady(uint32_t targetHandle) {
549     // Even if the target ColorBuffer has already been destroyed, the compose future should have
550     // been waited and set to the ready state.
551     for (auto i = m_composeTargetToComposeFuture.begin();
552          i != m_composeTargetToComposeFuture.end();) {
553         auto& composeFuture = i->second;
554         if (composeFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
555             i = m_composeTargetToComposeFuture.erase(i);
556         } else {
557             i++;
558         }
559     }
560     if (m_composeTargetToComposeFuture.find(targetHandle) == m_composeTargetToComposeFuture.end()) {
561         return true;
562     }
563     return false;
564 }
565