OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "base/bind.h" | |
6 #include "cc/output/compositor_frame.h" | |
7 #include "cc/output/delegated_frame_data.h" | |
8 #include "cc/surfaces/surface.h" | |
9 #include "cc/surfaces/surface_factory.h" | |
10 #include "cc/surfaces/surface_factory_client.h" | |
11 #include "cc/surfaces/surface_manager.h" | |
12 #include "testing/gtest/include/gtest/gtest.h" | |
13 #include "ui/gfx/geometry/size.h" | |
14 | |
15 namespace cc { | |
16 namespace { | |
17 | |
18 class TestSurfaceFactoryClient : public SurfaceFactoryClient { | |
19 public: | |
20 TestSurfaceFactoryClient() {} | |
21 ~TestSurfaceFactoryClient() override {} | |
22 | |
23 void ReturnResources(const ReturnedResourceArray& resources) override { | |
24 returned_resources_.insert( | |
25 returned_resources_.end(), resources.begin(), resources.end()); | |
26 } | |
27 | |
28 const ReturnedResourceArray& returned_resources() const { | |
29 return returned_resources_; | |
30 } | |
31 | |
32 void clear_returned_resources() { returned_resources_.clear(); } | |
33 | |
34 private: | |
35 ReturnedResourceArray returned_resources_; | |
36 | |
37 DISALLOW_COPY_AND_ASSIGN(TestSurfaceFactoryClient); | |
38 }; | |
39 | |
40 class SurfaceFactoryTest : public testing::Test { | |
41 public: | |
42 SurfaceFactoryTest() : factory_(&manager_, &client_), surface_id_(3) { | |
43 factory_.Create(surface_id_); | |
44 } | |
45 | |
46 ~SurfaceFactoryTest() override { | |
47 if (!surface_id_.is_null()) | |
48 factory_.Destroy(surface_id_); | |
49 } | |
50 | |
51 void SubmitFrameWithResources(ResourceProvider::ResourceId* resource_ids, | |
52 size_t num_resource_ids) { | |
53 scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData); | |
54 for (size_t i = 0u; i < num_resource_ids; ++i) { | |
55 TransferableResource resource; | |
56 resource.id = resource_ids[i]; | |
57 resource.mailbox_holder.texture_target = GL_TEXTURE_2D; | |
58 frame_data->resource_list.push_back(resource); | |
59 } | |
60 scoped_ptr<CompositorFrame> frame(new CompositorFrame); | |
61 frame->delegated_frame_data = frame_data.Pass(); | |
62 factory_.SubmitFrame(surface_id_, frame.Pass(), | |
63 SurfaceFactory::DrawCallback()); | |
64 } | |
65 | |
66 void UnrefResources(ResourceProvider::ResourceId* ids_to_unref, | |
67 int* counts_to_unref, | |
68 size_t num_ids_to_unref) { | |
69 ReturnedResourceArray unref_array; | |
70 for (size_t i = 0; i < num_ids_to_unref; ++i) { | |
71 ReturnedResource resource; | |
72 resource.id = ids_to_unref[i]; | |
73 resource.count = counts_to_unref[i]; | |
74 unref_array.push_back(resource); | |
75 } | |
76 factory_.UnrefResources(unref_array); | |
77 } | |
78 | |
79 void CheckReturnedResourcesMatchExpected( | |
80 ResourceProvider::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); | |
90 } | |
91 client_.clear_returned_resources(); | |
92 } | |
93 | |
94 void RefCurrentFrameResources() { | |
95 Surface* surface = manager_.GetSurfaceForId(surface_id_); | |
96 factory_.RefResources( | |
97 surface->GetEligibleFrame()->delegated_frame_data->resource_list); | |
98 } | |
99 | |
100 protected: | |
101 SurfaceManager manager_; | |
102 TestSurfaceFactoryClient client_; | |
103 SurfaceFactory factory_; | |
104 SurfaceId surface_id_; | |
105 }; | |
106 | |
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 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; | |
111 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); | |
112 | |
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 | |
115 // the client yet. | |
116 EXPECT_EQ(0u, client_.returned_resources().size()); | |
117 client_.clear_returned_resources(); | |
118 | |
119 // The second frame references no resources and thus should make all resources | |
120 // available to be returned. | |
121 SubmitFrameWithResources(NULL, 0); | |
122 | |
123 ResourceProvider::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)); | |
128 } | |
129 | |
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 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; | |
134 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); | |
135 | |
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 | |
138 // the client yet. | |
139 EXPECT_EQ(0u, client_.returned_resources().size()); | |
140 client_.clear_returned_resources(); | |
141 | |
142 // Hold on to everything. | |
143 RefCurrentFrameResources(); | |
144 | |
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); | |
148 | |
149 EXPECT_EQ(0u, client_.returned_resources().size()); | |
150 client_.clear_returned_resources(); | |
151 | |
152 int release_counts[] = {1, 1, 1}; | |
153 UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids)); | |
154 | |
155 ResourceProvider::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)); | |
160 } | |
161 | |
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 ResourceProvider::ResourceId first_frame_ids[] = {7}; | |
166 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); | |
167 | |
168 // This removes all references to resource id 7. | |
169 SubmitFrameWithResources(NULL, 0); | |
170 | |
171 // This references id 7 again. | |
172 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); | |
173 | |
174 // This removes it again. | |
175 SubmitFrameWithResources(NULL, 0); | |
176 | |
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; | |
185 } | |
186 EXPECT_EQ(2u, return_count); | |
187 } | |
188 | |
189 // Tests having resources referenced multiple times, as if referenced by | |
190 // multiple providers. | |
191 TEST_F(SurfaceFactoryTest, ResourceRefMultipleTimes) { | |
192 ResourceProvider::ResourceId first_frame_ids[] = {3, 4}; | |
193 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); | |
194 | |
195 // Ref resources from the first frame twice. | |
196 RefCurrentFrameResources(); | |
197 RefCurrentFrameResources(); | |
198 | |
199 ResourceProvider::ResourceId second_frame_ids[] = {4, 5}; | |
200 SubmitFrameWithResources(second_frame_ids, arraysize(second_frame_ids)); | |
201 | |
202 // Ref resources from the second frame 3 times. | |
203 RefCurrentFrameResources(); | |
204 RefCurrentFrameResources(); | |
205 RefCurrentFrameResources(); | |
206 | |
207 // Submit a frame with no resources to remove all current frame refs from | |
208 // submitted resources. | |
209 SubmitFrameWithResources(NULL, 0); | |
210 | |
211 EXPECT_EQ(0u, client_.returned_resources().size()); | |
212 client_.clear_returned_resources(); | |
213 | |
214 // Expected current refs: | |
215 // 3 -> 2 | |
216 // 4 -> 2 + 3 = 5 | |
217 // 5 -> 3 | |
218 { | |
219 SCOPED_TRACE("unref all 3"); | |
220 ResourceProvider::ResourceId ids_to_unref[] = {3, 4, 5}; | |
221 int counts[] = {1, 1, 1}; | |
222 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); | |
223 | |
224 EXPECT_EQ(0u, client_.returned_resources().size()); | |
225 client_.clear_returned_resources(); | |
226 | |
227 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); | |
228 | |
229 ResourceProvider::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)); | |
234 } | |
235 | |
236 // Expected refs remaining: | |
237 // 4 -> 3 | |
238 // 5 -> 1 | |
239 { | |
240 SCOPED_TRACE("unref 4 and 5"); | |
241 ResourceProvider::ResourceId ids_to_unref[] = {4, 5}; | |
242 int counts[] = {1, 1}; | |
243 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); | |
244 | |
245 ResourceProvider::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)); | |
250 } | |
251 | |
252 // Now, just 2 refs remaining on resource 4. Unref both at once and make sure | |
253 // the returned count is correct. | |
254 { | |
255 SCOPED_TRACE("unref only 4"); | |
256 ResourceProvider::ResourceId ids_to_unref[] = {4}; | |
257 int counts[] = {2}; | |
258 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); | |
259 | |
260 ResourceProvider::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)); | |
265 } | |
266 } | |
267 | |
268 TEST_F(SurfaceFactoryTest, ResourceLifetime) { | |
269 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; | |
270 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); | |
271 | |
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 | |
274 // the client yet. | |
275 EXPECT_EQ(0u, client_.returned_resources().size()); | |
276 client_.clear_returned_resources(); | |
277 | |
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 ResourceProvider::ResourceId second_frame_ids[] = {2, 3, 4}; | |
282 SubmitFrameWithResources(second_frame_ids, arraysize(second_frame_ids)); | |
283 | |
284 { | |
285 SCOPED_TRACE("second frame"); | |
286 ResourceProvider::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)); | |
291 } | |
292 | |
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 ResourceProvider::ResourceId third_frame_ids[] = {10, 11, 12, 13}; | |
298 SubmitFrameWithResources(third_frame_ids, arraysize(third_frame_ids)); | |
299 | |
300 { | |
301 SCOPED_TRACE("third frame"); | |
302 ResourceProvider::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)); | |
307 } | |
308 | |
309 // Simulate a ResourceProvider taking a ref on all of the resources. | |
310 RefCurrentFrameResources(); | |
311 | |
312 ResourceProvider::ResourceId fourth_frame_ids[] = {12, 13}; | |
313 SubmitFrameWithResources(fourth_frame_ids, arraysize(fourth_frame_ids)); | |
314 | |
315 EXPECT_EQ(0u, client_.returned_resources().size()); | |
316 | |
317 RefCurrentFrameResources(); | |
318 | |
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()); | |
322 | |
323 // Release resources associated with the first RefCurrentFrameResources() call | |
324 // first. | |
325 { | |
326 ResourceProvider::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)); | |
329 } | |
330 | |
331 { | |
332 SCOPED_TRACE("fourth frame, first unref"); | |
333 ResourceProvider::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)); | |
338 } | |
339 | |
340 { | |
341 ResourceProvider::ResourceId ids_to_unref[] = {12, 13}; | |
342 int counts[] = {1, 1}; | |
343 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); | |
344 } | |
345 | |
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()); | |
349 | |
350 // If we submit an empty frame, however, they should become available. | |
351 SubmitFrameWithResources(NULL, 0u); | |
352 | |
353 { | |
354 SCOPED_TRACE("fourth frame, second unref"); | |
355 ResourceProvider::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)); | |
360 } | |
361 } | |
362 | |
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); | |
371 | |
372 factory_.SubmitFrame(surface_id, frame.Pass(), | |
373 SurfaceFactory::DrawCallback()); | |
374 EXPECT_EQ(2, surface->frame_index()); | |
375 factory_.Destroy(surface_id); | |
376 } | |
377 | |
378 void DrawCallback(uint32* execute_count, | |
379 SurfaceDrawStatus* result, | |
380 SurfaceDrawStatus drawn) { | |
381 *execute_count += 1; | |
382 *result = drawn; | |
383 } | |
384 | |
385 // Tests doing a DestroyAll before shutting down the factory; | |
386 TEST_F(SurfaceFactoryTest, DestroyAll) { | |
387 SurfaceId id(7); | |
388 factory_.Create(id); | |
389 | |
390 scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData); | |
391 TransferableResource resource; | |
392 resource.id = 1; | |
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; | |
399 | |
400 factory_.SubmitFrame(id, frame.Pass(), | |
401 base::Bind(&DrawCallback, &execute_count, &drawn)); | |
402 | |
403 surface_id_ = SurfaceId(); | |
404 factory_.DestroyAll(); | |
405 EXPECT_EQ(1u, execute_count); | |
406 EXPECT_EQ(SurfaceDrawStatus::DRAW_SKIPPED, drawn); | |
407 } | |
408 | |
409 TEST_F(SurfaceFactoryTest, DestroySequence) { | |
410 SurfaceId id2(5); | |
411 factory_.Create(id2); | |
412 | |
413 // Check that waiting before the sequence is satisfied works. | |
414 manager_.GetSurfaceForId(id2) | |
415 ->AddDestructionDependency(SurfaceSequence(0, 4)); | |
416 factory_.Destroy(id2); | |
417 | |
418 scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData); | |
419 scoped_ptr<CompositorFrame> frame(new CompositorFrame); | |
420 frame->metadata.satisfies_sequences.push_back(6); | |
421 frame->metadata.satisfies_sequences.push_back(4); | |
422 frame->delegated_frame_data = frame_data.Pass(); | |
423 DCHECK(manager_.GetSurfaceForId(id2)); | |
424 factory_.SubmitFrame(surface_id_, frame.Pass(), | |
425 SurfaceFactory::DrawCallback()); | |
426 DCHECK(!manager_.GetSurfaceForId(id2)); | |
427 | |
428 // Check that waiting after the sequence is satisfied works. | |
429 factory_.Create(id2); | |
430 DCHECK(manager_.GetSurfaceForId(id2)); | |
431 manager_.GetSurfaceForId(id2) | |
432 ->AddDestructionDependency(SurfaceSequence(0, 6)); | |
433 factory_.Destroy(id2); | |
434 DCHECK(!manager_.GetSurfaceForId(id2)); | |
435 } | |
436 | |
437 } // namespace | |
438 } // namespace cc | |
OLD | NEW |