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 SubmitFrameWithResources(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_
.SubmitFrame(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 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
113 // All of the resources submitted in the first frame are still in use at this
114 // time by virtue of being in the pending frame, so none can be returned to
116 EXPECT_EQ(0u, client_
.returned_resources().size());
117 client_
.clear_returned_resources();
119 // The second frame references no resources and thus should make all resources
120 // available to be returned.
121 SubmitFrameWithResources(NULL
, 0);
123 ResourceId expected_returned_ids
[] = {1, 2, 3};
124 int expected_returned_counts
[] = {1, 1, 1};
125 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
126 expected_returned_counts
,
127 arraysize(expected_returned_counts
));
130 // Tests submitting a frame with resources followed by one with no resources
131 // with the resource provider holding everything alive.
132 TEST_F(SurfaceFactoryTest
, ResourceLifetimeSimpleWithProviderHoldingAlive
) {
133 ResourceId first_frame_ids
[] = {1, 2, 3};
134 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
136 // All of the resources submitted in the first frame are still in use at this
137 // time by virtue of being in the pending frame, so none can be returned to
139 EXPECT_EQ(0u, client_
.returned_resources().size());
140 client_
.clear_returned_resources();
142 // Hold on to everything.
143 RefCurrentFrameResources();
145 // The second frame references no resources and thus should make all resources
146 // available to be returned as soon as the resource provider releases them.
147 SubmitFrameWithResources(NULL
, 0);
149 EXPECT_EQ(0u, client_
.returned_resources().size());
150 client_
.clear_returned_resources();
152 int release_counts
[] = {1, 1, 1};
153 UnrefResources(first_frame_ids
, release_counts
, arraysize(first_frame_ids
));
155 ResourceId expected_returned_ids
[] = {1, 2, 3};
156 int expected_returned_counts
[] = {1, 1, 1};
157 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
158 expected_returned_counts
,
159 arraysize(expected_returned_counts
));
162 // Tests referencing a resource, unref'ing it to zero, then using it again
163 // before returning it to the client.
164 TEST_F(SurfaceFactoryTest
, ResourceReusedBeforeReturn
) {
165 ResourceId first_frame_ids
[] = {7};
166 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
168 // This removes all references to resource id 7.
169 SubmitFrameWithResources(NULL
, 0);
171 // This references id 7 again.
172 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
174 // This removes it again.
175 SubmitFrameWithResources(NULL
, 0);
177 // Now it should be returned.
178 // We don't care how many entries are in the returned array for 7, so long as
179 // the total returned count matches the submitted count.
180 const ReturnedResourceArray
& returned
= client_
.returned_resources();
181 size_t return_count
= 0;
182 for (size_t i
= 0; i
< returned
.size(); ++i
) {
183 EXPECT_EQ(7u, returned
[i
].id
);
184 return_count
+= returned
[i
].count
;
186 EXPECT_EQ(2u, return_count
);
189 // Tests having resources referenced multiple times, as if referenced by
190 // multiple providers.
191 TEST_F(SurfaceFactoryTest
, ResourceRefMultipleTimes
) {
192 ResourceId first_frame_ids
[] = {3, 4};
193 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
195 // Ref resources from the first frame twice.
196 RefCurrentFrameResources();
197 RefCurrentFrameResources();
199 ResourceId second_frame_ids
[] = {4, 5};
200 SubmitFrameWithResources(second_frame_ids
, arraysize(second_frame_ids
));
202 // Ref resources from the second frame 3 times.
203 RefCurrentFrameResources();
204 RefCurrentFrameResources();
205 RefCurrentFrameResources();
207 // Submit a frame with no resources to remove all current frame refs from
208 // submitted resources.
209 SubmitFrameWithResources(NULL
, 0);
211 EXPECT_EQ(0u, client_
.returned_resources().size());
212 client_
.clear_returned_resources();
214 // Expected current refs:
219 SCOPED_TRACE("unref all 3");
220 ResourceId ids_to_unref
[] = {3, 4, 5};
221 int counts
[] = {1, 1, 1};
222 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
224 EXPECT_EQ(0u, client_
.returned_resources().size());
225 client_
.clear_returned_resources();
227 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
229 ResourceId expected_returned_ids
[] = {3};
230 int expected_returned_counts
[] = {1};
231 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
232 expected_returned_counts
,
233 arraysize(expected_returned_counts
));
236 // Expected refs remaining:
240 SCOPED_TRACE("unref 4 and 5");
241 ResourceId ids_to_unref
[] = {4, 5};
242 int counts
[] = {1, 1};
243 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
245 ResourceId expected_returned_ids
[] = {5};
246 int expected_returned_counts
[] = {1};
247 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
248 expected_returned_counts
,
249 arraysize(expected_returned_counts
));
252 // Now, just 2 refs remaining on resource 4. Unref both at once and make sure
253 // the returned count is correct.
255 SCOPED_TRACE("unref only 4");
256 ResourceId ids_to_unref
[] = {4};
258 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
260 ResourceId expected_returned_ids
[] = {4};
261 int expected_returned_counts
[] = {2};
262 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
263 expected_returned_counts
,
264 arraysize(expected_returned_counts
));
268 TEST_F(SurfaceFactoryTest
, ResourceLifetime
) {
269 ResourceId first_frame_ids
[] = {1, 2, 3};
270 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
272 // All of the resources submitted in the first frame are still in use at this
273 // time by virtue of being in the pending frame, so none can be returned to
275 EXPECT_EQ(0u, client_
.returned_resources().size());
276 client_
.clear_returned_resources();
278 // The second frame references some of the same resources, but some different
279 // ones. We expect to receive back resource 1 with a count of 1 since it was
280 // only referenced by the first frame.
281 ResourceId second_frame_ids
[] = {2, 3, 4};
282 SubmitFrameWithResources(second_frame_ids
, arraysize(second_frame_ids
));
285 SCOPED_TRACE("second frame");
286 ResourceId expected_returned_ids
[] = {1};
287 int expected_returned_counts
[] = {1};
288 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
289 expected_returned_counts
,
290 arraysize(expected_returned_counts
));
293 // The third frame references a disjoint set of resources, so we expect to
294 // receive back all resources from the first and second frames. Resource IDs 2
295 // and 3 will have counts of 2, since they were used in both frames, and
296 // resource ID 4 will have a count of 1.
297 ResourceId third_frame_ids
[] = {10, 11, 12, 13};
298 SubmitFrameWithResources(third_frame_ids
, arraysize(third_frame_ids
));
301 SCOPED_TRACE("third frame");
302 ResourceId expected_returned_ids
[] = {2, 3, 4};
303 int expected_returned_counts
[] = {2, 2, 1};
304 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
305 expected_returned_counts
,
306 arraysize(expected_returned_counts
));
309 // Simulate a ResourceProvider taking a ref on all of the resources.
310 RefCurrentFrameResources();
312 ResourceId fourth_frame_ids
[] = {12, 13};
313 SubmitFrameWithResources(fourth_frame_ids
, arraysize(fourth_frame_ids
));
315 EXPECT_EQ(0u, client_
.returned_resources().size());
317 RefCurrentFrameResources();
319 // All resources are still being used by the external reference, so none can
320 // be returned to the client.
321 EXPECT_EQ(0u, client_
.returned_resources().size());
323 // Release resources associated with the first RefCurrentFrameResources() call
326 ResourceId ids_to_unref
[] = {10, 11, 12, 13};
327 int counts
[] = {1, 1, 1, 1};
328 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
332 SCOPED_TRACE("fourth frame, first unref");
333 ResourceId expected_returned_ids
[] = {10, 11};
334 int expected_returned_counts
[] = {1, 1};
335 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
336 expected_returned_counts
,
337 arraysize(expected_returned_counts
));
341 ResourceId ids_to_unref
[] = {12, 13};
342 int counts
[] = {1, 1};
343 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
346 // Resources 12 and 13 are still in use by the current frame, so they
347 // shouldn't be available to be returned.
348 EXPECT_EQ(0u, client_
.returned_resources().size());
350 // If we submit an empty frame, however, they should become available.
351 SubmitFrameWithResources(NULL
, 0u);
354 SCOPED_TRACE("fourth frame, second unref");
355 ResourceId expected_returned_ids
[] = {12, 13};
356 int expected_returned_counts
[] = {2, 2};
357 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
358 expected_returned_counts
,
359 arraysize(expected_returned_counts
));
363 TEST_F(SurfaceFactoryTest
, BlankNoIndexIncrement
) {
364 SurfaceId
surface_id(6);
365 factory_
.Create(surface_id
);
366 Surface
* surface
= manager_
.GetSurfaceForId(surface_id
);
367 ASSERT_NE(nullptr, surface
);
368 EXPECT_EQ(2, surface
->frame_index());
369 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
370 frame
->delegated_frame_data
.reset(new DelegatedFrameData
);
372 factory_
.SubmitFrame(surface_id
, frame
.Pass(),
373 SurfaceFactory::DrawCallback());
374 EXPECT_EQ(2, surface
->frame_index());
375 factory_
.Destroy(surface_id
);
378 void DrawCallback(uint32
* execute_count
,
379 SurfaceDrawStatus
* result
,
380 SurfaceDrawStatus drawn
) {
385 // Tests doing a DestroyAll before shutting down the factory;
386 TEST_F(SurfaceFactoryTest
, DestroyAll
) {
390 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
391 TransferableResource resource
;
393 resource
.mailbox_holder
.texture_target
= GL_TEXTURE_2D
;
394 frame_data
->resource_list
.push_back(resource
);
395 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
396 frame
->delegated_frame_data
= frame_data
.Pass();
397 uint32 execute_count
= 0;
398 SurfaceDrawStatus drawn
= SurfaceDrawStatus::DRAW_SKIPPED
;
400 factory_
.SubmitFrame(id
, frame
.Pass(),
401 base::Bind(&DrawCallback
, &execute_count
, &drawn
));
403 surface_id_
= SurfaceId();
404 factory_
.DestroyAll();
405 EXPECT_EQ(1u, execute_count
);
406 EXPECT_EQ(SurfaceDrawStatus::DRAW_SKIPPED
, drawn
);
409 TEST_F(SurfaceFactoryTest
, DestroySequence
) {
411 factory_
.Create(id2
);
413 manager_
.RegisterSurfaceIdNamespace(0);
415 // Check that waiting before the sequence is satisfied works.
416 manager_
.GetSurfaceForId(id2
)
417 ->AddDestructionDependency(SurfaceSequence(0, 4));
418 factory_
.Destroy(id2
);
420 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
421 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
422 frame
->metadata
.satisfies_sequences
.push_back(6);
423 frame
->metadata
.satisfies_sequences
.push_back(4);
424 frame
->delegated_frame_data
= frame_data
.Pass();
425 DCHECK(manager_
.GetSurfaceForId(id2
));
426 factory_
.SubmitFrame(surface_id_
, frame
.Pass(),
427 SurfaceFactory::DrawCallback());
428 DCHECK(!manager_
.GetSurfaceForId(id2
));
430 // Check that waiting after the sequence is satisfied works.
431 factory_
.Create(id2
);
432 DCHECK(manager_
.GetSurfaceForId(id2
));
433 manager_
.GetSurfaceForId(id2
)
434 ->AddDestructionDependency(SurfaceSequence(0, 6));
435 factory_
.Destroy(id2
);
436 DCHECK(!manager_
.GetSurfaceForId(id2
));
439 // Tests that Surface ID namespace invalidation correctly allows
440 // Sequences to be ignored.
441 TEST_F(SurfaceFactoryTest
, InvalidIdNamespace
) {
442 uint32_t id_namespace
= 9u;
446 manager_
.RegisterSurfaceIdNamespace(id_namespace
);
447 manager_
.GetSurfaceForId(id
)
448 ->AddDestructionDependency(SurfaceSequence(id_namespace
, 4));
449 factory_
.Destroy(id
);
451 // Verify the dependency has prevented the surface from getting destroyed.
452 EXPECT_TRUE(manager_
.GetSurfaceForId(id
));
454 manager_
.InvalidateSurfaceIdNamespace(id_namespace
);
456 // Verify that the invalidated namespace caused the unsatisfied sequence
458 EXPECT_FALSE(manager_
.GetSurfaceForId(id
));