e07aa106创建于 2012年9月23日历史提交
// Copyright 2012 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 "CCTextureUpdateController.h"

#include "CCSchedulerTestCommon.h"
#include "CCSingleThreadProxy.h" // For DebugScopedSetImplThread
#include "CCTiledLayerTestCommon.h"
#include "FakeWebCompositorOutputSurface.h"
#include "FakeWebGraphicsContext3D.h"
#include "WebCompositorInitializer.h"
#include <gtest/gtest.h>
#include <public/WebThread.h>
#include <wtf/RefPtr.h>

using namespace cc;
using namespace WebKit;
using namespace WebKitTests;
using testing::Test;


namespace {

const int kFlushPeriodFull = 4;
const int kFlushPeriodPartial = kFlushPeriodFull;

class CCTextureUpdateControllerTest;

class WebGraphicsContext3DForUploadTest : public FakeWebGraphicsContext3D {
public:
    WebGraphicsContext3DForUploadTest(CCTextureUpdateControllerTest *test)
        : m_test(test)
        , m_supportShallowFlush(true)
    { }

    virtual void flush(void);
    virtual void shallowFlushCHROMIUM(void);
    virtual GrGLInterface* onCreateGrGLInterface() { return 0; }

    virtual WebString getString(WGC3Denum name)
    {
        if (m_supportShallowFlush)
            return WebString("GL_CHROMIUM_shallow_flush");
        return WebString("");
    }

private:
    CCTextureUpdateControllerTest* m_test;
    bool m_supportShallowFlush;
};


class TextureUploaderForUploadTest : public FakeTextureUploader {
public:
    TextureUploaderForUploadTest(CCTextureUpdateControllerTest *test) : m_test(test) { }

    virtual void beginUploads() OVERRIDE;
    virtual void endUploads() OVERRIDE;
    virtual void uploadTexture(cc::CCResourceProvider*, Parameters) OVERRIDE;

private:
    CCTextureUpdateControllerTest* m_test;
};

class TextureForUploadTest : public LayerTextureUpdater::Texture {
public:
    TextureForUploadTest() 
        : LayerTextureUpdater::Texture(nullptr)
        , m_evicted(false)
    {
    }
    virtual void updateRect(CCResourceProvider*, const IntRect& sourceRect, const IntSize& destOffset) { }
    virtual bool backingResourceWasEvicted() const { return m_evicted; }
    void evictBackingResource() { m_evicted = true; }
private:
    bool m_evicted;
};


class CCTextureUpdateControllerTest : public Test {
public:
    CCTextureUpdateControllerTest()
        : m_queue(adoptPtr(new CCTextureUpdateQueue))
        , m_uploader(this)
        , m_compositorInitializer(m_thread.get())
        , m_fullUploadCountExpected(0)
        , m_partialCountExpected(0)
        , m_totalUploadCountExpected(0)
        , m_maxUploadCountPerUpdate(0)
        , m_numBeginUploads(0)
        , m_numEndUploads(0)
        , m_numConsecutiveFlushes(0)
        , m_numDanglingUploads(0)
        , m_numTotalUploads(0)
        , m_numTotalFlushes(0)
        , m_numPreviousUploads(0)
        , m_numPreviousFlushes(0)
    {
    }

public:
    void onFlush()
    {
        // Check for back-to-back flushes.
        EXPECT_EQ(0, m_numConsecutiveFlushes) << "Back-to-back flushes detected.";

        // Check for premature flushes
        if (m_numPreviousUploads != m_maxUploadCountPerUpdate) {
            if (m_numTotalUploads < m_fullUploadCountExpected)
                EXPECT_GE(m_numDanglingUploads, kFlushPeriodFull) << "Premature flush detected in full uploads.";
            else if (m_numTotalUploads > m_fullUploadCountExpected && m_numTotalUploads < m_totalUploadCountExpected)
                EXPECT_GE(m_numDanglingUploads, kFlushPeriodPartial) << "Premature flush detected in partial uploads.";
        }

        m_numDanglingUploads = 0;
        m_numConsecutiveFlushes++;
        m_numTotalFlushes++;
        m_numPreviousFlushes++;
    }

    void onBeginUploads()
    {
        EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";

        m_numPreviousFlushes = 0;
        m_numPreviousUploads = 0;
        m_numBeginUploads++;
    }

    void onUpload()
    {
        // Check for too many consecutive uploads
        if (m_numTotalUploads < m_fullUploadCountExpected)
            EXPECT_LT(m_numDanglingUploads, kFlushPeriodFull) << "Too many consecutive full uploads detected.";
        else
            EXPECT_LT(m_numDanglingUploads, kFlushPeriodPartial) << "Too many consecutive partial uploads detected.";

        m_numConsecutiveFlushes = 0;
        m_numDanglingUploads++;
        m_numTotalUploads++;
        m_numPreviousUploads++;
    }

    void onEndUploads()
    {
        // Note: The m_numTotalUploads != m_fullUploadCountExpected comparison
        // allows for the quota not to be hit in the case where we are trasitioning
        // from full uploads to partial uploads.
        if (m_numTotalUploads != m_totalUploadCountExpected && m_numTotalUploads != m_fullUploadCountExpected) {
            EXPECT_EQ(m_maxUploadCountPerUpdate, m_numPreviousUploads)
                << "endUpload() was called when there are textures to upload, but the upload quota hasn't been filled.";
        }

        m_numEndUploads++;
    }

protected:
    virtual void SetUp()
    {
        m_context = FakeWebCompositorOutputSurface::create(adoptPtr(new WebGraphicsContext3DForUploadTest(this)));
        DebugScopedSetImplThread implThread;
        m_resourceProvider = CCResourceProvider::create(m_context.get());
    }


    void appendFullUploadsOfIndexedTextureToUpdateQueue(int count, int textureIndex)
    {
        m_fullUploadCountExpected += count;
        m_totalUploadCountExpected += count;

        const IntRect rect(0, 0, 300, 150);
        const TextureUploader::Parameters upload = { &m_textures[textureIndex], rect, IntSize() };
        for (int i = 0; i < count; i++)
            m_queue->appendFullUpload(upload);
    }

    void appendFullUploadsToUpdateQueue(int count)
    {
        appendFullUploadsOfIndexedTextureToUpdateQueue(count, 0);
    }

    void appendPartialUploadsOfIndexedTextureToUpdateQueue(int count, int textureIndex)
    {
        m_partialCountExpected += count;
        m_totalUploadCountExpected += count;

        const IntRect rect(0, 0, 100, 100);
        const TextureUploader::Parameters upload = { &m_textures[textureIndex], rect, IntSize() };
        for (int i = 0; i < count; i++)
            m_queue->appendPartialUpload(upload);
    }

    void appendPartialUploadsToUpdateQueue(int count)
    {
        appendPartialUploadsOfIndexedTextureToUpdateQueue(count, 0);
    }

    void setMaxUploadCountPerUpdate(int count)
    {
        m_maxUploadCountPerUpdate = count;
    }

    void updateTextures()
    {
        OwnPtr<CCTextureUpdateController> updateController =
            CCTextureUpdateController::create(
                NULL,
                CCProxy::implThread(),
                m_queue.release(),
                m_resourceProvider.get(),
                &m_uploader);
        updateController->finalize();
    }

protected:
    // Classes required to interact and test the CCTextureUpdateController
    OwnPtr<CCGraphicsContext> m_context;
    OwnPtr<CCResourceProvider> m_resourceProvider;
    OwnPtr<CCTextureUpdateQueue> m_queue;
    TextureForUploadTest m_textures[4];
    TextureUploaderForUploadTest m_uploader;
    OwnPtr<WebThread> m_thread;
    WebCompositorInitializer m_compositorInitializer;


    // Properties / expectations of this test
    int m_fullUploadCountExpected;
    int m_partialCountExpected;
    int m_totalUploadCountExpected;
    int m_maxUploadCountPerUpdate;

    // Dynamic properties of this test
    int m_numBeginUploads;
    int m_numEndUploads;
    int m_numConsecutiveFlushes;
    int m_numDanglingUploads;
    int m_numTotalUploads;
    int m_numTotalFlushes;
    int m_numPreviousUploads;
    int m_numPreviousFlushes;
};

void WebGraphicsContext3DForUploadTest::flush(void)
{
    m_test->onFlush();
}

void WebGraphicsContext3DForUploadTest::shallowFlushCHROMIUM(void)
{
    m_test->onFlush();
}

void TextureUploaderForUploadTest::beginUploads()
{
    m_test->onBeginUploads();
}

void TextureUploaderForUploadTest::endUploads()
{
    m_test->onEndUploads();
}

void TextureUploaderForUploadTest::uploadTexture(cc::CCResourceProvider*, Parameters)
{
    m_test->onUpload();
}

// ZERO UPLOADS TESTS
TEST_F(CCTextureUpdateControllerTest, ZeroUploads)
{
    appendFullUploadsToUpdateQueue(0);
    appendPartialUploadsToUpdateQueue(0);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(0, m_numPreviousFlushes);
    EXPECT_EQ(0, m_numPreviousUploads);
}


// ONE UPLOAD TESTS
TEST_F(CCTextureUpdateControllerTest, OneFullUpload)
{
    appendFullUploadsToUpdateQueue(1);
    appendPartialUploadsToUpdateQueue(0);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(1, m_numPreviousFlushes);
    EXPECT_EQ(1, m_numPreviousUploads);
    EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
}

TEST_F(CCTextureUpdateControllerTest, OnePartialUpload)
{
    appendFullUploadsToUpdateQueue(0);
    appendPartialUploadsToUpdateQueue(1);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(1, m_numPreviousFlushes);
    EXPECT_EQ(1, m_numPreviousUploads);
    EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
}

TEST_F(CCTextureUpdateControllerTest, OneFullOnePartialUpload)
{
    appendFullUploadsToUpdateQueue(1);
    appendPartialUploadsToUpdateQueue(1);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(1, m_numPreviousFlushes);
    EXPECT_EQ(2, m_numPreviousUploads);
    EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
}


// This class of tests upload a number of textures that is a multiple of the flush period.
const int fullUploadFlushMultipler = 7;
const int fullCount = fullUploadFlushMultipler * kFlushPeriodFull;

const int partialUploadFlushMultipler = 11;
const int partialCount = partialUploadFlushMultipler * kFlushPeriodPartial;

TEST_F(CCTextureUpdateControllerTest, ManyFullUploads)
{
    appendFullUploadsToUpdateQueue(fullCount);
    appendPartialUploadsToUpdateQueue(0);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(fullUploadFlushMultipler, m_numPreviousFlushes);
    EXPECT_EQ(fullCount, m_numPreviousUploads);
    EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
}

TEST_F(CCTextureUpdateControllerTest, ManyPartialUploads)
{
    appendFullUploadsToUpdateQueue(0);
    appendPartialUploadsToUpdateQueue(partialCount);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(partialUploadFlushMultipler, m_numPreviousFlushes);
    EXPECT_EQ(partialCount, m_numPreviousUploads);
    EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
}

TEST_F(CCTextureUpdateControllerTest, ManyFullManyPartialUploads)
{
    appendFullUploadsToUpdateQueue(fullCount);
    appendPartialUploadsToUpdateQueue(partialCount);
    DebugScopedSetImplThread implThread;
    updateTextures();

    EXPECT_EQ(fullUploadFlushMultipler + partialUploadFlushMultipler, m_numPreviousFlushes);
    EXPECT_EQ(fullCount + partialCount, m_numPreviousUploads);
    EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
}

class FakeCCTextureUpdateControllerClient : public cc::CCTextureUpdateControllerClient {
public:
    FakeCCTextureUpdateControllerClient() { reset(); }
    void reset() { m_readyToFinalizeCalled = false; }
    bool readyToFinalizeCalled() const { return m_readyToFinalizeCalled; }

    virtual void readyToFinalizeTextureUpdates() OVERRIDE { m_readyToFinalizeCalled = true; }

protected:
    bool m_readyToFinalizeCalled;
};

class FakeCCTextureUpdateController : public cc::CCTextureUpdateController {
public:
    static PassOwnPtr<FakeCCTextureUpdateController> create(cc::CCTextureUpdateControllerClient* client, cc::CCThread* thread, PassOwnPtr<CCTextureUpdateQueue> queue, CCResourceProvider* resourceProvider, TextureUploader* uploader)
    {
        return adoptPtr(new FakeCCTextureUpdateController(client, thread, queue, resourceProvider, uploader));
    }

    void setMonotonicTimeNow(double time) { m_monotonicTimeNow = time; }
    virtual double monotonicTimeNow() const OVERRIDE { return m_monotonicTimeNow; }
    void setUpdateMoreTexturesTime(double time) { m_updateMoreTexturesTime = time; }
    virtual double updateMoreTexturesTime() const OVERRIDE { return m_updateMoreTexturesTime; }
    void setUpdateMoreTexturesSize(size_t size) { m_updateMoreTexturesSize = size; }
    virtual size_t updateMoreTexturesSize() const OVERRIDE { return m_updateMoreTexturesSize; }

protected:
    FakeCCTextureUpdateController(cc::CCTextureUpdateControllerClient* client, cc::CCThread* thread, PassOwnPtr<CCTextureUpdateQueue> queue, CCResourceProvider* resourceProvider, TextureUploader* uploader)
        : cc::CCTextureUpdateController(client, thread, queue, resourceProvider, uploader)
        , m_monotonicTimeNow(0)
        , m_updateMoreTexturesTime(0)
        , m_updateMoreTexturesSize(0) { }

    double m_monotonicTimeNow;
    double m_updateMoreTexturesTime;
    size_t m_updateMoreTexturesSize;
};

static void runPendingTask(FakeCCThread* thread, FakeCCTextureUpdateController* controller)
{
    EXPECT_TRUE(thread->hasPendingTask());
    controller->setMonotonicTimeNow(controller->monotonicTimeNow() + thread->pendingDelayMs() / 1000.0);
    thread->runPendingTask();
}

TEST_F(CCTextureUpdateControllerTest, UpdateMoreTextures)
{
    FakeCCTextureUpdateControllerClient client;
    FakeCCThread thread;

    setMaxUploadCountPerUpdate(1);
    appendFullUploadsToUpdateQueue(3);
    appendPartialUploadsToUpdateQueue(0);

    DebugScopedSetImplThread implThread;
    OwnPtr<FakeCCTextureUpdateController> controller(FakeCCTextureUpdateController::create(&client, &thread, m_queue.release(), m_resourceProvider.get(), &m_uploader));

    controller->setMonotonicTimeNow(0);
    controller->setUpdateMoreTexturesTime(0.1);
    controller->setUpdateMoreTexturesSize(1);
    // Not enough time for any updates.
    controller->performMoreUpdates(0.09);
    EXPECT_FALSE(thread.hasPendingTask());
    EXPECT_EQ(0, m_numBeginUploads);
    EXPECT_EQ(0, m_numEndUploads);

    controller->setMonotonicTimeNow(0);
    controller->setUpdateMoreTexturesTime(0.1);
    controller->setUpdateMoreTexturesSize(1);
    // Only enough time for 1 update.
    controller->performMoreUpdates(0.12);
    runPendingTask(&thread, controller.get());
    EXPECT_FALSE(thread.hasPendingTask());
    EXPECT_EQ(1, m_numBeginUploads);
    EXPECT_EQ(1, m_numEndUploads);
    EXPECT_EQ(1, m_numTotalUploads);

    controller->setMonotonicTimeNow(0);
    controller->setUpdateMoreTexturesTime(0.1);
    controller->setUpdateMoreTexturesSize(1);
    // Enough time for 2 updates.
    controller->performMoreUpdates(0.22);
    runPendingTask(&thread, controller.get());
    runPendingTask(&thread, controller.get());
    EXPECT_FALSE(thread.hasPendingTask());
    EXPECT_TRUE(client.readyToFinalizeCalled());
    EXPECT_EQ(3, m_numBeginUploads);
    EXPECT_EQ(3, m_numEndUploads);
    EXPECT_EQ(3, m_numTotalUploads);
}

TEST_F(CCTextureUpdateControllerTest, NoMoreUpdates)
{
    FakeCCTextureUpdateControllerClient client;
    FakeCCThread thread;

    setMaxUploadCountPerUpdate(1);
    appendFullUploadsToUpdateQueue(2);
    appendPartialUploadsToUpdateQueue(0);

    DebugScopedSetImplThread implThread;
    OwnPtr<FakeCCTextureUpdateController> controller(FakeCCTextureUpdateController::create(&client, &thread, m_queue.release(), m_resourceProvider.get(), &m_uploader));

    controller->setMonotonicTimeNow(0);
    controller->setUpdateMoreTexturesTime(0.1);
    controller->setUpdateMoreTexturesSize(1);
    // Enough time for 3 updates but only 2 necessary.
    controller->performMoreUpdates(0.31);
    runPendingTask(&thread, controller.get());
    runPendingTask(&thread, controller.get());
    EXPECT_FALSE(thread.hasPendingTask());
    EXPECT_TRUE(client.readyToFinalizeCalled());
    EXPECT_EQ(2, m_numBeginUploads);
    EXPECT_EQ(2, m_numEndUploads);
    EXPECT_EQ(2, m_numTotalUploads);

    controller->setMonotonicTimeNow(0);
    controller->setUpdateMoreTexturesTime(0.1);
    controller->setUpdateMoreTexturesSize(1);
    // Enough time for updates but no more updates left.
    controller->performMoreUpdates(0.31);
    // 0-delay task used to call readyToFinalizeTextureUpdates().
    runPendingTask(&thread, controller.get());
    EXPECT_FALSE(thread.hasPendingTask());
    EXPECT_TRUE(client.readyToFinalizeCalled());
    EXPECT_EQ(2, m_numBeginUploads);
    EXPECT_EQ(2, m_numEndUploads);
    EXPECT_EQ(2, m_numTotalUploads);
}

TEST_F(CCTextureUpdateControllerTest, UpdatesCompleteInFiniteTime)
{
    FakeCCTextureUpdateControllerClient client;
    FakeCCThread thread;

    setMaxUploadCountPerUpdate(1);
    appendFullUploadsToUpdateQueue(2);
    appendPartialUploadsToUpdateQueue(0);

    DebugScopedSetImplThread implThread;
    OwnPtr<FakeCCTextureUpdateController> controller(FakeCCTextureUpdateController::create(&client, &thread, m_queue.release(), m_resourceProvider.get(), &m_uploader));

    controller->setMonotonicTimeNow(0);
    controller->setUpdateMoreTexturesTime(0.5);
    controller->setUpdateMoreTexturesSize(1);

    for (int i = 0; i < 100; i++) {
        if (client.readyToFinalizeCalled())
            break;

        // Not enough time for any updates.
        controller->performMoreUpdates(0.4);

        if (thread.hasPendingTask())
            runPendingTask(&thread, controller.get());
    }

    EXPECT_FALSE(thread.hasPendingTask());
    EXPECT_TRUE(client.readyToFinalizeCalled());
    EXPECT_EQ(2, m_numBeginUploads);
    EXPECT_EQ(2, m_numEndUploads);
    EXPECT_EQ(2, m_numTotalUploads);
}

TEST_F(CCTextureUpdateControllerTest, ClearUploadsToEvictedResources)
{
    appendFullUploadsOfIndexedTextureToUpdateQueue(1, 0);
    appendPartialUploadsOfIndexedTextureToUpdateQueue(1, 1);
    appendFullUploadsOfIndexedTextureToUpdateQueue(1, 2);
    appendPartialUploadsOfIndexedTextureToUpdateQueue(1, 3);
    DebugScopedSetImplThread implThread;

    m_queue->clearUploadsToEvictedResources();
    EXPECT_EQ(2u, m_queue->fullUploadSize());
    EXPECT_EQ(2u, m_queue->partialUploadSize());

    m_textures[0].evictBackingResource();
    m_queue->clearUploadsToEvictedResources();
    EXPECT_EQ(1u, m_queue->fullUploadSize());
    EXPECT_EQ(2u, m_queue->partialUploadSize());

    m_textures[3].evictBackingResource();
    m_queue->clearUploadsToEvictedResources();
    EXPECT_EQ(1u, m_queue->fullUploadSize());
    EXPECT_EQ(1u, m_queue->partialUploadSize());

    m_textures[2].evictBackingResource();
    m_queue->clearUploadsToEvictedResources();
    EXPECT_EQ(0u, m_queue->fullUploadSize());
    EXPECT_EQ(1u, m_queue->partialUploadSize());

    m_textures[1].evictBackingResource();
    m_queue->clearUploadsToEvictedResources();
    EXPECT_EQ(0u, m_queue->fullUploadSize());
    EXPECT_EQ(0u, m_queue->partialUploadSize());
}

} // namespace