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 "cc/gl_renderer.h" | |
6 | |
7 #include "cc/compositor_frame_metadata.h" | |
8 #include "cc/draw_quad.h" | |
9 #include "cc/prioritized_resource_manager.h" | |
10 #include "cc/resource_provider.h" | |
11 #include "cc/test/fake_impl_proxy.h" | |
12 #include "cc/test/fake_layer_tree_host_impl.h" | |
13 #include "cc/test/fake_output_surface.h" | |
14 #include "cc/test/pixel_test.h" | |
15 #include "cc/test/render_pass_test_common.h" | |
16 #include "cc/test/render_pass_test_utils.h" | |
17 #include "cc/test/test_web_graphics_context_3d.h" | |
18 #include "testing/gmock/include/gmock/gmock.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 #include "third_party/khronos/GLES2/gl2.h" | |
21 #include "ui/gfx/transform.h" | |
22 | |
23 using namespace WebKit; | |
24 | |
25 using testing::_; | |
26 using testing::AnyNumber; | |
27 using testing::AtLeast; | |
28 using testing::Expectation; | |
29 using testing::InSequence; | |
30 using testing::Mock; | |
31 using testing::Return; | |
32 using testing::StrictMock; | |
33 | |
34 namespace cc { | |
35 | |
36 #define EXPECT_PROGRAM_VALID(program_binding) \ | |
37 do { \ | |
38 EXPECT_TRUE(program_binding->program()); \ | |
39 EXPECT_TRUE(program_binding->initialized()); \ | |
40 } while (false) | |
41 | |
42 // Explicitly named to be a friend in GLRenderer for shader access. | |
43 class GLRendererShaderTest : public PixelTest { | |
44 public: | |
45 void TestShaders() { | |
46 ASSERT_FALSE(renderer_->IsContextLost()); | |
47 EXPECT_PROGRAM_VALID(renderer_->GetTileProgram()); | |
48 EXPECT_PROGRAM_VALID(renderer_->GetTileProgramOpaque()); | |
49 EXPECT_PROGRAM_VALID(renderer_->GetTileProgramAA()); | |
50 EXPECT_PROGRAM_VALID(renderer_->GetTileProgramSwizzle()); | |
51 EXPECT_PROGRAM_VALID(renderer_->GetTileProgramSwizzleOpaque()); | |
52 EXPECT_PROGRAM_VALID(renderer_->GetTileProgramSwizzleAA()); | |
53 EXPECT_PROGRAM_VALID(renderer_->GetTileCheckerboardProgram()); | |
54 EXPECT_PROGRAM_VALID(renderer_->GetRenderPassProgram()); | |
55 EXPECT_PROGRAM_VALID(renderer_->GetRenderPassProgramAA()); | |
56 EXPECT_PROGRAM_VALID(renderer_->GetRenderPassMaskProgram()); | |
57 EXPECT_PROGRAM_VALID(renderer_->GetRenderPassMaskProgramAA()); | |
58 EXPECT_PROGRAM_VALID(renderer_->GetTextureProgram()); | |
59 EXPECT_PROGRAM_VALID(renderer_->GetTextureProgramFlip()); | |
60 EXPECT_PROGRAM_VALID(renderer_->GetTextureIOSurfaceProgram()); | |
61 EXPECT_PROGRAM_VALID(renderer_->GetVideoYUVProgram()); | |
62 // This is unlikely to be ever true in tests due to usage of osmesa. | |
63 if (renderer_->Capabilities().using_egl_image) | |
64 EXPECT_PROGRAM_VALID(renderer_->GetVideoStreamTextureProgram()); | |
65 else | |
66 EXPECT_FALSE(renderer_->GetVideoStreamTextureProgram()); | |
67 EXPECT_PROGRAM_VALID(renderer_->GetDebugBorderProgram()); | |
68 EXPECT_PROGRAM_VALID(renderer_->GetSolidColorProgram()); | |
69 EXPECT_PROGRAM_VALID(renderer_->GetSolidColorProgramAA()); | |
70 ASSERT_FALSE(renderer_->IsContextLost()); | |
71 } | |
72 }; | |
73 | |
74 namespace { | |
75 | |
76 #if !defined(OS_ANDROID) | |
77 TEST_F(GLRendererShaderTest, AllShadersCompile) { | |
78 TestShaders(); | |
79 } | |
80 #endif | |
81 | |
82 class FrameCountingMemoryAllocationSettingContext : public TestWebGraphicsContex
t3D { | |
83 public: | |
84 FrameCountingMemoryAllocationSettingContext() : m_frame(0) { } | |
85 | |
86 // WebGraphicsContext3D methods. | |
87 | |
88 // This method would normally do a glSwapBuffers under the hood. | |
89 virtual void prepareTexture() { m_frame++; } | |
90 virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAll
ocationChangedCallbackCHROMIUM* callback) { m_memoryAllocationChangedCallback =
callback; } | |
91 virtual WebString getString(WebKit::WGC3Denum name) | |
92 { | |
93 if (name == GL_EXTENSIONS) | |
94 return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_
manager GL_CHROMIUM_discard_backbuffer"); | |
95 return WebString(); | |
96 } | |
97 | |
98 // Methods added for test. | |
99 int frameCount() { return m_frame; } | |
100 void setMemoryAllocation(WebGraphicsMemoryAllocation allocation) | |
101 { | |
102 m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation)
; | |
103 } | |
104 | |
105 private: | |
106 int m_frame; | |
107 WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* m_memoryAllocationChange
dCallback; | |
108 }; | |
109 | |
110 class FakeRendererClient : public RendererClient { | |
111 public: | |
112 FakeRendererClient() | |
113 : m_hostImpl(&m_proxy) | |
114 , m_setFullRootLayerDamageCount(0) | |
115 , m_lastCallWasSetVisibility(0) | |
116 , m_rootLayer(LayerImpl::Create(m_hostImpl.active_tree(), 1)) | |
117 , m_memoryAllocationLimitBytes(PrioritizedResourceManager::defaultMemory
AllocationLimit()) | |
118 { | |
119 m_rootLayer->CreateRenderSurface(); | |
120 RenderPass::Id renderPassId = m_rootLayer->render_surface()->RenderPassI
d(); | |
121 scoped_ptr<RenderPass> root_render_pass = RenderPass::Create(); | |
122 root_render_pass->SetNew(renderPassId, gfx::Rect(), gfx::Rect(), gfx::Tr
ansform()); | |
123 m_renderPassesInDrawOrder.push_back(root_render_pass.Pass()); | |
124 } | |
125 | |
126 // RendererClient methods. | |
127 virtual gfx::Size DeviceViewportSize() const OVERRIDE { static gfx::Size fak
eSize(1, 1); return fakeSize; } | |
128 virtual const LayerTreeSettings& Settings() const OVERRIDE { static LayerTre
eSettings fakeSettings; return fakeSettings; } | |
129 virtual void DidLoseOutputSurface() OVERRIDE { } | |
130 virtual void OnSwapBuffersComplete() OVERRIDE { } | |
131 virtual void SetFullRootLayerDamage() OVERRIDE { m_setFullRootLayerDamageCou
nt++; } | |
132 virtual void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERR
IDE { m_memoryAllocationLimitBytes = policy.bytesLimitWhenVisible; } | |
133 virtual void EnforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy) O
VERRIDE { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisibility = false; } | |
134 virtual bool HasImplThread() const OVERRIDE { return false; } | |
135 virtual bool ShouldClearRootRenderPass() const OVERRIDE { return true; } | |
136 virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const | |
137 OVERRIDE { return CompositorFrameMetadata(); } | |
138 | |
139 // Methods added for test. | |
140 int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCou
nt; } | |
141 void setLastCallWasSetVisibilityPointer(bool* lastCallWasSetVisibility) { m_
lastCallWasSetVisibility = lastCallWasSetVisibility; } | |
142 | |
143 RenderPass* root_render_pass() { return m_renderPassesInDrawOrder.back(); } | |
144 RenderPassList& renderPassesInDrawOrder() { return m_renderPassesInDrawOrder
; } | |
145 | |
146 size_t memoryAllocationLimitBytes() const { return m_memoryAllocationLimitBy
tes; } | |
147 | |
148 private: | |
149 FakeImplProxy m_proxy; | |
150 FakeLayerTreeHostImpl m_hostImpl; | |
151 int m_setFullRootLayerDamageCount; | |
152 bool* m_lastCallWasSetVisibility; | |
153 scoped_ptr<LayerImpl> m_rootLayer; | |
154 RenderPassList m_renderPassesInDrawOrder; | |
155 size_t m_memoryAllocationLimitBytes; | |
156 }; | |
157 | |
158 class FakeRendererGL : public GLRenderer { | |
159 public: | |
160 FakeRendererGL(RendererClient* client, OutputSurface* outputSurface, Resourc
eProvider* resourceProvider) : GLRenderer(client, outputSurface, resourceProvide
r) { } | |
161 | |
162 // GLRenderer methods. | |
163 | |
164 // Changing visibility to public. | |
165 using GLRenderer::Initialize; | |
166 using GLRenderer::IsBackbufferDiscarded; | |
167 using GLRenderer::DoDrawQuad; | |
168 using GLRenderer::BeginDrawingFrame; | |
169 using GLRenderer::FinishDrawingQuadList; | |
170 }; | |
171 | |
172 class GLRendererTest : public testing::Test { | |
173 protected: | |
174 GLRendererTest() | |
175 : m_suggestHaveBackbufferYes(1, true) | |
176 , m_suggestHaveBackbufferNo(1, false) | |
177 , m_outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGrap
hicsContext3D>(new FrameCountingMemoryAllocationSettingContext()))) | |
178 , resource_provider_(ResourceProvider::Create(m_outputSurface.get())) | |
179 , m_renderer(&m_mockClient, m_outputSurface.get(), resource_provider_.ge
t()) | |
180 { | |
181 } | |
182 | |
183 virtual void SetUp() | |
184 { | |
185 m_renderer.Initialize(); | |
186 } | |
187 | |
188 void SwapBuffers() | |
189 { | |
190 m_renderer.SwapBuffers(); | |
191 } | |
192 | |
193 FrameCountingMemoryAllocationSettingContext* context() { return static_cast<
FrameCountingMemoryAllocationSettingContext*>(m_outputSurface->context3d()); } | |
194 | |
195 WebGraphicsMemoryAllocation m_suggestHaveBackbufferYes; | |
196 WebGraphicsMemoryAllocation m_suggestHaveBackbufferNo; | |
197 | |
198 scoped_ptr<OutputSurface> m_outputSurface; | |
199 FakeRendererClient m_mockClient; | |
200 scoped_ptr<ResourceProvider> resource_provider_; | |
201 FakeRendererGL m_renderer; | |
202 }; | |
203 | |
204 // Test GLRenderer discardBackbuffer functionality: | |
205 // Suggest recreating framebuffer when one already exists. | |
206 // Expected: it does nothing. | |
207 TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) | |
208 { | |
209 context()->setMemoryAllocation(m_suggestHaveBackbufferYes); | |
210 EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount()); | |
211 EXPECT_FALSE(m_renderer.IsBackbufferDiscarded()); | |
212 | |
213 SwapBuffers(); | |
214 EXPECT_EQ(1, context()->frameCount()); | |
215 } | |
216 | |
217 // Test GLRenderer discardBackbuffer functionality: | |
218 // Suggest discarding framebuffer when one exists and the renderer is not visibl
e. | |
219 // Expected: it is discarded and damage tracker is reset. | |
220 TEST_F(GLRendererTest, SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLa
yerWhileNotVisible) | |
221 { | |
222 m_renderer.SetVisible(false); | |
223 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
224 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
225 EXPECT_TRUE(m_renderer.IsBackbufferDiscarded()); | |
226 } | |
227 | |
228 // Test GLRenderer discardBackbuffer functionality: | |
229 // Suggest discarding framebuffer when one exists and the renderer is visible. | |
230 // Expected: the allocation is ignored. | |
231 TEST_F(GLRendererTest, SuggestBackbufferNoDoNothingWhenVisible) | |
232 { | |
233 m_renderer.SetVisible(true); | |
234 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
235 EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount()); | |
236 EXPECT_FALSE(m_renderer.IsBackbufferDiscarded()); | |
237 } | |
238 | |
239 | |
240 // Test GLRenderer discardBackbuffer functionality: | |
241 // Suggest discarding framebuffer when one does not exist. | |
242 // Expected: it does nothing. | |
243 TEST_F(GLRendererTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing) | |
244 { | |
245 m_renderer.SetVisible(false); | |
246 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
247 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
248 EXPECT_TRUE(m_renderer.IsBackbufferDiscarded()); | |
249 | |
250 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
251 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
252 EXPECT_TRUE(m_renderer.IsBackbufferDiscarded()); | |
253 } | |
254 | |
255 // Test GLRenderer discardBackbuffer functionality: | |
256 // Begin drawing a frame while a framebuffer is discarded. | |
257 // Expected: will recreate framebuffer. | |
258 TEST_F(GLRendererTest, DiscardedBackbufferIsRecreatedForScopeDuration) | |
259 { | |
260 m_renderer.SetVisible(false); | |
261 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
262 EXPECT_TRUE(m_renderer.IsBackbufferDiscarded()); | |
263 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
264 | |
265 m_renderer.SetVisible(true); | |
266 m_renderer.DrawFrame(m_mockClient.renderPassesInDrawOrder()); | |
267 EXPECT_FALSE(m_renderer.IsBackbufferDiscarded()); | |
268 | |
269 SwapBuffers(); | |
270 EXPECT_EQ(1, context()->frameCount()); | |
271 } | |
272 | |
273 TEST_F(GLRendererTest, FramebufferDiscardedAfterReadbackWhenNotVisible) | |
274 { | |
275 m_renderer.SetVisible(false); | |
276 context()->setMemoryAllocation(m_suggestHaveBackbufferNo); | |
277 EXPECT_TRUE(m_renderer.IsBackbufferDiscarded()); | |
278 EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); | |
279 | |
280 char pixels[4]; | |
281 m_renderer.DrawFrame(m_mockClient.renderPassesInDrawOrder()); | |
282 EXPECT_FALSE(m_renderer.IsBackbufferDiscarded()); | |
283 | |
284 m_renderer.GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1)); | |
285 EXPECT_TRUE(m_renderer.IsBackbufferDiscarded()); | |
286 EXPECT_EQ(2, m_mockClient.setFullRootLayerDamageCount()); | |
287 } | |
288 | |
289 class ForbidSynchronousCallContext : public TestWebGraphicsContext3D { | |
290 public: | |
291 ForbidSynchronousCallContext() { } | |
292 | |
293 virtual bool getActiveAttrib(WebGLId program, WGC3Duint index, ActiveInfo&)
{ ADD_FAILURE(); return false; } | |
294 virtual bool getActiveUniform(WebGLId program, WGC3Duint index, ActiveInfo&)
{ ADD_FAILURE(); return false; } | |
295 virtual void getAttachedShaders(WebGLId program, WGC3Dsizei maxCount, WGC3Ds
izei* count, WebGLId* shaders) { ADD_FAILURE(); } | |
296 virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) {
ADD_FAILURE(); return 0; } | |
297 virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { ADD_FAILURE
(); } | |
298 virtual void getBufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Din
t* value) { ADD_FAILURE(); } | |
299 virtual Attributes getContextAttributes() { ADD_FAILURE(); return attributes
_; } | |
300 virtual WGC3Denum getError() { ADD_FAILURE(); return 0; } | |
301 virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE();
} | |
302 virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, WGC3Denum
attachment, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } | |
303 virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value) | |
304 { | |
305 if (pname == GL_MAX_TEXTURE_SIZE) | |
306 *value = 1024; // MAX_TEXTURE_SIZE is cached client side, so it's OK
to query. | |
307 else | |
308 ADD_FAILURE(); | |
309 } | |
310 | |
311 // We allow querying the shader compilation and program link status in debug
mode, but not release. | |
312 virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) | |
313 { | |
314 #ifndef NDEBUG | |
315 *value = 1; | |
316 #else | |
317 ADD_FAILURE(); | |
318 #endif | |
319 } | |
320 | |
321 virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) | |
322 { | |
323 #ifndef NDEBUG | |
324 *value = 1; | |
325 #else | |
326 ADD_FAILURE(); | |
327 #endif | |
328 } | |
329 | |
330 virtual WebString getString(WGC3Denum name) | |
331 { | |
332 // We allow querying the extension string. | |
333 // FIXME: It'd be better to check that we only do this before starting a
ny other expensive work (like starting a compilation) | |
334 if (name != GL_EXTENSIONS) | |
335 ADD_FAILURE(); | |
336 return WebString(); | |
337 } | |
338 | |
339 virtual WebString getProgramInfoLog(WebGLId program) { ADD_FAILURE(); return
WebString(); } | |
340 virtual void getRenderbufferParameteriv(WGC3Denum target, WGC3Denum pname, W
GC3Dint* value) { ADD_FAILURE(); } | |
341 | |
342 virtual WebString getShaderInfoLog(WebGLId shader) { ADD_FAILURE(); return W
ebString(); } | |
343 virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precis
iontype, WGC3Dint* range, WGC3Dint* precision) { ADD_FAILURE(); } | |
344 virtual WebString getShaderSource(WebGLId shader) { ADD_FAILURE(); return We
bString(); } | |
345 virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat
* value) { ADD_FAILURE(); } | |
346 virtual void getTexParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint*
value) { ADD_FAILURE(); } | |
347 virtual void getUniformfv(WebGLId program, WGC3Dint location, WGC3Dfloat* va
lue) { ADD_FAILURE(); } | |
348 virtual void getUniformiv(WebGLId program, WGC3Dint location, WGC3Dint* valu
e) { ADD_FAILURE(); } | |
349 virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name)
{ ADD_FAILURE(); return 0; } | |
350 virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname, WGC3Dfloat*
value) { ADD_FAILURE(); } | |
351 virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname, WGC3Dint* v
alue) { ADD_FAILURE(); } | |
352 virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname
) { ADD_FAILURE(); return 0; } | |
353 }; | |
354 | |
355 // This test isn't using the same fixture as GLRendererTest, and you can't mix T
EST() and TEST_F() with the same name, hence LRC2. | |
356 TEST(GLRendererTest2, initializationDoesNotMakeSynchronousCalls) | |
357 { | |
358 FakeRendererClient mockClient; | |
359 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new ForbidSynchronousCallContext))); | |
360 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
361 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
362 | |
363 EXPECT_TRUE(renderer.Initialize()); | |
364 } | |
365 | |
366 class LoseContextOnFirstGetContext : public TestWebGraphicsContext3D { | |
367 public: | |
368 LoseContextOnFirstGetContext() | |
369 : m_contextLost(false) | |
370 { | |
371 } | |
372 | |
373 virtual bool makeContextCurrent() OVERRIDE | |
374 { | |
375 return !m_contextLost; | |
376 } | |
377 | |
378 virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value)
OVERRIDE | |
379 { | |
380 m_contextLost = true; | |
381 *value = 0; | |
382 } | |
383 | |
384 virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) O
VERRIDE | |
385 { | |
386 m_contextLost = true; | |
387 *value = 0; | |
388 } | |
389 | |
390 virtual WGC3Denum getGraphicsResetStatusARB() OVERRIDE | |
391 { | |
392 return m_contextLost ? 1 : 0; | |
393 } | |
394 | |
395 private: | |
396 bool m_contextLost; | |
397 }; | |
398 | |
399 TEST(GLRendererTest2, initializationWithQuicklyLostContextDoesNotAssert) | |
400 { | |
401 FakeRendererClient mockClient; | |
402 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new LoseContextOnFirstGetContext))); | |
403 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
404 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
405 | |
406 renderer.Initialize(); | |
407 } | |
408 | |
409 class ContextThatDoesNotSupportMemoryManagmentExtensions : public TestWebGraphic
sContext3D { | |
410 public: | |
411 ContextThatDoesNotSupportMemoryManagmentExtensions() { } | |
412 | |
413 // WebGraphicsContext3D methods. | |
414 | |
415 // This method would normally do a glSwapBuffers under the hood. | |
416 virtual void prepareTexture() { } | |
417 virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAll
ocationChangedCallbackCHROMIUM* callback) { } | |
418 virtual WebString getString(WebKit::WGC3Denum name) { return WebString(); } | |
419 }; | |
420 | |
421 TEST(GLRendererTest2, initializationWithoutGpuMemoryManagerExtensionSupportShoul
dDefaultToNonZeroAllocation) | |
422 { | |
423 FakeRendererClient mockClient; | |
424 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new ContextThatDoesNotSupportMemoryManagmentExt
ensions))); | |
425 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
426 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
427 | |
428 renderer.Initialize(); | |
429 | |
430 EXPECT_GT(mockClient.memoryAllocationLimitBytes(), 0ul); | |
431 } | |
432 | |
433 class ClearCountingContext : public TestWebGraphicsContext3D { | |
434 public: | |
435 ClearCountingContext() : m_clear(0) { } | |
436 | |
437 virtual void clear(WGC3Dbitfield) | |
438 { | |
439 m_clear++; | |
440 } | |
441 | |
442 int clearCount() const { return m_clear; } | |
443 | |
444 private: | |
445 int m_clear; | |
446 }; | |
447 | |
448 TEST(GLRendererTest2, opaqueBackground) | |
449 { | |
450 FakeRendererClient mockClient; | |
451 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new ClearCountingContext))); | |
452 ClearCountingContext* context = static_cast<ClearCountingContext*>(outputSur
face->context3d()); | |
453 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
454 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
455 | |
456 mockClient.root_render_pass()->has_transparent_background = false; | |
457 | |
458 EXPECT_TRUE(renderer.Initialize()); | |
459 | |
460 renderer.DrawFrame(mockClient.renderPassesInDrawOrder()); | |
461 | |
462 // On DEBUG builds, render passes with opaque background clear to blue to | |
463 // easily see regions that were not drawn on the screen. | |
464 #ifdef NDEBUG | |
465 EXPECT_EQ(0, context->clearCount()); | |
466 #else | |
467 EXPECT_EQ(1, context->clearCount()); | |
468 #endif | |
469 } | |
470 | |
471 TEST(GLRendererTest2, transparentBackground) | |
472 { | |
473 FakeRendererClient mockClient; | |
474 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new ClearCountingContext))); | |
475 ClearCountingContext* context = static_cast<ClearCountingContext*>(outputSur
face->context3d()); | |
476 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
477 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
478 | |
479 mockClient.root_render_pass()->has_transparent_background = true; | |
480 | |
481 EXPECT_TRUE(renderer.Initialize()); | |
482 | |
483 renderer.DrawFrame(mockClient.renderPassesInDrawOrder()); | |
484 | |
485 EXPECT_EQ(1, context->clearCount()); | |
486 } | |
487 | |
488 class VisibilityChangeIsLastCallTrackingContext : public TestWebGraphicsContext3
D { | |
489 public: | |
490 VisibilityChangeIsLastCallTrackingContext() | |
491 : m_lastCallWasSetVisibility(0) | |
492 { | |
493 } | |
494 | |
495 // WebGraphicsContext3D methods. | |
496 virtual void setVisibilityCHROMIUM(bool visible) { | |
497 if (!m_lastCallWasSetVisibility) | |
498 return; | |
499 DCHECK(*m_lastCallWasSetVisibility == false); | |
500 *m_lastCallWasSetVisibility = true; | |
501 } | |
502 virtual void flush() { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisi
bility = false; } | |
503 virtual void deleteTexture(WebGLId) { if (m_lastCallWasSetVisibility) *m_las
tCallWasSetVisibility = false; } | |
504 virtual void deleteFramebuffer(WebGLId) { if (m_lastCallWasSetVisibility) *m
_lastCallWasSetVisibility = false; } | |
505 virtual void deleteRenderbuffer(WebGLId) { if (m_lastCallWasSetVisibility) *
m_lastCallWasSetVisibility = false; } | |
506 | |
507 // This method would normally do a glSwapBuffers under the hood. | |
508 virtual WebString getString(WebKit::WGC3Denum name) | |
509 { | |
510 if (name == GL_EXTENSIONS) | |
511 return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_
manager GL_CHROMIUM_discard_backbuffer"); | |
512 return WebString(); | |
513 } | |
514 | |
515 // Methods added for test. | |
516 void setLastCallWasSetVisibilityPointer(bool* lastCallWasSetVisibility) { m_
lastCallWasSetVisibility = lastCallWasSetVisibility; } | |
517 | |
518 private: | |
519 bool* m_lastCallWasSetVisibility; | |
520 }; | |
521 | |
522 TEST(GLRendererTest2, visibilityChangeIsLastCall) | |
523 { | |
524 FakeRendererClient mockClient; | |
525 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new VisibilityChangeIsLastCallTrackingContext))
); | |
526 VisibilityChangeIsLastCallTrackingContext* context = static_cast<VisibilityC
hangeIsLastCallTrackingContext*>(outputSurface->context3d()); | |
527 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
528 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
529 | |
530 EXPECT_TRUE(renderer.Initialize()); | |
531 | |
532 bool lastCallWasSetVisiblity = false; | |
533 // Ensure that the call to setVisibilityCHROMIUM is the last call issue to t
he GPU | |
534 // process, after glFlush is called, and after the RendererClient's enforceM
anagedMemoryPolicy | |
535 // is called. Plumb this tracking between both the RenderClient and the Cont
ext by giving | |
536 // them both a pointer to a variable on the stack. | |
537 context->setLastCallWasSetVisibilityPointer(&lastCallWasSetVisiblity); | |
538 mockClient.setLastCallWasSetVisibilityPointer(&lastCallWasSetVisiblity); | |
539 renderer.SetVisible(true); | |
540 renderer.DrawFrame(mockClient.renderPassesInDrawOrder()); | |
541 renderer.SetVisible(false); | |
542 EXPECT_TRUE(lastCallWasSetVisiblity); | |
543 } | |
544 | |
545 class TextureStateTrackingContext : public TestWebGraphicsContext3D { | |
546 public: | |
547 TextureStateTrackingContext() | |
548 : m_activeTexture(GL_INVALID_ENUM) | |
549 { | |
550 } | |
551 | |
552 virtual WebString getString(WGC3Denum name) | |
553 { | |
554 if (name == GL_EXTENSIONS) | |
555 return WebString("GL_OES_EGL_image_external"); | |
556 return WebString(); | |
557 } | |
558 | |
559 MOCK_METHOD3(texParameteri, void(WGC3Denum target, WGC3Denum pname, WGC3Dint
param)); | |
560 MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum
type, WGC3Dintptr offset)); | |
561 | |
562 virtual void activeTexture(WGC3Denum texture) | |
563 { | |
564 EXPECT_NE(texture, m_activeTexture); | |
565 m_activeTexture = texture; | |
566 } | |
567 | |
568 WGC3Denum activeTexture() const { return m_activeTexture; } | |
569 | |
570 private: | |
571 WGC3Denum m_activeTexture; | |
572 }; | |
573 | |
574 TEST(GLRendererTest2, activeTextureState) | |
575 { | |
576 FakeRendererClient fakeClient; | |
577 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new TextureStateTrackingContext))); | |
578 TextureStateTrackingContext* context = static_cast<TextureStateTrackingConte
xt*>(outputSurface->context3d()); | |
579 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
580 FakeRendererGL renderer(&fakeClient, outputSurface.get(), resourceProvider.g
et()); | |
581 | |
582 // During initialization we are allowed to set any texture parameters. | |
583 EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber()); | |
584 EXPECT_TRUE(renderer.Initialize()); | |
585 | |
586 cc::RenderPass::Id id(1, 1); | |
587 scoped_ptr<TestRenderPass> pass = TestRenderPass::Create(); | |
588 pass->SetNew(id, gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 100, 100), gfx::
Transform()); | |
589 pass->AppendOneOfEveryQuadType(resourceProvider.get(), RenderPass::Id(2, 1))
; | |
590 | |
591 // Set up expected texture filter state transitions that match the quads | |
592 // created in AppendOneOfEveryQuadType(). | |
593 Mock::VerifyAndClearExpectations(context); | |
594 { | |
595 InSequence sequence; | |
596 | |
597 // yuv_quad is drawn with the default linear filter. | |
598 EXPECT_CALL(*context, drawElements(_, _, _, _)); | |
599 | |
600 // tile_quad is drawn with GL_NEAREST because it is not transformed or | |
601 // scaled. | |
602 EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER
, GL_NEAREST)); | |
603 EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER
, GL_NEAREST)); | |
604 EXPECT_CALL(*context, drawElements(_, _, _, _)); | |
605 | |
606 // transformed_tile_quad uses GL_LINEAR. | |
607 EXPECT_CALL(*context, drawElements(_, _, _, _)); | |
608 | |
609 // scaled_tile_quad also uses GL_LINEAR. | |
610 EXPECT_CALL(*context, drawElements(_, _, _, _)); | |
611 | |
612 // The remaining quads also use GL_LINEAR because nearest neighbor | |
613 // filtering is currently only used with tile quads. | |
614 EXPECT_CALL(*context, drawElements(_, _, _, _)).Times(6); | |
615 } | |
616 | |
617 cc::DirectRenderer::DrawingFrame drawingFrame; | |
618 renderer.BeginDrawingFrame(drawingFrame); | |
619 EXPECT_EQ(context->activeTexture(), GL_TEXTURE0); | |
620 | |
621 for (cc::QuadList::backToFrontIterator it = pass->quad_list.backToFrontBegin
(); | |
622 it != pass->quad_list.backToFrontEnd(); ++it) { | |
623 renderer.DoDrawQuad(drawingFrame, *it); | |
624 } | |
625 renderer.FinishDrawingQuadList(); | |
626 EXPECT_EQ(context->activeTexture(), GL_TEXTURE0); | |
627 Mock::VerifyAndClearExpectations(context); | |
628 } | |
629 | |
630 class NoClearRootRenderPassFakeClient : public FakeRendererClient { | |
631 public: | |
632 virtual bool ShouldClearRootRenderPass() const OVERRIDE { return false; } | |
633 }; | |
634 | |
635 class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D { | |
636 public: | |
637 MOCK_METHOD1(clear, void(WGC3Dbitfield mask)); | |
638 MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum
type, WGC3Dintptr offset)); | |
639 }; | |
640 | |
641 TEST(GLRendererTest2, shouldClearRootRenderPass) | |
642 { | |
643 NoClearRootRenderPassFakeClient mockClient; | |
644 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new NoClearRootRenderPassMockContext))); | |
645 NoClearRootRenderPassMockContext* mockContext = static_cast<NoClearRootRende
rPassMockContext*>(outputSurface->context3d()); | |
646 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
647 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
648 EXPECT_TRUE(renderer.Initialize()); | |
649 | |
650 gfx::Rect viewportRect(mockClient.DeviceViewportSize()); | |
651 ScopedPtrVector<RenderPass>& renderPasses = mockClient.renderPassesInDrawOrd
er(); | |
652 renderPasses.clear(); | |
653 | |
654 RenderPass::Id rootPassId(1, 0); | |
655 TestRenderPass* rootPass = addRenderPass(renderPasses, rootPassId, viewportR
ect, gfx::Transform()); | |
656 addQuad(rootPass, viewportRect, SK_ColorGREEN); | |
657 | |
658 RenderPass::Id childPassId(2, 0); | |
659 TestRenderPass* childPass = addRenderPass(renderPasses, childPassId, viewpor
tRect, gfx::Transform()); | |
660 addQuad(childPass, viewportRect, SK_ColorBLUE); | |
661 | |
662 addRenderPassQuad(rootPass, childPass); | |
663 | |
664 // First render pass is not the root one, clearing should happen. | |
665 EXPECT_CALL(*mockContext, clear(GL_COLOR_BUFFER_BIT)) | |
666 .Times(AtLeast(1)); | |
667 | |
668 Expectation firstRenderPass = EXPECT_CALL(*mockContext, drawElements(_, _, _
, _)) | |
669 .Times(1); | |
670 | |
671 // The second render pass is the root one, clearing should be prevented. | |
672 EXPECT_CALL(*mockContext, clear(GL_COLOR_BUFFER_BIT)) | |
673 .Times(0) | |
674 .After(firstRenderPass); | |
675 | |
676 EXPECT_CALL(*mockContext, drawElements(_, _, _, _)) | |
677 .Times(AnyNumber()) | |
678 .After(firstRenderPass); | |
679 | |
680 renderer.DecideRenderPassAllocationsForFrame(mockClient.renderPassesInDrawOr
der()); | |
681 renderer.DrawFrame(mockClient.renderPassesInDrawOrder()); | |
682 | |
683 // In multiple render passes all but the root pass should clear the framebuf
fer. | |
684 Mock::VerifyAndClearExpectations(&mockContext); | |
685 } | |
686 | |
687 class ScissorTestOnClearCheckingContext : public TestWebGraphicsContext3D { | |
688 public: | |
689 ScissorTestOnClearCheckingContext() : m_scissorEnabled(false) { } | |
690 | |
691 virtual void clear(WGC3Dbitfield) | |
692 { | |
693 EXPECT_FALSE(m_scissorEnabled); | |
694 } | |
695 | |
696 virtual void enable(WGC3Denum cap) | |
697 { | |
698 if (cap == GL_SCISSOR_TEST) | |
699 m_scissorEnabled = true; | |
700 } | |
701 | |
702 virtual void disable(WGC3Denum cap) | |
703 { | |
704 if (cap == GL_SCISSOR_TEST) | |
705 m_scissorEnabled = false; | |
706 } | |
707 | |
708 private: | |
709 bool m_scissorEnabled; | |
710 }; | |
711 | |
712 TEST(GLRendererTest2, scissorTestWhenClearing) { | |
713 FakeRendererClient mockClient; | |
714 scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_p
tr<WebKit::WebGraphicsContext3D>(new ScissorTestOnClearCheckingContext))); | |
715 scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outpu
tSurface.get())); | |
716 FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.g
et()); | |
717 EXPECT_TRUE(renderer.Initialize()); | |
718 EXPECT_FALSE(renderer.Capabilities().using_partial_swap); | |
719 | |
720 gfx::Rect viewportRect(mockClient.DeviceViewportSize()); | |
721 ScopedPtrVector<RenderPass>& renderPasses = mockClient.renderPassesInDrawOrd
er(); | |
722 renderPasses.clear(); | |
723 | |
724 gfx::Rect grandChildRect(25, 25); | |
725 RenderPass::Id grandChildPassId(3, 0); | |
726 TestRenderPass* grandChildPass = addRenderPass(renderPasses, grandChildPassI
d, grandChildRect, gfx::Transform()); | |
727 addClippedQuad(grandChildPass, grandChildRect, SK_ColorYELLOW); | |
728 | |
729 gfx::Rect childRect(50, 50); | |
730 RenderPass::Id childPassId(2, 0); | |
731 TestRenderPass* childPass = addRenderPass(renderPasses, childPassId, childRe
ct, gfx::Transform()); | |
732 addQuad(childPass, childRect, SK_ColorBLUE); | |
733 | |
734 RenderPass::Id rootPassId(1, 0); | |
735 TestRenderPass* rootPass = addRenderPass(renderPasses, rootPassId, viewportR
ect, gfx::Transform()); | |
736 addQuad(rootPass, viewportRect, SK_ColorGREEN); | |
737 | |
738 addRenderPassQuad(rootPass, childPass); | |
739 addRenderPassQuad(childPass, grandChildPass); | |
740 | |
741 renderer.DecideRenderPassAllocationsForFrame(mockClient.renderPassesInDrawOr
der()); | |
742 renderer.DrawFrame(mockClient.renderPassesInDrawOrder()); | |
743 } | |
744 | |
745 class OutputSurfaceMockContext : public TestWebGraphicsContext3D { | |
746 public: | |
747 // Specifically override methods even if they are unused (used in conjunctio
n with StrictMock). | |
748 // We need to make sure that GLRenderer does not issue framebuffer-related G
L calls directly. Instead these | |
749 // are supposed to go through the OutputSurface abstraction. | |
750 MOCK_METHOD0(ensureBackbufferCHROMIUM, void()); | |
751 MOCK_METHOD0(discardBackbufferCHROMIUM, void()); | |
752 MOCK_METHOD2(bindFramebuffer, void(WGC3Denum target, WebGLId framebuffer)); | |
753 MOCK_METHOD0(prepareTexture, void()); | |
754 MOCK_METHOD2(reshape, void(int width, int height)); | |
755 MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum
type, WGC3Dintptr offset)); | |
756 | |
757 virtual WebString getString(WebKit::WGC3Denum name) | |
758 { | |
759 if (name == GL_EXTENSIONS) | |
760 return WebString("GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_discard_ba
ckbuffer"); | |
761 return WebString(); | |
762 } | |
763 }; | |
764 | |
765 class MockOutputSurface : public OutputSurface { | |
766 public: | |
767 MockOutputSurface() | |
768 : OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D>(new StrictMock<
OutputSurfaceMockContext>)) { } | |
769 virtual ~MockOutputSurface() { } | |
770 | |
771 MOCK_METHOD1(SendFrameToParentCompositor, void(CompositorFrame* frame)); | |
772 MOCK_METHOD0(EnsureBackbuffer, void()); | |
773 MOCK_METHOD0(DiscardBackbuffer, void()); | |
774 MOCK_METHOD1(Reshape, void(gfx::Size size)); | |
775 MOCK_METHOD0(BindFramebuffer, void()); | |
776 MOCK_METHOD1(PostSubBuffer, void(gfx::Rect rect)); | |
777 MOCK_METHOD0(SwapBuffers, void()); | |
778 }; | |
779 | |
780 class MockOutputSurfaceTest : public testing::Test, | |
781 public FakeRendererClient { | |
782 protected: | |
783 MockOutputSurfaceTest() | |
784 : resource_provider_(ResourceProvider::Create(&m_outputSurface)) | |
785 , m_renderer(this, &m_outputSurface, resource_provider_.get()) | |
786 { | |
787 } | |
788 | |
789 virtual void SetUp() | |
790 { | |
791 EXPECT_TRUE(m_renderer.Initialize()); | |
792 } | |
793 | |
794 void SwapBuffers() | |
795 { | |
796 m_renderer.SwapBuffers(); | |
797 } | |
798 | |
799 void DrawFrame() | |
800 { | |
801 gfx::Rect viewportRect(DeviceViewportSize()); | |
802 ScopedPtrVector<RenderPass>& renderPasses = renderPassesInDrawOrder(); | |
803 renderPasses.clear(); | |
804 | |
805 RenderPass::Id renderPassId(1, 0); | |
806 TestRenderPass* renderPass = addRenderPass(renderPasses, renderPassId, v
iewportRect, gfx::Transform()); | |
807 addQuad(renderPass, viewportRect, SK_ColorGREEN); | |
808 | |
809 EXPECT_CALL(m_outputSurface, EnsureBackbuffer()) | |
810 .WillRepeatedly(Return()); | |
811 | |
812 EXPECT_CALL(m_outputSurface, Reshape(_)) | |
813 .Times(1); | |
814 | |
815 EXPECT_CALL(m_outputSurface, BindFramebuffer()) | |
816 .Times(1); | |
817 | |
818 EXPECT_CALL(*context(), drawElements(_, _, _, _)) | |
819 .Times(1); | |
820 | |
821 m_renderer.DecideRenderPassAllocationsForFrame(renderPassesInDrawOrder()
); | |
822 m_renderer.DrawFrame(renderPassesInDrawOrder()); | |
823 } | |
824 | |
825 OutputSurfaceMockContext* context() { return static_cast<OutputSurfaceMockCo
ntext*>(m_outputSurface.context3d()); } | |
826 | |
827 StrictMock<MockOutputSurface> m_outputSurface; | |
828 scoped_ptr<ResourceProvider> resource_provider_; | |
829 FakeRendererGL m_renderer; | |
830 }; | |
831 | |
832 TEST_F(MockOutputSurfaceTest, DrawFrameAndSwap) | |
833 { | |
834 DrawFrame(); | |
835 | |
836 EXPECT_CALL(m_outputSurface, SwapBuffers()) | |
837 .Times(1); | |
838 m_renderer.SwapBuffers(); | |
839 } | |
840 | |
841 class MockOutputSurfaceTestWithPartialSwap : public MockOutputSurfaceTest { | |
842 public: | |
843 virtual const LayerTreeSettings& Settings() const OVERRIDE | |
844 { | |
845 static LayerTreeSettings fakeSettings; | |
846 fakeSettings.partialSwapEnabled = true; | |
847 return fakeSettings; | |
848 } | |
849 }; | |
850 | |
851 TEST_F(MockOutputSurfaceTestWithPartialSwap, DrawFrameAndSwap) | |
852 { | |
853 DrawFrame(); | |
854 | |
855 EXPECT_CALL(m_outputSurface, PostSubBuffer(_)) | |
856 .Times(1); | |
857 m_renderer.SwapBuffers(); | |
858 } | |
859 | |
860 class MockOutputSurfaceTestWithSendCompositorFrame : public MockOutputSurfaceTes
t { | |
861 public: | |
862 virtual const LayerTreeSettings& Settings() const OVERRIDE | |
863 { | |
864 static LayerTreeSettings fakeSettings; | |
865 fakeSettings.compositorFrameMessage = true; | |
866 return fakeSettings; | |
867 } | |
868 }; | |
869 | |
870 TEST_F(MockOutputSurfaceTestWithSendCompositorFrame, DrawFrame) | |
871 { | |
872 EXPECT_CALL(m_outputSurface, SendFrameToParentCompositor(_)) | |
873 .Times(1); | |
874 DrawFrame(); | |
875 } | |
876 | |
877 } // namespace | |
878 } // namespace cc | |
OLD | NEW |