blob: 44890d1153abfdbe5b87c17e8c8a2fe8e78dc9c1 [file] [log] [blame]
// 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 <gmock/gmock.h>
#include <public/Platform.h>
#include <public/WebCompositorSupport.h>
#include <public/WebFilterOperation.h>
#include <public/WebFilterOperations.h>
#include <public/WebThread.h>
#include <wtf/Locker.h>
#include <wtf/PassRefPtr.h>
#include <wtf/Vector.h>
using namespace cc;
using namespace WebKit;
namespace WebKitTests {
PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> 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<int>(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<int>(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)
{
}
CompositorFakeWebGraphicsContext3DWithTextureTracking::~CompositorFakeWebGraphicsContext3DWithTextureTracking()
{
}
bool TestHooks::prepareToDrawOnCCThread(cc::CCLayerTreeHostImpl*)
{
return true;
}
PassOwnPtr<WebCompositorOutputSurface> TestHooks::createOutputSurface()
{
return FakeWebCompositorOutputSurface::create(CompositorFakeWebGraphicsContext3DWithTextureTracking::create(WebGraphicsContext3D::Attributes()));
}
PassOwnPtr<MockLayerTreeHostImpl> 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);
}
base::TimeDelta MockLayerTreeHostImpl::lowFrequencyAnimationInterval() const
{
return base::TimeDelta::FromMilliseconds(16);
}
MockLayerTreeHostImpl::MockLayerTreeHostImpl(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client)
: CCLayerTreeHostImpl(settings, client)
, m_testHooks(testHooks)
{
}
// Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl.
class MockLayerTreeHost : public cc::CCLayerTreeHost {
public:
static PassOwnPtr<MockLayerTreeHost> create(TestHooks* testHooks, cc::CCLayerTreeHostClient* client, PassRefPtr<cc::LayerChromium> rootLayer, const cc::CCLayerTreeSettings& settings)
{
OwnPtr<MockLayerTreeHost> 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<cc::CCLayerTreeHostImpl> createLayerTreeHostImpl(cc::CCLayerTreeHostImplClient* client)
{
return MockLayerTreeHostImpl::create(m_testHooks, settings(), client);
}
virtual void didAddAnimation() OVERRIDE
{
CCLayerTreeHost::didAddAnimation();
m_testHooks->didAddAnimation();
}
private:
MockLayerTreeHost(TestHooks* testHooks, cc::CCLayerTreeHostClient* client, const cc::CCLayerTreeSettings& settings)
: CCLayerTreeHost(client, settings)
, m_testHooks(testHooks)
{
}
TestHooks* m_testHooks;
};
// Implementation of CCLayerTreeHost callback interface.
class MockLayerTreeHostClient : public MockCCLayerTreeHostClient {
public:
static PassOwnPtr<MockLayerTreeHostClient> 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<WebCompositorOutputSurface> createOutputSurface() OVERRIDE
{
return m_testHooks->createOutputSurface();
}
virtual void didRecreateOutputSurface(bool succeeded) OVERRIDE
{
m_testHooks->didRecreateOutputSurface(succeeded);
}
virtual PassOwnPtr<CCInputHandler> 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;
};
CCThreadedTest::CCThreadedTest()
: m_beginning(false)
, m_endWhenBeginReturns(false)
, m_timedOut(false)
, m_finished(false)
, m_scheduled(false)
, m_started(false)
{
}
CCThreadedTest::~CCThreadedTest()
{
}
void CCThreadedTest::endTest()
{
m_finished = true;
// 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
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::realEndTest));
}
void CCThreadedTest::endTestAfterDelay(int delayMilliseconds)
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTest));
}
void CCThreadedTest::postSetNeedsAnimateToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchSetNeedsAnimate));
}
void CCThreadedTest::postAddAnimationToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchAddAnimation));
}
void CCThreadedTest::postAddInstantAnimationToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchAddInstantAnimation));
}
void CCThreadedTest::postSetNeedsCommitToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchSetNeedsCommit));
}
void CCThreadedTest::postAcquireLayerTextures()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchAcquireLayerTextures));
}
void CCThreadedTest::postSetNeedsRedrawToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchSetNeedsRedraw));
}
void CCThreadedTest::postSetNeedsAnimateAndCommitToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchSetNeedsAnimateAndCommit));
}
void CCThreadedTest::postSetVisibleToMainThread(bool visible)
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchSetVisible, visible));
}
void CCThreadedTest::postDidAddAnimationToMainThread()
{
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchDidAddAnimation));
}
void CCThreadedTest::doBeginTest()
{
ASSERT(CCProxy::isMainThread());
m_client = MockLayerTreeHostClient::create(this);
RefPtr<LayerChromium> 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)
realEndTest();
}
void CCThreadedTest::timeout()
{
m_timedOut = true;
endTest();
}
void CCThreadedTest::scheduleComposite()
{
if (!m_started || m_scheduled || m_finished)
return;
m_scheduled = true;
m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::dispatchComposite));
}
void CCThreadedTest::realEndTest()
{
ASSERT(CCProxy::isMainThread());
WebKit::Platform::current()->currentThread()->exitRunLoop();
}
void CCThreadedTest::dispatchSetNeedsAnimate()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost)
m_layerTreeHost->setNeedsAnimate();
}
void CCThreadedTest::dispatchAddInstantAnimation()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost && m_layerTreeHost->rootLayer())
addOpacityTransitionToLayer(*m_layerTreeHost->rootLayer(), 0, 0, 0.5, false);
}
void CCThreadedTest::dispatchAddAnimation()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost && m_layerTreeHost->rootLayer())
addOpacityTransitionToLayer(*m_layerTreeHost->rootLayer(), 10, 0, 0.5, true);
}
void CCThreadedTest::dispatchSetNeedsAnimateAndCommit()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost) {
m_layerTreeHost->setNeedsAnimate();
m_layerTreeHost->setNeedsCommit();
}
}
void CCThreadedTest::dispatchSetNeedsCommit()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost)
m_layerTreeHost->setNeedsCommit();
}
void CCThreadedTest::dispatchAcquireLayerTextures()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost)
m_layerTreeHost->acquireLayerTextures();
}
void CCThreadedTest::dispatchSetNeedsRedraw()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost)
m_layerTreeHost->setNeedsRedraw();
}
void CCThreadedTest::dispatchSetVisible(bool visible)
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost)
m_layerTreeHost->setVisible(visible);
}
void CCThreadedTest::dispatchComposite()
{
m_scheduled = false;
if (m_layerTreeHost && !m_finished)
m_layerTreeHost->composite();
}
void CCThreadedTest::dispatchDidAddAnimation()
{
ASSERT(CCProxy::isMainThread());
if (m_finished)
return;
if (m_layerTreeHost)
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();
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