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 "cc/output/compositor_frame.h" 6 #include "cc/output/delegated_frame_data.h" 7 #include "cc/surfaces/surface.h" 8 #include "cc/surfaces/surface_client.h" 9 #include "cc/surfaces/surface_manager.h" 10 #include "testing/gtest/include/gtest/gtest.h" 11 #include "ui/gfx/size.h" 12 13 namespace cc { 14 namespace { 15 16 TEST(SurfaceTest, SurfaceLifetime) { 17 SurfaceManager manager; 18 19 SurfaceId surface_id; 20 { 21 Surface surface(&manager, NULL, gfx::Size(5, 5)); 22 surface_id = surface.surface_id(); 23 EXPECT_TRUE(!surface_id.is_null()); 24 EXPECT_EQ(&surface, manager.GetSurfaceForId(surface_id)); 25 } 26 27 EXPECT_EQ(NULL, manager.GetSurfaceForId(surface_id)); 28 } 29 30 class TestSurfaceClient : public SurfaceClient { 31 public: 32 TestSurfaceClient() {} 33 virtual ~TestSurfaceClient() {} 34 35 virtual void ReturnResources( 36 const ReturnedResourceArray& resources) OVERRIDE { 37 returned_resources_ = resources; 38 } 39 40 const ReturnedResourceArray& returned_resources() const { 41 return returned_resources_; 42 } 43 44 void clear_returned_resources() { returned_resources_.clear(); } 45 46 private: 47 ReturnedResourceArray returned_resources_; 48 49 DISALLOW_COPY_AND_ASSIGN(TestSurfaceClient); 50 }; 51 52 void SubmitFrameWithResources(ResourceProvider::ResourceId* resource_ids, 53 size_t num_resource_ids, 54 Surface* surface) { 55 scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData); 56 for (size_t i = 0u; i < num_resource_ids; ++i) { 57 TransferableResource resource; 58 resource.id = resource_ids[i]; 59 resource.mailbox_holder.texture_target = GL_TEXTURE_2D; 60 frame_data->resource_list.push_back(resource); 61 } 62 scoped_ptr<CompositorFrame> frame(new CompositorFrame); 63 frame->delegated_frame_data = frame_data.Pass(); 64 surface->QueueFrame(frame.Pass()); 65 } 66 67 void CheckReturnedResourcesMatchExpected( 68 ResourceProvider::ResourceId* expected_returned_ids, 69 int* expected_returned_counts, 70 size_t expected_resources, 71 const ReturnedResourceArray& actual_resources) { 72 ASSERT_EQ(expected_resources, actual_resources.size()); 73 for (size_t i = 0; i < expected_resources; ++i) { 74 ReturnedResource resource = actual_resources[i]; 75 EXPECT_EQ(expected_returned_ids[i], resource.id); 76 EXPECT_EQ(expected_returned_counts[i], resource.count); 77 } 78 } 79 80 void UnrefResources(ResourceProvider::ResourceId* ids_to_unref, 81 int* counts_to_unref, 82 size_t num_ids_to_unref, 83 Surface* surface) { 84 ReturnedResourceArray unref_array; 85 for (size_t i = 0; i < num_ids_to_unref; ++i) { 86 ReturnedResource resource; 87 resource.id = ids_to_unref[i]; 88 resource.count = counts_to_unref[i]; 89 unref_array.push_back(resource); 90 } 91 surface->UnrefResources(unref_array); 92 } 93 94 // Tests submitting a frame with resources followed by one with no resources 95 // with no resource provider action in between. 96 TEST(SurfaceTest, ResourceLifetimeSimple) { 97 SurfaceManager manager; 98 TestSurfaceClient client; 99 Surface surface(&manager, &client, gfx::Size(5, 5)); 100 101 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; 102 SubmitFrameWithResources( 103 first_frame_ids, arraysize(first_frame_ids), &surface); 104 105 // All of the resources submitted in the first frame are still in use at this 106 // time by virtue of being in the pending frame, so none can be returned to 107 // the client yet. 108 surface.ReturnUnusedResourcesToClient(); 109 EXPECT_EQ(0u, client.returned_resources().size()); 110 client.clear_returned_resources(); 111 112 // The second frame references no resources and thus should make all resources 113 // available to be returned. 114 SubmitFrameWithResources(NULL, 0, &surface); 115 116 surface.ReturnUnusedResourcesToClient(); 117 ResourceProvider::ResourceId expected_returned_ids[] = {1, 2, 3}; 118 int expected_returned_counts[] = {1, 1, 1}; 119 CheckReturnedResourcesMatchExpected(expected_returned_ids, 120 expected_returned_counts, 121 arraysize(expected_returned_counts), 122 client.returned_resources()); 123 } 124 125 // Tests submitting a frame with resources followed by one with no resources 126 // with the resource provider holding everything alive. 127 TEST(SurfaceTest, ResourceLifetimeSimpleWithProviderHoldingAlive) { 128 SurfaceManager manager; 129 TestSurfaceClient client; 130 Surface surface(&manager, &client, gfx::Size(5, 5)); 131 132 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; 133 SubmitFrameWithResources( 134 first_frame_ids, arraysize(first_frame_ids), &surface); 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 surface.ReturnUnusedResourcesToClient(); 140 EXPECT_EQ(0u, client.returned_resources().size()); 141 client.clear_returned_resources(); 142 143 // Hold on to everything. 144 surface.RefCurrentFrameResources(); 145 146 // The second frame references no resources and thus should make all resources 147 // available to be returned as soon as the resource provider releases them. 148 SubmitFrameWithResources(NULL, 0, &surface); 149 150 surface.ReturnUnusedResourcesToClient(); 151 EXPECT_EQ(0u, client.returned_resources().size()); 152 client.clear_returned_resources(); 153 154 int release_counts[] = {1, 1, 1}; 155 UnrefResources( 156 first_frame_ids, release_counts, arraysize(first_frame_ids), &surface); 157 158 surface.ReturnUnusedResourcesToClient(); 159 ResourceProvider::ResourceId expected_returned_ids[] = {1, 2, 3}; 160 int expected_returned_counts[] = {1, 1, 1}; 161 CheckReturnedResourcesMatchExpected(expected_returned_ids, 162 expected_returned_counts, 163 arraysize(expected_returned_counts), 164 client.returned_resources()); 165 } 166 167 // Tests referencing a resource, unref'ing it to zero, then using it again 168 // before returning it to the client. 169 TEST(SurfaceTest, ResourceReusedBeforeReturn) { 170 SurfaceManager manager; 171 TestSurfaceClient client; 172 Surface surface(&manager, &client, gfx::Size(5, 5)); 173 174 ResourceProvider::ResourceId first_frame_ids[] = {7}; 175 SubmitFrameWithResources( 176 first_frame_ids, arraysize(first_frame_ids), &surface); 177 178 // This removes all references to resource id 7. 179 SubmitFrameWithResources(NULL, 0, &surface); 180 181 // This references id 7 again. 182 SubmitFrameWithResources( 183 first_frame_ids, arraysize(first_frame_ids), &surface); 184 185 // This removes it again. 186 SubmitFrameWithResources(NULL, 0, &surface); 187 188 // Now it should be returned. 189 surface.ReturnUnusedResourcesToClient(); 190 191 // We don't care how many entries are in the returned array for 7, so long as 192 // the total returned count matches the submitted count. 193 const ReturnedResourceArray& returned = client.returned_resources(); 194 size_t return_count = 0; 195 for (size_t i = 0; i < returned.size(); ++i) { 196 EXPECT_EQ(7u, returned[i].id); 197 return_count += returned[i].count; 198 } 199 EXPECT_EQ(2u, return_count); 200 } 201 202 // Tests having resources referenced multiple times, as if referenced by 203 // multiple providers. 204 TEST(SurfaceTest, ResourceRefMultipleTimes) { 205 SurfaceManager manager; 206 TestSurfaceClient client; 207 Surface surface(&manager, &client, gfx::Size(5, 5)); 208 209 ResourceProvider::ResourceId first_frame_ids[] = {3, 4}; 210 SubmitFrameWithResources( 211 first_frame_ids, arraysize(first_frame_ids), &surface); 212 213 // Ref resources from the first frame twice. 214 surface.RefCurrentFrameResources(); 215 surface.RefCurrentFrameResources(); 216 217 ResourceProvider::ResourceId second_frame_ids[] = {4, 5}; 218 SubmitFrameWithResources( 219 second_frame_ids, arraysize(second_frame_ids), &surface); 220 221 // Ref resources from the second frame 3 times. 222 surface.RefCurrentFrameResources(); 223 surface.RefCurrentFrameResources(); 224 surface.RefCurrentFrameResources(); 225 226 // Submit a frame with no resources to remove all current frame refs from 227 // submitted resources. 228 SubmitFrameWithResources(NULL, 0, &surface); 229 230 surface.ReturnUnusedResourcesToClient(); 231 EXPECT_EQ(0u, client.returned_resources().size()); 232 client.clear_returned_resources(); 233 234 // Expected current refs: 235 // 3 -> 2 236 // 4 -> 2 + 3 = 5 237 // 5 -> 3 238 { 239 SCOPED_TRACE("unref all 3"); 240 ResourceProvider::ResourceId ids_to_unref[] = {3, 4, 5}; 241 int counts[] = {1, 1, 1}; 242 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface); 243 244 surface.ReturnUnusedResourcesToClient(); 245 EXPECT_EQ(0u, client.returned_resources().size()); 246 client.clear_returned_resources(); 247 248 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface); 249 250 surface.ReturnUnusedResourcesToClient(); 251 ResourceProvider::ResourceId expected_returned_ids[] = {3}; 252 int expected_returned_counts[] = {1}; 253 CheckReturnedResourcesMatchExpected(expected_returned_ids, 254 expected_returned_counts, 255 arraysize(expected_returned_counts), 256 client.returned_resources()); 257 } 258 259 // Expected refs remaining: 260 // 4 -> 3 261 // 5 -> 1 262 { 263 SCOPED_TRACE("unref 4 and 5"); 264 ResourceProvider::ResourceId ids_to_unref[] = {4, 5}; 265 int counts[] = {1, 1}; 266 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface); 267 268 surface.ReturnUnusedResourcesToClient(); 269 ResourceProvider::ResourceId expected_returned_ids[] = {5}; 270 int expected_returned_counts[] = {1}; 271 CheckReturnedResourcesMatchExpected(expected_returned_ids, 272 expected_returned_counts, 273 arraysize(expected_returned_counts), 274 client.returned_resources()); 275 } 276 277 // Now, just 2 refs remaining on resource 4. Unref both at once and make sure 278 // the returned count is correct. 279 { 280 SCOPED_TRACE("unref only 4"); 281 ResourceProvider::ResourceId ids_to_unref[] = {4}; 282 int counts[] = {2}; 283 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface); 284 285 surface.ReturnUnusedResourcesToClient(); 286 ResourceProvider::ResourceId expected_returned_ids[] = {4}; 287 int expected_returned_counts[] = {2}; 288 CheckReturnedResourcesMatchExpected(expected_returned_ids, 289 expected_returned_counts, 290 arraysize(expected_returned_counts), 291 client.returned_resources()); 292 } 293 } 294 295 TEST(SurfaceTest, ResourceLifetime) { 296 SurfaceManager manager; 297 TestSurfaceClient client; 298 Surface surface(&manager, &client, gfx::Size(5, 5)); 299 300 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; 301 SubmitFrameWithResources( 302 first_frame_ids, arraysize(first_frame_ids), &surface); 303 304 // All of the resources submitted in the first frame are still in use at this 305 // time by virtue of being in the pending frame, so none can be returned to 306 // the client yet. 307 surface.ReturnUnusedResourcesToClient(); 308 EXPECT_EQ(0u, client.returned_resources().size()); 309 client.clear_returned_resources(); 310 311 // The second frame references some of the same resources, but some different 312 // ones. We expect to receive back resource 1 with a count of 1 since it was 313 // only referenced by the first frame. 314 ResourceProvider::ResourceId second_frame_ids[] = {2, 3, 4}; 315 SubmitFrameWithResources( 316 second_frame_ids, arraysize(second_frame_ids), &surface); 317 318 surface.ReturnUnusedResourcesToClient(); 319 { 320 SCOPED_TRACE("second frame"); 321 ResourceProvider::ResourceId expected_returned_ids[] = {1}; 322 int expected_returned_counts[] = {1}; 323 CheckReturnedResourcesMatchExpected(expected_returned_ids, 324 expected_returned_counts, 325 arraysize(expected_returned_counts), 326 client.returned_resources()); 327 } 328 329 // The third frame references a disjoint set of resources, so we expect to 330 // receive back all resources from the first and second frames. Resource IDs 2 331 // and 3 will have counts of 2, since they were used in both frames, and 332 // resource ID 4 will have a count of 1. 333 ResourceProvider::ResourceId third_frame_ids[] = {10, 11, 12, 13}; 334 SubmitFrameWithResources( 335 third_frame_ids, arraysize(third_frame_ids), &surface); 336 337 surface.ReturnUnusedResourcesToClient(); 338 { 339 SCOPED_TRACE("third frame"); 340 ResourceProvider::ResourceId expected_returned_ids[] = {2, 3, 4}; 341 int expected_returned_counts[] = {2, 2, 1}; 342 CheckReturnedResourcesMatchExpected(expected_returned_ids, 343 expected_returned_counts, 344 arraysize(expected_returned_counts), 345 client.returned_resources()); 346 } 347 348 // Simulate a ResourceProvider taking a ref on all of the resources. 349 surface.RefCurrentFrameResources(); 350 351 ResourceProvider::ResourceId fourth_frame_ids[] = {12, 13}; 352 SubmitFrameWithResources( 353 fourth_frame_ids, arraysize(fourth_frame_ids), &surface); 354 355 surface.ReturnUnusedResourcesToClient(); 356 EXPECT_EQ(0u, client.returned_resources().size()); 357 358 surface.RefCurrentFrameResources(); 359 360 // All resources are still being used by the external reference, so none can 361 // be returned to the client. 362 surface.ReturnUnusedResourcesToClient(); 363 EXPECT_EQ(0u, client.returned_resources().size()); 364 365 // Release resources associated with the first RefCurrentFrameResources() call 366 // first. 367 { 368 ResourceProvider::ResourceId ids_to_unref[] = {10, 11, 12, 13}; 369 int counts[] = {1, 1, 1, 1}; 370 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface); 371 } 372 373 surface.ReturnUnusedResourcesToClient(); 374 { 375 SCOPED_TRACE("fourth frame, first unref"); 376 ResourceProvider::ResourceId expected_returned_ids[] = {10, 11}; 377 int expected_returned_counts[] = {1, 1}; 378 CheckReturnedResourcesMatchExpected(expected_returned_ids, 379 expected_returned_counts, 380 arraysize(expected_returned_counts), 381 client.returned_resources()); 382 } 383 384 { 385 ResourceProvider::ResourceId ids_to_unref[] = {12, 13}; 386 int counts[] = {1, 1}; 387 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface); 388 } 389 390 // Resources 12 and 13 are still in use by the current frame, so they 391 // shouldn't be available to be returned. 392 surface.ReturnUnusedResourcesToClient(); 393 EXPECT_EQ(0u, client.returned_resources().size()); 394 395 // If we submit an empty frame, however, they should become available. 396 SubmitFrameWithResources(NULL, 0u, &surface); 397 398 surface.ReturnUnusedResourcesToClient(); 399 { 400 SCOPED_TRACE("fourth frame, second unref"); 401 ResourceProvider::ResourceId expected_returned_ids[] = {12, 13}; 402 int expected_returned_counts[] = {2, 2}; 403 CheckReturnedResourcesMatchExpected(expected_returned_ids, 404 expected_returned_counts, 405 arraysize(expected_returned_counts), 406 client.returned_resources()); 407 } 408 } 409 410 } // namespace 411 } // namespace cc 412