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_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" 13 14 namespace cc { 15 namespace { 16 17 class TestSurfaceFactoryClient : public SurfaceFactoryClient { 18 public: 19 TestSurfaceFactoryClient() {} 20 virtual ~TestSurfaceFactoryClient() {} 21 22 virtual void ReturnResources( 23 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_, gfx::Size(5, 5)); 44 } 45 46 virtual ~SurfaceFactoryTest() { factory_.Destroy(surface_id_); } 47 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); 56 } 57 scoped_ptr<CompositorFrame> frame(new CompositorFrame); 58 frame->delegated_frame_data = frame_data.Pass(); 59 factory_.SubmitFrame(surface_id_, frame.Pass(), base::Closure()); 60 } 61 62 void UnrefResources(ResourceProvider::ResourceId* ids_to_unref, 63 int* counts_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); 71 } 72 factory_.UnrefResources(unref_array); 73 } 74 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); 86 } 87 client_.clear_returned_resources(); 88 } 89 90 void RefCurrentFrameResources() { 91 Surface* surface = manager_.GetSurfaceForId(surface_id_); 92 factory_.RefResources( 93 surface->GetEligibleFrame()->delegated_frame_data->resource_list); 94 } 95 96 protected: 97 SurfaceManager manager_; 98 TestSurfaceFactoryClient client_; 99 SurfaceFactory factory_; 100 SurfaceId surface_id_; 101 }; 102 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)); 108 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 111 // the client yet. 112 EXPECT_EQ(0u, client_.returned_resources().size()); 113 client_.clear_returned_resources(); 114 115 // The second frame references no resources and thus should make all resources 116 // available to be returned. 117 SubmitFrameWithResources(NULL, 0); 118 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)); 124 } 125 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)); 131 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 134 // the client yet. 135 EXPECT_EQ(0u, client_.returned_resources().size()); 136 client_.clear_returned_resources(); 137 138 // Hold on to everything. 139 RefCurrentFrameResources(); 140 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); 144 145 EXPECT_EQ(0u, client_.returned_resources().size()); 146 client_.clear_returned_resources(); 147 148 int release_counts[] = {1, 1, 1}; 149 UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids)); 150 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)); 156 } 157 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)); 163 164 // This removes all references to resource id 7. 165 SubmitFrameWithResources(NULL, 0); 166 167 // This references id 7 again. 168 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); 169 170 // This removes it again. 171 SubmitFrameWithResources(NULL, 0); 172 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; 181 } 182 EXPECT_EQ(2u, return_count); 183 } 184 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)); 190 191 // Ref resources from the first frame twice. 192 RefCurrentFrameResources(); 193 RefCurrentFrameResources(); 194 195 ResourceProvider::ResourceId second_frame_ids[] = {4, 5}; 196 SubmitFrameWithResources(second_frame_ids, arraysize(second_frame_ids)); 197 198 // Ref resources from the second frame 3 times. 199 RefCurrentFrameResources(); 200 RefCurrentFrameResources(); 201 RefCurrentFrameResources(); 202 203 // Submit a frame with no resources to remove all current frame refs from 204 // submitted resources. 205 SubmitFrameWithResources(NULL, 0); 206 207 EXPECT_EQ(0u, client_.returned_resources().size()); 208 client_.clear_returned_resources(); 209 210 // Expected current refs: 211 // 3 -> 2 212 // 4 -> 2 + 3 = 5 213 // 5 -> 3 214 { 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)); 219 220 EXPECT_EQ(0u, client_.returned_resources().size()); 221 client_.clear_returned_resources(); 222 223 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); 224 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)); 230 } 231 232 // Expected refs remaining: 233 // 4 -> 3 234 // 5 -> 1 235 { 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)); 240 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)); 246 } 247 248 // Now, just 2 refs remaining on resource 4. Unref both at once and make sure 249 // the returned count is correct. 250 { 251 SCOPED_TRACE("unref only 4"); 252 ResourceProvider::ResourceId ids_to_unref[] = {4}; 253 int counts[] = {2}; 254 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); 255 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)); 261 } 262 } 263 264 TEST_F(SurfaceFactoryTest, ResourceLifetime) { 265 ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3}; 266 SubmitFrameWithResources(first_frame_ids, arraysize(first_frame_ids)); 267 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 270 // the client yet. 271 EXPECT_EQ(0u, client_.returned_resources().size()); 272 client_.clear_returned_resources(); 273 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)); 279 280 { 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)); 287 } 288 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)); 295 296 { 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)); 303 } 304 305 // Simulate a ResourceProvider taking a ref on all of the resources. 306 RefCurrentFrameResources(); 307 308 ResourceProvider::ResourceId fourth_frame_ids[] = {12, 13}; 309 SubmitFrameWithResources(fourth_frame_ids, arraysize(fourth_frame_ids)); 310 311 EXPECT_EQ(0u, client_.returned_resources().size()); 312 313 RefCurrentFrameResources(); 314 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()); 318 319 // Release resources associated with the first RefCurrentFrameResources() call 320 // first. 321 { 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)); 325 } 326 327 { 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)); 334 } 335 336 { 337 ResourceProvider::ResourceId ids_to_unref[] = {12, 13}; 338 int counts[] = {1, 1}; 339 UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); 340 } 341 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()); 345 346 // If we submit an empty frame, however, they should become available. 347 SubmitFrameWithResources(NULL, 0u); 348 349 { 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)); 356 } 357 } 358 359 // Tests shutting down the factory with a surface with outstanding refs still in 360 // the map. 361 TEST_F(SurfaceFactoryTest, DestroyWithResourceRefs) { 362 SurfaceId id(7); 363 factory_.Create(id, gfx::Size(1, 1)); 364 365 scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData); 366 TransferableResource resource; 367 resource.id = 1; 368 resource.mailbox_holder.texture_target = GL_TEXTURE_2D; 369 frame_data->resource_list.push_back(resource); 370 scoped_ptr<CompositorFrame> frame(new CompositorFrame); 371 frame->delegated_frame_data = frame_data.Pass(); 372 factory_.SubmitFrame(id, frame.Pass(), base::Closure()); 373 } 374 375 } // namespace 376 } // namespace cc 377