From 6adb00f70a9067fff0cf85854f56a0301b3d4eaf Mon Sep 17 00:00:00 2001 From: "jamesr@google.com" Date: Tue, 27 Sep 2011 02:18:14 +0000 Subject: [PATCH] [chromium] Make CCThreadProxy draw https://bugs.webkit.org/show_bug.cgi?id=67417 Source/WebCore: Update the CCThreadProxy to correctly implement the CCProxy interface, do all the right committing and updating steps, and draw a picture on the screen. Patch by Nat Duca on 2011-09-26 Reviewed by James Robinson. * platform/graphics/IntRect.h: * platform/graphics/chromium/LayerRendererChromium.cpp: (WebCore::LayerRendererChromium::~LayerRendererChromium): * platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp: (WebCore::CCHeadsUpDisplay::enabled): * platform/graphics/chromium/cc/CCLayerImpl.cpp: (WebCore::CCLayerImpl::CCLayerImpl): (WebCore::CCLayerImpl::~CCLayerImpl): * platform/graphics/chromium/cc/CCLayerTreeHost.cpp: (WebCore::CCLayerTreeHost::CCLayerTreeHost): (WebCore::CCLayerTreeHost::commitTo): (WebCore::CCLayerTreeHost::commitComplete): (WebCore::CCLayerTreeHost::setNeedsRedraw): * platform/graphics/chromium/cc/CCLayerTreeHost.h: * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp: (WebCore::CCLayerTreeHostImpl::CCLayerTreeHostImpl): (WebCore::CCLayerTreeHostImpl::~CCLayerTreeHostImpl): * platform/graphics/chromium/cc/CCSingleThreadProxy.cpp: (WebCore::CCSingleThreadProxy::finishAllRendering): (WebCore::CCSingleThreadProxy::setNeedsCommit): (WebCore::CCSingleThreadProxy::commitIfNeeded): * platform/graphics/chromium/cc/CCThreadProxy.cpp: (WebCore::CCThreadProxy::CCThreadProxy): (WebCore::CCThreadProxy::~CCThreadProxy): (WebCore::CCThreadProxy::compositeAndReadback): (WebCore::CCThreadProxy::drawLayersAndReadbackOnCCThread): (WebCore::CCThreadProxy::finishAllRendering): (WebCore::CCThreadProxy::isStarted): (WebCore::CCThreadProxy::setNeedsCommit): (WebCore::CCThreadProxy::setNeedsCommitAndRedraw): (WebCore::CCThreadProxy::setNeedsRedraw): (WebCore::CCThreadProxy::start): (WebCore::CCThreadProxy::stop): (WebCore::CCThreadProxy::finishAllRenderingOnCCThread): (WebCore::CCThreadProxy::createBeginFrameAndCommitTaskOnCCThread): (WebCore::CCThreadProxy::beginFrameAndCommit): (WebCore::CCThreadProxy::commitOnCCThread): (WebCore::CCThreadProxy::scheduleDrawTaskOnCCThread): (WebCore::CCThreadProxy::drawLayersAndPresentOnCCThread): (WebCore::CCThreadProxy::drawLayersOnCCThread): (WebCore::CCThreadProxy::updateSchedulerStateOnCCThread): * platform/graphics/chromium/cc/CCThreadProxy.h: Source/WebKit/chromium: Disable CCLayerTreeHostTest temporarily. Will re-enable with https://bugs.webkit.org/show_bug.cgi?id=67418 Patch by Nat Duca on 2011-09-26 Reviewed by James Robinson. * tests/CCLayerTreeHostTest.cpp: git-svn-id: http://svn.webkit.org/repository/webkit/trunk@96066 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebCore/ChangeLog | 54 +++++ Source/WebCore/platform/CrossThreadCopier.h | 6 + .../graphics/chromium/LayerRendererChromium.cpp | 2 + .../graphics/chromium/cc/CCHeadsUpDisplay.cpp | 3 + .../platform/graphics/chromium/cc/CCLayerImpl.cpp | 2 + .../graphics/chromium/cc/CCLayerTreeHost.cpp | 19 +- .../graphics/chromium/cc/CCLayerTreeHost.h | 1 + .../graphics/chromium/cc/CCLayerTreeHostImpl.cpp | 2 + .../platform/graphics/chromium/cc/CCProxy.cpp | 9 +- .../platform/graphics/chromium/cc/CCProxy.h | 13 +- .../graphics/chromium/cc/CCSingleThreadProxy.cpp | 51 +++-- .../graphics/chromium/cc/CCSingleThreadProxy.h | 1 + .../graphics/chromium/cc/CCThreadProxy.cpp | 250 ++++++++++++++++----- .../platform/graphics/chromium/cc/CCThreadProxy.h | 25 ++- Source/WebKit/chromium/ChangeLog | 12 + .../WebKit/chromium/tests/CCLayerTreeHostTest.cpp | 21 +- .../WebKit/chromium/tests/TreeSynchronizerTest.cpp | 23 ++ 17 files changed, 401 insertions(+), 93 deletions(-) diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 0888813..9223660 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,57 @@ +2011-09-26 Nat Duca + + [chromium] Make CCThreadProxy draw + https://bugs.webkit.org/show_bug.cgi?id=67417 + + Update the CCThreadProxy to correctly implement the CCProxy + interface, do all the right committing and updating steps, and + draw a picture on the screen. + + Reviewed by James Robinson. + + * platform/graphics/IntRect.h: + * platform/graphics/chromium/LayerRendererChromium.cpp: + (WebCore::LayerRendererChromium::~LayerRendererChromium): + * platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp: + (WebCore::CCHeadsUpDisplay::enabled): + * platform/graphics/chromium/cc/CCLayerImpl.cpp: + (WebCore::CCLayerImpl::CCLayerImpl): + (WebCore::CCLayerImpl::~CCLayerImpl): + * platform/graphics/chromium/cc/CCLayerTreeHost.cpp: + (WebCore::CCLayerTreeHost::CCLayerTreeHost): + (WebCore::CCLayerTreeHost::commitTo): + (WebCore::CCLayerTreeHost::commitComplete): + (WebCore::CCLayerTreeHost::setNeedsRedraw): + * platform/graphics/chromium/cc/CCLayerTreeHost.h: + * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp: + (WebCore::CCLayerTreeHostImpl::CCLayerTreeHostImpl): + (WebCore::CCLayerTreeHostImpl::~CCLayerTreeHostImpl): + * platform/graphics/chromium/cc/CCSingleThreadProxy.cpp: + (WebCore::CCSingleThreadProxy::finishAllRendering): + (WebCore::CCSingleThreadProxy::setNeedsCommit): + (WebCore::CCSingleThreadProxy::commitIfNeeded): + * platform/graphics/chromium/cc/CCThreadProxy.cpp: + (WebCore::CCThreadProxy::CCThreadProxy): + (WebCore::CCThreadProxy::~CCThreadProxy): + (WebCore::CCThreadProxy::compositeAndReadback): + (WebCore::CCThreadProxy::drawLayersAndReadbackOnCCThread): + (WebCore::CCThreadProxy::finishAllRendering): + (WebCore::CCThreadProxy::isStarted): + (WebCore::CCThreadProxy::setNeedsCommit): + (WebCore::CCThreadProxy::setNeedsCommitAndRedraw): + (WebCore::CCThreadProxy::setNeedsRedraw): + (WebCore::CCThreadProxy::start): + (WebCore::CCThreadProxy::stop): + (WebCore::CCThreadProxy::finishAllRenderingOnCCThread): + (WebCore::CCThreadProxy::createBeginFrameAndCommitTaskOnCCThread): + (WebCore::CCThreadProxy::beginFrameAndCommit): + (WebCore::CCThreadProxy::commitOnCCThread): + (WebCore::CCThreadProxy::scheduleDrawTaskOnCCThread): + (WebCore::CCThreadProxy::drawLayersAndPresentOnCCThread): + (WebCore::CCThreadProxy::drawLayersOnCCThread): + (WebCore::CCThreadProxy::updateSchedulerStateOnCCThread): + * platform/graphics/chromium/cc/CCThreadProxy.h: + 2011-09-26 Adam Klein [MutationObservers] implement MutationRecord diff --git a/Source/WebCore/platform/CrossThreadCopier.h b/Source/WebCore/platform/CrossThreadCopier.h index 7ce7f98..39d11b5 100644 --- a/Source/WebCore/platform/CrossThreadCopier.h +++ b/Source/WebCore/platform/CrossThreadCopier.h @@ -41,6 +41,7 @@ namespace WebCore { + class IntRect; class KURL; class ResourceError; class ResourceRequest; @@ -63,9 +64,14 @@ namespace WebCore { template struct CrossThreadCopierBase : public CrossThreadCopierPassThrough { }; + // To allow a type to be passed across threads using its copy constructor, add a forward declaration of the type and + // a CopyThreadCopierBase : public CrossThreadCopierPassThrough { }; to this file. template<> struct CrossThreadCopierBase : public CrossThreadCopierPassThrough { }; + template<> struct CrossThreadCopierBase : public CrossThreadCopierPassThrough { + }; + // Custom copy methods. template struct CrossThreadCopierBase { typedef typename WTF::RemoveTemplate::Type TypeWithoutRefPtr; diff --git a/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp index ea139d4..4058889 100644 --- a/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp @@ -181,6 +181,8 @@ bool LayerRendererChromium::initialize() LayerRendererChromium::~LayerRendererChromium() { + ASSERT(CCProxy::isImplThread +()); m_headsUpDisplay.clear(); // Explicitly destroy the HUD before the TextureManager dies. cleanupSharedObjects(); } diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp index 30aa82c..dc27148 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp @@ -89,6 +89,9 @@ void CCHeadsUpDisplay::onPresent() bool CCHeadsUpDisplay::enabled() const { + // FIXME: HUD does not work in compositor thread mode. + if (settings().enableCompositorThread) + return false; return settings().showPlatformLayerTree || settings().showFPSCounter; } diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCLayerImpl.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCLayerImpl.cpp index c986591..64168c2 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCLayerImpl.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCLayerImpl.cpp @@ -55,10 +55,12 @@ CCLayerImpl::CCLayerImpl(int id) , m_debugBorderColor(0, 0, 0, 0) , m_debugBorderWidth(0) { + ASSERT(CCProxy::isImplThread()); } CCLayerImpl::~CCLayerImpl() { + ASSERT(CCProxy::isImplThread()); } void CCLayerImpl::addChild(PassRefPtr child) diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp index 4104678..a25bbe6 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp @@ -55,6 +55,7 @@ CCLayerTreeHost::CCLayerTreeHost(CCLayerTreeHostClient* client, PassRefPtrdeleteEvictedTextures(hostImpl->context()); updateCompositorResources(m_updateList, hostImpl->context()); - clearPendingUpdate(); hostImpl->setVisible(m_visible); hostImpl->setZoomAnimatorTransform(m_zoomAnimatorTransform); hostImpl->setViewport(viewportSize()); hostImpl->layerRenderer()->setContentsTextureMemoryUseBytes(m_contentsTextureManager->currentMemoryUseBytes()); - m_contentsTextureManager->unprotectAllTextures(); // Synchronize trees, if one exists at all... if (rootLayer()) @@ -133,6 +141,12 @@ void CCLayerTreeHost::commitTo(CCLayerTreeHostImpl* hostImpl) m_frameNumber++; } +void CCLayerTreeHost::commitComplete() +{ + clearPendingUpdate(); + m_contentsTextureManager->unprotectAllTextures(); +} + PassOwnPtr CCLayerTreeHost::createCompositorThread() { return m_client->createCompositorThread(); @@ -209,7 +223,6 @@ void CCLayerTreeHost::setNeedsCommitAndRedraw() void CCLayerTreeHost::setNeedsRedraw() { #if USE(THREADED_COMPOSITING) - TRACE_EVENT("CCLayerTreeHost::setNeedsRedraw", this, 0); m_proxy->setNeedsRedraw(); #else m_client->scheduleComposite(); diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.h b/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.h index b16285a..e35968f 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.h +++ b/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.h @@ -97,6 +97,7 @@ public: // CCLayerTreeHost interface to CCProxy. void animateAndLayout(double frameBeginTime); + void commitComplete(); void commitTo(CCLayerTreeHostImpl*); PassOwnPtr createCompositorThread(); PassRefPtr createLayerTreeHostContext3D(); diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp index ae1c5db..5961216 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp @@ -48,10 +48,12 @@ CCLayerTreeHostImpl::CCLayerTreeHostImpl(const CCSettings& settings) , m_frameNumber(0) , m_settings(settings) { + ASSERT(CCProxy::isImplThread()); } CCLayerTreeHostImpl::~CCLayerTreeHostImpl() { + ASSERT(CCProxy::isImplThread()); TRACE_EVENT("CCLayerTreeHostImpl::~CCLayerTreeHostImpl()", this, 0); if (m_layerRenderer) m_layerRenderer->close(); diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCProxy.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCProxy.cpp index d61a2e5..b7eccec 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCProxy.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCProxy.cpp @@ -37,16 +37,17 @@ using namespace WTF; namespace WebCore { #ifndef NDEBUG -bool CCProxy::isMainThread() -{ - return ::isMainThread(); -} namespace { bool fakeImplThread = false; static WTF::ThreadIdentifier implThreadID; } +bool CCProxy::isMainThread() +{ + return ::isMainThread() && !fakeImplThread; +} + bool CCProxy::isImplThread() { return fakeImplThread || currentThread() == implThreadID; diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCProxy.h b/Source/WebCore/platform/graphics/chromium/cc/CCProxy.h index f5e418b..b222b99 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCProxy.h +++ b/Source/WebCore/platform/graphics/chromium/cc/CCProxy.h @@ -75,19 +75,20 @@ public: static bool isImplThread(); #endif - // Testing hooks - virtual void loseCompositorContext(int numTimes) = 0; - // Temporary hack while render_widget still does scheduling for CCLayerTreeHostMainThreadI virtual GraphicsContext3D* context() = 0; -protected: - CCProxy() { } - friend class ScopedSetImplThread; + // Testing hooks + virtual void loseCompositorContext(int numTimes) = 0; + #ifndef NDEBUG static void setImplThread(bool); static void setImplThread(WTF::ThreadIdentifier); #endif + +protected: + CCProxy() { } + friend class ScopedSetImplThread; }; } diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp index 2d27d72..9acdc64 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp @@ -42,6 +42,7 @@ public: ScopedSetImplThread() { #ifndef NDEBUG + ASSERT(CCProxy::isMainThread()); CCProxy::setImplThread(true); #endif } @@ -65,7 +66,7 @@ CCSingleThreadProxy::CCSingleThreadProxy(CCLayerTreeHost* layerTreeHost) , m_timesRecreateShouldFail(0) { TRACE_EVENT("CCSingleThreadProxy::CCSingleThreadProxy", this, 0); - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); } void CCSingleThreadProxy::start() @@ -77,13 +78,13 @@ void CCSingleThreadProxy::start() CCSingleThreadProxy::~CCSingleThreadProxy() { TRACE_EVENT("CCSingleThreadProxy::~CCSingleThreadProxy", this, 0); - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); ASSERT(!m_layerTreeHostImpl && !m_layerTreeHost); // make sure stop() got called. } bool CCSingleThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect) { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); if (!recreateContextIfNeeded()) return false; @@ -103,27 +104,29 @@ bool CCSingleThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect GraphicsContext3D* CCSingleThreadProxy::context() { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); ScopedSetImplThread impl; return m_layerTreeHostImpl->context(); } void CCSingleThreadProxy::finishAllRendering() { - ASSERT(isMainThread()); - ScopedSetImplThread impl; - m_layerTreeHostImpl->finishAllRendering(); + ASSERT(CCProxy::isMainThread()); + { + ScopedSetImplThread impl; + m_layerTreeHostImpl->finishAllRendering(); + } } bool CCSingleThreadProxy::isStarted() const { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); return m_layerTreeHostImpl; } bool CCSingleThreadProxy::initializeLayerRenderer() { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); RefPtr context = m_layerTreeHost->createLayerTreeHostContext3D(); if (!context) return false; @@ -131,14 +134,17 @@ bool CCSingleThreadProxy::initializeLayerRenderer() { ScopedSetImplThread impl; - return m_layerTreeHostImpl->initializeLayerRenderer(context); + bool ok = m_layerTreeHostImpl->initializeLayerRenderer(context); + if (ok) + m_layerRendererCapabilitiesForMainThread = m_layerTreeHostImpl->layerRendererCapabilities(); + return ok; } } const LayerRendererCapabilities& CCSingleThreadProxy::layerRendererCapabilities() const { - ScopedSetImplThread impl; - return m_layerTreeHostImpl->layerRendererCapabilities(); + // Note: this gets called during the commit by the "impl" thread + return m_layerRendererCapabilitiesForMainThread; } void CCSingleThreadProxy::loseCompositorContext(int numTimes) @@ -149,7 +155,7 @@ void CCSingleThreadProxy::loseCompositorContext(int numTimes) void CCSingleThreadProxy::setNeedsCommit() { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); // Commit immediately { ScopedSetImplThread impl; @@ -157,11 +163,12 @@ void CCSingleThreadProxy::setNeedsCommit() m_layerTreeHost->commitTo(m_layerTreeHostImpl.get()); m_layerTreeHostImpl->commitComplete(); } + m_layerTreeHost->commitComplete(); } void CCSingleThreadProxy::setNeedsCommitAndRedraw() { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); #if !USE(THREADED_COMPOSITING) m_layerTreeHost->scheduleComposite(); #else @@ -180,7 +187,7 @@ void CCSingleThreadProxy::setNeedsRedraw() void CCSingleThreadProxy::stop() { TRACE_EVENT("CCSingleThreadProxy::stop", this, 0); - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); { ScopedSetImplThread impl; m_layerTreeHost->deleteContentsTextures(m_layerTreeHostImpl->context()); @@ -206,7 +213,7 @@ void CCSingleThreadProxy::compositeImmediately() bool CCSingleThreadProxy::recreateContextIfNeeded() { - ASSERT(isMainThread()); + ASSERT(CCProxy::isMainThread()); if (!m_graphicsContextLost) return true; RefPtr context; @@ -217,7 +224,14 @@ bool CCSingleThreadProxy::recreateContextIfNeeded() if (context) { ASSERT(context->hasOneRef()); - if (m_layerTreeHostImpl->initializeLayerRenderer(context)) { + bool ok; + { + ScopedSetImplThread impl; + ok = m_layerTreeHostImpl->initializeLayerRenderer(context); + if (ok) + m_layerRendererCapabilitiesForMainThread = m_layerTreeHostImpl->layerRendererCapabilities(); + } + if (ok) { m_layerTreeHost->didRecreateGraphicsContext(true); m_graphicsContextLost = false; return true; @@ -240,6 +254,8 @@ bool CCSingleThreadProxy::recreateContextIfNeeded() void CCSingleThreadProxy::commitIfNeeded() { + ASSERT(CCProxy::isMainThread()); + // Update m_layerTreeHost->updateLayers(); @@ -250,6 +266,7 @@ void CCSingleThreadProxy::commitIfNeeded() m_layerTreeHost->commitTo(m_layerTreeHostImpl.get()); m_layerTreeHostImpl->commitComplete(); } + m_layerTreeHost->commitComplete(); } bool CCSingleThreadProxy::doComposite() diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.h b/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.h index bbbe5f5..c18e7f3 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.h +++ b/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.h @@ -69,6 +69,7 @@ private: // Used on the CCThread, but checked on main thread during initialization/shutdown. OwnPtr m_layerTreeHostImpl; + LayerRendererCapabilities m_layerRendererCapabilitiesForMainThread; int m_numFailedRecreateAttempts; bool m_graphicsContextLost; diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp index 9a84da6..a1e71da 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp +++ b/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp @@ -31,6 +31,7 @@ #include "cc/CCLayerTreeHost.h" #include "cc/CCMainThreadTask.h" #include "cc/CCThreadTask.h" +#include #include using namespace WTF; @@ -48,8 +49,14 @@ PassOwnPtr CCThreadProxy::create(CCLayerTreeHost* layerTreeHost) } CCThreadProxy::CCThreadProxy(CCLayerTreeHost* layerTreeHost) - : m_commitPending(false) + : m_commitRequested(false) , m_layerTreeHost(layerTreeHost) + , m_started(false) + , m_lastExecutedBeginFrameAndCommitSequenceNumber(-1) + , m_numBeginFrameAndCommitsIssuedOnCCThread(0) + , m_beginFrameAndCommitPendingOnCCThread(false) + , m_drawTaskPostedOnCCThread(false) + , m_redrawRequestedOnCCThread(false) { TRACE_EVENT("CCThreadProxy::CCThreadProxy", this, 0); ASSERT(isMainThread()); @@ -66,8 +73,7 @@ CCThreadProxy::~CCThreadProxy() { TRACE_EVENT("CCThreadProxy::~CCThreadProxy", this, 0); ASSERT(isMainThread()); - ASSERT(!m_layerTreeHostImpl); // Make sure stop() got called. - ASSERT(!m_layerTreeHost); // Make sure stop() got called. + ASSERT(!m_started); numProxies--; if (!numProxies) { @@ -78,8 +84,47 @@ CCThreadProxy::~CCThreadProxy() bool CCThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect) { - ASSERT_NOT_REACHED(); - return false; + 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 createBeginFrameAndCommitTaskOnCCThread, + // and trying to get the CCMainThread::Task it returns so we can run it. + OwnPtr beginFrameAndCommitTask; + { + CCMainThread::Task* taskPtr = 0; + CCCompletionEvent completion; + 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; + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::drawLayersAndReadbackOnCCThread, AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(&success), AllowCrossThreadAccess(pixels), rect)); + completion.wait(); + return success; +} + +void CCThreadProxy::drawLayersAndReadbackOnCCThread(CCCompletionEvent* completion, bool* success, void* pixels, const IntRect& rect) +{ + ASSERT(CCProxy::isImplThread()); + if (!m_layerTreeHostImpl) { + *success = false; + completion->signal(); + return; + } + drawLayersOnCCThread(); + m_layerTreeHostImpl->readback(pixels, rect); + *success = m_layerTreeHostImpl->isContextLost(); + completion->signal(); } GraphicsContext3D* CCThreadProxy::context() @@ -89,12 +134,18 @@ GraphicsContext3D* CCThreadProxy::context() void CCThreadProxy::finishAllRendering() { - ASSERT_NOT_REACHED(); + ASSERT(CCProxy::isMainThread()); + + // Make sure all GL drawing is finished on the impl thread. + CCCompletionEvent completion; + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::finishAllRenderingOnCCThread, AllowCrossThreadAccess(&completion))); + completion.wait(); } bool CCThreadProxy::isStarted() const { - return m_layerTreeHostImpl; + ASSERT(CCProxy::isMainThread()); + return m_started; } bool CCThreadProxy::initializeLayerRenderer() @@ -111,7 +162,7 @@ bool CCThreadProxy::initializeLayerRenderer() // Make a blocking call to initializeLayerRendererOnCCThread. The results of that call // are pushed into the initializeSucceeded and capabilities local variables. CCCompletionEvent completion; - bool initializeSucceeded; + bool initializeSucceeded = false; LayerRendererCapabilities capabilities; ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::initializeLayerRendererOnCCThread, AllowCrossThreadAccess(contextPtr), AllowCrossThreadAccess(&completion), AllowCrossThreadAccess(&initializeSucceeded), AllowCrossThreadAccess(&capabilities))); @@ -135,43 +186,57 @@ void CCThreadProxy::loseCompositorContext(int numTimes) void CCThreadProxy::setNeedsCommit() { ASSERT(isMainThread()); - if (m_commitPending) + if (m_commitRequested) return; TRACE_EVENT("CCThreadProxy::setNeedsCommit", this, 0); - m_commitPending = true; - ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::setNeedsCommitOnCCThread)); + m_commitRequested = true; + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::updateSchedulerStateOnCCThread, m_commitRequested, true)); } void CCThreadProxy::setNeedsCommitAndRedraw() { ASSERT(isMainThread()); - if (m_commitPending) + if (m_commitRequested) return; + m_commitRequested = true; TRACE_EVENT("CCThreadProxy::setNeedsCommitAndRedraw", this, 0); - m_commitPending = true; - ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::setNeedsCommitAndRedrawOnCCThread)); + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::updateSchedulerStateOnCCThread, m_commitRequested, true)); } void CCThreadProxy::setNeedsRedraw() { ASSERT(isMainThread()); - ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::setNeedsRedrawOnCCThread)); + if (m_commitRequested) // Implies that a commit is in flight. + return; + // Unlike setNeedsCommit that tracks whether a commit message has been sent, + // setNeedsRedraw always sends a message to the compositor thread. This is + // because the compositor thread can draw without telling the main + // thread. This should not be much of a problem because calls to + // setNeedsRedraw messages are uncommon (only triggered by WM_PAINT/etc), + // compared to setNeedsCommitAndRedraw messages. + TRACE_EVENT("CCThreadProxy::setNeedsRedraw", this, 0); + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::updateSchedulerStateOnCCThread, false, true)); } void CCThreadProxy::start() { + ASSERT(isMainThread()); // Create LayerTreeHostImpl. CCCompletionEvent completion; ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::initializeImplOnCCThread, AllowCrossThreadAccess(&completion))); completion.wait(); + + m_started = true; } void CCThreadProxy::stop() { TRACE_EVENT("CCThreadProxy::stop", this, 0); ASSERT(isMainThread()); + ASSERT(m_started); + // Synchronously deletes the impl. CCCompletionEvent completion; ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::layerTreeHostClosedOnCCThread, AllowCrossThreadAccess(&completion))); @@ -179,82 +244,167 @@ void CCThreadProxy::stop() ASSERT(!m_layerTreeHostImpl); // verify that the impl deleted. m_layerTreeHost = 0; + m_started = false; } -void CCThreadProxy::beginFrameAndCommitOnCCThread() +void CCThreadProxy::finishAllRenderingOnCCThread(CCCompletionEvent* completion) { - TRACE_EVENT("CCThreadProxy::beginFrameAndCommitOnCCThread", this, 0); + TRACE_EVENT("CCThreadProxy::finishAllRenderingOnCCThread", this, 0); + ASSERT(isImplThread()); + ASSERT(!m_beginFrameAndCommitPendingOnCCThread); + if (m_redrawRequestedOnCCThread) { + drawLayersOnCCThread(); + m_layerTreeHostImpl->present(); + m_redrawRequestedOnCCThread = false; + } + m_layerTreeHostImpl->finishAllRendering(); + completion->signal(); +} + +void CCThreadProxy::obtainBeginFrameAndCommitTaskFromCCThread(CCCompletionEvent* completion, CCMainThread::Task** taskPtr) +{ + OwnPtr task = createBeginFrameAndCommitTaskOnCCThread(); + *taskPtr = task.leakPtr(); + completion->signal(); +} + +PassOwnPtr CCThreadProxy::createBeginFrameAndCommitTaskOnCCThread() +{ + TRACE_EVENT("CCThreadProxy::createBeginFrameAndCommitTaskOnCCThread", this, 0); ASSERT(isImplThread()); - // TEMP HACK so we can exercise this code in unit tests. - CCMainThread::postTask(createMainThreadTask(this, &CCThreadProxy::beginFrameAndCommit, 0.0)); + double frameBeginTime = currentTime(); + m_beginFrameAndCommitPendingOnCCThread = true; + + // NOTE, it is possible to receieve a request for a + // beginFrameAndCommitOnCCThread from finishAllRendering while a + // beginFrameAndCommitOnCCThread is enqueued. Since CCMainThread doesn't + // provide a threadsafe way to cancel tasks, it is important that + // beginFrameAndCommit be structured to understand that it may get called at + // a point that it shouldn't. We do this by assigning a sequence number to + // every new beginFrameAndCommit task. Then, beginFrameAndCommit tracks the + // last executed sequence number, dropping beginFrameAndCommit with sequence + // numbers below the last executed one. + int thisTaskSequenceNumber = m_numBeginFrameAndCommitsIssuedOnCCThread; + m_numBeginFrameAndCommitsIssuedOnCCThread++; + return createMainThreadTask(this, &CCThreadProxy::beginFrameAndCommit, thisTaskSequenceNumber, frameBeginTime); } -void CCThreadProxy::beginFrameAndCommit(double frameBeginTime) +void CCThreadProxy::beginFrameAndCommit(int sequenceNumber, double frameBeginTime) { + TRACE_EVENT("CCThreadProxy::beginFrameAndCommit", this, 0); ASSERT(isMainThread()); if (!m_layerTreeHost) return; - TRACE_EVENT("CCThreadProxy::requestFrameAndCommit", this, 0); + // Drop beginFrameAndCommit calls that occur out of sequence. See createBeginFrameAndCommitTaskOnCCThread 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; + + ASSERT(m_commitRequested); + + // FIXME: recreate the context if it was requested by the impl thread { TRACE_EVENT("CCLayerTreeHost::animateAndLayout", this, 0); m_layerTreeHost->animateAndLayout(frameBeginTime); } - m_commitPending = false; + ASSERT(m_lastExecutedBeginFrameAndCommitSequenceNumber == sequenceNumber); - // Blocking call to CCThreadProxy::performCommit - CCCompletionEvent completion; - ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::commitOnCCThread, AllowCrossThreadAccess(&completion))); - completion.wait(); + // 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::commitOnCCThread + TRACE_EVENT("commit", this, 0); + CCCompletionEvent completion; + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::commitOnCCThread, AllowCrossThreadAccess(&completion))); + completion.wait(); + } + + m_layerTreeHost->commitComplete(); + + ASSERT(m_lastExecutedBeginFrameAndCommitSequenceNumber == sequenceNumber); } void CCThreadProxy::commitOnCCThread(CCCompletionEvent* completion) { + TRACE_EVENT("CCThreadProxy::beginFrameAndCommitOnCCThread", this, 0); ASSERT(isImplThread()); - TRACE_EVENT("CCThreadProxy::commitOnCCThread", this, 0); - m_layerTreeHostImpl->beginCommit(); - { - TRACE_EVENT("CCLayerTreeHost::commit", this, 0); - m_layerTreeHost->commitTo(m_layerTreeHostImpl.get()); + ASSERT(m_beginFrameAndCommitPendingOnCCThread); + m_beginFrameAndCommitPendingOnCCThread = false; + if (!m_layerTreeHostImpl) { + completion->signal(); + return; } + m_layerTreeHostImpl->beginCommit(); + m_layerTreeHost->commitTo(m_layerTreeHostImpl.get()); + m_layerTreeHostImpl->commitComplete(); + completion->signal(); - m_layerTreeHostImpl->commitComplete(); - setNeedsRedrawOnCCThread(); + if (m_redrawRequestedOnCCThread) + scheduleDrawTaskOnCCThread(); } -void CCThreadProxy::drawLayersOnCCThread() +void CCThreadProxy::scheduleDrawTaskOnCCThread() { - TRACE_EVENT("CCThreadProxy::drawLayersOnCCThread", this, 0); ASSERT(isImplThread()); - if (m_layerTreeHostImpl) - m_layerTreeHostImpl->drawLayers(); + if (m_drawTaskPostedOnCCThread) + return; + TRACE_EVENT("CCThreadProxy::scheduleDrawTaskOnCCThread", this, 0); + ASSERT(m_layerTreeHostImpl); + m_drawTaskPostedOnCCThread = true; + ccThread->postTask(createCCThreadTask(this, &CCThreadProxy::drawLayersAndPresentOnCCThread)); } -void CCThreadProxy::setNeedsCommitOnCCThread() +void CCThreadProxy::drawLayersAndPresentOnCCThread() { - TRACE_EVENT("CCThreadProxy::setNeedsCommitOnCCThread", this, 0); + TRACE_EVENT("CCThreadProxy::drawLayersOnCCThread", this, 0); ASSERT(isImplThread()); - ASSERT(m_layerTreeHostImpl); - // FIXME: Not yet implemented, see https://bugs.webkit.org/show_bug.cgi?id=67417 - ASSERT_NOT_REACHED(); + if (!m_layerTreeHostImpl) + return; + + drawLayersOnCCThread(); + m_layerTreeHostImpl->present(); + m_redrawRequestedOnCCThread = false; + m_drawTaskPostedOnCCThread = false; } -void CCThreadProxy::setNeedsCommitAndRedrawOnCCThread() +void CCThreadProxy::drawLayersOnCCThread() { - TRACE_EVENT("CCThreadProxy::setNeedsCommitAndRedrawOnCCThread", this, 0); + TRACE_EVENT("CCThreadProxy::drawLayersOnCCThread", this, 0); ASSERT(isImplThread()); ASSERT(m_layerTreeHostImpl); - // TEMP HACK so we can exercise this code in unit tests. - CCMainThread::postTask(createMainThreadTask(this, &CCThreadProxy::beginFrameAndCommit, 0.0)); + + m_layerTreeHostImpl->drawLayers(); + ASSERT(!m_layerTreeHostImpl->isContextLost()); } -void CCThreadProxy::setNeedsRedrawOnCCThread() +void CCThreadProxy::updateSchedulerStateOnCCThread(bool commitRequested, bool redrawRequested) { - TRACE_EVENT("CCThreadProxy::setNeedsRedrawOnCCThread", this, 0); - // TEMP HACK so we can exercise this code in unit tests. - drawLayersOnCCThread(); + TRACE_EVENT("CCThreadProxy::updateSchedulerStateOnCCThread", this, 0); + ASSERT(isImplThread()); + ASSERT(m_layerTreeHostImpl); + + // FIXME: use CCScheduler to decide when to manage the conversion of this + // commit request into an actual createBeginFrameAndCommitTaskOnCCThread call. + m_redrawRequestedOnCCThread |= redrawRequested; + if (!m_beginFrameAndCommitPendingOnCCThread) { + CCMainThread::postTask(createBeginFrameAndCommitTaskOnCCThread()); + return; + } + + // If no commit is pending, but a redraw is requested, then post a redraw right away + if (m_redrawRequestedOnCCThread) + scheduleDrawTaskOnCCThread(); } void CCThreadProxy::initializeImplOnCCThread(CCCompletionEvent* completion) diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.h b/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.h index 2467724..d3b487f 100644 --- a/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.h +++ b/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.h @@ -27,6 +27,7 @@ #include "cc/CCCompletionEvent.h" #include "cc/CCLayerTreeHostImpl.h" +#include "cc/CCMainThread.h" #include "cc/CCProxy.h" #include @@ -58,28 +59,36 @@ private: explicit CCThreadProxy(CCLayerTreeHost*); // Called on CCMainThread - void beginFrameAndCommit(double frameBeginTime); + void beginFrameAndCommit(int sequenceNumber, double frameBeginTime); // Called on CCThread - void beginFrameAndCommitOnCCThread(); + PassOwnPtr createBeginFrameAndCommitTaskOnCCThread(); + void obtainBeginFrameAndCommitTaskFromCCThread(CCCompletionEvent*, CCMainThread::Task**); void commitOnCCThread(CCCompletionEvent*); + void drawLayersAndPresentOnCCThread(); void drawLayersOnCCThread(); + void drawLayersAndReadbackOnCCThread(CCCompletionEvent*, bool* success, void* pixels, const IntRect&); + void finishAllRenderingOnCCThread(CCCompletionEvent*); void initializeImplOnCCThread(CCCompletionEvent*); void initializeLayerRendererOnCCThread(GraphicsContext3D*, CCCompletionEvent*, bool* initializeSucceeded, LayerRendererCapabilities*); void setNeedsCommitOnCCThread(); - void setNeedsCommitAndRedrawOnCCThread(); - void setNeedsRedrawOnCCThread(); + void updateSchedulerStateOnCCThread(bool commitRequested, bool redrawRequested); void layerTreeHostClosedOnCCThread(CCCompletionEvent*); - - // Used on main-thread only. - bool m_commitPending; + void scheduleDrawTaskOnCCThread(); // Accessed on main thread only. + bool m_commitRequested; CCLayerTreeHost* m_layerTreeHost; LayerRendererCapabilities m_layerRendererCapabilitiesMainThreadCopy; + bool m_started; + int m_lastExecutedBeginFrameAndCommitSequenceNumber; - // Used on the CCThread, but checked on main thread during initialization/shutdown. + // Used on the CCThread only OwnPtr m_layerTreeHostImpl; + int m_numBeginFrameAndCommitsIssuedOnCCThread; + bool m_beginFrameAndCommitPendingOnCCThread; + bool m_drawTaskPostedOnCCThread; + bool m_redrawRequestedOnCCThread; }; } diff --git a/Source/WebKit/chromium/ChangeLog b/Source/WebKit/chromium/ChangeLog index 96aff24..b84992a 100644 --- a/Source/WebKit/chromium/ChangeLog +++ b/Source/WebKit/chromium/ChangeLog @@ -1,3 +1,15 @@ +2011-09-26 Nat Duca + + [chromium] Make CCThreadProxy draw + https://bugs.webkit.org/show_bug.cgi?id=67417 + + Disable CCLayerTreeHostTest temporarily. Will re-enable + with https://bugs.webkit.org/show_bug.cgi?id=67418 + + Reviewed by James Robinson. + + * tests/CCLayerTreeHostTest.cpp: + 2011-09-26 Joshua Bell IndexedDB: Second half of IDBFactory.getDatabaseNames implementation diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp index ff4ffbe..334050d 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp @@ -170,6 +170,10 @@ public: { } +#if !USE(THREADED_COMPOSITING) + virtual void scheduleComposite() { } +#endif + private: explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { } @@ -208,7 +212,14 @@ protected: : m_beginning(false) , m_endWhenBeginReturns(false) , m_running(false) - , m_timedOut(false) { } + , m_timedOut(false) + { +#if USE(THREADED_COMPOSITING) + m_settings.enableCompositorThread = true; +#else + m_settings.enableCompositorThread = false; +#endif + } void doBeginTest(); @@ -270,6 +281,7 @@ protected: test->endTest(); } + CCSettings m_settings; OwnPtr m_client; RefPtr m_layerTreeHost; @@ -287,10 +299,8 @@ void CCLayerTreeHostTest::doBeginTest() m_running = true; m_client = MockLayerTreeHostClient::create(this); - CCSettings settings; - settings.enableCompositorThread = true; RefPtr rootLayer; - m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, settings); + m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings); ASSERT(m_layerTreeHost); m_beginning = true; @@ -545,9 +555,10 @@ private: }; TEST_F(CCLayerTreeHostTestSetNeedsRedraw, run) { + CCSettings setings; runTest(); } } // namespace -#endif // USE(THREADED_COMPOSITING) +#endif diff --git a/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp b/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp index 8adf476..c37de73 100644 --- a/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp +++ b/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp @@ -28,12 +28,29 @@ #include "LayerChromium.h" #include "cc/CCLayerImpl.h" +#include "cc/CCProxy.h" #include using namespace WebCore; namespace { +class ScopedSetImplThread { +public: + ScopedSetImplThread() + { +#ifndef NDEBUG + CCProxy::setImplThread(true); +#endif + } + ~ScopedSetImplThread() + { +#ifndef NDEBUG + CCProxy::setImplThread(false); +#endif + } +}; + class MockCCLayerImpl : public CCLayerImpl { public: static PassRefPtr create(int layerId) @@ -116,6 +133,7 @@ void expectTreesAreIdentical(LayerChromium* layer, CCLayerImpl* ccLayer) // Constructs a very simple tree and synchronizes it without trying to reuse any preexisting layers. TEST(TreeSynchronizerTest, syncSimpleTreeFromEmpty) { + ScopedSetImplThread impl; RefPtr layerTreeRoot = LayerChromium::create(0); layerTreeRoot->addChild(LayerChromium::create(0)); layerTreeRoot->addChild(LayerChromium::create(0)); @@ -128,6 +146,7 @@ TEST(TreeSynchronizerTest, syncSimpleTreeFromEmpty) // Constructs a very simple tree and synchronizes it attempting to reuse some layers TEST(TreeSynchronizerTest, syncSimpleTreeReusingLayers) { + ScopedSetImplThread impl; Vector ccLayerDestructionList; RefPtr layerTreeRoot = MockLayerChromium::create(&ccLayerDestructionList); @@ -153,6 +172,7 @@ TEST(TreeSynchronizerTest, syncSimpleTreeReusingLayers) TEST(TreeSynchronizerTest, syncSimpleTreeAndProperties) { + ScopedSetImplThread impl; RefPtr layerTreeRoot = LayerChromium::create(0); layerTreeRoot->addChild(LayerChromium::create(0)); layerTreeRoot->addChild(LayerChromium::create(0)); @@ -184,6 +204,7 @@ TEST(TreeSynchronizerTest, syncSimpleTreeAndProperties) TEST(TreeSynchronizerTest, reuseCCLayersAfterStructuralChange) { + ScopedSetImplThread impl; Vector ccLayerDestructionList; // Set up a tree with this sort of structure: @@ -230,6 +251,7 @@ TEST(TreeSynchronizerTest, reuseCCLayersAfterStructuralChange) // Constructs a very simple tree, synchronizes it, then synchronizes to a totally new tree. All layers from the old tree should be deleted. TEST(TreeSynchronizerTest, syncSimpleTreeThenDestroy) { + ScopedSetImplThread impl; Vector ccLayerDestructionList; RefPtr oldLayerTreeRoot = MockLayerChromium::create(&ccLayerDestructionList); @@ -260,6 +282,7 @@ TEST(TreeSynchronizerTest, syncSimpleTreeThenDestroy) // Constructs+syncs a tree with mask, replica, and replica mask layers. TEST(TreeSynchronizerTest, syncMaskReplicaAndReplicaMaskLayers) { + ScopedSetImplThread impl; RefPtr layerTreeRoot = LayerChromium::create(0); layerTreeRoot->addChild(LayerChromium::create(0)); layerTreeRoot->addChild(LayerChromium::create(0)); -- 2.7.4