1 // Copyright 2014 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.
7 #include "cc/test/test_context_provider.h"
8 #include "cc/test/test_web_graphics_context_3d.h"
9 #include "content/browser/compositor/buffer_queue.h"
10 #include "content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h"
11 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
12 #include "content/common/gpu/client/gl_helper.h"
13 #include "gpu/GLES2/gl2extchromium.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/khronos/GLES2/gl2ext.h"
19 using ::testing::Expectation
;
21 using ::testing::Return
;
25 class StubGpuMemoryBufferImpl
: public gfx::GpuMemoryBuffer
{
27 StubGpuMemoryBufferImpl() {}
29 // Overridden from gfx::GpuMemoryBuffer:
30 bool Map(void** data
) override
{ return false; }
31 void Unmap() override
{}
32 bool IsMapped() const override
{ return false; }
33 Format
GetFormat() const override
{ return gfx::GpuMemoryBuffer::RGBX_8888
; }
34 void GetStride(int* stride
) const override
{}
35 gfx::GpuMemoryBufferId
GetId() const override
{ return 0; }
36 gfx::GpuMemoryBufferHandle
GetHandle() const override
{
37 return gfx::GpuMemoryBufferHandle();
39 ClientBuffer
AsClientBuffer() override
{
40 return reinterpret_cast<ClientBuffer
>(this);
44 class StubBrowserGpuMemoryBufferManager
: public BrowserGpuMemoryBufferManager
{
46 StubBrowserGpuMemoryBufferManager() : BrowserGpuMemoryBufferManager(1) {}
48 scoped_ptr
<gfx::GpuMemoryBuffer
> AllocateGpuMemoryBufferForScanout(
49 const gfx::Size
& size
,
50 gfx::GpuMemoryBuffer::Format format
,
51 int32 surface_id
) override
{
52 return make_scoped_ptr
<gfx::GpuMemoryBuffer
>(new StubGpuMemoryBufferImpl
);
56 class MockBufferQueue
: public BufferQueue
{
58 MockBufferQueue(scoped_refptr
<cc::ContextProvider
> context_provider
,
59 BrowserGpuMemoryBufferManager
* gpu_memory_buffer_manager
,
61 unsigned int internalformat
)
62 : BufferQueue(context_provider
,
66 gpu_memory_buffer_manager
,
68 MOCK_METHOD4(CopyBufferDamage
,
69 void(int, int, const gfx::Rect
&, const gfx::Rect
&));
72 class BufferQueueTest
: public ::testing::Test
{
74 BufferQueueTest() : doublebuffering_(true), first_frame_(true) {}
76 void SetUp() override
{
77 InitWithContext(cc::TestWebGraphicsContext3D::Create());
80 void InitWithContext(scoped_ptr
<cc::TestWebGraphicsContext3D
> context
) {
81 scoped_refptr
<cc::TestContextProvider
> context_provider
=
82 cc::TestContextProvider::Create(context
.Pass());
83 context_provider
->BindToCurrentThread();
84 gpu_memory_buffer_manager_
.reset(new StubBrowserGpuMemoryBufferManager
);
85 mock_output_surface_
=
86 new MockBufferQueue(context_provider
, gpu_memory_buffer_manager_
.get(),
87 GL_TEXTURE_2D
, GL_RGBA
);
88 output_surface_
.reset(mock_output_surface_
);
89 output_surface_
->Initialize();
92 unsigned current_surface() { return output_surface_
->current_surface_
.image
; }
93 const std::vector
<BufferQueue::AllocatedSurface
>& available_surfaces() {
94 return output_surface_
->available_surfaces_
;
96 const std::deque
<BufferQueue::AllocatedSurface
>& in_flight_surfaces() {
97 return output_surface_
->in_flight_surfaces_
;
100 const BufferQueue::AllocatedSurface
& displayed_frame() {
101 return output_surface_
->displayed_surface_
;
103 const BufferQueue::AllocatedSurface
& current_frame() {
104 return output_surface_
->current_surface_
;
106 const BufferQueue::AllocatedSurface
& last_frame() {
107 return output_surface_
->in_flight_surfaces_
.back();
109 const BufferQueue::AllocatedSurface
& next_frame() {
110 return output_surface_
->available_surfaces_
.back();
112 const gfx::Size
size() { return output_surface_
->size_
; }
115 int n
= available_surfaces().size() + in_flight_surfaces().size() +
116 (displayed_frame().texture
? 1 : 0);
117 if (current_surface())
122 // Check that each buffer is unique if present.
124 std::set
<unsigned> buffers
;
125 EXPECT_TRUE(InsertUnique(&buffers
, current_surface()));
126 EXPECT_TRUE(InsertUnique(&buffers
, displayed_frame().image
));
127 for (size_t i
= 0; i
< available_surfaces().size(); i
++)
128 EXPECT_TRUE(InsertUnique(&buffers
, available_surfaces()[i
].image
));
129 for (std::deque
<BufferQueue::AllocatedSurface
>::const_iterator it
=
130 in_flight_surfaces().begin();
131 it
!= in_flight_surfaces().end();
133 EXPECT_TRUE(InsertUnique(&buffers
, it
->image
));
137 output_surface_
->SwapBuffers(gfx::Rect(output_surface_
->size_
));
140 void SendDamagedFrame(const gfx::Rect
& damage
) {
141 // We don't care about the GL-level implementation here, just how it uses
143 output_surface_
->BindFramebuffer();
144 output_surface_
->SwapBuffers(damage
);
145 if (doublebuffering_
|| !first_frame_
)
146 output_surface_
->PageFlipComplete();
147 first_frame_
= false;
150 void SendFullFrame() { SendDamagedFrame(gfx::Rect(output_surface_
->size_
)); }
153 bool InsertUnique(std::set
<unsigned>* set
, unsigned value
) {
156 if (set
->find(value
) != set
->end())
162 scoped_ptr
<BrowserGpuMemoryBufferManager
> gpu_memory_buffer_manager_
;
163 scoped_ptr
<BufferQueue
> output_surface_
;
164 MockBufferQueue
* mock_output_surface_
;
165 bool doublebuffering_
;
170 const gfx::Size screen_size
= gfx::Size(30, 30);
171 const gfx::Rect screen_rect
= gfx::Rect(screen_size
);
172 const gfx::Rect small_damage
= gfx::Rect(gfx::Size(10, 10));
173 const gfx::Rect large_damage
= gfx::Rect(gfx::Size(20, 20));
174 const gfx::Rect overlapping_damage
= gfx::Rect(gfx::Size(5, 20));
176 GLuint
CreateImageDefault() {
177 static GLuint id
= 0;
181 class MockedContext
: public cc::TestWebGraphicsContext3D
{
184 ON_CALL(*this, createImageCHROMIUM(_
, _
, _
, _
))
185 .WillByDefault(testing::InvokeWithoutArgs(&CreateImageDefault
));
187 MOCK_METHOD2(bindFramebuffer
, void(GLenum
, GLuint
));
188 MOCK_METHOD2(bindTexture
, void(GLenum
, GLuint
));
189 MOCK_METHOD2(bindTexImage2DCHROMIUM
, void(GLenum
, GLint
));
190 MOCK_METHOD4(createImageCHROMIUM
,
191 GLuint(ClientBuffer
, GLsizei
, GLsizei
, GLenum
));
192 MOCK_METHOD1(destroyImageCHROMIUM
, void(GLuint
));
193 MOCK_METHOD5(framebufferTexture2D
,
194 void(GLenum
, GLenum
, GLenum
, GLuint
, GLint
));
197 class BufferQueueMockedContextTest
: public BufferQueueTest
{
199 void SetUp() override
{
200 context_
= new MockedContext();
201 InitWithContext(scoped_ptr
<cc::TestWebGraphicsContext3D
>(context_
));
205 MockedContext
* context_
;
208 scoped_ptr
<BufferQueue
> CreateOutputSurfaceWithMock(
210 MockedContext
** context
,
211 BrowserGpuMemoryBufferManager
* gpu_memory_buffer_manager
) {
212 *context
= new MockedContext();
213 scoped_refptr
<cc::TestContextProvider
> context_provider
=
214 cc::TestContextProvider::Create(
215 scoped_ptr
<cc::TestWebGraphicsContext3D
>(*context
));
216 context_provider
->BindToCurrentThread();
217 scoped_ptr
<BufferQueue
> buffer_queue(
218 new BufferQueue(context_provider
, target
, GL_RGBA
, nullptr,
219 gpu_memory_buffer_manager
, 1));
220 buffer_queue
->Initialize();
221 return buffer_queue
.Pass();
224 TEST(BufferQueueStandaloneTest
, FboInitialization
) {
225 MockedContext
* context
;
226 scoped_ptr
<BrowserGpuMemoryBufferManager
> gpu_memory_buffer_manager(
227 new StubBrowserGpuMemoryBufferManager
);
228 scoped_ptr
<BufferQueue
> output_surface
= CreateOutputSurfaceWithMock(
229 GL_TEXTURE_2D
, &context
, gpu_memory_buffer_manager
.get());
231 EXPECT_CALL(*context
, bindFramebuffer(GL_FRAMEBUFFER
, Ne(0U)));
232 ON_CALL(*context
, framebufferTexture2D(_
, _
, _
, _
, _
))
233 .WillByDefault(Return());
235 output_surface
->Reshape(gfx::Size(10, 20), 1.0f
);
238 TEST(BufferQueueStandaloneTest
, FboBinding
) {
239 GLenum targets
[] = { GL_TEXTURE_2D
, GL_TEXTURE_RECTANGLE_ARB
};
240 for (size_t i
= 0; i
< 2; ++i
) {
241 GLenum target
= targets
[i
];
242 MockedContext
* context
;
243 scoped_ptr
<BrowserGpuMemoryBufferManager
> gpu_memory_buffer_manager(
244 new StubBrowserGpuMemoryBufferManager
);
245 scoped_ptr
<BufferQueue
> output_surface
= CreateOutputSurfaceWithMock(
246 target
, &context
, gpu_memory_buffer_manager
.get());
247 EXPECT_CALL(*context
, bindTexture(target
, Ne(0U)));
248 EXPECT_CALL(*context
, destroyImageCHROMIUM(1));
250 EXPECT_CALL(*context
, createImageCHROMIUM(_
, 0, 0, GL_RGBA
))
251 .WillOnce(Return(1));
253 EXPECT_CALL(*context
, bindFramebuffer(GL_FRAMEBUFFER
, Ne(0U)));
254 Expectation tex
= EXPECT_CALL(*context
, bindTexture(target
, Ne(0U)));
255 Expectation bind_tex
=
256 EXPECT_CALL(*context
, bindTexImage2DCHROMIUM(target
, 1))
260 framebufferTexture2D(
261 GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, target
, Ne(0U), _
))
262 .After(fb
, bind_tex
);
264 output_surface
->BindFramebuffer();
268 TEST(BufferQueueStandaloneTest
, CheckBoundFramebuffer
) {
269 scoped_ptr
<BrowserGpuMemoryBufferManager
> gpu_memory_buffer_manager
;
270 scoped_ptr
<BufferQueue
> output_surface
;
271 scoped_refptr
<cc::TestContextProvider
> context_provider
=
272 cc::TestContextProvider::Create(cc::TestWebGraphicsContext3D::Create());
273 context_provider
->BindToCurrentThread();
274 gpu_memory_buffer_manager
.reset(new StubBrowserGpuMemoryBufferManager
);
276 scoped_ptr
<GLHelper
> gl_helper
;
277 gl_helper
.reset(new GLHelper(context_provider
->ContextGL(),
278 context_provider
->ContextSupport()));
280 output_surface
.reset(new BufferQueue(context_provider
, GL_TEXTURE_2D
, GL_RGBA
,
282 gpu_memory_buffer_manager
.get(), 1));
283 output_surface
->Initialize();
284 output_surface
->Reshape(screen_size
, 1.0f
);
285 // Trigger a sub-buffer copy to exercise all paths.
286 output_surface
->BindFramebuffer();
287 output_surface
->SwapBuffers(screen_rect
);
288 output_surface
->PageFlipComplete();
289 output_surface
->BindFramebuffer();
290 output_surface
->SwapBuffers(small_damage
);
293 context_provider
->ContextGL()->GetIntegerv(GL_FRAMEBUFFER_BINDING
,
295 EXPECT_EQ(static_cast<int>(output_surface
->fbo()), current_fbo
);
298 TEST_F(BufferQueueTest
, PartialSwapReuse
) {
299 output_surface_
->Reshape(screen_size
, 1.0f
);
300 ASSERT_TRUE(doublebuffering_
);
301 EXPECT_CALL(*mock_output_surface_
,
302 CopyBufferDamage(_
, _
, small_damage
, screen_rect
)).Times(1);
303 EXPECT_CALL(*mock_output_surface_
,
304 CopyBufferDamage(_
, _
, small_damage
, small_damage
)).Times(1);
305 EXPECT_CALL(*mock_output_surface_
,
306 CopyBufferDamage(_
, _
, large_damage
, small_damage
)).Times(1);
308 SendDamagedFrame(small_damage
);
309 SendDamagedFrame(small_damage
);
310 SendDamagedFrame(large_damage
);
311 // Verify that the damage has propagated.
312 EXPECT_EQ(next_frame().damage
, large_damage
);
315 TEST_F(BufferQueueTest
, PartialSwapFullFrame
) {
316 output_surface_
->Reshape(screen_size
, 1.0f
);
317 ASSERT_TRUE(doublebuffering_
);
318 EXPECT_CALL(*mock_output_surface_
,
319 CopyBufferDamage(_
, _
, small_damage
, screen_rect
)).Times(1);
321 SendDamagedFrame(small_damage
);
324 EXPECT_EQ(next_frame().damage
, screen_rect
);
327 TEST_F(BufferQueueTest
, PartialSwapOverlapping
) {
328 output_surface_
->Reshape(screen_size
, 1.0f
);
329 ASSERT_TRUE(doublebuffering_
);
330 EXPECT_CALL(*mock_output_surface_
,
331 CopyBufferDamage(_
, _
, small_damage
, screen_rect
)).Times(1);
332 EXPECT_CALL(*mock_output_surface_
, CopyBufferDamage(_
, _
, overlapping_damage
,
333 small_damage
)).Times(1);
336 SendDamagedFrame(small_damage
);
337 SendDamagedFrame(overlapping_damage
);
338 EXPECT_EQ(next_frame().damage
, overlapping_damage
);
341 TEST_F(BufferQueueTest
, MultipleBindCalls
) {
342 // Check that multiple bind calls do not create or change surfaces.
343 output_surface_
->BindFramebuffer();
344 EXPECT_EQ(1, CountBuffers());
345 unsigned int fb
= current_surface();
346 output_surface_
->BindFramebuffer();
347 EXPECT_EQ(1, CountBuffers());
348 EXPECT_EQ(fb
, current_surface());
351 TEST_F(BufferQueueTest
, CheckDoubleBuffering
) {
352 // Check buffer flow through double buffering path.
353 EXPECT_EQ(0, CountBuffers());
354 output_surface_
->BindFramebuffer();
355 EXPECT_EQ(1, CountBuffers());
356 EXPECT_NE(0U, current_surface());
357 EXPECT_FALSE(displayed_frame().texture
);
359 EXPECT_EQ(1U, in_flight_surfaces().size());
360 output_surface_
->PageFlipComplete();
361 EXPECT_EQ(0U, in_flight_surfaces().size());
362 EXPECT_TRUE(displayed_frame().texture
);
363 output_surface_
->BindFramebuffer();
364 EXPECT_EQ(2, CountBuffers());
366 EXPECT_NE(0U, current_surface());
367 EXPECT_EQ(0U, in_flight_surfaces().size());
368 EXPECT_TRUE(displayed_frame().texture
);
371 EXPECT_EQ(1U, in_flight_surfaces().size());
372 EXPECT_TRUE(displayed_frame().texture
);
373 output_surface_
->PageFlipComplete();
375 EXPECT_EQ(0U, in_flight_surfaces().size());
376 EXPECT_EQ(1U, available_surfaces().size());
377 EXPECT_TRUE(displayed_frame().texture
);
378 output_surface_
->BindFramebuffer();
379 EXPECT_EQ(2, CountBuffers());
381 EXPECT_TRUE(available_surfaces().empty());
384 TEST_F(BufferQueueTest
, CheckTripleBuffering
) {
385 // Check buffer flow through triple buffering path.
387 // This bit is the same sequence tested in the doublebuffering case.
388 output_surface_
->BindFramebuffer();
389 EXPECT_FALSE(displayed_frame().texture
);
391 output_surface_
->PageFlipComplete();
392 output_surface_
->BindFramebuffer();
395 EXPECT_EQ(2, CountBuffers());
397 EXPECT_EQ(1U, in_flight_surfaces().size());
398 EXPECT_TRUE(displayed_frame().texture
);
399 output_surface_
->BindFramebuffer();
400 EXPECT_EQ(3, CountBuffers());
402 EXPECT_NE(0U, current_surface());
403 EXPECT_EQ(1U, in_flight_surfaces().size());
404 EXPECT_TRUE(displayed_frame().texture
);
405 output_surface_
->PageFlipComplete();
406 EXPECT_EQ(3, CountBuffers());
408 EXPECT_NE(0U, current_surface());
409 EXPECT_EQ(0U, in_flight_surfaces().size());
410 EXPECT_TRUE(displayed_frame().texture
);
411 EXPECT_EQ(1U, available_surfaces().size());
414 TEST_F(BufferQueueTest
, CheckCorrectBufferOrdering
) {
415 const size_t kSwapCount
= 3;
416 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
417 output_surface_
->BindFramebuffer();
421 EXPECT_EQ(kSwapCount
, in_flight_surfaces().size());
422 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
423 unsigned int next_texture_id
= in_flight_surfaces().front().texture
;
424 output_surface_
->PageFlipComplete();
425 EXPECT_EQ(displayed_frame().texture
, next_texture_id
);
429 TEST_F(BufferQueueTest
, ReshapeWithInFlightSurfaces
) {
430 const size_t kSwapCount
= 3;
431 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
432 output_surface_
->BindFramebuffer();
436 output_surface_
->Reshape(gfx::Size(10, 20), 1.0f
);
437 EXPECT_EQ(3u, in_flight_surfaces().size());
439 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
440 output_surface_
->PageFlipComplete();
441 EXPECT_EQ(0u, displayed_frame().texture
);
444 // The dummy surfacess left should be discarded.
445 EXPECT_EQ(0u, available_surfaces().size());
448 TEST_F(BufferQueueTest
, SwapAfterReshape
) {
449 const size_t kSwapCount
= 3;
450 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
451 output_surface_
->BindFramebuffer();
455 output_surface_
->Reshape(gfx::Size(10, 20), 1.0f
);
457 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
458 output_surface_
->BindFramebuffer();
462 EXPECT_EQ(2 * kSwapCount
, in_flight_surfaces().size());
464 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
465 output_surface_
->PageFlipComplete();
466 EXPECT_EQ(0u, displayed_frame().texture
);
471 for (size_t i
= 0; i
< kSwapCount
; ++i
) {
472 unsigned int next_texture_id
= in_flight_surfaces().front().texture
;
473 output_surface_
->PageFlipComplete();
474 EXPECT_EQ(displayed_frame().texture
, next_texture_id
);
475 EXPECT_NE(0u, displayed_frame().texture
);
479 TEST_F(BufferQueueMockedContextTest
, RecreateBuffers
) {
480 // This setup is to easily get one frame in each of:
481 // - currently bound for drawing.
482 // - in flight to GPU.
483 // - currently displayed.
485 // This tests buffers in all states.
486 // Bind/swap pushes frames into the in flight list, then the PageFlipComplete
487 // calls pull one frame into displayed and another into the free list.
488 output_surface_
->BindFramebuffer();
490 output_surface_
->BindFramebuffer();
492 output_surface_
->BindFramebuffer();
494 output_surface_
->BindFramebuffer();
495 output_surface_
->PageFlipComplete();
496 output_surface_
->PageFlipComplete();
497 // We should have one buffer in each possible state right now, including one
499 ASSERT_EQ(1U, in_flight_surfaces().size());
500 ASSERT_EQ(1U, available_surfaces().size());
501 EXPECT_TRUE(displayed_frame().texture
);
502 EXPECT_TRUE(current_frame().texture
);
504 auto current
= current_frame();
505 auto displayed
= displayed_frame();
506 auto in_flight
= in_flight_surfaces().front();
507 auto available
= available_surfaces().front();
509 // Expect all 4 images to be destroyed, 3 of the existing textures to be
510 // copied from and 3 new images to be created.
511 EXPECT_CALL(*context_
, createImageCHROMIUM(_
, 0, 0, GL_RGBA
)).Times(3);
513 EXPECT_CALL(*mock_output_surface_
,
514 CopyBufferDamage(_
, displayed
.texture
, _
, _
)).Times(1);
516 EXPECT_CALL(*mock_output_surface_
,
517 CopyBufferDamage(_
, current
.texture
, _
, _
)).Times(1);
519 EXPECT_CALL(*mock_output_surface_
,
520 CopyBufferDamage(_
, in_flight
.texture
, _
, _
)).Times(1);
522 EXPECT_CALL(*context_
, destroyImageCHROMIUM(displayed
.image
))
525 EXPECT_CALL(*context_
, destroyImageCHROMIUM(current
.image
))
528 EXPECT_CALL(*context_
, destroyImageCHROMIUM(in_flight
.image
))
531 EXPECT_CALL(*context_
, destroyImageCHROMIUM(available
.image
)).Times(1);
532 // After copying, we expect the framebuffer binding to be updated.
533 EXPECT_CALL(*context_
, bindFramebuffer(_
, _
))
537 EXPECT_CALL(*context_
, framebufferTexture2D(_
, _
, _
, _
, _
))
542 output_surface_
->RecreateBuffers();
543 testing::Mock::VerifyAndClearExpectations(context_
);
544 testing::Mock::VerifyAndClearExpectations(mock_output_surface_
);
546 // All free buffers should be destroyed, the remaining buffers should all
547 // be replaced but still valid.
548 EXPECT_EQ(1U, in_flight_surfaces().size());
549 EXPECT_EQ(0U, available_surfaces().size());
550 EXPECT_TRUE(displayed_frame().texture
);
551 EXPECT_TRUE(current_frame().texture
);
555 } // namespace content