[chromium] Skip frames when checkerboarding an animation
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Mar 2012 01:51:35 +0000 (01:51 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Mar 2012 01:51:35 +0000 (01:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81716

Patch by Dana Jansens <danakj@chromium.org> on 2012-03-22
Reviewed by Adrienne Walker.

Source/WebCore:

This will stop drawing frames when prepareToDraw fails, if the draw is
not forced. The expected behaviour is outlined below by the unit tests.

When a draw fails, we:
1. Set m_needsRedraw to try again next vsync
2. Set m_needsCommit because we need more data from webkit to succeed
3. Set m_drawIfPossibleFailed. This allows us to try draw again within
the same vsync *if* a commit finishes during this time.

Unit test: CCSchedulerTest.RequestRedrawInsideFailedDraw
           CCSchedulerTest.RequestCommitInsideFailedDraw
           CCSchedulerTest.NoBeginFrameWhenDrawFails
           CCSchedulerStateMachineTest.TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain
           CCSchedulerStateMachineTest.TestSetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw
           CCSchedulerStateMachineTest.TestCommitAfterFailedDrawAllowsDrawInSameFrame
           CCSchedulerStateMachineTest.TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame
           CCSchedulerStateMachineTest.TestFailedDrawIsRetriedNextVSync

* platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
(WebCore::CCLayerTreeHostImpl::calculateRenderPasses):
(WebCore::CCLayerTreeHostImpl::prepareToDraw):
* platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:
(CCLayerTreeHostImpl):
* platform/graphics/chromium/cc/CCScheduler.cpp:
(WebCore::CCScheduler::processScheduledActions):
* platform/graphics/chromium/cc/CCScheduler.h:
(CCSchedulerClient):
* platform/graphics/chromium/cc/CCSchedulerStateMachine.cpp:
(WebCore::CCSchedulerStateMachine::CCSchedulerStateMachine):
(WebCore::CCSchedulerStateMachine::nextAction):
(WebCore::CCSchedulerStateMachine::updateState):
(WebCore::CCSchedulerStateMachine::didDrawIfPossibleCompleted):
(WebCore):
* platform/graphics/chromium/cc/CCSchedulerStateMachine.h:
(CCSchedulerStateMachine):
* platform/graphics/chromium/cc/CCThreadProxy.cpp:
(WebCore::CCThreadProxy::scheduledActionDrawAndSwapInternal):
(WebCore):
(WebCore::CCThreadProxy::scheduledActionDrawAndSwapIfPossible):
(WebCore::CCThreadProxy::scheduledActionDrawAndSwapForced):
* platform/graphics/chromium/cc/CCThreadProxy.h:
(CCThreadProxy):

Source/WebKit/chromium:

* tests/CCLayerTreeHostImplTest.cpp:
(WebKitTests::TEST_F):
* tests/CCSchedulerStateMachineTest.cpp:
(WebCore::TEST):
(WebCore):
* tests/CCSchedulerTest.cpp:
(WebKitTests::FakeCCSchedulerClient::reset):
(WebKitTests::FakeCCSchedulerClient::numDraws):
(WebKitTests::FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible):
(FakeCCSchedulerClient):
(WebKitTests::FakeCCSchedulerClient::scheduledActionDrawAndSwapForced):
(WebKitTests::FakeCCSchedulerClient::setDrawSuccess):
(WebKitTests::TEST):
(WebKitTests::SchedulerClientThatSetNeedsDrawInsideDraw::SchedulerClientThatSetNeedsDrawInsideDraw):
(WebKitTests::SchedulerClientThatSetNeedsDrawInsideDraw::scheduledActionDrawAndSwapIfPossible):
(WebKitTests::SchedulerClientThatSetNeedsDrawInsideDraw::scheduledActionDrawAndSwapForced):
(SchedulerClientThatSetNeedsDrawInsideDraw):
(WebKitTests):
(WebKitTests::SchedulerClientThatSetNeedsCommitInsideDraw::SchedulerClientThatSetNeedsCommitInsideDraw):
(WebKitTests::SchedulerClientThatSetNeedsCommitInsideDraw::scheduledActionDrawAndSwapIfPossible):
(WebKitTests::SchedulerClientThatSetNeedsCommitInsideDraw::scheduledActionDrawAndSwapForced):
(SchedulerClientThatSetNeedsCommitInsideDraw):
* tests/CCSchedulerTestCommon.h:
(FakeCCFrameRateController):
(WebKitTests::FakeCCFrameRateController::FakeCCFrameRateController):
(WebKitTests::FakeCCFrameRateController::numFramesPending):
(WebKitTests):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111805 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h
Source/WebCore/platform/graphics/chromium/cc/CCScheduler.cpp
Source/WebCore/platform/graphics/chromium/cc/CCScheduler.h
Source/WebCore/platform/graphics/chromium/cc/CCSchedulerStateMachine.cpp
Source/WebCore/platform/graphics/chromium/cc/CCSchedulerStateMachine.h
Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp
Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp
Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp
Source/WebKit/chromium/tests/CCSchedulerTest.cpp
Source/WebKit/chromium/tests/CCSchedulerTestCommon.h

index 37b72a8..9b0998d 100644 (file)
@@ -1,3 +1,53 @@
+2012-03-22  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Skip frames when checkerboarding an animation
+        https://bugs.webkit.org/show_bug.cgi?id=81716
+
+        Reviewed by Adrienne Walker.
+
+        This will stop drawing frames when prepareToDraw fails, if the draw is
+        not forced. The expected behaviour is outlined below by the unit tests.
+
+        When a draw fails, we:
+        1. Set m_needsRedraw to try again next vsync
+        2. Set m_needsCommit because we need more data from webkit to succeed
+        3. Set m_drawIfPossibleFailed. This allows us to try draw again within
+        the same vsync *if* a commit finishes during this time.
+
+        Unit test: CCSchedulerTest.RequestRedrawInsideFailedDraw
+                   CCSchedulerTest.RequestCommitInsideFailedDraw
+                   CCSchedulerTest.NoBeginFrameWhenDrawFails
+                   CCSchedulerStateMachineTest.TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain
+                   CCSchedulerStateMachineTest.TestSetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw
+                   CCSchedulerStateMachineTest.TestCommitAfterFailedDrawAllowsDrawInSameFrame
+                   CCSchedulerStateMachineTest.TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame
+                   CCSchedulerStateMachineTest.TestFailedDrawIsRetriedNextVSync
+
+        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
+        (WebCore::CCLayerTreeHostImpl::calculateRenderPasses):
+        (WebCore::CCLayerTreeHostImpl::prepareToDraw):
+        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:
+        (CCLayerTreeHostImpl):
+        * platform/graphics/chromium/cc/CCScheduler.cpp:
+        (WebCore::CCScheduler::processScheduledActions):
+        * platform/graphics/chromium/cc/CCScheduler.h:
+        (CCSchedulerClient):
+        * platform/graphics/chromium/cc/CCSchedulerStateMachine.cpp:
+        (WebCore::CCSchedulerStateMachine::CCSchedulerStateMachine):
+        (WebCore::CCSchedulerStateMachine::nextAction):
+        (WebCore::CCSchedulerStateMachine::updateState):
+        (WebCore::CCSchedulerStateMachine::didDrawIfPossibleCompleted):
+        (WebCore):
+        * platform/graphics/chromium/cc/CCSchedulerStateMachine.h:
+        (CCSchedulerStateMachine):
+        * platform/graphics/chromium/cc/CCThreadProxy.cpp:
+        (WebCore::CCThreadProxy::scheduledActionDrawAndSwapInternal):
+        (WebCore):
+        (WebCore::CCThreadProxy::scheduledActionDrawAndSwapIfPossible):
+        (WebCore::CCThreadProxy::scheduledActionDrawAndSwapForced):
+        * platform/graphics/chromium/cc/CCThreadProxy.h:
+        (CCThreadProxy):
+
 2012-03-22  W. James MacLean  <wjmaclean@chromium.org>
 
         [chromium] Force update of nonFastScrollableRegion if target CCLayerImpl has been freshly created.
index 4f6ec73..5949395 100644 (file)
@@ -275,7 +275,9 @@ bool CCLayerTreeHostImpl::calculateRenderPasses(CCRenderPassList& passes, CCLaye
     // Add quads to the Render passes in FrontToBack order to allow for testing occlusion and performing culling during the tree walk.
     typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType;
 
-    // If we are unable to draw an animation on some layer, then we abort the entire frame.
+    // Typically when we are missing a texture and use a checkerboard quad, we still draw the frame. However when the layer being
+    // checkerboarded is moving due to an impl-animation, we drop the frame to avoid flashing due to the texture suddenly appearing
+    // in the future.
     bool drawFrame = true;
 
     CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
@@ -306,10 +308,8 @@ bool CCLayerTreeHostImpl::calculateRenderPasses(CCRenderPassList& passes, CCLaye
         pass->appendQuadsForLayer(*it, &occlusionTracker, usedCheckerboard);
         if (usedCheckerboard) {
             bool layerHasAnimatingTransform = it->screenSpaceTransformIsAnimating() || it->drawTransformIsAnimating();
-            if (layerHasAnimatingTransform) {
+            if (layerHasAnimatingTransform)
                 drawFrame = false;
-                break;
-            }
         }
 
         occlusionTracker.markOccludedBehindLayer(*it);
@@ -367,11 +367,8 @@ bool CCLayerTreeHostImpl::prepareToDraw(FrameData& frame)
     if (!rootLayer())
         return false;
 
-    if (!calculateRenderPasses(frame.renderPasses, frame.renderSurfaceLayerList)) {
-        frame.renderPasses.clear();
-        frame.renderSurfaceLayerList.clear();
+    if (!calculateRenderPasses(frame.renderPasses, frame.renderSurfaceLayerList))
         return false;
-    }
 
     // If we return true, then we expect drawLayers() to be called before this function is called again.
     return true;
index 77eb892..33c9e91 100644 (file)
@@ -88,6 +88,7 @@ public:
     virtual void beginCommit();
     virtual void commitComplete();
     virtual void animate(double monotonicTime, double wallClockTime);
+    // Returns false if problems occured preparing the frame, and we should try to avoid displaying the frame.
     virtual bool prepareToDraw(FrameData&);
     virtual void drawLayers(const FrameData&);
 
index 61bd931..13b741e 100644 (file)
@@ -162,8 +162,15 @@ void CCScheduler::processScheduledActions()
         case CCSchedulerStateMachine::ACTION_COMMIT:
             m_client->scheduledActionCommit();
             break;
-        case CCSchedulerStateMachine::ACTION_DRAW:
-            m_client->scheduledActionDrawAndSwap();
+        case CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: {
+            bool drawSuccess = m_client->scheduledActionDrawAndSwapIfPossible();
+            m_stateMachine.didDrawIfPossibleCompleted(drawSuccess);
+            if (drawSuccess)
+                m_frameRateController->didBeginFrame();
+            break;
+        }
+        case CCSchedulerStateMachine::ACTION_DRAW_FORCED:
+            m_client->scheduledActionDrawAndSwapForced();
             m_frameRateController->didBeginFrame();
             break;
         case CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION:
index 8ef5dcf..c876e20 100644 (file)
@@ -41,7 +41,8 @@ public:
     virtual bool hasMoreResourceUpdates() const = 0;
 
     virtual void scheduledActionBeginFrame() = 0;
-    virtual void scheduledActionDrawAndSwap() = 0;
+    virtual bool scheduledActionDrawAndSwapIfPossible() = 0;
+    virtual void scheduledActionDrawAndSwapForced() = 0;
     virtual void scheduledActionUpdateMoreResources() = 0;
     virtual void scheduledActionCommit() = 0;
     virtual void scheduledActionBeginContextRecreation() = 0;
index f37f0b4..1c942e3 100644 (file)
@@ -40,6 +40,7 @@ CCSchedulerStateMachine::CCSchedulerStateMachine()
     , m_insideVSync(false)
     , m_visible(false)
     , m_canDraw(true)
+    , m_drawIfPossibleFailed(false)
     , m_contextState(CONTEXT_ACTIVE)
 {
 }
@@ -71,11 +72,10 @@ bool CCSchedulerStateMachine::shouldDraw() const
 
 CCSchedulerStateMachine::Action CCSchedulerStateMachine::nextAction() const
 {
-
     switch (m_commitState) {
     case COMMIT_STATE_IDLE:
         if (m_contextState != CONTEXT_ACTIVE && m_needsForcedRedraw)
-            return ACTION_DRAW;
+            return ACTION_DRAW_FORCED;
         if (m_contextState != CONTEXT_ACTIVE && m_needsForcedCommit)
             return ACTION_BEGIN_FRAME;
         if (m_contextState == CONTEXT_LOST)
@@ -83,19 +83,19 @@ CCSchedulerStateMachine::Action CCSchedulerStateMachine::nextAction() const
         if (m_contextState == CONTEXT_RECREATING)
             return ACTION_NONE;
         if (shouldDraw())
-            return ACTION_DRAW;
+            return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
         if (m_needsCommit && (m_visible || m_needsForcedCommit))
             return ACTION_BEGIN_FRAME;
         return ACTION_NONE;
 
     case COMMIT_STATE_FRAME_IN_PROGRESS:
         if (shouldDraw())
-            return ACTION_DRAW;
+            return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
         return ACTION_NONE;
 
     case COMMIT_STATE_UPDATING_RESOURCES:
         if (shouldDraw())
-            return ACTION_DRAW;
+            return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
         if (!m_updateMoreResourcesPending)
             return ACTION_BEGIN_UPDATE_MORE_RESOURCES;
         return ACTION_NONE;
@@ -105,7 +105,7 @@ CCSchedulerStateMachine::Action CCSchedulerStateMachine::nextAction() const
 
     case COMMIT_STATE_WAITING_FOR_FIRST_DRAW:
         if (shouldDraw() || m_contextState == CONTEXT_LOST)
-            return ACTION_DRAW;
+            return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
         // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If m_canDraw is false,
         // proceed to the next step (similar as in COMMIT_STATE_IDLE).
         if (!m_canDraw && m_needsCommit && (m_visible || m_needsForcedCommit))
@@ -140,11 +140,15 @@ void CCSchedulerStateMachine::updateState(Action action)
         else
             m_commitState = COMMIT_STATE_IDLE;
         m_needsRedraw = true;
+        if (m_drawIfPossibleFailed)
+            m_lastFrameNumberWhereDrawWasCalled = -1;
         return;
 
-    case ACTION_DRAW:
+    case ACTION_DRAW_FORCED:
+    case ACTION_DRAW_IF_POSSIBLE:
         m_needsRedraw = false;
         m_needsForcedRedraw = false;
+        m_drawIfPossibleFailed = false;
         if (m_insideVSync)
             m_lastFrameNumberWhereDrawWasCalled = m_currentFrameNumber;
         if (m_commitState == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) {
@@ -199,6 +203,15 @@ void CCSchedulerStateMachine::setNeedsForcedRedraw()
     m_needsForcedRedraw = true;
 }
 
+void CCSchedulerStateMachine::didDrawIfPossibleCompleted(bool success)
+{
+    m_drawIfPossibleFailed = !success;
+    if (m_drawIfPossibleFailed) {
+        m_needsRedraw = true;
+        m_needsCommit = true;
+    }
+}
+
 void CCSchedulerStateMachine::setNeedsCommit()
 {
     m_needsCommit = true;
index 61bec39..2ecfd1c 100644 (file)
@@ -69,7 +69,8 @@ public:
         ACTION_BEGIN_FRAME,
         ACTION_BEGIN_UPDATE_MORE_RESOURCES,
         ACTION_COMMIT,
-        ACTION_DRAW,
+        ACTION_DRAW_IF_POSSIBLE,
+        ACTION_DRAW_FORCED,
         ACTION_BEGIN_CONTEXT_RECREATION
     };
     Action nextAction() const;
@@ -95,6 +96,9 @@ public:
     // we are not visible.
     void setNeedsForcedRedraw();
 
+    // Indicates whether ACTION_DRAW_IF_POSSIBLE drew to the screen or not.
+    void didDrawIfPossibleCompleted(bool success);
+
     // Indicates that a new commit flow needs to be performed, either to pull
     // updates from the main thread to the impl, or to push deltas from the impl
     // thread to main.
@@ -122,6 +126,7 @@ public:
     void didRecreateContext();
 
 protected:
+    bool shouldDrawForced() const;
     bool shouldDraw() const;
     bool hasDrawnThisFrame() const;
 
@@ -137,6 +142,7 @@ protected:
     bool m_insideVSync;
     bool m_visible;
     bool m_canDraw;
+    bool m_drawIfPossibleFailed;
     ContextState m_contextState;
 };
 
index 8270740..dcfa3bd 100644 (file)
@@ -544,12 +544,13 @@ void CCThreadProxy::scheduledActionBeginContextRecreation()
     m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::beginContextRecreation));
 }
 
-void CCThreadProxy::scheduledActionDrawAndSwap()
+bool CCThreadProxy::scheduledActionDrawAndSwapInternal(bool forcedDraw)
 {
     TRACE_EVENT("CCThreadProxy::scheduledActionDrawAndSwap", this, 0);
     ASSERT(isImplThread());
+    ASSERT(m_layerTreeHostImpl);
     if (!m_layerTreeHostImpl)
-        return;
+        return false;
 
     // FIXME: compute the frame display time more intelligently
     double monotonicTime = monotonicallyIncreasingTime();
@@ -558,21 +559,25 @@ void CCThreadProxy::scheduledActionDrawAndSwap()
     m_inputHandlerOnImplThread->animate(monotonicTime);
     m_layerTreeHostImpl->animate(monotonicTime, wallClockTime);
     CCLayerTreeHostImpl::FrameData frame;
-    m_layerTreeHostImpl->prepareToDraw(frame);
-    m_layerTreeHostImpl->drawLayers(frame);
+    bool drawFrame = m_layerTreeHostImpl->prepareToDraw(frame) || forcedDraw;
+    if (drawFrame)
+        m_layerTreeHostImpl->drawLayers(frame);
 
     // Check for a pending compositeAndReadback.
     if (m_readbackRequestOnImplThread) {
+        ASSERT(drawFrame); // This should be a forcedDraw
         m_layerTreeHostImpl->readback(m_readbackRequestOnImplThread->pixels, m_readbackRequestOnImplThread->rect);
         m_readbackRequestOnImplThread->success = !m_layerTreeHostImpl->isContextLost();
         m_readbackRequestOnImplThread->completion.signal();
         m_readbackRequestOnImplThread = 0;
     }
 
-    m_layerTreeHostImpl->swapBuffers();
+    if (drawFrame)
+        m_layerTreeHostImpl->swapBuffers();
 
     // Process any finish request
     if (m_finishAllRenderingCompletionEventOnImplThread) {
+        ASSERT(drawFrame); // This should be a forcedDraw
         m_layerTreeHostImpl->finishAllRendering();
         m_finishAllRenderingCompletionEventOnImplThread->signal();
         m_finishAllRenderingCompletionEventOnImplThread = 0;
@@ -583,6 +588,19 @@ void CCThreadProxy::scheduledActionDrawAndSwap()
         m_nextFrameIsNewlyCommittedFrameOnImplThread = false;
         m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::didCommitAndDrawFrame));
     }
+
+    ASSERT(drawFrame || (!drawFrame && !forcedDraw));
+    return drawFrame;
+}
+
+bool CCThreadProxy::scheduledActionDrawAndSwapIfPossible()
+{
+    return scheduledActionDrawAndSwapInternal(false);
+}
+
+void CCThreadProxy::scheduledActionDrawAndSwapForced()
+{
+    scheduledActionDrawAndSwapInternal(true);
 }
 
 void CCThreadProxy::didCommitAndDrawFrame()
index e7bada6..4861f79 100644 (file)
@@ -81,7 +81,8 @@ public:
     virtual bool canDraw();
     virtual bool hasMoreResourceUpdates() const;
     virtual void scheduledActionBeginFrame();
-    virtual void scheduledActionDrawAndSwap();
+    virtual bool scheduledActionDrawAndSwapIfPossible();
+    virtual void scheduledActionDrawAndSwapForced();
     virtual void scheduledActionUpdateMoreResources();
     virtual void scheduledActionCommit();
     virtual void scheduledActionBeginContextRecreation();
@@ -125,6 +126,7 @@ private:
     void layerTreeHostClosedOnImplThread(CCCompletionEvent*);
     void setFullRootLayerDamageOnImplThread();
     void recreateContextOnImplThread(CCCompletionEvent*, GraphicsContext3D*, bool* recreateSucceeded, LayerRendererCapabilities*);
+    bool scheduledActionDrawAndSwapInternal(bool forcedDraw);
 
     // Accessed on main thread only.
     bool m_animateRequested;
index 3e9f5d4..328297d 100644 (file)
@@ -1,3 +1,38 @@
+2012-03-22  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Skip frames when checkerboarding an animation
+        https://bugs.webkit.org/show_bug.cgi?id=81716
+
+        Reviewed by Adrienne Walker.
+
+        * tests/CCLayerTreeHostImplTest.cpp:
+        (WebKitTests::TEST_F):
+        * tests/CCSchedulerStateMachineTest.cpp:
+        (WebCore::TEST):
+        (WebCore):
+        * tests/CCSchedulerTest.cpp:
+        (WebKitTests::FakeCCSchedulerClient::reset):
+        (WebKitTests::FakeCCSchedulerClient::numDraws):
+        (WebKitTests::FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible):
+        (FakeCCSchedulerClient):
+        (WebKitTests::FakeCCSchedulerClient::scheduledActionDrawAndSwapForced):
+        (WebKitTests::FakeCCSchedulerClient::setDrawSuccess):
+        (WebKitTests::TEST):
+        (WebKitTests::SchedulerClientThatSetNeedsDrawInsideDraw::SchedulerClientThatSetNeedsDrawInsideDraw):
+        (WebKitTests::SchedulerClientThatSetNeedsDrawInsideDraw::scheduledActionDrawAndSwapIfPossible):
+        (WebKitTests::SchedulerClientThatSetNeedsDrawInsideDraw::scheduledActionDrawAndSwapForced):
+        (SchedulerClientThatSetNeedsDrawInsideDraw):
+        (WebKitTests):
+        (WebKitTests::SchedulerClientThatSetNeedsCommitInsideDraw::SchedulerClientThatSetNeedsCommitInsideDraw):
+        (WebKitTests::SchedulerClientThatSetNeedsCommitInsideDraw::scheduledActionDrawAndSwapIfPossible):
+        (WebKitTests::SchedulerClientThatSetNeedsCommitInsideDraw::scheduledActionDrawAndSwapForced):
+        (SchedulerClientThatSetNeedsCommitInsideDraw):
+        * tests/CCSchedulerTestCommon.h:
+        (FakeCCFrameRateController):
+        (WebKitTests::FakeCCFrameRateController::FakeCCFrameRateController):
+        (WebKitTests::FakeCCFrameRateController::numFramesPending):
+        (WebKitTests):
+
 2012-03-22  W. James MacLean  <wjmaclean@chromium.org>
 
         [chromium] Force update of nonFastScrollableRegion if target CCLayerImpl has been freshly created.
index a57b5b3..ea98758 100644 (file)
@@ -544,13 +544,14 @@ TEST_F(CCLayerTreeHostImplTest, prepareToDrawFailsWhenAnimationUsesCheckerboard)
     EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
     m_hostImpl->drawLayers(frame);
 
-    // When a texture is missing and we're animating, we don't draw anything.
+    // When a texture is missing and we're animating, we don't want to draw anything.
     m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0));
     root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
     root->addChild(MissingTextureAnimatingLayer::create(1, true, false, true));
     layer = static_cast<MissingTextureAnimatingLayer*>(root->children()[0].get());
 
     EXPECT_FALSE(m_hostImpl->prepareToDraw(frame));
+    m_hostImpl->drawLayers(frame);
 
     // When the layer skips draw and we're animating, we still draw the frame.
     m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0));
index ea6b927..e4a5d7c 100644 (file)
@@ -119,6 +119,175 @@ TEST(CCSchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw)
     EXPECT_TRUE(state.vsyncCallbackNeeded());
 }
 
+TEST(CCSchedulerStateMachineTest, TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain)
+{
+    CCSchedulerStateMachine state;
+    state.setVisible(true);
+    state.setNeedsRedraw();
+    EXPECT_TRUE(state.redrawPending());
+    EXPECT_TRUE(state.vsyncCallbackNeeded());
+    state.didEnterVSync();
+
+    // We're drawing now.
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+    EXPECT_FALSE(state.redrawPending());
+    EXPECT_FALSE(state.commitPending());
+
+    // Failing the draw makes us require a commit.
+    state.didDrawIfPossibleCompleted(false);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+    EXPECT_TRUE(state.redrawPending());
+    EXPECT_TRUE(state.commitPending());
+}
+
+TEST(CCSchedulerStateMachineTest, TestSetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw)
+{
+    CCSchedulerStateMachine state;
+    state.setVisible(true);
+    state.setNeedsRedraw();
+    EXPECT_TRUE(state.redrawPending());
+    EXPECT_TRUE(state.vsyncCallbackNeeded());
+    state.didEnterVSync();
+
+    // We're drawing now.
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+    EXPECT_FALSE(state.redrawPending());
+    EXPECT_FALSE(state.commitPending());
+
+    // While still in the same vsync callback, set needs redraw again.
+    // This should not redraw.
+    state.setNeedsRedraw();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+    // Failing the draw makes us require a commit.
+    state.didDrawIfPossibleCompleted(false);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+    EXPECT_TRUE(state.redrawPending());
+}
+
+TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedDrawAllowsDrawInSameFrame)
+{
+    CCSchedulerStateMachine state;
+    state.setVisible(true);
+
+    // Start a commit.
+    state.setNeedsCommit();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+    EXPECT_TRUE(state.commitPending());
+
+    // Then initiate a draw.
+    state.setNeedsRedraw();
+    EXPECT_TRUE(state.vsyncCallbackNeeded());
+    state.didEnterVSync();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    EXPECT_TRUE(state.redrawPending());
+
+    // Fail the draw.
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+    state.didDrawIfPossibleCompleted(false);
+    EXPECT_TRUE(state.redrawPending());
+    // But the commit is ongoing.
+    EXPECT_TRUE(state.commitPending());
+
+    // Finish the commit.
+    state.beginFrameComplete();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+    state.beginUpdateMoreResourcesComplete(false);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+    EXPECT_TRUE(state.redrawPending());
+
+    // And we should be allowed to draw again.
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame)
+{
+    CCSchedulerStateMachine state;
+    state.setVisible(true);
+
+    // Start a commit.
+    state.setNeedsCommit();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+    EXPECT_TRUE(state.commitPending());
+
+    // Then initiate a draw.
+    state.setNeedsRedraw();
+    EXPECT_TRUE(state.vsyncCallbackNeeded());
+    state.didEnterVSync();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    EXPECT_TRUE(state.redrawPending());
+
+    // Fail the draw.
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+    state.didDrawIfPossibleCompleted(false);
+    EXPECT_TRUE(state.redrawPending());
+    // But the commit is ongoing.
+    EXPECT_TRUE(state.commitPending());
+
+    // Force a draw.
+    state.setNeedsForcedRedraw();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
+
+    // Do the forced draw.
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_FORCED);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+    EXPECT_FALSE(state.redrawPending());
+    // And the commit is still ongoing.
+    EXPECT_TRUE(state.commitPending());
+
+    // Finish the commit.
+    state.beginFrameComplete();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+    state.beginUpdateMoreResourcesComplete(false);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+    EXPECT_TRUE(state.redrawPending());
+
+    // And we should not be allowed to draw again in the same frame..
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestFailedDrawIsRetriedNextVSync)
+{
+    CCSchedulerStateMachine state;
+    state.setVisible(true);
+
+    // Start a draw.
+    state.setNeedsRedraw();
+    EXPECT_TRUE(state.vsyncCallbackNeeded());
+    state.didEnterVSync();
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    EXPECT_TRUE(state.redrawPending());
+
+    // Fail the draw.
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+    state.didDrawIfPossibleCompleted(false);
+    EXPECT_TRUE(state.redrawPending());
+
+    // We should not be trying to draw again now, but we have a commit pending.
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+
+    state.didLeaveVSync();
+    EXPECT_TRUE(state.vsyncCallbackNeeded());
+    state.didEnterVSync();
+
+    // We should try draw again in the next vsync.
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+}
+
 TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame)
 {
     CCSchedulerStateMachine state;
@@ -126,8 +295,8 @@ TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame)
     state.setNeedsRedraw();
     EXPECT_TRUE(state.vsyncCallbackNeeded());
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
-    state.updateState(CCSchedulerStateMachine::ACTION_DRAW);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
 
     // While still in the same vsync callback, set needs redraw again.
     // This should not redraw.
@@ -135,12 +304,14 @@ TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame)
     EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
 
     // Move to another frame. This should now draw.
+    state.didDrawIfPossibleCompleted(true);
     state.didLeaveVSync();
     EXPECT_TRUE(state.vsyncCallbackNeeded());
     state.didEnterVSync();
 
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
-    state.updateState(CCSchedulerStateMachine::ACTION_DRAW);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    state.didDrawIfPossibleCompleted(true);
     EXPECT_FALSE(state.vsyncCallbackNeeded());
 }
 
@@ -161,11 +332,11 @@ TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync)
 
             // Case 1: needsCommit=false
             state.setNeedsCommit(false);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
 
             // Case 2: needsCommit=true
             state.setNeedsCommit(true);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
         }
     }
 
@@ -174,7 +345,8 @@ TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync)
         for (unsigned j = 0; j < 2; ++j) {
             StateMachine state;
             state.setCommitState(allCommitStates[i]);
-            if (!j) {
+            bool forcedDraw = j;
+            if (!forcedDraw) {
                 state.didEnterVSync();
                 state.setNeedsRedraw(true);
                 state.setVisible(true);
@@ -183,7 +355,7 @@ TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync)
 
             CCSchedulerStateMachine::Action expectedAction;
             if (allCommitStates[i] != CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT)
-                expectedAction = CCSchedulerStateMachine::ACTION_DRAW;
+                expectedAction = forcedDraw ? CCSchedulerStateMachine::ACTION_DRAW_FORCED : CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE;
             else
                 expectedAction = CCSchedulerStateMachine::ACTION_COMMIT;
 
@@ -231,22 +403,22 @@ TEST(CCSchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible)
             // Case 1: needsCommit=false updateMoreResourcesPending=false.
             state.setNeedsCommit(false);
             state.setUpdateMoreResourcesPending(false);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
 
             // Case 2: needsCommit=false updateMoreResourcesPending=true.
             state.setNeedsCommit(false);
             state.setUpdateMoreResourcesPending(true);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
 
             // Case 3: needsCommit=true updateMoreResourcesPending=false.
             state.setNeedsCommit(true);
             state.setUpdateMoreResourcesPending(false);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
 
             // Case 4: needsCommit=true updateMoreResourcesPending=true.
             state.setNeedsCommit(true);
             state.setUpdateMoreResourcesPending(true);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
         }
     }
 }
@@ -266,7 +438,7 @@ TEST(CCSchedulerStateMachineTest, TestCanRedraw_StopsDraw)
                 state.didEnterVSync();
 
             state.setCanDraw(false);
-            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+            EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
         }
     }
 }
@@ -381,8 +553,9 @@ TEST(CCSchedulerStateMachineTest, TestUpdates_WithRedraw_OneRoundOfUpdates)
 
     // Ensure we draw on the next vsync even though an update is in-progress.
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
-    state.updateState(CCSchedulerStateMachine::ACTION_DRAW);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    state.didDrawIfPossibleCompleted(true);
 
     // Ensure that we once we have drawn, we dont do anything else.
     EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
@@ -446,9 +619,10 @@ TEST(CCSchedulerStateMachineTest, TestSetNeedsCommitIsNotLost)
 
     // Commit and make sure we draw on next vsync
     state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
     EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState());
-    state.updateState(CCSchedulerStateMachine::ACTION_DRAW);
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    state.didDrawIfPossibleCompleted(true);
 
     // Verify that another commit will begin.
     state.didLeaveVSync();
@@ -491,8 +665,9 @@ TEST(CCSchedulerStateMachineTest, TestFullCycle)
 
     // At vsync, draw.
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
-    state.updateState(CCSchedulerStateMachine::ACTION_DRAW);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    state.didDrawIfPossibleCompleted(true);
     state.didLeaveVSync();
 
     // Should be synchronized, no draw needed, no action needed.
@@ -541,8 +716,9 @@ TEST(CCSchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween)
 
     // At vsync, draw.
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
-    state.updateState(CCSchedulerStateMachine::ACTION_DRAW);
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+    state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+    state.didDrawIfPossibleCompleted(true);
     state.didLeaveVSync();
 
     // Should be synchronized, no draw needed, no action needed.
@@ -652,7 +828,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhile
     // setCanDraw.
     state.setNeedsRedraw(true);
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
     state.setCanDraw(false);
     EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
     state.setCanDraw(true);
@@ -672,7 +848,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgress)
     // Set damage and expect a draw.
     state.setNeedsRedraw(true);
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
     state.updateState(state.nextAction());
     state.didLeaveVSync();
 
@@ -712,7 +888,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnother
     // Set damage and expect a draw.
     state.setNeedsRedraw(true);
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
     state.updateState(state.nextAction());
     state.didLeaveVSync();
 
@@ -738,7 +914,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnother
 
     EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState());
 
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
     state.updateState(state.nextAction());
 
     // Expect to be told to begin context recreation, independent of vsync state
@@ -760,7 +936,7 @@ TEST(CCSchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost)
     // Ask a forced redraw and verify it ocurrs.
     state.setNeedsForcedRedraw(true);
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
     state.didLeaveVSync();
 
     // Clear the forced redraw bit.
@@ -773,7 +949,7 @@ TEST(CCSchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost)
     // Ask a forced redraw and verify it ocurrs.
     state.setNeedsForcedRedraw(true);
     state.didEnterVSync();
-    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction());
+    EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
     state.didLeaveVSync();
 }
 
index 45f61b9..8df5a11 100644 (file)
@@ -45,25 +45,44 @@ public:
         m_actions.clear();
         m_hasMoreResourceUpdates = false;
         m_canDraw = true;
+        m_drawSuccess = 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]; }
 
     virtual bool canDraw() { return m_canDraw; }
     virtual bool hasMoreResourceUpdates() const { return m_hasMoreResourceUpdates; }
     virtual void scheduledActionBeginFrame() { m_actions.push_back("scheduledActionBeginFrame"); }
-    virtual void scheduledActionDrawAndSwap() { m_actions.push_back("scheduledActionDrawAndSwap"); }
+    virtual bool scheduledActionDrawAndSwapIfPossible()
+    {
+        m_actions.push_back("scheduledActionDrawAndSwapIfPossible");
+        m_numDraws++;
+        return m_drawSuccess;
+    }
+
+    virtual void scheduledActionDrawAndSwapForced()
+    {
+        m_actions.push_back("scheduledActionDrawAndSwapForced");
+        m_numDraws++;
+    }
+
     virtual void scheduledActionUpdateMoreResources() { m_actions.push_back("scheduledActionUpdateMoreResources"); }
     virtual void scheduledActionCommit() { m_actions.push_back("scheduledActionCommit"); }
     virtual void scheduledActionBeginContextRecreation() { m_actions.push_back("scheduledActionBeginContextRecreation"); }
 
+    void setDrawSuccess(bool drawSuccess) { m_drawSuccess = drawSuccess; }
+
 protected:
     bool m_hasMoreResourceUpdates;
     bool m_canDraw;
+    bool m_drawSuccess;
+    int m_numDraws;
     std::vector<const char*> m_actions;
 };
 
@@ -94,7 +113,7 @@ TEST(CCSchedulerTest, RequestCommit)
     // Tick should draw.
     timeSource->tick();
     EXPECT_EQ(1, client.numActions());
-    EXPECT_STREQ("scheduledActionDrawAndSwap", client.action(0));
+    EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0));
     EXPECT_FALSE(timeSource->active());
     client.reset();
 
@@ -131,38 +150,33 @@ TEST(CCSchedulerTest, RequestCommitAfterBeginFrame)
     timeSource->tick();
     EXPECT_FALSE(timeSource->active());
     EXPECT_EQ(2, client.numActions());
-    EXPECT_STREQ("scheduledActionDrawAndSwap", client.action(0));
+    EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0));
     EXPECT_STREQ("scheduledActionBeginFrame", client.action(1));
     client.reset();
 }
 
-class SchedulerClientThatSetNeedsDrawInsideDraw : public CCSchedulerClient {
+class SchedulerClientThatSetNeedsDrawInsideDraw : public FakeCCSchedulerClient {
 public:
     SchedulerClientThatSetNeedsDrawInsideDraw()
-        : m_numDraws(0)
-        , m_scheduler(0) { }
+        : m_scheduler(0) { }
 
     void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; }
 
-    int numDraws() const { return m_numDraws; }
-
-    virtual bool hasMoreResourceUpdates() const { return false; }
-    virtual bool canDraw() { return true; }
     virtual void scheduledActionBeginFrame() { }
-    virtual void scheduledActionDrawAndSwap()
+    virtual bool scheduledActionDrawAndSwapIfPossible()
     {
         // Only setNeedsRedraw the first time this is called
         if (!m_numDraws)
             m_scheduler->setNeedsRedraw();
-        m_numDraws++;
+        return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible();
     }
 
+    virtual void scheduledActionDrawAndSwapForced() { }
     virtual void scheduledActionUpdateMoreResources() { }
     virtual void scheduledActionCommit() { }
     virtual void scheduledActionBeginContextRecreation() { }
 
 protected:
-    int m_numDraws;
     CCScheduler* m_scheduler;
 };
 
@@ -194,33 +208,68 @@ TEST(CCSchedulerTest, RequestRedrawInsideDraw)
     EXPECT_FALSE(timeSource->active());
 }
 
-class SchedulerClientThatSetNeedsCommitInsideDraw : public CCSchedulerClient {
+// 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->setVisible(true);
+    client.setDrawSuccess(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.setDrawSuccess(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_numDraws(0)
-        , m_scheduler(0) { }
+        : m_scheduler(0) { }
 
     void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; }
 
-    int numDraws() const { return m_numDraws; }
-
-    virtual bool hasMoreResourceUpdates() const { return false; }
-    virtual bool canDraw() { return true; }
     virtual void scheduledActionBeginFrame() { }
-    virtual void scheduledActionDrawAndSwap()
+    virtual bool scheduledActionDrawAndSwapIfPossible()
     {
         // Only setNeedsCommit the first time this is called
         if (!m_numDraws)
             m_scheduler->setNeedsCommit();
-        m_numDraws++;
+        return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible();
     }
 
+    virtual void scheduledActionDrawAndSwapForced() { }
     virtual void scheduledActionUpdateMoreResources() { }
     virtual void scheduledActionCommit() { }
     virtual void scheduledActionBeginContextRecreation() { }
 
 protected:
-    int m_numDraws;
     CCScheduler* m_scheduler;
 };
 
@@ -251,4 +300,90 @@ TEST(CCSchedulerTest, RequestCommitInsideDraw)
     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->setVisible(true);
+    client.setDrawSuccess(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.setDrawSuccess(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->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());
+
+    // Draw successfully, this starts another frame.
+    timeSource->tick();
+    EXPECT_EQ(2, 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.setDrawSuccess(false);
+    timeSource->tick();
+    EXPECT_EQ(3, client.numDraws());
+    EXPECT_EQ(0, controllerPtr->numFramesPending());
+}
+
 }
index 39cf9e4..0c10558 100644 (file)
@@ -26,6 +26,7 @@
 #define CCSchedulerTestCommon_h
 
 #include "cc/CCDelayBasedTimeSource.h"
+#include "cc/CCFrameRateController.h"
 #include "cc/CCThread.h"
 #include <gtest/gtest.h>
 #include <wtf/OwnPtr.h>
@@ -123,6 +124,13 @@ protected:
     double m_monotonicallyIncreasingTime;
 };
 
+class FakeCCFrameRateController : public WebCore::CCFrameRateController {
+public:
+    FakeCCFrameRateController(PassRefPtr<WebCore::CCTimeSource> timer) : WebCore::CCFrameRateController(timer) { }
+
+    int numFramesPending() const { return m_numFramesPending; }
+};
+
 }
 
 #endif // CCSchedulerTestCommon_h