OLD | NEW |
| (Empty) |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "config.h" | |
6 #include "CCRendererGL.h" | |
7 | |
8 #include "CCDrawQuad.h" | |
9 #include "CCPrioritizedTextureManager.h" | |
10 #include "CCResourceProvider.h" | |
11 #include "CCSettings.h" | |
12 #include "CCSingleThreadProxy.h" | |
13 #include "CCTestCommon.h" | |
14 #include "FakeWebCompositorOutputSurface.h" | |
15 #include "FakeWebGraphicsContext3D.h" | |
16 #include "GraphicsContext3D.h" | |
17 #include "WebCompositorInitializer.h" | |
18 #include "testing/gmock/include/gmock/gmock.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 #include <public/WebTransformationMatrix.h> | |
21 | |
22 using namespace cc; | |
23 using namespace WebKit; | |
24 using namespace WebKitTests; | |
25 | |
26 class FrameCountingMemoryAllocationSettingContext : public FakeWebGraphicsContex
t3D { | |
27 public: | |
28 FrameCountingMemoryAllocationSettingContext() : m_frame(0) { } | |
29 | |
30 // WebGraphicsContext3D methods. | |
31 | |
32 // This method would normally do a glSwapBuffers under the hood. | |
33 virtual void prepareTexture() { m_frame++; } | |
34 virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAll
ocationChangedCallbackCHROMIUM* callback) { m_memoryAllocationChangedCallback =
callback; } | |
35 virtual WebString getString(WebKit::WGC3Denum name) | |
36 { | |
37 if (name == GraphicsContext3D::EXTENSIONS) | |
38 return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_
manager GL_CHROMIUM_discard_framebuffer"); | |
39 return WebString(); | |
40 } | |
41 | |
42 // Methods added for test. | |
43 int frameCount() { return m_frame; } | |
44 void setMemoryAllocation(WebGraphicsMemoryAllocation allocation) | |
45 { | |
46 ASSERT(CCProxy::isImplThread()); | |
47 // In single threaded mode we expect this callback on main thread. | |
48 DebugScopedSetMainThread main; | |
49 m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation)
; | |
50 } | |
51 | |
52 private: | |
53 int m_frame; | |
54 WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* m_memoryAllocationChange
dCallback; | |
55 }; | |
56 | |
57 class FakeCCRendererClient : public CCRendererClient { | |
58 public: | |
59 FakeCCRendererClient() | |
60 : m_setFullRootLayerDamageCount(0) | |
61 , m_rootLayer(CCLayerImpl::create(1)) | |
62 , m_memoryAllocationLimitBytes(CCPrioritizedTextureManager::defaultMemor
yAllocationLimit()) | |
63 { | |
64 m_rootLayer->createRenderSurface(); | |
65 CCRenderPass::Id renderPassId = m_rootLayer->renderSurface()->renderPass
Id(); | |
66 scoped_ptr<CCRenderPass> rootRenderPass = CCRenderPass::create(renderPas
sId, IntRect(), WebTransformationMatrix()); | |
67 m_renderPassesInDrawOrder.push_back(rootRenderPass.get()); | |
68 m_renderPasses.set(renderPassId, rootRenderPass.Pass()); | |
69 } | |
70 | |
71 // CCRendererClient methods. | |
72 virtual const IntSize& deviceViewportSize() const OVERRIDE { static IntSize
fakeSize(1, 1); return fakeSize; } | |
73 virtual const CCLayerTreeSettings& settings() const OVERRIDE { static CCLaye
rTreeSettings fakeSettings; return fakeSettings; } | |
74 virtual void didLoseContext() OVERRIDE { } | |
75 virtual void onSwapBuffersComplete() OVERRIDE { } | |
76 virtual void setFullRootLayerDamage() OVERRIDE { m_setFullRootLayerDamageCou
nt++; } | |
77 virtual void releaseContentsTextures() OVERRIDE { } | |
78 virtual void setMemoryAllocationLimitBytes(size_t bytes) OVERRIDE { m_memory
AllocationLimitBytes = bytes; } | |
79 | |
80 // Methods added for test. | |
81 int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCou
nt; } | |
82 | |
83 CCRenderPass* rootRenderPass() { return m_renderPassesInDrawOrder.back(); } | |
84 const CCRenderPassList& renderPassesInDrawOrder() const { return m_renderPas
sesInDrawOrder; } | |
85 const CCRenderPassIdHashMap& renderPasses() const { return m_renderPasses; } | |
86 | |
87 size_t memoryAllocationLimitBytes() const { return m_memoryAllocationLimitBy
tes; } | |
88 | |
89 private: | |
90 int m_setFullRootLayerDamageCount; | |
91 DebugScopedSetImplThread m_implThread; | |
92 scoped_ptr<CCLayerImpl> m_rootLayer; | |
93 CCRenderPassList m_renderPassesInDrawOrder; | |
94 CCRenderPassIdHashMap m_renderPasses; | |
95 size_t m_memoryAllocationLimitBytes; | |
96 }; | |
97 | |
98 class FakeCCRendererGL : public CCRendererGL { | |
99 public: | |
100 FakeCCRendererGL(CCRendererClient* client, CCResourceProvider* resourceProvi
der) : CCRendererGL(client, resourceProvider) { } | |
101 | |
102 // CCRendererGL methods. | |
103 | |
104 // Changing visibility to public. | |
105 using CCRendererGL::initialize; | |
106 using CCRendererGL::isFramebufferDiscarded; | |
107 }; | |
108 | |
109 class CCRendererGLTest : public testing::Test { | |
110 protected: | |
111 CCRendererGLTest() | |
112 : m_suggestHaveBackbufferYes(1, true) | |
113 , m_suggestHaveBackbufferNo(1, false) | |
114 , m_compositorInitializer(0) | |
115 , m_context(FakeWebCompositorOutputSurface::create(adoptPtr(new FrameCou
ntingMemoryAllocationSettingContext()))) | |
116 , m_resourceProvider(CCResourceProvider::create(m_context.get())) | |
117 , m_renderer(&m_mockClient, m_resourceProvider.get()) | |
118 { | |
119 } | |
120 | |
121 virtual void SetUp() | |
122 { | |
123 m_renderer.initialize(); | |
124 } | |
125 | |
126 void swapBuffers() | |
127 { | |
128 m_renderer.swapBuffers(); | |
129 } | |
130 | |
131 FrameCountingMemoryAllocationSettingContext* context() { return static_cast<
FrameCountingMemoryAllocationSettingContext*>(m_context->context3D()); } | |
132 | |
133 WebGraphicsMemoryAllocation m_suggestHaveBackbufferYes; | |
134 WebGraphicsMemoryAllocation m_suggestHaveBackbufferNo; | |
135 | |
136 WebCompositorInitializer m_compositorInitializer; | |
137 scoped_ptr<CCGraphicsContext> m_context; | |
138 FakeCCRendererClient m_mockClient; | |
139 OwnPtr<CCResourceProvider> m_resourceProvider; | |
140 FakeCCRendererGL m_renderer; | |
141 CCScopedSettings m_scopedSettings; | |
142 }; | |
143 | |
144 // Test CCRendererGL discardFramebuffer functionality: | |
145 // Suggest recreating framebuffer when one already exists. | |
146 // Expected: it does nothing. | |
147 TEST_F(CCRendererGLTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) | |
148 { | |
149 context()->setMemoryAllocation(m_suggestHaveBackbufferYes); | |
150 EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount()); | |
151 EXPECT_FALSE(m_renderer.isFramebufferDiscarded()); | |
152 | |
153 swapBuffers(); | |
154 EXPECT_EQ(1, context()->frameCount()); | |
155 } | |
156 | |
157 // Test CCRendererGL discardFramebuffer functionality: | |
158 // Suggest discarding framebuffer when one exists and the renderer is not visibl
e. | |
159 // Expected: it is discarded and damage tracker is reset. | |
160 TEST_F(CCRendererGLTest, SuggestBackbufferNoShouldDiscardBackbufferAndDamageRoot
LayerWhileNotVisible) | |
161 { | |
162 m_renderer.setVisible(false); | |
163 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
164 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
165 EXPECT_TRUE(m_renderer.isFramebufferDiscarded()); | |
166 } | |
167 | |
168 // Test CCRendererGL discardFramebuffer functionality: | |
169 // Suggest discarding framebuffer when one exists and the renderer is visible. | |
170 // Expected: the allocation is ignored. | |
171 TEST_F(CCRendererGLTest, SuggestBackbufferNoDoNothingWhenVisible) | |
172 { | |
173 m_renderer.setVisible(true); | |
174 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
175 EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount()); | |
176 EXPECT_FALSE(m_renderer.isFramebufferDiscarded()); | |
177 } | |
178 | |
179 | |
180 // Test CCRendererGL discardFramebuffer functionality: | |
181 // Suggest discarding framebuffer when one does not exist. | |
182 // Expected: it does nothing. | |
183 TEST_F(CCRendererGLTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing) | |
184 { | |
185 m_renderer.setVisible(false); | |
186 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
187 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
188 EXPECT_TRUE(m_renderer.isFramebufferDiscarded()); | |
189 | |
190 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
191 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
192 EXPECT_TRUE(m_renderer.isFramebufferDiscarded()); | |
193 } | |
194 | |
195 // Test CCRendererGL discardFramebuffer functionality: | |
196 // Begin drawing a frame while a framebuffer is discarded. | |
197 // Expected: will recreate framebuffer. | |
198 TEST_F(CCRendererGLTest, DiscardedBackbufferIsRecreatedForScopeDuration) | |
199 { | |
200 m_renderer.setVisible(false); | |
201 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
202 EXPECT_TRUE(m_renderer.isFramebufferDiscarded()); | |
203 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
204 | |
205 m_renderer.setVisible(true); | |
206 m_renderer.drawFrame(m_mockClient.renderPassesInDrawOrder(), m_mockClient.re
nderPasses()); | |
207 EXPECT_FALSE(m_renderer.isFramebufferDiscarded()); | |
208 | |
209 swapBuffers(); | |
210 EXPECT_EQ(1, context()->frameCount()); | |
211 } | |
212 | |
213 TEST_F(CCRendererGLTest, FramebufferDiscardedAfterReadbackWhenNotVisible) | |
214 { | |
215 m_renderer.setVisible(false); | |
216 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
217 EXPECT_TRUE(m_renderer.isFramebufferDiscarded()); | |
218 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
219 | |
220 char pixels[4]; | |
221 m_renderer.drawFrame(m_mockClient.renderPassesInDrawOrder(), m_mockClient.re
nderPasses()); | |
222 EXPECT_FALSE(m_renderer.isFramebufferDiscarded()); | |
223 | |
224 m_renderer.getFramebufferPixels(pixels, IntRect(0, 0, 1, 1)); | |
225 EXPECT_TRUE(m_renderer.isFramebufferDiscarded()); | |
226 EXPECT_EQ(2, m_mockClient.setFullRootLayerDamageCount()); | |
227 } | |
228 | |
229 class ForbidSynchronousCallContext : public FakeWebGraphicsContext3D { | |
230 public: | |
231 ForbidSynchronousCallContext() { } | |
232 | |
233 virtual bool getActiveAttrib(WebGLId program, WGC3Duint index, ActiveInfo&)
{ ADD_FAILURE(); return false; } | |
234 virtual bool getActiveUniform(WebGLId program, WGC3Duint index, ActiveInfo&)
{ ADD_FAILURE(); return false; } | |
235 virtual void getAttachedShaders(WebGLId program, WGC3Dsizei maxCount, WGC3Ds
izei* count, WebGLId* shaders) { ADD_FAILURE(); } | |
236 virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) {
ADD_FAILURE(); return 0; } | |
237 virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { ADD_FAILURE
(); } | |
238 virtual void getBufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Din
t* value) { ADD_FAILURE(); } | |
239 virtual Attributes getContextAttributes() { ADD_FAILURE(); return m_attrs; } | |
240 virtual WGC3Denum getError() { ADD_FAILURE(); return 0; } | |
241 virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE();
} | |
242 virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, WGC3Denum
attachment, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } | |
243 virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value) | |
244 { | |
245 if (pname == GraphicsContext3D::MAX_TEXTURE_SIZE) | |
246 *value = 1024; // MAX_TEXTURE_SIZE is cached client side, so it's OK
to query. | |
247 else | |
248 ADD_FAILURE(); | |
249 } | |
250 | |
251 // We allow querying the shader compilation and program link status in debug
mode, but not release. | |
252 virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) | |
253 { | |
254 #ifndef NDEBUG | |
255 *value = 1; | |
256 #else | |
257 ADD_FAILURE(); | |
258 #endif | |
259 } | |
260 | |
261 virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) | |
262 { | |
263 #ifndef NDEBUG | |
264 *value = 1; | |
265 #else | |
266 ADD_FAILURE(); | |
267 #endif | |
268 } | |
269 | |
270 virtual WebString getString(WGC3Denum name) | |
271 { | |
272 // We allow querying the extension string. | |
273 // FIXME: It'd be better to check that we only do this before starting a
ny other expensive work (like starting a compilation) | |
274 if (name != GraphicsContext3D::EXTENSIONS) | |
275 ADD_FAILURE(); | |
276 return WebString(); | |
277 } | |
278 | |
279 virtual WebString getProgramInfoLog(WebGLId program) { ADD_FAILURE(); return
WebString(); } | |
280 virtual void getRenderbufferParameteriv(WGC3Denum target, WGC3Denum pname, W
GC3Dint* value) { ADD_FAILURE(); } | |
281 | |
282 virtual WebString getShaderInfoLog(WebGLId shader) { ADD_FAILURE(); return W
ebString(); } | |
283 virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precis
iontype, WGC3Dint* range, WGC3Dint* precision) { ADD_FAILURE(); } | |
284 virtual WebString getShaderSource(WebGLId shader) { ADD_FAILURE(); return We
bString(); } | |
285 virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat
* value) { ADD_FAILURE(); } | |
286 virtual void getTexParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint*
value) { ADD_FAILURE(); } | |
287 virtual void getUniformfv(WebGLId program, WGC3Dint location, WGC3Dfloat* va
lue) { ADD_FAILURE(); } | |
288 virtual void getUniformiv(WebGLId program, WGC3Dint location, WGC3Dint* valu
e) { ADD_FAILURE(); } | |
289 virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name)
{ ADD_FAILURE(); return 0; } | |
290 virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname, WGC3Dfloat*
value) { ADD_FAILURE(); } | |
291 virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname, WGC3Dint* v
alue) { ADD_FAILURE(); } | |
292 virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname
) { ADD_FAILURE(); return 0; } | |
293 }; | |
294 | |
295 // This test isn't using the same fixture as CCRendererGLTest, and you can't mix
TEST() and TEST_F() with the same name, hence LRC2. | |
296 TEST(CCRendererGLTest2, initializationDoesNotMakeSynchronousCalls) | |
297 { | |
298 CCScopedSettings scopedSettings; | |
299 FakeCCRendererClient mockClient; | |
300 scoped_ptr<CCGraphicsContext> context(FakeWebCompositorOutputSurface::create
(adoptPtr(new ForbidSynchronousCallContext))); | |
301 OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(conte
xt.get())); | |
302 FakeCCRendererGL renderer(&mockClient, resourceProvider.get()); | |
303 | |
304 EXPECT_TRUE(renderer.initialize()); | |
305 } | |
306 | |
307 class LoseContextOnFirstGetContext : public FakeWebGraphicsContext3D { | |
308 public: | |
309 LoseContextOnFirstGetContext() | |
310 : m_contextLost(false) | |
311 { | |
312 } | |
313 | |
314 virtual bool makeContextCurrent() OVERRIDE | |
315 { | |
316 return !m_contextLost; | |
317 } | |
318 | |
319 virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value)
OVERRIDE | |
320 { | |
321 m_contextLost = true; | |
322 *value = 0; | |
323 } | |
324 | |
325 virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) O
VERRIDE | |
326 { | |
327 m_contextLost = true; | |
328 *value = 0; | |
329 } | |
330 | |
331 virtual WGC3Denum getGraphicsResetStatusARB() OVERRIDE | |
332 { | |
333 return m_contextLost ? 1 : 0; | |
334 } | |
335 | |
336 private: | |
337 bool m_contextLost; | |
338 }; | |
339 | |
340 TEST(CCRendererGLTest2, initializationWithQuicklyLostContextDoesNotAssert) | |
341 { | |
342 CCScopedSettings scopedSettings; | |
343 FakeCCRendererClient mockClient; | |
344 scoped_ptr<CCGraphicsContext> context(FakeWebCompositorOutputSurface::create
(adoptPtr(new LoseContextOnFirstGetContext))); | |
345 OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(conte
xt.get())); | |
346 FakeCCRendererGL renderer(&mockClient, resourceProvider.get()); | |
347 | |
348 renderer.initialize(); | |
349 } | |
350 | |
351 class ContextThatDoesNotSupportMemoryManagmentExtensions : public FakeWebGraphic
sContext3D { | |
352 public: | |
353 ContextThatDoesNotSupportMemoryManagmentExtensions() { } | |
354 | |
355 // WebGraphicsContext3D methods. | |
356 | |
357 // This method would normally do a glSwapBuffers under the hood. | |
358 virtual void prepareTexture() { } | |
359 virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAll
ocationChangedCallbackCHROMIUM* callback) { } | |
360 virtual WebString getString(WebKit::WGC3Denum name) { return WebString(); } | |
361 }; | |
362 | |
363 TEST(CCRendererGLTest2, initializationWithoutGpuMemoryManagerExtensionSupportSho
uldDefaultToNonZeroAllocation) | |
364 { | |
365 FakeCCRendererClient mockClient; | |
366 scoped_ptr<CCGraphicsContext> context(FakeWebCompositorOutputSurface::create
(adoptPtr(new ContextThatDoesNotSupportMemoryManagmentExtensions))); | |
367 OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(conte
xt.get())); | |
368 FakeCCRendererGL renderer(&mockClient, resourceProvider.get()); | |
369 | |
370 renderer.initialize(); | |
371 | |
372 EXPECT_GT(mockClient.memoryAllocationLimitBytes(), 0ul); | |
373 } | |
374 | |
375 class ClearCountingContext : public FakeWebGraphicsContext3D { | |
376 public: | |
377 ClearCountingContext() : m_clear(0) { } | |
378 | |
379 virtual void clear(WGC3Dbitfield) | |
380 { | |
381 m_clear++; | |
382 } | |
383 | |
384 int clearCount() const { return m_clear; } | |
385 | |
386 private: | |
387 int m_clear; | |
388 }; | |
389 | |
390 TEST(CCRendererGLTest2, opaqueBackground) | |
391 { | |
392 FakeCCRendererClient mockClient; | |
393 scoped_ptr<CCGraphicsContext> ccContext(FakeWebCompositorOutputSurface::crea
te(adoptPtr(new ClearCountingContext))); | |
394 ClearCountingContext* context = static_cast<ClearCountingContext*>(ccContext
->context3D()); | |
395 OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(ccCon
text.get())); | |
396 FakeCCRendererGL renderer(&mockClient, resourceProvider.get()); | |
397 | |
398 mockClient.rootRenderPass()->setHasTransparentBackground(false); | |
399 | |
400 EXPECT_TRUE(renderer.initialize()); | |
401 | |
402 renderer.drawFrame(mockClient.renderPassesInDrawOrder(), mockClient.renderPa
sses()); | |
403 | |
404 // On DEBUG builds, render passes with opaque background clear to blue to | |
405 // easily see regions that were not drawn on the screen. | |
406 #if defined(NDEBUG) | |
407 EXPECT_EQ(0, context->clearCount()); | |
408 #else | |
409 EXPECT_EQ(1, context->clearCount()); | |
410 #endif | |
411 } | |
412 | |
413 TEST(CCRendererGLTest2, transparentBackground) | |
414 { | |
415 FakeCCRendererClient mockClient; | |
416 scoped_ptr<CCGraphicsContext> ccContext(FakeWebCompositorOutputSurface::crea
te(adoptPtr(new ClearCountingContext))); | |
417 ClearCountingContext* context = static_cast<ClearCountingContext*>(ccContext
->context3D()); | |
418 OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(ccCon
text.get())); | |
419 FakeCCRendererGL renderer(&mockClient, resourceProvider.get()); | |
420 | |
421 mockClient.rootRenderPass()->setHasTransparentBackground(true); | |
422 | |
423 EXPECT_TRUE(renderer.initialize()); | |
424 | |
425 renderer.drawFrame(mockClient.renderPassesInDrawOrder(), mockClient.renderPa
sses()); | |
426 | |
427 EXPECT_EQ(1, context->clearCount()); | |
428 } | |
OLD | NEW |