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.
6 #include "cc/output/compositor_frame.h"
7 #include "cc/output/delegated_frame_data.h"
8 #include "cc/resources/resource_provider.h"
9 #include "cc/surfaces/surface.h"
10 #include "cc/surfaces/surface_factory.h"
11 #include "cc/surfaces/surface_factory_client.h"
12 #include "cc/surfaces/surface_manager.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "ui/gfx/geometry/size.h"
19 class TestSurfaceFactoryClient
: public SurfaceFactoryClient
{
21 TestSurfaceFactoryClient() {}
22 ~TestSurfaceFactoryClient() override
{}
24 void ReturnResources(const ReturnedResourceArray
& resources
) override
{
25 returned_resources_
.insert(
26 returned_resources_
.end(), resources
.begin(), resources
.end());
29 const ReturnedResourceArray
& returned_resources() const {
30 return returned_resources_
;
33 void clear_returned_resources() { returned_resources_
.clear(); }
36 ReturnedResourceArray returned_resources_
;
38 DISALLOW_COPY_AND_ASSIGN(TestSurfaceFactoryClient
);
41 class SurfaceFactoryTest
: public testing::Test
{
43 SurfaceFactoryTest() : factory_(&manager_
, &client_
), surface_id_(3) {
44 factory_
.Create(surface_id_
);
47 ~SurfaceFactoryTest() override
{
48 if (!surface_id_
.is_null())
49 factory_
.Destroy(surface_id_
);
52 void SubmitCompositorFrameWithResources(ResourceId
* resource_ids
,
53 size_t num_resource_ids
) {
54 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
55 for (size_t i
= 0u; i
< num_resource_ids
; ++i
) {
56 TransferableResource resource
;
57 resource
.id
= resource_ids
[i
];
58 resource
.mailbox_holder
.texture_target
= GL_TEXTURE_2D
;
59 frame_data
->resource_list
.push_back(resource
);
61 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
62 frame
->delegated_frame_data
= frame_data
.Pass();
63 factory_
.SubmitCompositorFrame(surface_id_
, frame
.Pass(),
64 SurfaceFactory::DrawCallback());
67 void UnrefResources(ResourceId
* ids_to_unref
,
69 size_t num_ids_to_unref
) {
70 ReturnedResourceArray unref_array
;
71 for (size_t i
= 0; i
< num_ids_to_unref
; ++i
) {
72 ReturnedResource resource
;
73 resource
.id
= ids_to_unref
[i
];
74 resource
.count
= counts_to_unref
[i
];
75 unref_array
.push_back(resource
);
77 factory_
.UnrefResources(unref_array
);
80 void CheckReturnedResourcesMatchExpected(ResourceId
* expected_returned_ids
,
81 int* expected_returned_counts
,
82 size_t expected_resources
) {
83 const ReturnedResourceArray
& actual_resources
=
84 client_
.returned_resources();
85 ASSERT_EQ(expected_resources
, actual_resources
.size());
86 for (size_t i
= 0; i
< expected_resources
; ++i
) {
87 ReturnedResource resource
= actual_resources
[i
];
88 EXPECT_EQ(expected_returned_ids
[i
], resource
.id
);
89 EXPECT_EQ(expected_returned_counts
[i
], resource
.count
);
91 client_
.clear_returned_resources();
94 void RefCurrentFrameResources() {
95 Surface
* surface
= manager_
.GetSurfaceForId(surface_id_
);
96 factory_
.RefResources(
97 surface
->GetEligibleFrame()->delegated_frame_data
->resource_list
);
101 SurfaceManager manager_
;
102 TestSurfaceFactoryClient client_
;
103 SurfaceFactory factory_
;
104 SurfaceId surface_id_
;
107 // Tests submitting a frame with resources followed by one with no resources
108 // with no resource provider action in between.
109 TEST_F(SurfaceFactoryTest
, ResourceLifetimeSimple
) {
110 ResourceId first_frame_ids
[] = {1, 2, 3};
111 SubmitCompositorFrameWithResources(first_frame_ids
,
112 arraysize(first_frame_ids
));
114 // All of the resources submitted in the first frame are still in use at this
115 // time by virtue of being in the pending frame, so none can be returned to
117 EXPECT_EQ(0u, client_
.returned_resources().size());
118 client_
.clear_returned_resources();
120 // The second frame references no resources and thus should make all resources
121 // available to be returned.
122 SubmitCompositorFrameWithResources(NULL
, 0);
124 ResourceId expected_returned_ids
[] = {1, 2, 3};
125 int expected_returned_counts
[] = {1, 1, 1};
126 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
127 expected_returned_counts
,
128 arraysize(expected_returned_counts
));
131 // Tests submitting a frame with resources followed by one with no resources
132 // with the resource provider holding everything alive.
133 TEST_F(SurfaceFactoryTest
, ResourceLifetimeSimpleWithProviderHoldingAlive
) {
134 ResourceId first_frame_ids
[] = {1, 2, 3};
135 SubmitCompositorFrameWithResources(first_frame_ids
,
136 arraysize(first_frame_ids
));
138 // All of the resources submitted in the first frame are still in use at this
139 // time by virtue of being in the pending frame, so none can be returned to
141 EXPECT_EQ(0u, client_
.returned_resources().size());
142 client_
.clear_returned_resources();
144 // Hold on to everything.
145 RefCurrentFrameResources();
147 // The second frame references no resources and thus should make all resources
148 // available to be returned as soon as the resource provider releases them.
149 SubmitCompositorFrameWithResources(NULL
, 0);
151 EXPECT_EQ(0u, client_
.returned_resources().size());
152 client_
.clear_returned_resources();
154 int release_counts
[] = {1, 1, 1};
155 UnrefResources(first_frame_ids
, release_counts
, arraysize(first_frame_ids
));
157 ResourceId expected_returned_ids
[] = {1, 2, 3};
158 int expected_returned_counts
[] = {1, 1, 1};
159 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
160 expected_returned_counts
,
161 arraysize(expected_returned_counts
));
164 // Tests referencing a resource, unref'ing it to zero, then using it again
165 // before returning it to the client.
166 TEST_F(SurfaceFactoryTest
, ResourceReusedBeforeReturn
) {
167 ResourceId first_frame_ids
[] = {7};
168 SubmitCompositorFrameWithResources(first_frame_ids
,
169 arraysize(first_frame_ids
));
171 // This removes all references to resource id 7.
172 SubmitCompositorFrameWithResources(NULL
, 0);
174 // This references id 7 again.
175 SubmitCompositorFrameWithResources(first_frame_ids
,
176 arraysize(first_frame_ids
));
178 // This removes it again.
179 SubmitCompositorFrameWithResources(NULL
, 0);
181 // Now it should be returned.
182 // We don't care how many entries are in the returned array for 7, so long as
183 // the total returned count matches the submitted count.
184 const ReturnedResourceArray
& returned
= client_
.returned_resources();
185 size_t return_count
= 0;
186 for (size_t i
= 0; i
< returned
.size(); ++i
) {
187 EXPECT_EQ(7u, returned
[i
].id
);
188 return_count
+= returned
[i
].count
;
190 EXPECT_EQ(2u, return_count
);
193 // Tests having resources referenced multiple times, as if referenced by
194 // multiple providers.
195 TEST_F(SurfaceFactoryTest
, ResourceRefMultipleTimes
) {
196 ResourceId first_frame_ids
[] = {3, 4};
197 SubmitCompositorFrameWithResources(first_frame_ids
,
198 arraysize(first_frame_ids
));
200 // Ref resources from the first frame twice.
201 RefCurrentFrameResources();
202 RefCurrentFrameResources();
204 ResourceId second_frame_ids
[] = {4, 5};
205 SubmitCompositorFrameWithResources(second_frame_ids
,
206 arraysize(second_frame_ids
));
208 // Ref resources from the second frame 3 times.
209 RefCurrentFrameResources();
210 RefCurrentFrameResources();
211 RefCurrentFrameResources();
213 // Submit a frame with no resources to remove all current frame refs from
214 // submitted resources.
215 SubmitCompositorFrameWithResources(NULL
, 0);
217 EXPECT_EQ(0u, client_
.returned_resources().size());
218 client_
.clear_returned_resources();
220 // Expected current refs:
225 SCOPED_TRACE("unref all 3");
226 ResourceId ids_to_unref
[] = {3, 4, 5};
227 int counts
[] = {1, 1, 1};
228 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
230 EXPECT_EQ(0u, client_
.returned_resources().size());
231 client_
.clear_returned_resources();
233 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
235 ResourceId expected_returned_ids
[] = {3};
236 int expected_returned_counts
[] = {1};
237 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
238 expected_returned_counts
,
239 arraysize(expected_returned_counts
));
242 // Expected refs remaining:
246 SCOPED_TRACE("unref 4 and 5");
247 ResourceId ids_to_unref
[] = {4, 5};
248 int counts
[] = {1, 1};
249 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
251 ResourceId expected_returned_ids
[] = {5};
252 int expected_returned_counts
[] = {1};
253 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
254 expected_returned_counts
,
255 arraysize(expected_returned_counts
));
258 // Now, just 2 refs remaining on resource 4. Unref both at once and make sure
259 // the returned count is correct.
261 SCOPED_TRACE("unref only 4");
262 ResourceId ids_to_unref
[] = {4};
264 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
266 ResourceId expected_returned_ids
[] = {4};
267 int expected_returned_counts
[] = {2};
268 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
269 expected_returned_counts
,
270 arraysize(expected_returned_counts
));
274 TEST_F(SurfaceFactoryTest
, ResourceLifetime
) {
275 ResourceId first_frame_ids
[] = {1, 2, 3};
276 SubmitCompositorFrameWithResources(first_frame_ids
,
277 arraysize(first_frame_ids
));
279 // All of the resources submitted in the first frame are still in use at this
280 // time by virtue of being in the pending frame, so none can be returned to
282 EXPECT_EQ(0u, client_
.returned_resources().size());
283 client_
.clear_returned_resources();
285 // The second frame references some of the same resources, but some different
286 // ones. We expect to receive back resource 1 with a count of 1 since it was
287 // only referenced by the first frame.
288 ResourceId second_frame_ids
[] = {2, 3, 4};
289 SubmitCompositorFrameWithResources(second_frame_ids
,
290 arraysize(second_frame_ids
));
293 SCOPED_TRACE("second frame");
294 ResourceId expected_returned_ids
[] = {1};
295 int expected_returned_counts
[] = {1};
296 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
297 expected_returned_counts
,
298 arraysize(expected_returned_counts
));
301 // The third frame references a disjoint set of resources, so we expect to
302 // receive back all resources from the first and second frames. Resource IDs 2
303 // and 3 will have counts of 2, since they were used in both frames, and
304 // resource ID 4 will have a count of 1.
305 ResourceId third_frame_ids
[] = {10, 11, 12, 13};
306 SubmitCompositorFrameWithResources(third_frame_ids
,
307 arraysize(third_frame_ids
));
310 SCOPED_TRACE("third frame");
311 ResourceId expected_returned_ids
[] = {2, 3, 4};
312 int expected_returned_counts
[] = {2, 2, 1};
313 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
314 expected_returned_counts
,
315 arraysize(expected_returned_counts
));
318 // Simulate a ResourceProvider taking a ref on all of the resources.
319 RefCurrentFrameResources();
321 ResourceId fourth_frame_ids
[] = {12, 13};
322 SubmitCompositorFrameWithResources(fourth_frame_ids
,
323 arraysize(fourth_frame_ids
));
325 EXPECT_EQ(0u, client_
.returned_resources().size());
327 RefCurrentFrameResources();
329 // All resources are still being used by the external reference, so none can
330 // be returned to the client.
331 EXPECT_EQ(0u, client_
.returned_resources().size());
333 // Release resources associated with the first RefCurrentFrameResources() call
336 ResourceId ids_to_unref
[] = {10, 11, 12, 13};
337 int counts
[] = {1, 1, 1, 1};
338 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
342 SCOPED_TRACE("fourth frame, first unref");
343 ResourceId expected_returned_ids
[] = {10, 11};
344 int expected_returned_counts
[] = {1, 1};
345 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
346 expected_returned_counts
,
347 arraysize(expected_returned_counts
));
351 ResourceId ids_to_unref
[] = {12, 13};
352 int counts
[] = {1, 1};
353 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
356 // Resources 12 and 13 are still in use by the current frame, so they
357 // shouldn't be available to be returned.
358 EXPECT_EQ(0u, client_
.returned_resources().size());
360 // If we submit an empty frame, however, they should become available.
361 SubmitCompositorFrameWithResources(NULL
, 0u);
364 SCOPED_TRACE("fourth frame, second unref");
365 ResourceId expected_returned_ids
[] = {12, 13};
366 int expected_returned_counts
[] = {2, 2};
367 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
368 expected_returned_counts
,
369 arraysize(expected_returned_counts
));
373 TEST_F(SurfaceFactoryTest
, BlankNoIndexIncrement
) {
374 SurfaceId
surface_id(6);
375 factory_
.Create(surface_id
);
376 Surface
* surface
= manager_
.GetSurfaceForId(surface_id
);
377 ASSERT_NE(nullptr, surface
);
378 EXPECT_EQ(2, surface
->frame_index());
379 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
380 frame
->delegated_frame_data
.reset(new DelegatedFrameData
);
382 factory_
.SubmitCompositorFrame(surface_id
, frame
.Pass(),
383 SurfaceFactory::DrawCallback());
384 EXPECT_EQ(2, surface
->frame_index());
385 factory_
.Destroy(surface_id
);
388 void DrawCallback(uint32
* execute_count
,
389 SurfaceDrawStatus
* result
,
390 SurfaceDrawStatus drawn
) {
395 // Tests doing a DestroyAll before shutting down the factory;
396 TEST_F(SurfaceFactoryTest
, DestroyAll
) {
400 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
401 TransferableResource resource
;
403 resource
.mailbox_holder
.texture_target
= GL_TEXTURE_2D
;
404 frame_data
->resource_list
.push_back(resource
);
405 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
406 frame
->delegated_frame_data
= frame_data
.Pass();
407 uint32 execute_count
= 0;
408 SurfaceDrawStatus drawn
= SurfaceDrawStatus::DRAW_SKIPPED
;
410 factory_
.SubmitCompositorFrame(
411 id
, frame
.Pass(), base::Bind(&DrawCallback
, &execute_count
, &drawn
));
413 surface_id_
= SurfaceId();
414 factory_
.DestroyAll();
415 EXPECT_EQ(1u, execute_count
);
416 EXPECT_EQ(SurfaceDrawStatus::DRAW_SKIPPED
, drawn
);
419 TEST_F(SurfaceFactoryTest
, DestroySequence
) {
421 factory_
.Create(id2
);
423 manager_
.RegisterSurfaceIdNamespace(0);
425 // Check that waiting before the sequence is satisfied works.
426 manager_
.GetSurfaceForId(id2
)
427 ->AddDestructionDependency(SurfaceSequence(0, 4));
428 factory_
.Destroy(id2
);
430 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
431 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
432 frame
->metadata
.satisfies_sequences
.push_back(6);
433 frame
->metadata
.satisfies_sequences
.push_back(4);
434 frame
->delegated_frame_data
= frame_data
.Pass();
435 DCHECK(manager_
.GetSurfaceForId(id2
));
436 factory_
.SubmitCompositorFrame(surface_id_
, frame
.Pass(),
437 SurfaceFactory::DrawCallback());
438 DCHECK(!manager_
.GetSurfaceForId(id2
));
440 // Check that waiting after the sequence is satisfied works.
441 factory_
.Create(id2
);
442 DCHECK(manager_
.GetSurfaceForId(id2
));
443 manager_
.GetSurfaceForId(id2
)
444 ->AddDestructionDependency(SurfaceSequence(0, 6));
445 factory_
.Destroy(id2
);
446 DCHECK(!manager_
.GetSurfaceForId(id2
));
449 // Tests that Surface ID namespace invalidation correctly allows
450 // Sequences to be ignored.
451 TEST_F(SurfaceFactoryTest
, InvalidIdNamespace
) {
452 uint32_t id_namespace
= 9u;
456 manager_
.RegisterSurfaceIdNamespace(id_namespace
);
457 manager_
.GetSurfaceForId(id
)
458 ->AddDestructionDependency(SurfaceSequence(id_namespace
, 4));
459 factory_
.Destroy(id
);
461 // Verify the dependency has prevented the surface from getting destroyed.
462 EXPECT_TRUE(manager_
.GetSurfaceForId(id
));
464 manager_
.InvalidateSurfaceIdNamespace(id_namespace
);
466 // Verify that the invalidated namespace caused the unsatisfied sequence
468 EXPECT_FALSE(manager_
.GetSurfaceForId(id
));
471 TEST_F(SurfaceFactoryTest
, DestroyCycle
) {
473 factory_
.Create(id2
);
475 manager_
.RegisterSurfaceIdNamespace(0);
477 manager_
.GetSurfaceForId(id2
)
478 ->AddDestructionDependency(SurfaceSequence(0, 4));
480 // Give id2 a frame that references surface_id_.
482 scoped_ptr
<RenderPass
> render_pass(RenderPass::Create());
483 render_pass
->referenced_surfaces
.push_back(surface_id_
);
484 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
485 frame_data
->render_pass_list
.push_back(render_pass
.Pass());
486 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
487 frame
->delegated_frame_data
= frame_data
.Pass();
488 factory_
.SubmitCompositorFrame(id2
, frame
.Pass(),
489 SurfaceFactory::DrawCallback());
491 factory_
.Destroy(id2
);
493 // Give surface_id_ a frame that references id2.
495 scoped_ptr
<RenderPass
> render_pass(RenderPass::Create());
496 render_pass
->referenced_surfaces
.push_back(id2
);
497 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
498 frame_data
->render_pass_list
.push_back(render_pass
.Pass());
499 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
500 frame
->delegated_frame_data
= frame_data
.Pass();
501 factory_
.SubmitCompositorFrame(surface_id_
, frame
.Pass(),
502 SurfaceFactory::DrawCallback());
504 factory_
.Destroy(surface_id_
);
505 EXPECT_TRUE(manager_
.GetSurfaceForId(id2
));
506 // surface_id_ should be retained by reference from id2.
507 EXPECT_TRUE(manager_
.GetSurfaceForId(surface_id_
));
509 // Satisfy last destruction dependency for id2.
510 std::vector
<uint32_t> to_satisfy
;
511 to_satisfy
.push_back(4);
512 manager_
.DidSatisfySequences(0, &to_satisfy
);
514 // id2 and surface_id_ are in a reference cycle that has no surface
515 // sequences holding on to it, so they should be destroyed.
516 EXPECT_TRUE(!manager_
.GetSurfaceForId(id2
));
517 EXPECT_TRUE(!manager_
.GetSurfaceForId(surface_id_
));
519 surface_id_
= SurfaceId();