ff4ffbe651844f5ca5f2971814fac659278a301d
[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 #if USE(THREADED_COMPOSITING)
28
29 #include "cc/CCLayerTreeHost.h"
30
31 #include "CCThreadImpl.h"
32 #include "GraphicsContext3DPrivate.h"
33 #include "LayerChromium.h"
34 #include "LayerPainterChromium.h"
35 #include "MockWebGraphicsContext3D.h"
36 #include "TextureManager.h"
37 #include "cc/CCLayerTreeHostImpl.h"
38 #include "cc/CCMainThreadTask.h"
39 #include "cc/CCThreadTask.h"
40 #include <gtest/gtest.h>
41 #include <webkit/support/webkit_support.h>
42 #include <wtf/MainThread.h>
43 #include <wtf/PassRefPtr.h>
44 #include <wtf/Vector.h>
45
46 using namespace WebCore;
47 using namespace WebKit;
48 using namespace WTF;
49
50 namespace {
51
52 // Used by test stubs to notify the test when something interesting happens.
53 class TestHooks {
54 public:
55     virtual void beginCommitOnCCThread(CCLayerTreeHostImpl*) { }
56     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) { }
57     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) { }
58 };
59
60 // Adapts CCLayerTreeHostImpl for test. Runs real code, then invokes test hooks.
61 class MockLayerTreeHostImpl : public CCLayerTreeHostImpl {
62 public:
63     static PassOwnPtr<MockLayerTreeHostImpl> create(TestHooks* testHooks, const CCSettings& settings)
64     {
65         return adoptPtr(new MockLayerTreeHostImpl(testHooks, settings));
66     }
67
68     virtual void beginCommit()
69     {
70         CCLayerTreeHostImpl::beginCommit();
71         m_testHooks->beginCommitOnCCThread(this);
72     }
73
74     virtual void commitComplete()
75     {
76         CCLayerTreeHostImpl::commitComplete();
77         m_testHooks->commitCompleteOnCCThread(this);
78     }
79
80     virtual void drawLayers()
81     {
82         CCLayerTreeHostImpl::drawLayers();
83         m_testHooks->drawLayersOnCCThread(this);
84     }
85
86 private:
87     MockLayerTreeHostImpl(TestHooks* testHooks, const CCSettings& settings)
88         : CCLayerTreeHostImpl(settings)
89         , m_testHooks(testHooks)
90     {
91     }
92
93     TestHooks* m_testHooks;
94 };
95
96 // Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl.
97 class MockLayerTreeHost : public CCLayerTreeHost {
98 public:
99     static PassRefPtr<MockLayerTreeHost> create(TestHooks* testHooks, CCLayerTreeHostClient* client, PassRefPtr<LayerChromium> rootLayer, const CCSettings& settings)
100     {
101         return adoptRef(new MockLayerTreeHost(testHooks, client, rootLayer, settings));
102     }
103
104     virtual PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHostImpl()
105     {
106         return MockLayerTreeHostImpl::create(m_testHooks, settings());
107     }
108
109 private:
110     MockLayerTreeHost(TestHooks* testHooks, CCLayerTreeHostClient* client, PassRefPtr<LayerChromium> rootLayer, const CCSettings& settings)
111         : CCLayerTreeHost(client, rootLayer, settings)
112         , m_testHooks(testHooks)
113     {
114         bool success = initialize();
115         ASSERT(success);
116     }
117
118     TestHooks* m_testHooks;
119 };
120
121 // Test stub for WebGraphicsContext3D. Returns canned values needed for compositor initialization.
122 class CompositorMockWebGraphicsContext3D : public MockWebGraphicsContext3D {
123 public:
124     static PassOwnPtr<CompositorMockWebGraphicsContext3D> create()
125     {
126         return adoptPtr(new CompositorMockWebGraphicsContext3D());
127     }
128
129     virtual bool makeContextCurrent() { return true; }
130     virtual WebGLId createProgram() { return 1; }
131     virtual WebGLId createShader(WGC3Denum) { return 1; }
132     virtual void getShaderiv(WebGLId, WGC3Denum, WGC3Dint* value) { *value = 1; }
133     virtual void getProgramiv(WebGLId, WGC3Denum, WGC3Dint* value) { *value = 1; }
134
135 private:
136     CompositorMockWebGraphicsContext3D() { }
137 };
138
139 // Implementation of CCLayerTreeHost callback interface.
140 class MockLayerTreeHostClient : public CCLayerTreeHostClient {
141 public:
142     static PassOwnPtr<MockLayerTreeHostClient> create(TestHooks* testHooks)
143     {
144         return adoptPtr(new MockLayerTreeHostClient(testHooks));
145     }
146
147     virtual void animateAndLayout(double frameBeginTime)
148     {
149     }
150
151     virtual PassOwnPtr<CCThread> createCompositorThread()
152     {
153         return CCThreadImpl::create();
154     }
155
156     virtual PassRefPtr<GraphicsContext3D> createLayerTreeHostContext3D()
157     {
158         OwnPtr<WebGraphicsContext3D> mock = CompositorMockWebGraphicsContext3D::create();
159         GraphicsContext3D::Attributes attrs;
160         RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(mock.release(), attrs, 0, GraphicsContext3D::RenderDirectlyToHostWindow, GraphicsContext3DPrivate::ForUseOnAnotherThread);
161         return context;
162     }
163
164     virtual PassOwnPtr<LayerPainterChromium> createRootLayerPainter()
165     {
166         return nullptr;
167     }
168
169     virtual void didRecreateGraphicsContext(bool)
170     {
171     }
172
173 private:
174     explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { }
175
176     TestHooks* m_testHooks;
177 };
178
179 // The CCLayerTreeHostTest runs with the main loop running. It instantiates a single MockLayerTreeHost and associated
180 // MockLayerTreeHostImpl/MockLayerTreeHostClient.
181 //
182 // beginTest() is called once the main message loop is running and the layer tree host is initialized.
183 //
184 // Key stages of the drawing loop, e.g. drawing or commiting, redirect to CCLayerTreeHostTest methods of similar names.
185 // To track the commit process, override these functions.
186 //
187 // The test continues until someone calls endTest. endTest can be called on any thread, but be aware that
188 // ending the test is an asynchronous process.
189 class CCLayerTreeHostTest : public testing::Test, TestHooks {
190 public:
191     virtual void afterTest() = 0;
192     virtual void beginTest() = 0;
193
194     void endTest();
195
196     void postSetNeedsCommitToMainThread()
197     {
198         callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsCommit, this);
199     }
200
201     void postSetNeedsRedrawToMainThread()
202     {
203         callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsRedraw, this);
204     }
205
206 protected:
207     CCLayerTreeHostTest()
208         : m_beginning(false)
209         , m_endWhenBeginReturns(false)
210         , m_running(false)
211         , m_timedOut(false) { }
212
213     void doBeginTest();
214
215     static void onBeginTest(void* self)
216     {
217         static_cast<CCLayerTreeHostTest*>(self)->doBeginTest();
218     }
219
220     static void onEndTest(void* self)
221     {
222         ASSERT(isMainThread());
223         webkit_support::QuitMessageLoop();
224         CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
225         ASSERT(test);
226         test->m_layerTreeHost.clear();
227     }
228
229     static void dispatchSetNeedsCommit(void* self)
230     {
231       ASSERT(isMainThread());
232       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
233       ASSERT(test);
234       if (test->m_layerTreeHost)
235           test->m_layerTreeHost->setNeedsCommitAndRedraw();
236     }
237
238     static void dispatchSetNeedsRedraw(void* self)
239     {
240       ASSERT(isMainThread());
241       CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
242       ASSERT(test);
243       if (test->m_layerTreeHost)
244           test->m_layerTreeHost->setNeedsRedraw();
245     }
246
247     void runTest()
248     {
249         webkit_support::PostDelayedTask(CCLayerTreeHostTest::onBeginTest, static_cast<void*>(this), 0);
250         webkit_support::PostDelayedTask(CCLayerTreeHostTest::testTimeout, static_cast<void*>(this), 5000);
251         webkit_support::RunMessageLoop();
252         m_running = false;
253         bool timedOut = m_timedOut; // Save whether we're timed out in case RunAllPendingMessages has the timeout.
254         webkit_support::RunAllPendingMessages();
255         ASSERT(!m_layerTreeHost.get());
256         m_client.clear();
257         if (timedOut) {
258             FAIL() << "Test timed out";
259             return;
260         }
261         afterTest();
262     }
263
264     static void testTimeout(void* self)
265     {
266         CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self);
267         if (!test->m_running)
268             return;
269         test->m_timedOut = true;
270         test->endTest();
271     }
272
273     OwnPtr<MockLayerTreeHostClient> m_client;
274     RefPtr<CCLayerTreeHost> m_layerTreeHost;
275
276 private:
277     bool m_beginning;
278     bool m_endWhenBeginReturns;
279     bool m_running;
280     bool m_timedOut;
281 };
282
283 void CCLayerTreeHostTest::doBeginTest()
284 {
285     ASSERT(isMainThread());
286     ASSERT(!m_running);
287     m_running = true;
288     m_client = MockLayerTreeHostClient::create(this);
289
290     CCSettings settings;
291     settings.enableCompositorThread = true;
292     RefPtr<LayerChromium> rootLayer;
293     m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, settings);
294     ASSERT(m_layerTreeHost);
295
296     m_beginning = true;
297     beginTest();
298     m_beginning = false;
299     if (m_endWhenBeginReturns)
300         onEndTest(static_cast<void*>(this));
301 }
302
303 void CCLayerTreeHostTest::endTest()
304 {
305     // If we are called from the CCThread, re-call endTest on the main thread.
306     if (!isMainThread())
307         CCMainThread::postTask(createMainThreadTask(this, &CCLayerTreeHostTest::endTest));
308     else {
309         // For the case where we endTest during beginTest(), set a flag to indicate that
310         // the test should end the second beginTest regains control.
311         if (m_beginning)
312             m_endWhenBeginReturns = true;
313         else
314             onEndTest(static_cast<void*>(this));
315     }
316 }
317
318 // Shortlived layerTreeHosts shouldn't die.
319 class CCLayerTreeHostTestShortlived1 : public CCLayerTreeHostTest {
320 public:
321     CCLayerTreeHostTestShortlived1() { }
322
323     virtual void beginTest()
324     {
325         endTest();
326     }
327
328     virtual void afterTest()
329     {
330     }
331 };
332 TEST_F(CCLayerTreeHostTestShortlived1, run)
333 {
334     runTest();
335 }
336
337 // Shortlived layerTreeHosts shouldn't die with a commit in flight.
338 class CCLayerTreeHostTestShortlived2 : public CCLayerTreeHostTest {
339 public:
340     CCLayerTreeHostTestShortlived2() { }
341
342     virtual void beginTest()
343     {
344         postSetNeedsCommitToMainThread();
345         endTest();
346     }
347
348     virtual void afterTest()
349     {
350     }
351 };
352 TEST_F(CCLayerTreeHostTestShortlived2, run)
353 {
354     runTest();
355 }
356
357 // Shortlived layerTreeHosts shouldn't die with a redraw in flight.
358 class CCLayerTreeHostTestShortlived3 : public CCLayerTreeHostTest {
359 public:
360     CCLayerTreeHostTestShortlived3() { }
361
362     virtual void beginTest()
363     {
364         postSetNeedsRedrawToMainThread();
365         endTest();
366     }
367
368     virtual void afterTest()
369     {
370     }
371 };
372 TEST_F(CCLayerTreeHostTestShortlived3, run)
373 {
374     runTest();
375 }
376
377 // Constantly redrawing layerTreeHosts shouldn't die when they commit
378 class CCLayerTreeHostTestCommitingWithContinuousRedraw : public CCLayerTreeHostTest {
379 public:
380     CCLayerTreeHostTestCommitingWithContinuousRedraw()
381         : m_numCompleteCommits(0)
382         , m_numDraws(0)
383     {
384     }
385
386     virtual void beginTest()
387     {
388         postSetNeedsCommitToMainThread();
389         endTest();
390     }
391
392     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
393     {
394         m_numCompleteCommits++;
395         if (m_numCompleteCommits == 2)
396             endTest();
397     }
398
399     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*)
400     {
401         if (m_numDraws == 1)
402           postSetNeedsCommitToMainThread();
403         m_numDraws++;
404         postSetNeedsRedrawToMainThread();
405     }
406
407     virtual void afterTest()
408     {
409     }
410
411 private:
412     int m_numCompleteCommits;
413     int m_numDraws;
414 };
415 TEST_F(CCLayerTreeHostTestCommitingWithContinuousRedraw, run)
416 {
417     runTest();
418 }
419
420 // Two setNeedsCommits in a row should lead to at least 1 commit and at least 1
421 // draw with frame 0.
422 class CCLayerTreeHostTestSetNeedsCommit1 : public CCLayerTreeHostTest {
423 public:
424     CCLayerTreeHostTestSetNeedsCommit1()
425         : m_numCommits(0)
426         , m_numDraws(0)
427     {
428     }
429
430     virtual void beginTest()
431     {
432         postSetNeedsCommitToMainThread();
433         postSetNeedsCommitToMainThread();
434     }
435
436     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
437     {
438         m_numDraws++;
439         if (!impl->sourceFrameNumber())
440             endTest();
441     }
442
443     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
444     {
445         m_numCommits++;
446     }
447
448     virtual void afterTest()
449     {
450         EXPECT_GE(1, m_numCommits);
451         EXPECT_GE(1, m_numDraws);
452     }
453
454 private:
455     int m_numCommits;
456     int m_numDraws;
457 };
458 TEST_F(CCLayerTreeHostTestSetNeedsCommit1, run)
459 {
460     runTest();
461 }
462
463 // A setNeedsCommit should lead to 1 commit. Issuing a second commit after that
464 // first committed frame draws should lead to another commit.
465 class CCLayerTreeHostTestSetNeedsCommit2 : public CCLayerTreeHostTest {
466 public:
467     CCLayerTreeHostTestSetNeedsCommit2()
468         : m_numCommits(0)
469         , m_numDraws(0)
470     {
471     }
472
473     virtual void beginTest()
474     {
475         postSetNeedsCommitToMainThread();
476     }
477
478     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
479     {
480         if (!impl->sourceFrameNumber())
481             postSetNeedsCommitToMainThread();
482         else if (impl->sourceFrameNumber() == 1)
483             endTest();
484     }
485
486     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
487     {
488         m_numCommits++;
489     }
490
491     virtual void afterTest()
492     {
493         EXPECT_EQ(2, m_numCommits);
494         EXPECT_GE(2, m_numDraws);
495     }
496
497 private:
498     int m_numCommits;
499     int m_numDraws;
500 };
501 TEST_F(CCLayerTreeHostTestSetNeedsCommit2, run)
502 {
503     runTest();
504 }
505
506 // 1 setNeedsRedraw after the first commit has completed should lead to 1
507 // additional draw.
508 class CCLayerTreeHostTestSetNeedsRedraw : public CCLayerTreeHostTest {
509 public:
510     CCLayerTreeHostTestSetNeedsRedraw()
511         : m_numCommits(0)
512         , m_numDraws(0)
513     {
514     }
515
516     virtual void beginTest()
517     {
518         postSetNeedsCommitToMainThread();
519     }
520
521     virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl)
522     {
523         EXPECT_EQ(0, impl->sourceFrameNumber());
524         if (!m_numDraws)
525             postSetNeedsRedrawToMainThread(); // Redraw again to verify that the second redraw doesn't commit.
526         else
527             endTest();
528         m_numDraws++;
529     }
530
531     virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*)
532     {
533         m_numCommits++;
534     }
535
536     virtual void afterTest()
537     {
538         EXPECT_GE(2, m_numDraws);
539         EXPECT_EQ(1, m_numCommits);
540     }
541
542 private:
543     int m_numCommits;
544     int m_numDraws;
545 };
546 TEST_F(CCLayerTreeHostTestSetNeedsRedraw, run)
547 {
548     runTest();
549 }
550
551 } // namespace
552
553 #endif // USE(THREADED_COMPOSITING)