80b93403创建于 2012年9月22日历史提交
// 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