[Chromium] Incremental texture updates are not atomic.
[WebKit-https.git] / Source / WebKit / chromium / tests / CCLayerTreeHostTest.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26
27 #include "cc/CCLayerTreeHost.h"
28
29 #include "CompositorFakeWebGraphicsContext3D.h"
30 #include "ContentLayerChromium.h"
31 #include "GraphicsContext3DPrivate.h"
32 #include "LayerChromium.h"
33 #include "TextureManager.h"
34 #include "WebCompositor.h"
35 #include "WebKit.h"
36 #include "cc/CCLayerImpl.h"
37 #include "cc/CCLayerTreeHostImpl.h"
38 #include "cc/CCScopedThreadProxy.h"
39 #include "cc/CCTextureUpdater.h"
40 #include "cc/CCThreadTask.h"
41 #include "platform/WebKitPlatformSupport.h"
42 #include "platform/WebThread.h"
43 #include <gmock/gmock.h>
44 #include <gtest/gtest.h>
45 #include <webkit/support/webkit_support.h>
46 #include <wtf/MainThread.h>
47 #include <wtf/PassRefPtr.h>
48 #include <wtf/Vector.h>
49
50 using namespace WebCore;
51 using namespace WebKit;
52 using namespace WTF;
53
54 namespace {
55
56 // Used by test stubs to notify the test when something interesting happens.
57 class TestHooks {
58 public:
59     virtual void beginCommitOnCCThread(CCLayerTreeHostImpl*) { }
60     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) { }
61     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) { }
62     virtual void applyScrollAndScale(const IntSize&, float) { }
63     virtual void updateAnimations(double frameBeginTime) { }
64     virtual void layout() { }
65 };
66
67 // Adapts CCLayerTreeHostImpl for test. Runs real code, then invokes test hooks.
68 class MockLayerTreeHostImpl : public CCLayerTreeHostImpl {
69 public:
70     static PassOwnPtr<MockLayerTreeHostImpl> create(TestHooks* testHooks, const CCSettings& settings, CCLayerTreeHostImplClient* client)
71     {
72         return adoptPtr(new MockLayerTreeHostImpl(testHooks, settings, client));
73     }
74
75     virtual void beginCommit()
76     {
77         CCLayerTreeHostImpl::beginCommit();
78         m_testHooks->beginCommitOnCCThread(this);
79     }
80
81     virtual void commitComplete()
82     {
83         CCLayerTreeHostImpl::commitComplete();
84         m_testHooks->commitCompleteOnCCThread(this);
85     }
86
87     virtual void drawLayers()
88     {
89         CCLayerTreeHostImpl::drawLayers();
90         m_testHooks->drawLayersOnCCThread(this);
91     }
92
93 private:
94     MockLayerTreeHostImpl(TestHooks* testHooks, const CCSettings& settings, CCLayerTreeHostImplClient* client)
95         : CCLayerTreeHostImpl(settings, client)
96         , m_testHooks(testHooks)
97     {
98     }
99
100     TestHooks* m_testHooks;
101 };
102
103 // Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl.
104 class MockLayerTreeHost : public CCLayerTreeHost {
105 public:
106     static PassRefPtr<MockLayerTreeHost> create(TestHooks* testHooks, CCLayerTreeHostClient* client, PassRefPtr<LayerChromium> rootLayer, const CCSettings& settings)
107     {
108         RefPtr<MockLayerTreeHost> layerTreeHost = adoptRef(new MockLayerTreeHost(testHooks, client, settings));
109         bool success = layerTreeHost->initialize();
110         EXPECT_TRUE(success);
111         layerTreeHost->setRootLayer(rootLayer);
112
113         // LayerTreeHostImpl won't draw if it has 1x1 viewport.
114         layerTreeHost->setViewportSize(IntSize(1, 1));
115
116         return layerTreeHost.release();
117     }
118
119     virtual PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHostImpl(CCLayerTreeHostImplClient* client)
120     {
121         return MockLayerTreeHostImpl::create(m_testHooks, settings(), client);
122     }
123
124 private:
125     MockLayerTreeHost(TestHooks* testHooks, CCLayerTreeHostClient* client, const CCSettings& settings)
126         : CCLayerTreeHost(client, settings)
127         , m_testHooks(testHooks)
128     {
129     }
130
131     TestHooks* m_testHooks;
132 };
133
134 class CompositorFakeWebGraphicsContext3DWithTextureTracking : public CompositorFakeWebGraphicsContext3D {
135 public:
136     static PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> create(Attributes attrs)
137     {
138         return adoptPtr(new CompositorFakeWebGraphicsContext3DWithTextureTracking(attrs));
139     }
140
141     virtual WebGLId createTexture()
142     {
143         WebGLId texture = m_textures.size() + 1;
144         m_textures.append(texture);
145         return texture;
146     }
147
148     virtual void deleteTexture(WebGLId texture)
149     {
150         for (size_t i = 0; i < m_textures.size(); i++) {
151             if (m_textures[i] == texture) {
152                 m_textures.remove(i);
153                 break;
154             }
155         }
156     }
157
158     virtual void bindTexture(WGC3Denum /* target */, WebGLId texture)
159     {
160         m_usedTextures.add(texture);
161     }
162
163     int numTextures() const { return static_cast<int>(m_textures.size()); }
164     int texture(int i) const { return m_textures[i]; }
165     void resetTextures() { m_textures.clear(); }
166
167     int numUsedTextures() const { return static_cast<int>(m_usedTextures.size()); }
168     bool usedTexture(int texture) const { return m_usedTextures.find(texture) != m_usedTextures.end(); }
169     void resetUsedTextures() { m_usedTextures.clear(); }
170
171 private:
172     explicit CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs) : CompositorFakeWebGraphicsContext3D(attrs)
173     {
174     }
175
176     Vector<WebGLId> m_textures;
177     HashSet<WebGLId> m_usedTextures;
178 };
179
180 // Implementation of CCLayerTreeHost callback interface.
181 class MockLayerTreeHostClient : public CCLayerTreeHostClient {
182 public:
183     static PassOwnPtr<MockLayerTreeHostClient> create(TestHooks* testHooks)
184     {
185         return adoptPtr(new MockLayerTreeHostClient(testHooks));
186     }
187
188     virtual void updateAnimations(double frameBeginTime)
189     {
190         m_testHooks->updateAnimations(frameBeginTime);
191     }
192
193     virtual void layout()
194     {
195         m_testHooks->layout();
196     }
197
198     virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale)
199     {
200         m_testHooks->applyScrollAndScale(scrollDelta, scale);
201     }
202
203     virtual PassRefPtr<GraphicsContext3D> createLayerTreeHostContext3D()
204     {
205         GraphicsContext3D::Attributes attrs;
206         WebGraphicsContext3D::Attributes webAttrs;
207         webAttrs.alpha = attrs.alpha;
208
209         OwnPtr<WebGraphicsContext3D> webContext = CompositorFakeWebGraphicsContext3DWithTextureTracking::create(webAttrs);
210         return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(
211             webContext.release(), attrs, 0,
212             GraphicsContext3D::RenderDirectlyToHostWindow,
213             GraphicsContext3DPrivate::ForUseOnAnotherThread);
214     }
215
216     virtual void didCommitAndDrawFrame()
217     {
218     }
219
220     virtual void didCompleteSwapBuffers()
221     {
222     }
223
224     virtual void didRecreateGraphicsContext(bool)
225     {
226     }
227
228     virtual void scheduleComposite() { }
229
230 private:
231     explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { }
232
233     TestHooks* m_testHooks;
234 };
235
236 // The CCLayerTreeHostTest runs with the main loop running. It instantiates a single MockLayerTreeHost and associated
237 // MockLayerTreeHostImpl/MockLayerTreeHostClient.
238 //
239 // beginTest() is called once the main message loop is running and the layer tree host is initialized.
240 //
241 // Key stages of the drawing loop, e.g. drawing or commiting, redirect to CCLayerTreeHostTest methods of similar names.
242 // To track the commit process, override these functions.
243 //
244 // The test continues until someone calls endTest. endTest can be called on any thread, but be aware that
245 // ending the test is an asynchronous process.
246 class CCLayerTreeHostTest : public testing::Test, TestHooks {
247 public:
248     virtual void afterTest() = 0;
249     virtual void beginTest() = 0;
250
251     void endTest();
252
253     void postSetNeedsAnimateToMainThread()
254     {
255         callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsAnimate, this);
256     }
257
258     void postSetNeedsCommitToMainThread()
259     {
260         callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsCommit, this);
261     }
262
263     void postSetNeedsRedrawToMainThread()
264     {
265         callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsRedraw, this);
266     }
267
268     void postSetNeedsAnimateAndCommitToMainThread()
269     {
270         callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsAnimateAndCommit, this);
271     }
272
273
274     void postSetVisibleToMainThread(bool visible)
275     {
276         callOnMainThread(visible ? CCLayerTreeHostTest::dispatchSetVisible : CCLayerTreeHostTest::dispatchSetInvisible, this);
277     }
278
279     void timeout()
280     {
281         m_timedOut = true;
282         endTest();
283     }
284
285     void clearTimeout()
286     {
287         m_timeoutTask = 0;
288     }
289
290     CCLayerTreeHost* layerTreeHost() { return m_layerTreeHost.get(); }
291
292
293 protected:
294     CCLayerTreeHostTest()
295         : m_beginning(false)
296         , m_endWhenBeginReturns(false)
297         , m_timedOut(false) { }
298
299     void doBeginTest();
300
301     static void onBeginTest(void* self)
302     {
303         static_cast<CCLayerTreeHostTest*>(self)->doBeginTest();
304     }
305
306     static void onEndTest(void* self)
307     {
308         ASSERT(isMainThread());
309         webkit_support::QuitMessageLoop();
310         webkit_support::RunAllPendingMessages();
311     }
312
313     static void dispatchSetNeedsAnimate(void* self)
314     {
315       ASSERT(isMainThread());
316       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
317       ASSERT(test);
318       if (test->m_layerTreeHost)
319           test->m_layerTreeHost->setNeedsAnimate();
320     }
321
322     static void dispatchSetNeedsAnimateAndCommit(void* self)
323     {
324       ASSERT(isMainThread());
325       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
326       ASSERT(test);
327       if (test->m_layerTreeHost) {
328           test->m_layerTreeHost->setNeedsAnimate();
329           test->m_layerTreeHost->setNeedsCommit();
330       }
331     }
332
333     static void dispatchSetNeedsCommit(void* self)
334     {
335       ASSERT(isMainThread());
336       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
337       ASSERT_TRUE(test);
338       if (test->m_layerTreeHost)
339           test->m_layerTreeHost->setNeedsCommit();
340     }
341
342     static void dispatchSetNeedsRedraw(void* self)
343     {
344       ASSERT(isMainThread());
345       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
346       ASSERT_TRUE(test);
347       if (test->m_layerTreeHost)
348           test->m_layerTreeHost->setNeedsRedraw();
349     }
350
351     static void dispatchSetVisible(void* self)
352     {
353       ASSERT(isMainThread());
354       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
355       ASSERT(test);
356       if (test->m_layerTreeHost)
357           test->m_layerTreeHost->setVisible(true);
358     }
359
360     static void dispatchSetInvisible(void* self)
361     {
362       ASSERT(isMainThread());
363       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
364       ASSERT(test);
365       if (test->m_layerTreeHost)
366           test->m_layerTreeHost->setVisible(false);
367     }
368
369     class TimeoutTask : public webkit_support::TaskAdaptor {
370     public:
371         explicit TimeoutTask(CCLayerTreeHostTest* test)
372             : m_test(test)
373         {
374         }
375
376         void clearTest()
377         {
378             m_test = 0;
379         }
380
381         virtual ~TimeoutTask()
382         {
383             if (m_test)
384                 m_test->clearTimeout();
385         }
386
387         virtual void Run()
388         {
389             if (m_test)
390                 m_test->timeout();
391         }
392
393     private:
394         CCLayerTreeHostTest* m_test;
395     };
396
397     virtual void runTest(bool threaded)
398     {
399         m_settings.refreshRate = 100.0;
400
401         if (threaded) {
402             m_webThread = adoptPtr(webKitPlatformSupport()->createThread("CCLayerTreeHostTest"));
403             WebCompositor::initialize(m_webThread.get());
404         } else
405             WebCompositor::initialize(0);
406
407         ASSERT(CCProxy::isMainThread());
408         m_mainThreadProxy = CCScopedThreadProxy::create(CCProxy::mainThread());
409
410         webkit_support::PostDelayedTask(CCLayerTreeHostTest::onBeginTest, static_cast<void*>(this), 0);
411         m_timeoutTask = new TimeoutTask(this);
412         webkit_support::PostDelayedTask(m_timeoutTask, 5000); // webkit_support takes ownership of the task
413         webkit_support::RunMessageLoop();
414         webkit_support::RunAllPendingMessages();
415
416         if (m_layerTreeHost && m_layerTreeHost->rootLayer())
417             m_layerTreeHost->rootLayer()->setLayerTreeHost(0);
418         m_layerTreeHost.clear();
419
420         if (m_timeoutTask)
421             m_timeoutTask->clearTest();
422
423         ASSERT_FALSE(m_layerTreeHost.get());
424         m_client.clear();
425         if (m_timedOut) {
426             FAIL() << "Test timed out";
427             WebCompositor::shutdown();
428             return;
429         }
430         afterTest();
431         WebCompositor::shutdown();
432     }
433
434     CCSettings m_settings;
435     OwnPtr<MockLayerTreeHostClient> m_client;
436     RefPtr<CCLayerTreeHost> m_layerTreeHost;
437
438 private:
439     bool m_beginning;
440     bool m_endWhenBeginReturns;
441     bool m_timedOut;
442
443     OwnPtr<WebThread> m_webThread;
444     RefPtr<CCScopedThreadProxy> m_mainThreadProxy;
445     TimeoutTask* m_timeoutTask;
446 };
447
448 void CCLayerTreeHostTest::doBeginTest()
449 {
450     ASSERT(isMainThread());
451     m_client = MockLayerTreeHostClient::create(this);
452
453     RefPtr<LayerChromium> rootLayer = LayerChromium::create();
454     m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings);
455     ASSERT_TRUE(m_layerTreeHost);
456     rootLayer->setLayerTreeHost(m_layerTreeHost.get());
457
458     m_beginning = true;
459     beginTest();
460     m_beginning = false;
461     if (m_endWhenBeginReturns)
462         onEndTest(static_cast<void*>(this));
463 }
464
465 void CCLayerTreeHostTest::endTest()
466 {
467     // If we are called from the CCThread, re-call endTest on the main thread.
468     if (!isMainThread())
469         m_mainThreadProxy->postTask(createCCThreadTask(this, &CCLayerTreeHostTest::endTest));
470     else {
471         // For the case where we endTest during beginTest(), set a flag to indicate that
472         // the test should end the second beginTest regains control.
473         if (m_beginning)
474             m_endWhenBeginReturns = true;
475         else
476             onEndTest(static_cast<void*>(this));
477     }
478 }
479
480 class CCLayerTreeHostTestThreadOnly : public CCLayerTreeHostTest {
481 public:
482     void runTestThreaded()
483     {
484         CCLayerTreeHostTest::runTest(true);
485     }
486 };
487
488 // Shortlived layerTreeHosts shouldn't die.
489 class CCLayerTreeHostTestShortlived1 : public CCLayerTreeHostTest {
490 public:
491     CCLayerTreeHostTestShortlived1() { }
492
493     virtual void beginTest()
494     {
495         // Kill the layerTreeHost immediately.
496         m_layerTreeHost->setRootLayer(0);
497         m_layerTreeHost.clear();
498
499         endTest();
500     }
501
502     virtual void afterTest()
503     {
504     }
505 };
506
507 #define SINGLE_AND_MULTI_THREAD_TEST_F(TEST_FIXTURE_NAME) \
508     TEST_F(TEST_FIXTURE_NAME, runSingleThread)            \
509     {                                                     \
510         runTest(false);                                   \
511     }                                                     \
512     TEST_F(TEST_FIXTURE_NAME, runMultiThread)             \
513     {                                                     \
514         runTest(true);                                    \
515     }
516
517 SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestShortlived1)
518
519 // Shortlived layerTreeHosts shouldn't die with a commit in flight.
520 class CCLayerTreeHostTestShortlived2 : public CCLayerTreeHostTest {
521 public:
522     CCLayerTreeHostTestShortlived2() { }
523
524     virtual void beginTest()
525     {
526         postSetNeedsCommitToMainThread();
527
528         // Kill the layerTreeHost immediately.
529         m_layerTreeHost->setRootLayer(0);
530         m_layerTreeHost.clear();
531
532         endTest();
533     }
534
535     virtual void afterTest()
536     {
537     }
538 };
539
540 SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestShortlived2)
541
542 // Shortlived layerTreeHosts shouldn't die with a redraw in flight.
543 class CCLayerTreeHostTestShortlived3 : public CCLayerTreeHostTest {
544 public:
545     CCLayerTreeHostTestShortlived3() { }
546
547     virtual void beginTest()
548     {
549         postSetNeedsRedrawToMainThread();
550
551         // Kill the layerTreeHost immediately.
552         m_layerTreeHost->setRootLayer(0);
553         m_layerTreeHost.clear();
554
555         endTest();
556     }
557
558     virtual void afterTest()
559     {
560     }
561 };
562
563 SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestShortlived3)
564
565 // Test interleaving of redraws and commits
566 class CCLayerTreeHostTestCommitingWithContinuousRedraw : public CCLayerTreeHostTestThreadOnly {
567 public:
568     CCLayerTreeHostTestCommitingWithContinuousRedraw()
569         : m_numCompleteCommits(0)
570         , m_numDraws(0)
571     {
572     }
573
574     virtual void beginTest()
575     {
576         postSetNeedsCommitToMainThread();
577     }
578
579     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
580     {
581         m_numCompleteCommits++;
582         if (m_numCompleteCommits == 2)
583             endTest();
584     }
585
586     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*)
587     {
588         if (m_numDraws == 1)
589           postSetNeedsCommitToMainThread();
590         m_numDraws++;
591         postSetNeedsRedrawToMainThread();
592     }
593
594     virtual void afterTest()
595     {
596     }
597
598 private:
599     int m_numCompleteCommits;
600     int m_numDraws;
601 };
602
603 TEST_F(CCLayerTreeHostTestCommitingWithContinuousRedraw, runMultiThread)
604 {
605     runTestThreaded();
606 }
607
608 // Two setNeedsCommits in a row should lead to at least 1 commit and at least 1
609 // draw with frame 0.
610 class CCLayerTreeHostTestSetNeedsCommit1 : public CCLayerTreeHostTestThreadOnly {
611 public:
612     CCLayerTreeHostTestSetNeedsCommit1()
613         : m_numCommits(0)
614         , m_numDraws(0)
615     {
616     }
617
618     virtual void beginTest()
619     {
620         postSetNeedsCommitToMainThread();
621         postSetNeedsCommitToMainThread();
622     }
623
624     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
625     {
626         m_numDraws++;
627         if (!impl->sourceFrameNumber())
628             endTest();
629     }
630
631     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
632     {
633         m_numCommits++;
634     }
635
636     virtual void afterTest()
637     {
638         EXPECT_GE(1, m_numCommits);
639         EXPECT_GE(1, m_numDraws);
640     }
641
642 private:
643     int m_numCommits;
644     int m_numDraws;
645 };
646
647 TEST_F(CCLayerTreeHostTestSetNeedsCommit1, DISABLED_runMultiThread)
648 {
649     runTestThreaded();
650 }
651
652 // A setNeedsCommit should lead to 1 commit. Issuing a second commit after that
653 // first committed frame draws should lead to another commit.
654 class CCLayerTreeHostTestSetNeedsCommit2 : public CCLayerTreeHostTestThreadOnly {
655 public:
656     CCLayerTreeHostTestSetNeedsCommit2()
657         : m_numCommits(0)
658         , m_numDraws(0)
659     {
660     }
661
662     virtual void beginTest()
663     {
664         postSetNeedsCommitToMainThread();
665     }
666
667     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
668     {
669         if (!impl->sourceFrameNumber())
670             postSetNeedsCommitToMainThread();
671         else if (impl->sourceFrameNumber() == 1)
672             endTest();
673     }
674
675     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
676     {
677         m_numCommits++;
678     }
679
680     virtual void afterTest()
681     {
682         EXPECT_EQ(2, m_numCommits);
683         EXPECT_GE(2, m_numDraws);
684     }
685
686 private:
687     int m_numCommits;
688     int m_numDraws;
689 };
690
691 TEST_F(CCLayerTreeHostTestSetNeedsCommit2, runMultiThread)
692 {
693     runTestThreaded();
694 }
695
696 // 1 setNeedsRedraw after the first commit has completed should lead to 1
697 // additional draw.
698 class CCLayerTreeHostTestSetNeedsRedraw : public CCLayerTreeHostTestThreadOnly {
699 public:
700     CCLayerTreeHostTestSetNeedsRedraw()
701         : m_numCommits(0)
702         , m_numDraws(0)
703     {
704     }
705
706     virtual void beginTest()
707     {
708         postSetNeedsCommitToMainThread();
709     }
710
711     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
712     {
713         EXPECT_EQ(0, impl->sourceFrameNumber());
714         if (!m_numDraws)
715             postSetNeedsRedrawToMainThread(); // Redraw again to verify that the second redraw doesn't commit.
716         else
717             endTest();
718         m_numDraws++;
719     }
720
721     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
722     {
723         EXPECT_EQ(0, m_numDraws);
724         m_numCommits++;
725     }
726
727     virtual void afterTest()
728     {
729         EXPECT_GE(2, m_numDraws);
730         EXPECT_EQ(1, m_numCommits);
731     }
732
733 private:
734     int m_numCommits;
735     int m_numDraws;
736 };
737
738 TEST_F(CCLayerTreeHostTestSetNeedsRedraw, runMultiThread)
739 {
740     runTestThreaded();
741 }
742
743 // Trigger a frame with setNeedsCommit. Then, inside the resulting animate
744 // callback, requet another frame using setNeedsAnimate. End the test when
745 // animate gets called yet-again, indicating that the proxy is correctly
746 // handling the case where setNeedsAnimate() is called inside the begin frame
747 // flow.
748 class CCLayerTreeHostTestSetNeedsAnimateInsideAnimationCallback : public CCLayerTreeHostTestThreadOnly {
749 public:
750     CCLayerTreeHostTestSetNeedsAnimateInsideAnimationCallback()
751         : m_numAnimates(0)
752     {
753     }
754
755     virtual void beginTest()
756     {
757         postSetNeedsAnimateToMainThread();
758     }
759
760     virtual void updateAnimations(double)
761     {
762         if (!m_numAnimates) {
763             m_layerTreeHost->setNeedsAnimate();
764             m_numAnimates++;
765             return;
766         }
767         endTest();
768     }
769
770     virtual void afterTest()
771     {
772     }
773
774 private:
775     int m_numAnimates;
776 };
777
778 TEST_F(CCLayerTreeHostTestSetNeedsAnimateInsideAnimationCallback, runMultiThread)
779 {
780     runTestThreaded();
781 }
782
783 class CCLayerTreeHostTestScrollSimple : public CCLayerTreeHostTestThreadOnly {
784 public:
785     CCLayerTreeHostTestScrollSimple()
786         : m_initialScroll(IntPoint(10, 20))
787         , m_secondScroll(IntPoint(40, 5))
788         , m_scrollAmount(2, -1)
789         , m_scrolls(0)
790     {
791     }
792
793     virtual void beginTest()
794     {
795         m_layerTreeHost->rootLayer()->setScrollable(true);
796         m_layerTreeHost->rootLayer()->setScrollPosition(m_initialScroll);
797         postSetNeedsCommitToMainThread();
798     }
799
800     virtual void layout()
801     {
802         LayerChromium* root = m_layerTreeHost->rootLayer();
803         if (!m_layerTreeHost->frameNumber())
804             EXPECT_EQ(root->scrollPosition(), m_initialScroll);
805         else {
806             EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount);
807
808             // Pretend like Javascript updated the scroll position itself.
809             root->setScrollPosition(m_secondScroll);
810         }
811     }
812
813     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
814     {
815         CCLayerImpl* root = impl->rootLayer();
816         EXPECT_EQ(root->scrollDelta(), IntSize());
817
818         root->setScrollable(true);
819         root->setMaxScrollPosition(IntSize(100, 100));
820         root->scrollBy(m_scrollAmount);
821
822         if (impl->frameNumber() == 1) {
823             EXPECT_EQ(root->scrollPosition(), m_initialScroll);
824             EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
825             postSetNeedsCommitToMainThread();
826         } else if (impl->frameNumber() == 2) {
827             EXPECT_EQ(root->scrollPosition(), m_secondScroll);
828             EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
829             endTest();
830         }
831     }
832
833     virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale)
834     {
835         IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition();
836         m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta);
837         m_scrolls++;
838     }
839
840     virtual void afterTest()
841     {
842         EXPECT_EQ(1, m_scrolls);
843     }
844 private:
845     IntPoint m_initialScroll;
846     IntPoint m_secondScroll;
847     IntSize m_scrollAmount;
848     int m_scrolls;
849 };
850
851 TEST_F(CCLayerTreeHostTestScrollSimple, DISABLED_runMultiThread)
852 {
853     runTestThreaded();
854 }
855
856 class CCLayerTreeHostTestScrollMultipleRedraw : public CCLayerTreeHostTestThreadOnly {
857 public:
858     CCLayerTreeHostTestScrollMultipleRedraw()
859         : m_initialScroll(IntPoint(40, 10))
860         , m_scrollAmount(-3, 17)
861         , m_scrolls(0)
862     {
863     }
864
865     virtual void beginTest()
866     {
867         m_layerTreeHost->rootLayer()->setScrollable(true);
868         m_layerTreeHost->rootLayer()->setScrollPosition(m_initialScroll);
869         postSetNeedsCommitToMainThread();
870     }
871
872     virtual void beginCommitOnCCThread(CCLayerTreeHostImpl* impl)
873     {
874         LayerChromium* root = m_layerTreeHost->rootLayer();
875         if (!m_layerTreeHost->frameNumber())
876             EXPECT_EQ(root->scrollPosition(), m_initialScroll);
877         else if (m_layerTreeHost->frameNumber() == 1)
878             EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount + m_scrollAmount);
879         else if (m_layerTreeHost->frameNumber() == 2)
880             EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount + m_scrollAmount);
881     }
882
883     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
884     {
885         CCLayerImpl* root = impl->rootLayer();
886         root->setScrollable(true);
887         root->setMaxScrollPosition(IntSize(100, 100));
888
889         if (impl->frameNumber() == 1) {
890             EXPECT_EQ(root->scrollDelta(), IntSize());
891             root->scrollBy(m_scrollAmount);
892             EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
893
894             EXPECT_EQ(root->scrollPosition(), m_initialScroll);
895             postSetNeedsRedrawToMainThread();
896         } else if (impl->frameNumber() == 2) {
897             EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
898             root->scrollBy(m_scrollAmount);
899             EXPECT_EQ(root->scrollDelta(), m_scrollAmount + m_scrollAmount);
900
901             EXPECT_EQ(root->scrollPosition(), m_initialScroll);
902             postSetNeedsCommitToMainThread();
903         } else if (impl->frameNumber() == 3) {
904             EXPECT_EQ(root->scrollDelta(), IntSize());
905             EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount + m_scrollAmount);
906             endTest();
907         }
908     }
909
910     virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale)
911     {
912         IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition();
913         m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta);
914         m_scrolls++;
915     }
916
917     virtual void afterTest()
918     {
919         EXPECT_EQ(1, m_scrolls);
920     }
921 private:
922     IntPoint m_initialScroll;
923     IntSize m_scrollAmount;
924     int m_scrolls;
925 };
926
927 TEST_F(CCLayerTreeHostTestScrollMultipleRedraw, DISABLED_runMultiThread)
928 {
929     runTestThreaded();
930 }
931
932 class CCLayerTreeHostTestSetVisible : public CCLayerTreeHostTest {
933 public:
934
935     CCLayerTreeHostTestSetVisible()
936         : m_numCommits(0)
937         , m_numDraws(0)
938     {
939     }
940
941     virtual void beginTest()
942     {
943         postSetVisibleToMainThread(false);
944         postSetNeedsRedrawToMainThread(); // This is suppressed while we're invisible.
945         postSetVisibleToMainThread(true); // Triggers the redraw.
946     }
947
948     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
949     {
950         EXPECT_TRUE(impl->visible());
951         ++m_numDraws;
952         endTest();
953     }
954
955     virtual void afterTest()
956     {
957         EXPECT_EQ(1, m_numDraws);
958     }
959
960 private:
961     int m_numCommits;
962     int m_numDraws;
963 };
964
965 TEST_F(CCLayerTreeHostTestSetVisible, runMultiThread)
966 {
967     runTest(true);
968 }
969
970 class TestOpacityChangeLayerDelegate : public ContentLayerDelegate {
971 public:
972     TestOpacityChangeLayerDelegate(CCLayerTreeHostTest* test)
973         : m_test(test)
974     {
975     }
976
977     virtual void paintContents(GraphicsContext&, const IntRect&)
978     {
979         // Set layer opacity to 0.
980         m_test->layerTreeHost()->rootLayer()->setOpacity(0);
981     }
982
983     virtual bool preserves3D() { return false; }
984
985 private:
986     CCLayerTreeHostTest* m_test;
987 };
988
989 class ContentLayerChromiumWithUpdateTracking : public ContentLayerChromium {
990 public:
991     static PassRefPtr<ContentLayerChromiumWithUpdateTracking> create(ContentLayerDelegate *delegate) { return adoptRef(new ContentLayerChromiumWithUpdateTracking(delegate)); }
992
993     int paintContentsCount() { return m_paintContentsCount; }
994     int idlePaintContentsCount() { return m_idlePaintContentsCount; }
995     void resetPaintContentsCount() { m_paintContentsCount = 0; m_idlePaintContentsCount = 0;}
996
997     int updateCount() { return m_updateCount; }
998     void resetUpdateCount() { m_updateCount = 0; }
999
1000     virtual void paintContentsIfDirty()
1001     {
1002         ContentLayerChromium::paintContentsIfDirty();
1003         m_paintContentsCount++;
1004     }
1005
1006     virtual void idlePaintContentsIfDirty()
1007     {
1008         ContentLayerChromium::idlePaintContentsIfDirty();
1009         m_idlePaintContentsCount++;
1010     }
1011
1012     virtual void updateCompositorResources(GraphicsContext3D* context, CCTextureUpdater& updater)
1013     {
1014         ContentLayerChromium::updateCompositorResources(context, updater);
1015         m_updateCount++;
1016     }
1017
1018 private:
1019     explicit ContentLayerChromiumWithUpdateTracking(ContentLayerDelegate* delegate)
1020         : ContentLayerChromium(delegate)
1021         , m_paintContentsCount(0)
1022         , m_idlePaintContentsCount(0)
1023         , m_updateCount(0)
1024     {
1025         setBounds(IntSize(10, 10));
1026         setIsDrawable(true);
1027     }
1028
1029     int m_paintContentsCount;
1030     int m_idlePaintContentsCount;
1031     int m_updateCount;
1032 };
1033
1034 // Layer opacity change during paint should not prevent compositor resources from being updated during commit.
1035 class CCLayerTreeHostTestOpacityChange : public CCLayerTreeHostTest {
1036 public:
1037     CCLayerTreeHostTestOpacityChange()
1038         : m_testOpacityChangeDelegate(this)
1039         , m_updateCheckLayer(ContentLayerChromiumWithUpdateTracking::create(&m_testOpacityChangeDelegate))
1040     {
1041     }
1042
1043     virtual void beginTest()
1044     {
1045         m_layerTreeHost->setRootLayer(m_updateCheckLayer);
1046         m_layerTreeHost->setViewportSize(IntSize(10, 10));
1047
1048         postSetNeedsCommitToMainThread();
1049     }
1050
1051     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
1052     {
1053         endTest();
1054     }
1055
1056     virtual void afterTest()
1057     {
1058         // paintContentsIfDirty() should have been called once.
1059         EXPECT_EQ(1, m_updateCheckLayer->paintContentsCount());
1060
1061         // idlePaintContentsIfDirty() should have been called once
1062         EXPECT_EQ(1, m_updateCheckLayer->idlePaintContentsCount());
1063
1064         // updateCompositorResources() should have been called the same
1065         // amout of times as paintContentsIfDirty().
1066         EXPECT_EQ(m_updateCheckLayer->paintContentsCount(),
1067                   m_updateCheckLayer->updateCount());
1068
1069         // clear m_updateCheckLayer so CCLayerTreeHost dies.
1070         m_updateCheckLayer.clear();
1071     }
1072
1073 private:
1074     TestOpacityChangeLayerDelegate m_testOpacityChangeDelegate;
1075     RefPtr<ContentLayerChromiumWithUpdateTracking> m_updateCheckLayer;
1076 };
1077
1078 TEST_F(CCLayerTreeHostTestOpacityChange, runMultiThread)
1079 {
1080     runTest(true);
1081 }
1082
1083 class CCLayerTreeHostTestSetViewportSize : public CCLayerTreeHostTest {
1084 public:
1085
1086     CCLayerTreeHostTestSetViewportSize()
1087         : m_numCommits(0)
1088         , m_numDraws(0)
1089     {
1090     }
1091
1092     virtual void beginTest()
1093     {
1094         IntSize viewportSize(10, 10);
1095         layerTreeHost()->setViewportSize(viewportSize);
1096         EXPECT_EQ(viewportSize, layerTreeHost()->viewportSize());
1097         EXPECT_EQ(TextureManager::highLimitBytes(viewportSize), layerTreeHost()->contentsTextureManager()->maxMemoryLimitBytes());
1098         EXPECT_EQ(TextureManager::reclaimLimitBytes(viewportSize), layerTreeHost()->contentsTextureManager()->preferredMemoryLimitBytes());
1099
1100         // setViewportSize() should not call TextureManager::setMaxMemoryLimitBytes() or TextureManager::setPreferredMemoryLimitBytes()
1101         // if the viewport size is not changed.
1102         IntSize fakeSize(5, 5);
1103         layerTreeHost()->contentsTextureManager()->setMaxMemoryLimitBytes(TextureManager::highLimitBytes(fakeSize));
1104         layerTreeHost()->contentsTextureManager()->setPreferredMemoryLimitBytes(TextureManager::reclaimLimitBytes(fakeSize));
1105         layerTreeHost()->setViewportSize(viewportSize);
1106         EXPECT_EQ(TextureManager::highLimitBytes(fakeSize), layerTreeHost()->contentsTextureManager()->maxMemoryLimitBytes());
1107         EXPECT_EQ(TextureManager::reclaimLimitBytes(fakeSize), layerTreeHost()->contentsTextureManager()->preferredMemoryLimitBytes());
1108
1109         endTest();
1110     }
1111
1112     virtual void afterTest()
1113     {
1114     }
1115
1116 private:
1117     int m_numCommits;
1118     int m_numDraws;
1119 };
1120
1121 TEST_F(CCLayerTreeHostTestSetViewportSize, runSingleThread)
1122 {
1123     runTest(false);
1124 }
1125
1126 class MockContentLayerDelegate : public ContentLayerDelegate {
1127 public:
1128     bool drawsContent() const { return true; }
1129     MOCK_CONST_METHOD0(preserves3D, bool());
1130     void paintContents(GraphicsContext&, const IntRect&) { }
1131     void notifySyncRequired() { }
1132 };
1133
1134 // Verify atomicity of commits and reuse of textures.
1135 class CCLayerTreeHostTestAtomicCommit : public CCLayerTreeHostTest {
1136 public:
1137     CCLayerTreeHostTestAtomicCommit()
1138         : m_updateCheckLayer(ContentLayerChromiumWithUpdateTracking::create(&m_delegate))
1139         , m_numCommits(0)
1140     {
1141         // Make sure partial texture updates are turned off.
1142         m_settings.partialTextureUpdates = false;
1143     }
1144
1145     virtual void beginTest()
1146     {
1147         m_layerTreeHost->setRootLayer(m_updateCheckLayer);
1148         m_layerTreeHost->setViewportSize(IntSize(10, 10));
1149
1150         postSetNeedsCommitToMainThread();
1151         postSetNeedsRedrawToMainThread();
1152     }
1153
1154     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl)
1155     {
1156         CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context()));
1157
1158         switch (impl->frameNumber()) {
1159         case 0:
1160             // Number of textures should be one.
1161             EXPECT_EQ(1, context->numTextures());
1162             // Number of textures used for commit should be one.
1163             EXPECT_EQ(1, context->numUsedTextures());
1164             // Verify used texture is correct.
1165             EXPECT_TRUE(context->usedTexture(context->texture(0)));
1166
1167             context->resetUsedTextures();
1168             break;
1169         case 1:
1170             // Number of textures should be two as the first texture
1171             // is used by impl thread and cannot by used for update.
1172             EXPECT_EQ(2, context->numTextures());
1173             // Number of textures used for commit should still be one.
1174             EXPECT_EQ(1, context->numUsedTextures());
1175             // First texture should not have been used.
1176             EXPECT_FALSE(context->usedTexture(context->texture(0)));
1177             // New texture should have been used.
1178             EXPECT_TRUE(context->usedTexture(context->texture(1)));
1179
1180             context->resetUsedTextures();
1181             break;
1182         default:
1183             ASSERT_NOT_REACHED();
1184             break;
1185         }
1186     }
1187
1188     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
1189     {
1190         CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context()));
1191
1192         // Number of textures used for draw should always be one.
1193         EXPECT_EQ(1, context->numUsedTextures());
1194
1195         if (impl->frameNumber() < 2) {
1196             context->resetUsedTextures();
1197             postSetNeedsAnimateAndCommitToMainThread();
1198             postSetNeedsRedrawToMainThread();
1199         } else
1200             endTest();
1201     }
1202
1203     virtual void layout()
1204     {
1205         m_updateCheckLayer->setNeedsDisplay();
1206     }
1207
1208     virtual void afterTest()
1209     {
1210     }
1211
1212 private:
1213     MockContentLayerDelegate m_delegate;
1214     RefPtr<ContentLayerChromiumWithUpdateTracking> m_updateCheckLayer;
1215     int m_numCommits;
1216 };
1217
1218 TEST_F(CCLayerTreeHostTestAtomicCommit, runMultiThread)
1219 {
1220     runTest(true);
1221 }
1222
1223 } // namespace