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.
5 #include "cc/output/compositor_frame.h"
6 #include "cc/output/delegated_frame_data.h"
7 #include "cc/surfaces/surface.h"
8 #include "cc/surfaces/surface_factory.h"
9 #include "cc/surfaces/surface_factory_client.h"
10 #include "cc/surfaces/surface_manager.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/gfx/size.h"
17 class TestSurfaceFactoryClient
: public SurfaceFactoryClient
{
19 TestSurfaceFactoryClient() {}
20 virtual ~TestSurfaceFactoryClient() {}
22 virtual void ReturnResources(
23 const ReturnedResourceArray
& resources
) OVERRIDE
{
24 returned_resources_
.insert(
25 returned_resources_
.end(), resources
.begin(), resources
.end());
28 const ReturnedResourceArray
& returned_resources() const {
29 return returned_resources_
;
32 void clear_returned_resources() { returned_resources_
.clear(); }
35 ReturnedResourceArray returned_resources_
;
37 DISALLOW_COPY_AND_ASSIGN(TestSurfaceFactoryClient
);
40 class SurfaceFactoryTest
: public testing::Test
{
42 SurfaceFactoryTest() : factory_(&manager_
, &client_
), surface_id_(3) {
43 factory_
.Create(surface_id_
, gfx::Size(5, 5));
46 virtual ~SurfaceFactoryTest() { factory_
.Destroy(surface_id_
); }
48 void SubmitFrameWithResources(ResourceProvider::ResourceId
* resource_ids
,
49 size_t num_resource_ids
) {
50 scoped_ptr
<DelegatedFrameData
> frame_data(new DelegatedFrameData
);
51 for (size_t i
= 0u; i
< num_resource_ids
; ++i
) {
52 TransferableResource resource
;
53 resource
.id
= resource_ids
[i
];
54 resource
.mailbox_holder
.texture_target
= GL_TEXTURE_2D
;
55 frame_data
->resource_list
.push_back(resource
);
57 scoped_ptr
<CompositorFrame
> frame(new CompositorFrame
);
58 frame
->delegated_frame_data
= frame_data
.Pass();
59 factory_
.SubmitFrame(surface_id_
, frame
.Pass(), base::Closure());
62 void UnrefResources(ResourceProvider::ResourceId
* ids_to_unref
,
64 size_t num_ids_to_unref
) {
65 ReturnedResourceArray unref_array
;
66 for (size_t i
= 0; i
< num_ids_to_unref
; ++i
) {
67 ReturnedResource resource
;
68 resource
.id
= ids_to_unref
[i
];
69 resource
.count
= counts_to_unref
[i
];
70 unref_array
.push_back(resource
);
72 factory_
.UnrefResources(unref_array
);
75 void CheckReturnedResourcesMatchExpected(
76 ResourceProvider::ResourceId
* expected_returned_ids
,
77 int* expected_returned_counts
,
78 size_t expected_resources
) {
79 const ReturnedResourceArray
& actual_resources
=
80 client_
.returned_resources();
81 ASSERT_EQ(expected_resources
, actual_resources
.size());
82 for (size_t i
= 0; i
< expected_resources
; ++i
) {
83 ReturnedResource resource
= actual_resources
[i
];
84 EXPECT_EQ(expected_returned_ids
[i
], resource
.id
);
85 EXPECT_EQ(expected_returned_counts
[i
], resource
.count
);
87 client_
.clear_returned_resources();
90 void RefCurrentFrameResources() {
91 Surface
* surface
= manager_
.GetSurfaceForId(surface_id_
);
92 factory_
.RefResources(
93 surface
->GetEligibleFrame()->delegated_frame_data
->resource_list
);
97 SurfaceManager manager_
;
98 TestSurfaceFactoryClient client_
;
99 SurfaceFactory factory_
;
100 SurfaceId surface_id_
;
103 // Tests submitting a frame with resources followed by one with no resources
104 // with no resource provider action in between.
105 TEST_F(SurfaceFactoryTest
, ResourceLifetimeSimple
) {
106 ResourceProvider::ResourceId first_frame_ids
[] = {1, 2, 3};
107 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
109 // All of the resources submitted in the first frame are still in use at this
110 // time by virtue of being in the pending frame, so none can be returned to
112 EXPECT_EQ(0u, client_
.returned_resources().size());
113 client_
.clear_returned_resources();
115 // The second frame references no resources and thus should make all resources
116 // available to be returned.
117 SubmitFrameWithResources(NULL
, 0);
119 ResourceProvider::ResourceId expected_returned_ids
[] = {1, 2, 3};
120 int expected_returned_counts
[] = {1, 1, 1};
121 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
122 expected_returned_counts
,
123 arraysize(expected_returned_counts
));
126 // Tests submitting a frame with resources followed by one with no resources
127 // with the resource provider holding everything alive.
128 TEST_F(SurfaceFactoryTest
, ResourceLifetimeSimpleWithProviderHoldingAlive
) {
129 ResourceProvider::ResourceId first_frame_ids
[] = {1, 2, 3};
130 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
132 // All of the resources submitted in the first frame are still in use at this
133 // time by virtue of being in the pending frame, so none can be returned to
135 EXPECT_EQ(0u, client_
.returned_resources().size());
136 client_
.clear_returned_resources();
138 // Hold on to everything.
139 RefCurrentFrameResources();
141 // The second frame references no resources and thus should make all resources
142 // available to be returned as soon as the resource provider releases them.
143 SubmitFrameWithResources(NULL
, 0);
145 EXPECT_EQ(0u, client_
.returned_resources().size());
146 client_
.clear_returned_resources();
148 int release_counts
[] = {1, 1, 1};
149 UnrefResources(first_frame_ids
, release_counts
, arraysize(first_frame_ids
));
151 ResourceProvider::ResourceId expected_returned_ids
[] = {1, 2, 3};
152 int expected_returned_counts
[] = {1, 1, 1};
153 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
154 expected_returned_counts
,
155 arraysize(expected_returned_counts
));
158 // Tests referencing a resource, unref'ing it to zero, then using it again
159 // before returning it to the client.
160 TEST_F(SurfaceFactoryTest
, ResourceReusedBeforeReturn
) {
161 ResourceProvider::ResourceId first_frame_ids
[] = {7};
162 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
164 // This removes all references to resource id 7.
165 SubmitFrameWithResources(NULL
, 0);
167 // This references id 7 again.
168 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
170 // This removes it again.
171 SubmitFrameWithResources(NULL
, 0);
173 // Now it should be returned.
174 // We don't care how many entries are in the returned array for 7, so long as
175 // the total returned count matches the submitted count.
176 const ReturnedResourceArray
& returned
= client_
.returned_resources();
177 size_t return_count
= 0;
178 for (size_t i
= 0; i
< returned
.size(); ++i
) {
179 EXPECT_EQ(7u, returned
[i
].id
);
180 return_count
+= returned
[i
].count
;
182 EXPECT_EQ(2u, return_count
);
185 // Tests having resources referenced multiple times, as if referenced by
186 // multiple providers.
187 TEST_F(SurfaceFactoryTest
, ResourceRefMultipleTimes
) {
188 ResourceProvider::ResourceId first_frame_ids
[] = {3, 4};
189 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
191 // Ref resources from the first frame twice.
192 RefCurrentFrameResources();
193 RefCurrentFrameResources();
195 ResourceProvider::ResourceId second_frame_ids
[] = {4, 5};
196 SubmitFrameWithResources(second_frame_ids
, arraysize(second_frame_ids
));
198 // Ref resources from the second frame 3 times.
199 RefCurrentFrameResources();
200 RefCurrentFrameResources();
201 RefCurrentFrameResources();
203 // Submit a frame with no resources to remove all current frame refs from
204 // submitted resources.
205 SubmitFrameWithResources(NULL
, 0);
207 EXPECT_EQ(0u, client_
.returned_resources().size());
208 client_
.clear_returned_resources();
210 // Expected current refs:
215 SCOPED_TRACE("unref all 3");
216 ResourceProvider::ResourceId ids_to_unref
[] = {3, 4, 5};
217 int counts
[] = {1, 1, 1};
218 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
220 EXPECT_EQ(0u, client_
.returned_resources().size());
221 client_
.clear_returned_resources();
223 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
225 ResourceProvider::ResourceId expected_returned_ids
[] = {3};
226 int expected_returned_counts
[] = {1};
227 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
228 expected_returned_counts
,
229 arraysize(expected_returned_counts
));
232 // Expected refs remaining:
236 SCOPED_TRACE("unref 4 and 5");
237 ResourceProvider::ResourceId ids_to_unref
[] = {4, 5};
238 int counts
[] = {1, 1};
239 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
241 ResourceProvider::ResourceId expected_returned_ids
[] = {5};
242 int expected_returned_counts
[] = {1};
243 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
244 expected_returned_counts
,
245 arraysize(expected_returned_counts
));
248 // Now, just 2 refs remaining on resource 4. Unref both at once and make sure
249 // the returned count is correct.
251 SCOPED_TRACE("unref only 4");
252 ResourceProvider::ResourceId ids_to_unref
[] = {4};
254 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
256 ResourceProvider::ResourceId expected_returned_ids
[] = {4};
257 int expected_returned_counts
[] = {2};
258 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
259 expected_returned_counts
,
260 arraysize(expected_returned_counts
));
264 TEST_F(SurfaceFactoryTest
, ResourceLifetime
) {
265 ResourceProvider::ResourceId first_frame_ids
[] = {1, 2, 3};
266 SubmitFrameWithResources(first_frame_ids
, arraysize(first_frame_ids
));
268 // All of the resources submitted in the first frame are still in use at this
269 // time by virtue of being in the pending frame, so none can be returned to
271 EXPECT_EQ(0u, client_
.returned_resources().size());
272 client_
.clear_returned_resources();
274 // The second frame references some of the same resources, but some different
275 // ones. We expect to receive back resource 1 with a count of 1 since it was
276 // only referenced by the first frame.
277 ResourceProvider::ResourceId second_frame_ids
[] = {2, 3, 4};
278 SubmitFrameWithResources(second_frame_ids
, arraysize(second_frame_ids
));
281 SCOPED_TRACE("second frame");
282 ResourceProvider::ResourceId expected_returned_ids
[] = {1};
283 int expected_returned_counts
[] = {1};
284 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
285 expected_returned_counts
,
286 arraysize(expected_returned_counts
));
289 // The third frame references a disjoint set of resources, so we expect to
290 // receive back all resources from the first and second frames. Resource IDs 2
291 // and 3 will have counts of 2, since they were used in both frames, and
292 // resource ID 4 will have a count of 1.
293 ResourceProvider::ResourceId third_frame_ids
[] = {10, 11, 12, 13};
294 SubmitFrameWithResources(third_frame_ids
, arraysize(third_frame_ids
));
297 SCOPED_TRACE("third frame");
298 ResourceProvider::ResourceId expected_returned_ids
[] = {2, 3, 4};
299 int expected_returned_counts
[] = {2, 2, 1};
300 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
301 expected_returned_counts
,
302 arraysize(expected_returned_counts
));
305 // Simulate a ResourceProvider taking a ref on all of the resources.
306 RefCurrentFrameResources();
308 ResourceProvider::ResourceId fourth_frame_ids
[] = {12, 13};
309 SubmitFrameWithResources(fourth_frame_ids
, arraysize(fourth_frame_ids
));
311 EXPECT_EQ(0u, client_
.returned_resources().size());
313 RefCurrentFrameResources();
315 // All resources are still being used by the external reference, so none can
316 // be returned to the client.
317 EXPECT_EQ(0u, client_
.returned_resources().size());
319 // Release resources associated with the first RefCurrentFrameResources() call
322 ResourceProvider::ResourceId ids_to_unref
[] = {10, 11, 12, 13};
323 int counts
[] = {1, 1, 1, 1};
324 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
328 SCOPED_TRACE("fourth frame, first unref");
329 ResourceProvider::ResourceId expected_returned_ids
[] = {10, 11};
330 int expected_returned_counts
[] = {1, 1};
331 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
332 expected_returned_counts
,
333 arraysize(expected_returned_counts
));
337 ResourceProvider::ResourceId ids_to_unref
[] = {12, 13};
338 int counts
[] = {1, 1};
339 UnrefResources(ids_to_unref
, counts
, arraysize(ids_to_unref
));
342 // Resources 12 and 13 are still in use by the current frame, so they
343 // shouldn't be available to be returned.
344 EXPECT_EQ(0u, client_
.returned_resources().size());
346 // If we submit an empty frame, however, they should become available.
347 SubmitFrameWithResources(NULL
, 0u);
350 SCOPED_TRACE("fourth frame, second unref");
351 ResourceProvider::ResourceId expected_returned_ids
[] = {12, 13};
352 int expected_returned_counts
[] = {2, 2};
353 CheckReturnedResourcesMatchExpected(expected_returned_ids
,
354 expected_returned_counts
,
355 arraysize(expected_returned_counts
));