Add some instrumentation to investigate a possible UAF.
[chromium-blink-merge.git] / cc / surfaces / surface_factory_unittest.cc
blobb9c6bf5c20738f6c255d6dd16a34362375e681d6
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 "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"
15 namespace cc {
16 namespace {
18 class TestSurfaceFactoryClient : public SurfaceFactoryClient {
19 public:
20 TestSurfaceFactoryClient() {}
21 ~TestSurfaceFactoryClient() override {}
23 void ReturnResources(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(); }
34 private:
35 ReturnedResourceArray returned_resources_;
37 DISALLOW_COPY_AND_ASSIGN(TestSurfaceFactoryClient);
40 class SurfaceFactoryTest : public testing::Test {
41 public:
42 SurfaceFactoryTest() : factory_(&manager_, &client_), surface_id_(3) {
43 factory_.Create(surface_id_);
46 ~SurfaceFactoryTest() override {
47 if (!surface_id_.is_null())
48 factory_.Destroy(surface_id_);
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);
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());
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);
76 factory_.UnrefResources(unref_array);
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);
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);
100 protected:
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 ResourceProvider::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
115 // the client yet.
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 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));
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));
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();
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 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));
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));
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 ResourceProvider::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 ResourceProvider::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:
215 // 3 -> 2
216 // 4 -> 2 + 3 = 5
217 // 5 -> 3
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));
224 EXPECT_EQ(0u, client_.returned_resources().size());
225 client_.clear_returned_resources();
227 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
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));
236 // Expected refs remaining:
237 // 4 -> 3
238 // 5 -> 1
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));
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));
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 ResourceProvider::ResourceId ids_to_unref[] = {4};
257 int counts[] = {2};
258 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref));
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));
268 TEST_F(SurfaceFactoryTest, ResourceLifetime) {
269 ResourceProvider::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
274 // the client yet.
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 ResourceProvider::ResourceId second_frame_ids[] = {2, 3, 4};
282 SubmitFrameWithResources(second_frame_ids, arraysize(second_frame_ids));
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));
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));
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));
309 // Simulate a ResourceProvider taking a ref on all of the resources.
310 RefCurrentFrameResources();
312 ResourceProvider::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
324 // first.
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));
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));
341 ResourceProvider::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 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));
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) {
381 *execute_count += 1;
382 *result = drawn;
385 // Tests doing a DestroyAll before shutting down the factory;
386 TEST_F(SurfaceFactoryTest, DestroyAll) {
387 SurfaceId id(7);
388 factory_.Create(id);
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;
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) {
410 SurfaceId id2(5);
411 factory_.Create(id2);
413 // Check that waiting before the sequence is satisfied works.
414 manager_.GetSurfaceForId(id2)
415 ->AddDestructionDependency(SurfaceSequence(0, 4));
416 factory_.Destroy(id2);
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));
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));
437 } // namespace
438 } // namespace cc