// Copyright 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "config.h" #include "CCThreadedTest.h" #include "CCActiveAnimation.h" #include "CCAnimationTestCommon.h" #include "CCInputHandler.h" #include "CCLayerAnimationController.h" #include "CCLayerImpl.h" #include "CCLayerTreeHostImpl.h" #include "CCOcclusionTrackerTestCommon.h" #include "CCScopedThreadProxy.h" #include "CCSingleThreadProxy.h" #include "CCTextureUpdateQueue.h" #include "CCThreadTask.h" #include "CCTiledLayerTestCommon.h" #include "CCTimingFunction.h" #include "ContentLayerChromium.h" #include "FakeWebCompositorOutputSurface.h" #include "FakeWebGraphicsContext3D.h" #include "LayerChromium.h" #include #include #include #include #include #include #include #include #include #include #include using namespace WebCore; using namespace WebKit; namespace WebKitTests { PassOwnPtr CompositorFakeWebGraphicsContext3DWithTextureTracking::create(Attributes attrs) { return adoptPtr(new CompositorFakeWebGraphicsContext3DWithTextureTracking(attrs)); } WebGLId CompositorFakeWebGraphicsContext3DWithTextureTracking::createTexture() { WebGLId texture = m_textures.size() + 1; m_textures.append(texture); return texture; } void CompositorFakeWebGraphicsContext3DWithTextureTracking::deleteTexture(WebGLId texture) { for (size_t i = 0; i < m_textures.size(); i++) { if (m_textures[i] == texture) { m_textures.remove(i); break; } } } void CompositorFakeWebGraphicsContext3DWithTextureTracking::bindTexture(WGC3Denum /* target */, WebGLId texture) { m_usedTextures.add(texture); } int CompositorFakeWebGraphicsContext3DWithTextureTracking::numTextures() const { return static_cast(m_textures.size()); } int CompositorFakeWebGraphicsContext3DWithTextureTracking::texture(int i) const { return m_textures[i]; } void CompositorFakeWebGraphicsContext3DWithTextureTracking::resetTextures() { m_textures.clear(); } int CompositorFakeWebGraphicsContext3DWithTextureTracking::numUsedTextures() const { return static_cast(m_usedTextures.size()); } bool CompositorFakeWebGraphicsContext3DWithTextureTracking::usedTexture(int texture) const { return m_usedTextures.find(texture) != m_usedTextures.end(); } void CompositorFakeWebGraphicsContext3DWithTextureTracking::resetUsedTextures() { m_usedTextures.clear(); } CompositorFakeWebGraphicsContext3DWithTextureTracking::CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs) : CompositorFakeWebGraphicsContext3D(attrs) { } PassOwnPtr TestHooks::createOutputSurface() { return FakeWebCompositorOutputSurface::create(CompositorFakeWebGraphicsContext3DWithTextureTracking::create(WebGraphicsContext3D::Attributes())); } PassOwnPtr MockLayerTreeHostImpl::create(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client) { return adoptPtr(new MockLayerTreeHostImpl(testHooks, settings, client)); } void MockLayerTreeHostImpl::beginCommit() { CCLayerTreeHostImpl::beginCommit(); m_testHooks->beginCommitOnCCThread(this); } void MockLayerTreeHostImpl::commitComplete() { CCLayerTreeHostImpl::commitComplete(); m_testHooks->commitCompleteOnCCThread(this); } bool MockLayerTreeHostImpl::prepareToDraw(FrameData& frame) { bool result = CCLayerTreeHostImpl::prepareToDraw(frame); if (!m_testHooks->prepareToDrawOnCCThread(this)) result = false; return result; } void MockLayerTreeHostImpl::drawLayers(const FrameData& frame) { CCLayerTreeHostImpl::drawLayers(frame); m_testHooks->drawLayersOnCCThread(this); } void MockLayerTreeHostImpl::animateLayers(double monotonicTime, double wallClockTime) { m_testHooks->willAnimateLayers(this, monotonicTime); CCLayerTreeHostImpl::animateLayers(monotonicTime, wallClockTime); m_testHooks->animateLayers(this, monotonicTime); } double MockLayerTreeHostImpl::lowFrequencyAnimationInterval() const { return 1.0 / 60; } MockLayerTreeHostImpl::MockLayerTreeHostImpl(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client) : CCLayerTreeHostImpl(settings, client) , m_testHooks(testHooks) { } // Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl. class MockLayerTreeHost : public WebCore::CCLayerTreeHost { public: static PassOwnPtr create(TestHooks* testHooks, WebCore::CCLayerTreeHostClient* client, PassRefPtr rootLayer, const WebCore::CCLayerTreeSettings& settings) { OwnPtr layerTreeHost(adoptPtr(new MockLayerTreeHost(testHooks, client, settings))); bool success = layerTreeHost->initialize(); EXPECT_TRUE(success); layerTreeHost->setRootLayer(rootLayer); // LayerTreeHostImpl won't draw if it has 1x1 viewport. layerTreeHost->setViewportSize(IntSize(1, 1), IntSize(1, 1)); layerTreeHost->rootLayer()->setLayerAnimationDelegate(testHooks); return layerTreeHost.release(); } virtual PassOwnPtr createLayerTreeHostImpl(WebCore::CCLayerTreeHostImplClient* client) { return MockLayerTreeHostImpl::create(m_testHooks, settings(), client); } virtual void didAddAnimation() OVERRIDE { CCLayerTreeHost::didAddAnimation(); m_testHooks->didAddAnimation(); } private: MockLayerTreeHost(TestHooks* testHooks, WebCore::CCLayerTreeHostClient* client, const WebCore::CCLayerTreeSettings& settings) : CCLayerTreeHost(client, settings) , m_testHooks(testHooks) { } TestHooks* m_testHooks; }; // Implementation of CCLayerTreeHost callback interface. class MockLayerTreeHostClient : public MockCCLayerTreeHostClient { public: static PassOwnPtr create(TestHooks* testHooks) { return adoptPtr(new MockLayerTreeHostClient(testHooks)); } virtual void willBeginFrame() OVERRIDE { } virtual void didBeginFrame() OVERRIDE { } virtual void animate(double monotonicTime) OVERRIDE { m_testHooks->animate(monotonicTime); } virtual void layout() OVERRIDE { m_testHooks->layout(); } virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE { m_testHooks->applyScrollAndScale(scrollDelta, scale); } virtual PassOwnPtr createOutputSurface() OVERRIDE { return m_testHooks->createOutputSurface(); } virtual void didRecreateOutputSurface(bool succeeded) OVERRIDE { m_testHooks->didRecreateOutputSurface(succeeded); } virtual PassOwnPtr createInputHandler() OVERRIDE { return nullptr; } virtual void willCommit() OVERRIDE { } virtual void didCommit() OVERRIDE { m_testHooks->didCommit(); } virtual void didCommitAndDrawFrame() OVERRIDE { m_testHooks->didCommitAndDrawFrame(); } virtual void didCompleteSwapBuffers() OVERRIDE { } virtual void scheduleComposite() OVERRIDE { m_testHooks->scheduleComposite(); } private: explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { } TestHooks* m_testHooks; }; class TimeoutTask : public WebThread::Task { public: explicit TimeoutTask(CCThreadedTest* test) : m_test(test) { } void clearTest() { m_test = 0; } virtual ~TimeoutTask() { if (m_test) m_test->clearTimeout(); } virtual void run() { if (m_test) m_test->timeout(); } private: CCThreadedTest* m_test; }; class BeginTask : public WebThread::Task { public: explicit BeginTask(CCThreadedTest* test) : m_test(test) { } virtual ~BeginTask() { } virtual void run() { m_test->doBeginTest(); } private: CCThreadedTest* m_test; }; class EndTestTask : public WebThread::Task { public: explicit EndTestTask(CCThreadedTest* test) : m_test(test) { } virtual ~EndTestTask() { if (m_test) m_test->clearEndTestTask(); } void clearTest() { m_test = 0; } virtual void run() { if (m_test) m_test->endTest(); } private: CCThreadedTest* m_test; }; CCThreadedTest::CCThreadedTest() : m_beginning(false) , m_endWhenBeginReturns(false) , m_timedOut(false) , m_finished(false) , m_scheduled(false) , m_started(false) , m_endTestTask(0) { } void CCThreadedTest::endTest() { m_finished = true; // If we are called from the CCThread, re-call endTest on the main thread. if (!isMainThread()) m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTest)); else { // For the case where we endTest during beginTest(), set a flag to indicate that // the test should end the second beginTest regains control. if (m_beginning) m_endWhenBeginReturns = true; else onEndTest(static_cast(this)); } } void CCThreadedTest::endTestAfterDelay(int delayMilliseconds) { // If we are called from the CCThread, re-call endTest on the main thread. if (!isMainThread()) m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTestAfterDelay, delayMilliseconds)); else { m_endTestTask = new EndTestTask(this); WebKit::Platform::current()->currentThread()->postDelayedTask(m_endTestTask, delayMilliseconds); } } void CCThreadedTest::postSetNeedsAnimateToMainThread() { callOnMainThread(CCThreadedTest::dispatchSetNeedsAnimate, this); } void CCThreadedTest::postAddAnimationToMainThread() { callOnMainThread(CCThreadedTest::dispatchAddAnimation, this); } void CCThreadedTest::postAddInstantAnimationToMainThread() { callOnMainThread(CCThreadedTest::dispatchAddInstantAnimation, this); } void CCThreadedTest::postSetNeedsCommitToMainThread() { callOnMainThread(CCThreadedTest::dispatchSetNeedsCommit, this); } void CCThreadedTest::postAcquireLayerTextures() { callOnMainThread(CCThreadedTest::dispatchAcquireLayerTextures, this); } void CCThreadedTest::postSetNeedsRedrawToMainThread() { callOnMainThread(CCThreadedTest::dispatchSetNeedsRedraw, this); } void CCThreadedTest::postSetNeedsAnimateAndCommitToMainThread() { callOnMainThread(CCThreadedTest::dispatchSetNeedsAnimateAndCommit, this); } void CCThreadedTest::postSetVisibleToMainThread(bool visible) { callOnMainThread(visible ? CCThreadedTest::dispatchSetVisible : CCThreadedTest::dispatchSetInvisible, this); } void CCThreadedTest::postDidAddAnimationToMainThread() { callOnMainThread(CCThreadedTest::dispatchDidAddAnimation, this); } void CCThreadedTest::doBeginTest() { ASSERT(isMainThread()); m_client = MockLayerTreeHostClient::create(this); RefPtr rootLayer = LayerChromium::create(); m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings); ASSERT_TRUE(m_layerTreeHost); rootLayer->setLayerTreeHost(m_layerTreeHost.get()); m_layerTreeHost->setSurfaceReady(); m_started = true; m_beginning = true; beginTest(); m_beginning = false; if (m_endWhenBeginReturns) onEndTest(static_cast(this)); } void CCThreadedTest::timeout() { m_timedOut = true; endTest(); } void CCThreadedTest::scheduleComposite() { if (!m_started || m_scheduled || m_finished) return; m_scheduled = true; callOnMainThread(&CCThreadedTest::dispatchComposite, this); } void CCThreadedTest::onEndTest(void* self) { ASSERT(isMainThread()); WebKit::Platform::current()->currentThread()->exitRunLoop(); } void CCThreadedTest::dispatchSetNeedsAnimate(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->setNeedsAnimate(); } void CCThreadedTest::dispatchAddInstantAnimation(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 0, 0, 0.5, false); } void CCThreadedTest::dispatchAddAnimation(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 10, 0, 0.5, true); } void CCThreadedTest::dispatchSetNeedsAnimateAndCommit(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost) { test->m_layerTreeHost->setNeedsAnimate(); test->m_layerTreeHost->setNeedsCommit(); } } void CCThreadedTest::dispatchSetNeedsCommit(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT_TRUE(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->setNeedsCommit(); } void CCThreadedTest::dispatchAcquireLayerTextures(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT_TRUE(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->acquireLayerTextures(); } void CCThreadedTest::dispatchSetNeedsRedraw(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT_TRUE(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->setNeedsRedraw(); } void CCThreadedTest::dispatchSetVisible(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->setVisible(true); } void CCThreadedTest::dispatchSetInvisible(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->setVisible(false); } void CCThreadedTest::dispatchComposite(void* self) { CCThreadedTest* test = static_cast(self); ASSERT(isMainThread()); ASSERT(test); test->m_scheduled = false; if (test->m_layerTreeHost && !test->m_finished) test->m_layerTreeHost->composite(); } void CCThreadedTest::dispatchDidAddAnimation(void* self) { ASSERT(isMainThread()); CCThreadedTest* test = static_cast(self); ASSERT(test); if (test->m_finished) return; if (test->m_layerTreeHost) test->m_layerTreeHost->didAddAnimation(); } void CCThreadedTest::runTest(bool threaded) { // For these tests, we will enable threaded animations. Platform::current()->compositorSupport()->setAcceleratedAnimationEnabled(true); if (threaded) { m_webThread = adoptPtr(WebKit::Platform::current()->createThread("CCThreadedTest")); Platform::current()->compositorSupport()->initialize(m_webThread.get()); } else Platform::current()->compositorSupport()->initialize(0); ASSERT(CCProxy::isMainThread()); m_mainThreadProxy = CCScopedThreadProxy::create(CCProxy::mainThread()); initializeSettings(m_settings); m_beginTask = new BeginTask(this); WebKit::Platform::current()->currentThread()->postDelayedTask(m_beginTask, 0); // postDelayedTask takes ownership of the task m_timeoutTask = new TimeoutTask(this); WebKit::Platform::current()->currentThread()->postDelayedTask(m_timeoutTask, 5000); WebKit::Platform::current()->currentThread()->enterRunLoop(); if (m_layerTreeHost && m_layerTreeHost->rootLayer()) m_layerTreeHost->rootLayer()->setLayerTreeHost(0); m_layerTreeHost.clear(); if (m_timeoutTask) m_timeoutTask->clearTest(); if (m_endTestTask) m_endTestTask->clearTest(); ASSERT_FALSE(m_layerTreeHost.get()); m_client.clear(); if (m_timedOut) { FAIL() << "Test timed out"; Platform::current()->compositorSupport()->shutdown(); return; } afterTest(); Platform::current()->compositorSupport()->shutdown(); } } // namespace WebKitTests