void CCThreadProxy::setVisible(bool visible) { ASSERT(isMainThread()); CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setVisibleOnImplThread, AllowCrossThreadAccess(&completion), visible)); completion.wait(); }
bool CCThreadProxy::recreateContext() { TRACE_EVENT0("cc", "CCThreadProxy::recreateContext"); ASSERT(isMainThread()); // Try to create the context. OwnPtr<CCGraphicsContext> context = m_layerTreeHost->createContext(); if (!context) return false; if (m_layerTreeHost->needsSharedContext() && !m_layerTreeHost->settings().forceSoftwareCompositing) if (!SharedGraphicsContext3D::createForImplThread()) return false; // Make a blocking call to recreateContextOnImplThread. The results of that // call are pushed into the recreateSucceeded and capabilities local // variables. CCCompletionEvent completion; bool recreateSucceeded = false; LayerRendererCapabilities capabilities; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::recreateContextOnImplThread, AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(context.leakPtr()), AllowCrossThreadAccess(&recreateSucceeded), AllowCrossThreadAccess(&capabilities))); completion.wait(); if (recreateSucceeded) m_layerRendererCapabilitiesMainThreadCopy = capabilities; return recreateSucceeded; }
void CCThreadProxy::forceSerializeOnSwapBuffers() { DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::forceSerializeOnSwapBuffersOnImplThread, &completion)); completion.wait(); }
void CCThreadProxy::start() { // Create LayerTreeHostImpl. CCCompletionEvent completion; ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::initializeImplOnCCThread, AllowCrossThreadAccess(&completion))); completion.wait(); }
bool CCThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect) { TRACE_EVENT0("cc", "CCThreadPRoxy::compositeAndReadback"); ASSERT(isMainThread()); ASSERT(m_layerTreeHost); if (!m_layerTreeHost->initializeRendererIfNeeded()) { TRACE_EVENT0("cc", "compositeAndReadback_EarlyOut_LR_Uninitialized"); return false; } // Perform a synchronous commit. { DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent beginFrameCompletion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::forceBeginFrameOnImplThread, &beginFrameCompletion)); beginFrameCompletion.wait(); } m_inCompositeAndReadback = true; beginFrame(); m_inCompositeAndReadback = false; // Perform a synchronous readback. ReadbackRequest request; request.rect = rect; request.pixels = pixels; { DebugScopedSetMainThreadBlocked mainThreadBlocked; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::requestReadbackOnImplThread, &request)); request.completion.wait(); } return request.success; }
void CCThreadProxy::setVisible(bool visible) { TRACE_EVENT0("cc", "CCThreadProxy::setVisible"); CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setVisibleOnImplThread, &completion, visible)); completion.wait(); }
bool CCThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect) { TRACE_EVENT("CCThreadPRoxy::compositeAndReadback", this, 0); ASSERT(isMainThread()); ASSERT(m_layerTreeHost); if (!m_layerRendererInitialized) { TRACE_EVENT("compositeAndReadback_EarlyOut_LR_Uninitialized", this, 0); return false; } // Perform a synchronous commit. CCCompletionEvent beginFrameCompletion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::forceBeginFrameOnImplThread, AllowCrossThreadAccess(&beginFrameCompletion))); beginFrameCompletion.wait(); beginFrame(); // Perform a synchronous readback. ReadbackRequest request; request.rect = rect; request.pixels = pixels; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::requestReadbackOnImplThread, AllowCrossThreadAccess(&request))); request.completion.wait(); return request.success; }
bool CCThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect) { TRACE_EVENT("CCThreadPRoxy::compositeAndReadback", this, 0); ASSERT(isMainThread()); ASSERT(m_layerTreeHost); // If a commit is pending, perform the commit first. if (m_commitRequested) { // This bit of code is uglier than it should be because returning // pointers via the CCThread task model is really messy. Effectively, we // are making a blocking call to createBeginFrameAndCommitTaskOnImplThread, // and trying to get the CCMainThread::Task it returns so we can run it. OwnPtr<CCMainThread::Task> beginFrameAndCommitTask; { CCMainThread::Task* taskPtr = 0; CCCompletionEvent completion; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::obtainBeginFrameAndCommitTaskFromCCThread, AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(&taskPtr))); completion.wait(); beginFrameAndCommitTask = adoptPtr(taskPtr); } beginFrameAndCommitTask->performTask(); } // Draw using the new tree and read back the results. bool success = false; CCCompletionEvent completion; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::drawLayersAndReadbackOnImplThread, AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(&success), AllowCrossThreadAccess(pixels), rect)); completion.wait(); return success; }
bool CCThreadProxy::recreateContext() { TRACE_EVENT0("cc", "CCThreadProxy::recreateContext"); ASSERT(isMainThread()); // Try to create the context. OwnPtr<CCGraphicsContext> context = m_layerTreeHost->createContext(); if (!context) return false; if (m_layerTreeHost->needsSharedContext()) if (!WebSharedGraphicsContext3D::createCompositorThreadContext()) return false; // Make a blocking call to recreateContextOnImplThread. The results of that // call are pushed into the recreateSucceeded and capabilities local // variables. CCCompletionEvent completion; bool recreateSucceeded = false; RendererCapabilities capabilities; DebugScopedSetMainThreadBlocked mainThreadBlocked; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::recreateContextOnImplThread, &completion, context.leakPtr(), &recreateSucceeded, &capabilities)); completion.wait(); if (recreateSucceeded) m_RendererCapabilitiesMainThreadCopy = capabilities; return recreateSucceeded; }
bool CCThreadProxy::recreateContext() { TRACE_EVENT0("cc", "CCThreadProxy::recreateContext"); ASSERT(isMainThread()); // Try to create the context. RefPtr<GraphicsContext3D> context = m_layerTreeHost->createContext(); if (!context) return false; ASSERT(context->hasOneRef()); // Leak the context pointer so we can transfer ownership of it to the other side... GraphicsContext3D* contextPtr = context.release().leakRef(); ASSERT(contextPtr->hasOneRef()); // Make a blocking call to recreateContextOnImplThread. The results of that // call are pushed into the recreateSucceeded and capabilities local // variables. CCCompletionEvent completion; bool recreateSucceeded = false; LayerRendererCapabilities capabilities; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::recreateContextOnImplThread, AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(contextPtr), AllowCrossThreadAccess(&recreateSucceeded), AllowCrossThreadAccess(&capabilities))); completion.wait(); if (recreateSucceeded) m_layerRendererCapabilitiesMainThreadCopy = capabilities; return recreateSucceeded; }
bool CCThreadProxy::initializeLayerRenderer() { TRACE_EVENT("CCThreadProxy::initializeLayerRenderer", this, 0); RefPtr<GraphicsContext3D> context = m_layerTreeHost->createLayerTreeHostContext3D(); if (!context) return false; ASSERT(context->hasOneRef()); // Leak the context pointer so we can transfer ownership of it to the other side... GraphicsContext3D* contextPtr = context.release().leakRef(); ASSERT(contextPtr->hasOneRef()); // Make a blocking call to initializeLayerRendererOnImplThread. The results of that call // are pushed into the initializeSucceeded and capabilities local variables. CCCompletionEvent completion; bool initializeSucceeded = false; LayerRendererCapabilities capabilities; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::initializeLayerRendererOnImplThread, AllowCrossThreadAccess(contextPtr), AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(&initializeSucceeded), AllowCrossThreadAccess(&capabilities), AllowCrossThreadAccess(&m_compositorIdentifier))); completion.wait(); if (initializeSucceeded) m_layerRendererCapabilitiesMainThreadCopy = capabilities; return initializeSucceeded; }
void CCThreadProxy::finishAllRendering() { ASSERT(CCProxy::isMainThread()); // Make sure all GL drawing is finished on the impl thread. CCCompletionEvent completion; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::finishAllRenderingOnImplThread, AllowCrossThreadAccess(&completion))); completion.wait(); }
void CCThreadProxy::finishAllRendering() { ASSERT(CCProxy::isMainThread()); // Make sure all GL drawing is finished on the impl thread. DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::finishAllRenderingOnImplThread, &completion)); completion.wait(); }
void CCThreadProxy::implSideRenderingStats(CCRenderingStats& stats) { ASSERT(isMainThread()); CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::implSideRenderingStatsOnImplThread, &completion, &stats)); completion.wait(); }
CCThreadImpl::CCThreadImpl(WebThread* thread) : m_thread(thread) { // Get the threadId for the newly-created thread by running a task // on that thread, blocking on the result. m_threadID = currentThread(); CCCompletionEvent completion; m_thread->postTask(new GetThreadIDTask(&m_threadID, &completion)); completion.wait(); }
void CCThreadProxy::start() { ASSERT(isMainThread()); ASSERT(CCProxy::implThread()); // Create LayerTreeHostImpl. CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeImplOnImplThread, &completion)); completion.wait(); m_started = true; }
void CCThreadProxy::start() { ASSERT(isMainThread()); ASSERT(s_ccThread); // Create LayerTreeHostImpl. CCCompletionEvent completion; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::initializeImplOnImplThread, AllowCrossThreadAccess(&completion))); completion.wait(); m_started = true; }
void CCThreadProxy::stop() { TRACE_EVENT("CCThreadProxy::stop", this, 0); ASSERT(isMainThread()); // Synchronously deletes the impl. CCCompletionEvent completion; ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::layerTreeHostClosedOnCCThread, AllowCrossThreadAccess(&completion))); completion.wait(); ASSERT(!m_layerTreeHostImpl); // verify that the impl deleted. m_layerTreeHost = 0; }
void CCThreadProxy::start() { ASSERT(isMainThread()); ASSERT(CCProxy::implThread()); // Create LayerTreeHostImpl. DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent completion; OwnPtr<CCInputHandler> handler = m_layerTreeHost->createInputHandler(); CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeImplOnImplThread, &completion, handler.release())); completion.wait(); m_started = true; }
void CCThreadProxy::renderingStats(CCRenderingStats* stats) { ASSERT(isMainThread()); DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::renderingStatsOnImplThread, &completion, stats)); stats->totalCommitTimeInSeconds = m_totalCommitTime.InSecondsF(); stats->totalCommitCount = m_totalCommitCount; completion.wait(); }
void CCThreadProxy::beginFrameAndCommit(int sequenceNumber, double frameBeginTime, PassOwnPtr<CCScrollUpdateSet> scrollInfo) { TRACE_EVENT("CCThreadProxy::beginFrameAndCommit", this, 0); ASSERT(isMainThread()); if (!m_layerTreeHost) return; // Scroll deltas need to be applied even if the commit will be dropped. m_layerTreeHost->applyScrollDeltas(*scrollInfo.get()); // Drop beginFrameAndCommit calls that occur out of sequence. See createBeginFrameAndCommitTaskOnImplThread for // an explanation of how out-of-sequence beginFrameAndCommit tasks can occur. if (sequenceNumber < m_lastExecutedBeginFrameAndCommitSequenceNumber) { TRACE_EVENT("EarlyOut_StaleBeginFrameAndCommit", this, 0); return; } m_lastExecutedBeginFrameAndCommitSequenceNumber = sequenceNumber; // FIXME: recreate the context if it was requested by the impl thread { TRACE_EVENT("CCLayerTreeHost::animateAndLayout", this, 0); m_layerTreeHost->animateAndLayout(frameBeginTime); } ASSERT(m_lastExecutedBeginFrameAndCommitSequenceNumber == sequenceNumber); // Clear the commit flag after animateAndLayout here --- objects that only // layout when painted will trigger another setNeedsCommit inside // updateLayers. m_commitRequested = false; m_layerTreeHost->updateLayers(); { // Blocking call to CCThreadProxy::commitOnImplThread TRACE_EVENT("commit", this, 0); CCCompletionEvent completion; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::commitOnImplThread, AllowCrossThreadAccess(&completion))); completion.wait(); } m_layerTreeHost->commitComplete(); if (m_redrawAfterCommit) setNeedsRedraw(); m_redrawAfterCommit = false; ASSERT(m_lastExecutedBeginFrameAndCommitSequenceNumber == sequenceNumber); }
CCThreadImpl::CCThreadImpl(WebThread* thread) : m_thread(thread) { if (thread == webKitPlatformSupport()->currentThread()) { m_threadID = currentThread(); return; } // Get the threadId for the newly-created thread by running a task // on that thread, blocking on the result. m_threadID = currentThread(); CCCompletionEvent completion; m_thread->postTask(new GetThreadIDTask(&m_threadID, &completion)); completion.wait(); }
void CCThreadProxy::stop() { TRACE_EVENT("CCThreadProxy::stop", this, 0); ASSERT(isMainThread()); ASSERT(m_started); // Synchronously deletes the impl. CCCompletionEvent completion; s_ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::layerTreeHostClosedOnImplThread, AllowCrossThreadAccess(&completion))); completion.wait(); m_mainThreadProxy->shutdown(); // Stop running tasks posted to us. ASSERT(!m_layerTreeHostImpl); // verify that the impl deleted. m_layerTreeHost = 0; m_started = false; }
void CCThreadProxy::beginFrameAndCommit(double frameBeginTime) { ASSERT(isMainThread()); if (!m_layerTreeHost) return; TRACE_EVENT("CCThreadProxy::requestFrameAndCommit", this, 0); { TRACE_EVENT("CCLayerTreeHost::animateAndLayout", this, 0); m_layerTreeHost->animateAndLayout(frameBeginTime); } m_commitPending = false; // Blocking call to CCThreadProxy::performCommit CCCompletionEvent completion; ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::commitOnCCThread, AllowCrossThreadAccess(&completion))); completion.wait(); }
void CCThreadProxy::acquireLayerTextures() { // Called when the main thread needs to modify a layer texture that is used // directly by the compositor. // This method will block until the next compositor draw if there is a // previously committed frame that is still undrawn. This is necessary to // ensure that the main thread does not monopolize access to the textures. ASSERT(isMainThread()); if (m_texturesAcquired) return; TRACE_EVENT0("cc", "CCThreadProxy::acquireLayerTextures"); CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::acquireLayerTexturesForMainThreadOnImplThread, &completion)); completion.wait(); // Block until it is safe to write to layer textures from the main thread. m_texturesAcquired = true; }
bool CCThreadProxy::initializeLayerRenderer() { TRACE_EVENT("CCThreadProxy::initializeLayerRenderer", this, 0); // Make a blocking call to initializeLayerRendererOnImplThread. The results of that call // are pushed into the initializeSucceeded and capabilities local variables. CCCompletionEvent completion; bool initializeSucceeded = false; LayerRendererCapabilities capabilities; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeLayerRendererOnImplThread, AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(&initializeSucceeded), AllowCrossThreadAccess(&capabilities))); completion.wait(); if (initializeSucceeded) { m_layerRendererInitialized = true; m_layerRendererCapabilitiesMainThreadCopy = capabilities; } return initializeSucceeded; }
void CCThreadProxy::stop() { TRACE_EVENT0("cc", "CCThreadProxy::stop"); ASSERT(isMainThread()); ASSERT(m_started); // Synchronously deletes the impl. { DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::layerTreeHostClosedOnImplThread, &completion)); completion.wait(); } m_mainThreadProxy->shutdown(); // Stop running tasks posted to us. ASSERT(!m_layerTreeHostImpl); // verify that the impl deleted. m_layerTreeHost = 0; m_started = false; }
bool CCThreadProxy::initializeRenderer() { TRACE_EVENT0("cc", "CCThreadProxy::initializeRenderer"); // Make a blocking call to initializeRendererOnImplThread. The results of that call // are pushed into the initializeSucceeded and capabilities local variables. CCCompletionEvent completion; bool initializeSucceeded = false; RendererCapabilities capabilities; DebugScopedSetMainThreadBlocked mainThreadBlocked; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeRendererOnImplThread, &completion, &initializeSucceeded, &capabilities)); completion.wait(); if (initializeSucceeded) { m_rendererInitialized = true; m_RendererCapabilitiesMainThreadCopy = capabilities; } return initializeSucceeded; }
void CCThreadProxy::beginFrame() { TRACE_EVENT0("cc", "CCThreadProxy::beginFrame"); ASSERT(isMainThread()); if (!m_layerTreeHost) return; if (!m_pendingBeginFrameRequest) { TRACE_EVENT0("cc", "EarlyOut_StaleBeginFrameMessage"); return; } if (m_layerTreeHost->needsSharedContext() && !WebSharedGraphicsContext3D::haveCompositorThreadContext()) WebSharedGraphicsContext3D::createCompositorThreadContext(); OwnPtr<BeginFrameAndCommitState> request(m_pendingBeginFrameRequest.release()); // Do not notify the impl thread of commit requests that occur during // the apply/animate/layout part of the beginFrameAndCommit process since // those commit requests will get painted immediately. Once we have done // the paint, m_commitRequested will be set to false to allow new commit // requests to be scheduled. m_commitRequested = true; m_commitRequestSentToImplThread = true; // On the other hand, the animationRequested flag needs to be cleared // here so that any animation requests generated by the apply or animate // callbacks will trigger another frame. m_animateRequested = false; // FIXME: technically, scroll deltas need to be applied for dropped commits as well. // Re-do the commit flow so that we don't send the scrollInfo on the BFAC message. m_layerTreeHost->applyScrollAndScale(*request->scrollInfo); if (!m_inCompositeAndReadback && !m_layerTreeHost->visible()) { m_commitRequested = false; m_commitRequestSentToImplThread = false; m_forcedCommitRequested = false; TRACE_EVENT0("cc", "EarlyOut_NotVisible"); CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::beginFrameAbortedOnImplThread)); return; } m_layerTreeHost->willBeginFrame(); m_layerTreeHost->updateAnimations(request->monotonicFrameBeginTime); m_layerTreeHost->layout(); // Clear the commit flag after updating animations and layout here --- objects that only // layout when painted will trigger another setNeedsCommit inside // updateLayers. m_commitRequested = false; m_commitRequestSentToImplThread = false; m_forcedCommitRequested = false; if (!m_layerTreeHost->initializeRendererIfNeeded()) { TRACE_EVENT0("cc", "EarlyOut_InitializeFailed"); return; } if (request->contentsTexturesWereDeleted) m_layerTreeHost->evictAllContentTextures(); OwnPtr<CCTextureUpdateQueue> queue = adoptPtr(new CCTextureUpdateQueue); m_layerTreeHost->updateLayers(*(queue.get()), request->memoryAllocationLimitBytes); // Once single buffered layers are committed, they cannot be modified until // they are drawn by the impl thread. m_texturesAcquired = false; m_layerTreeHost->willCommit(); // Before applying scrolls and calling animate, we set m_animateRequested to // false. If it is true now, it means setNeedAnimate was called again, but // during a state when m_commitRequestSentToImplThread = true. We need to // force that call to happen again now so that the commit request is sent to // the impl thread. if (m_animateRequested) { // Forces setNeedsAnimate to consider posting a commit task. m_animateRequested = false; setNeedsAnimate(); } // Notify the impl thread that the beginFrame has completed. This will // begin the commit process, which is blocking from the main thread's // point of view, but asynchronously performed on the impl thread, // coordinated by the CCScheduler. { TRACE_EVENT0("cc", "commit"); DebugScopedSetMainThreadBlocked mainThreadBlocked; CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::beginFrameCompleteOnImplThread, &completion, queue.release(), request->contentsTexturesWereDeleted)); completion.wait(); } m_layerTreeHost->commitComplete(); m_layerTreeHost->didBeginFrame(); }
void CCThreadProxy::beginFrame() { TRACE_EVENT0("cc", "CCThreadProxy::beginFrame"); ASSERT(isMainThread()); if (!m_layerTreeHost) return; if (!m_pendingBeginFrameRequest) { TRACE_EVENT0("cc", "EarlyOut_StaleBeginFrameMessage"); return; } OwnPtr<BeginFrameAndCommitState> request(m_pendingBeginFrameRequest.release()); // Do not notify the impl thread of commit requests that occur during // the apply/animate/layout part of the beginFrameAndCommit process since // those commit requests will get painted immediately. Once we have done // the paint, m_commitRequested will be set to false to allow new commit // requests to be scheduled. m_commitRequested = true; // On the other hand, the animationRequested flag needs to be cleared // here so that any animation requests generated by the apply or animate // callbacks will trigger another frame. m_animateRequested = false; // FIXME: technically, scroll deltas need to be applied for dropped commits as well. // Re-do the commit flow so that we don't send the scrollInfo on the BFAC message. m_layerTreeHost->applyScrollAndScale(*request->scrollInfo); // FIXME: recreate the context if it was requested by the impl thread. m_layerTreeHost->updateAnimations(request->frameBeginTime); m_layerTreeHost->layout(); // Clear the commit flag after updating animations and layout here --- objects that only // layout when painted will trigger another setNeedsCommit inside // updateLayers. m_commitRequested = false; if (!m_layerTreeHost->updateLayers()) return; // Before applying scrolls and calling animate, we set m_animateRequested to false. // If it is true now, it means setNeedAnimate was called again. Call setNeedsCommit // now so that we get begin frame when this one is done. if (m_animateRequested) setNeedsCommit(); // Notify the impl thread that the beginFrame has completed. This will // begin the commit process, which is blocking from the main thread's // point of view, but asynchronously performed on the impl thread, // coordinated by the CCScheduler. { TRACE_EVENT("commit", this, 0); CCCompletionEvent completion; CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::beginFrameCompleteOnImplThread, AllowCrossThreadAccess(&completion))); completion.wait(); } m_layerTreeHost->commitComplete(); }