diff options
Diffstat (limited to 'cc/CCSchedulerTest.cpp')
-rw-r--r-- | cc/CCSchedulerTest.cpp | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/cc/CCSchedulerTest.cpp b/cc/CCSchedulerTest.cpp new file mode 100644 index 0000000..ec68c71 --- /dev/null +++ b/cc/CCSchedulerTest.cpp @@ -0,0 +1,472 @@ +// 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 "CCScheduler.h" + +#include "CCSchedulerTestCommon.h" +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <wtf/OwnPtr.h> + +using namespace WTF; +using namespace WebCore; +using namespace WebKitTests; + +namespace { + +class FakeCCSchedulerClient : public CCSchedulerClient { +public: + FakeCCSchedulerClient() { reset(); } + void reset() + { + m_actions.clear(); + m_hasMoreResourceUpdates = false; + m_canDraw = true; + m_drawWillHappen = true; + m_swapWillHappenIfDrawHappens = true; + m_numDraws = 0; + } + + void setHasMoreResourceUpdates(bool b) { m_hasMoreResourceUpdates = b; } + void setCanDraw(bool b) { m_canDraw = b; } + + int numDraws() const { return m_numDraws; } + int numActions() const { return static_cast<int>(m_actions.size()); } + const char* action(int i) const { return m_actions[i]; } + + bool hasAction(const char* action) const + { + for (size_t i = 0; i < m_actions.size(); i++) + if (!strcmp(m_actions[i], action)) + return true; + return false; + } + + virtual bool canDraw() OVERRIDE { return m_canDraw; } + virtual bool hasMoreResourceUpdates() const OVERRIDE { return m_hasMoreResourceUpdates; } + virtual void scheduledActionBeginFrame() OVERRIDE { m_actions.push_back("scheduledActionBeginFrame"); } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE + { + m_actions.push_back("scheduledActionDrawAndSwapIfPossible"); + m_numDraws++; + return CCScheduledActionDrawAndSwapResult(m_drawWillHappen, m_drawWillHappen && m_swapWillHappenIfDrawHappens); + } + + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE + { + m_actions.push_back("scheduledActionDrawAndSwapForced"); + return CCScheduledActionDrawAndSwapResult(true, m_swapWillHappenIfDrawHappens); + } + + virtual void scheduledActionUpdateMoreResources(double) OVERRIDE { m_actions.push_back("scheduledActionUpdateMoreResources"); } + virtual void scheduledActionCommit() OVERRIDE { m_actions.push_back("scheduledActionCommit"); } + virtual void scheduledActionBeginContextRecreation() OVERRIDE { m_actions.push_back("scheduledActionBeginContextRecreation"); } + virtual void scheduledActionAcquireLayerTexturesForMainThread() OVERRIDE { m_actions.push_back("scheduledActionAcquireLayerTexturesForMainThread"); } + + void setDrawWillHappen(bool drawWillHappen) { m_drawWillHappen = drawWillHappen; } + void setSwapWillHappenIfDrawHappens(bool swapWillHappenIfDrawHappens) { m_swapWillHappenIfDrawHappens = swapWillHappenIfDrawHappens; } + +protected: + bool m_hasMoreResourceUpdates; + bool m_canDraw; + bool m_drawWillHappen; + bool m_swapWillHappenIfDrawHappens; + int m_numDraws; + std::vector<const char*> m_actions; +}; + +TEST(CCSchedulerTest, RequestCommit) +{ + FakeCCSchedulerClient client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + // SetNeedsCommit should begin the frame. + scheduler->setNeedsCommit(); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(0)); + EXPECT_FALSE(timeSource->active()); + client.reset(); + + // Since, hasMoreResourceUpdates is set to false, + // beginFrameComplete should commit + scheduler->beginFrameComplete(); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionCommit", client.action(0)); + EXPECT_TRUE(timeSource->active()); + client.reset(); + + // Tick should draw. + timeSource->tick(); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0)); + EXPECT_FALSE(timeSource->active()); + client.reset(); + + // Timer should be off. + EXPECT_FALSE(timeSource->active()); +} + +TEST(CCSchedulerTest, RequestCommitAfterBeginFrame) +{ + FakeCCSchedulerClient client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + // SetNedsCommit should begin the frame. + scheduler->setNeedsCommit(); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(0)); + client.reset(); + + // Now setNeedsCommit again. Calling here means we need a second frame. + scheduler->setNeedsCommit(); + + // Since, hasMoreResourceUpdates is set to false, and another commit is + // needed, beginFrameComplete should commit, then begin another frame. + scheduler->beginFrameComplete(); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionCommit", client.action(0)); + client.reset(); + + // Tick should draw but then begin another frame. + timeSource->tick(); + EXPECT_FALSE(timeSource->active()); + EXPECT_EQ(2, client.numActions()); + EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0)); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(1)); + client.reset(); +} + +TEST(CCSchedulerTest, TextureAcquisitionCollision) +{ + FakeCCSchedulerClient client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + scheduler->setNeedsCommit(); + scheduler->setMainThreadNeedsLayerTextures(); + EXPECT_EQ(2, client.numActions()); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(0)); + EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(1)); + client.reset(); + + // Compositor not scheduled to draw because textures are locked by main thread + EXPECT_FALSE(timeSource->active()); + + // Trigger the commit + scheduler->beginFrameComplete(); + EXPECT_TRUE(timeSource->active()); + client.reset(); + + // Between commit and draw, texture acquisition for main thread delayed, + // and main thread blocks. + scheduler->setMainThreadNeedsLayerTextures(); + EXPECT_EQ(0, client.numActions()); + client.reset(); + + // Once compositor draw complete, the delayed texture acquisition fires. + timeSource->tick(); + EXPECT_EQ(3, client.numActions()); + EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0)); + EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(1)); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(2)); + client.reset(); +} + +TEST(CCSchedulerTest, VisibilitySwitchWithTextureAcquisition) +{ + FakeCCSchedulerClient client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + scheduler->setNeedsCommit(); + scheduler->beginFrameComplete(); + scheduler->setMainThreadNeedsLayerTextures(); + client.reset(); + // Verify that pending texture acquisition fires when visibility + // is lost in order to avoid a deadlock. + scheduler->setVisible(false); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(0)); + client.reset(); + + // Regaining visibility with textures acquired by main thread while + // compositor is waiting for first draw should result in a request + // for a new frame in order to escape a deadlock. + scheduler->setVisible(true); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(0)); + client.reset(); +} + +class SchedulerClientThatSetNeedsDrawInsideDraw : public FakeCCSchedulerClient { +public: + SchedulerClientThatSetNeedsDrawInsideDraw() + : m_scheduler(0) { } + + void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; } + + virtual void scheduledActionBeginFrame() OVERRIDE { } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE + { + // Only setNeedsRedraw the first time this is called + if (!m_numDraws) + m_scheduler->setNeedsRedraw(); + return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible(); + } + + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE + { + ASSERT_NOT_REACHED(); + return CCScheduledActionDrawAndSwapResult(true, true); + } + + virtual void scheduledActionUpdateMoreResources(double) OVERRIDE { } + virtual void scheduledActionCommit() OVERRIDE { } + virtual void scheduledActionBeginContextRecreation() OVERRIDE { } + +protected: + CCScheduler* m_scheduler; +}; + +// Tests for two different situations: +// 1. the scheduler dropping setNeedsRedraw requests that happen inside +// a scheduledActionDrawAndSwap +// 2. the scheduler drawing twice inside a single tick +TEST(CCSchedulerTest, RequestRedrawInsideDraw) +{ + SchedulerClientThatSetNeedsDrawInsideDraw client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_FALSE(scheduler->redrawPending()); + EXPECT_FALSE(timeSource->active()); +} + +// Test that requesting redraw inside a failed draw doesn't lose the request. +TEST(CCSchedulerTest, RequestRedrawInsideFailedDraw) +{ + SchedulerClientThatSetNeedsDrawInsideDraw client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + client.setDrawWillHappen(false); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + // Fail the draw. + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + + // We have a commit pending and the draw failed, and we didn't lose the redraw request. + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Fail the draw again. + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Draw successfully. + client.setDrawWillHappen(true); + timeSource->tick(); + EXPECT_EQ(3, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_FALSE(scheduler->redrawPending()); + EXPECT_FALSE(timeSource->active()); +} + +class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeCCSchedulerClient { +public: + SchedulerClientThatSetNeedsCommitInsideDraw() + : m_scheduler(0) { } + + void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; } + + virtual void scheduledActionBeginFrame() OVERRIDE { } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE + { + // Only setNeedsCommit the first time this is called + if (!m_numDraws) + m_scheduler->setNeedsCommit(); + return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible(); + } + + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE + { + ASSERT_NOT_REACHED(); + return CCScheduledActionDrawAndSwapResult(true, true); + } + + virtual void scheduledActionUpdateMoreResources(double) OVERRIDE { } + virtual void scheduledActionCommit() OVERRIDE { } + virtual void scheduledActionBeginContextRecreation() OVERRIDE { } + +protected: + CCScheduler* m_scheduler; +}; + +// Tests for the scheduler infinite-looping on setNeedsCommit requests that +// happen inside a scheduledActionDrawAndSwap +TEST(CCSchedulerTest, RequestCommitInsideDraw) +{ + SchedulerClientThatSetNeedsCommitInsideDraw client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_EQ(0, client.numDraws()); + EXPECT_TRUE(timeSource->active()); + + timeSource->tick(); + EXPECT_FALSE(timeSource->active()); + EXPECT_EQ(1, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + scheduler->beginFrameComplete(); + + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_FALSE(timeSource->active()); + EXPECT_FALSE(scheduler->redrawPending()); +} + +// Tests that when a draw fails then the pending commit should not be dropped. +TEST(CCSchedulerTest, RequestCommitInsideFailedDraw) +{ + SchedulerClientThatSetNeedsDrawInsideDraw client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + client.setDrawWillHappen(false); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + // Fail the draw. + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + + // We have a commit pending and the draw failed, and we didn't lose the commit request. + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Fail the draw again. + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Draw successfully. + client.setDrawWillHappen(true); + timeSource->tick(); + EXPECT_EQ(3, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_FALSE(scheduler->redrawPending()); + EXPECT_FALSE(timeSource->active()); +} + +TEST(CCSchedulerTest, NoBeginFrameWhenDrawFails) +{ + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + SchedulerClientThatSetNeedsCommitInsideDraw client; + OwnPtr<FakeCCFrameRateController> controller = adoptPtr(new FakeCCFrameRateController(timeSource)); + FakeCCFrameRateController* controllerPtr = controller.get(); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, controller.release()); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + EXPECT_EQ(0, controllerPtr->numFramesPending()); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + // Draw successfully, this starts a new frame. + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + EXPECT_EQ(1, controllerPtr->numFramesPending()); + scheduler->didSwapBuffersComplete(); + EXPECT_EQ(0, controllerPtr->numFramesPending()); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Fail to draw, this should not start a frame. + client.setDrawWillHappen(false); + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_EQ(0, controllerPtr->numFramesPending()); +} + +TEST(CCSchedulerTest, NoBeginFrameWhenSwapFailsDuringForcedCommit) +{ + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + FakeCCSchedulerClient client; + OwnPtr<FakeCCFrameRateController> controller = adoptPtr(new FakeCCFrameRateController(timeSource)); + FakeCCFrameRateController* controllerPtr = controller.get(); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, controller.release()); + + EXPECT_EQ(0, controllerPtr->numFramesPending()); + + // Tell the client that it will fail to swap. + client.setDrawWillHappen(true); + client.setSwapWillHappenIfDrawHappens(false); + + // Get the compositor to do a scheduledActionDrawAndSwapForced. + scheduler->setNeedsRedraw(); + scheduler->setNeedsForcedRedraw(); + EXPECT_TRUE(client.hasAction("scheduledActionDrawAndSwapForced")); + + // We should not have told the frame rate controller that we began a frame. + EXPECT_EQ(0, controllerPtr->numFramesPending()); +} + +} |