1 // Copyright 2012 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/resources/resource_update_controller.h" 6 7 #include "base/test/test_simple_task_runner.h" 8 #include "cc/resources/prioritized_resource_manager.h" 9 #include "cc/test/fake_output_surface.h" 10 #include "cc/test/fake_output_surface_client.h" 11 #include "cc/test/fake_proxy.h" 12 #include "cc/test/scheduler_test_common.h" 13 #include "cc/test/test_shared_bitmap_manager.h" 14 #include "cc/test/test_web_graphics_context_3d.h" 15 #include "cc/test/tiled_layer_test_common.h" 16 #include "cc/trees/single_thread_proxy.h" // For DebugScopedSetImplThread 17 #include "testing/gtest/include/gtest/gtest.h" 18 #include "third_party/khronos/GLES2/gl2ext.h" 19 20 using testing::Test; 21 22 namespace cc { 23 namespace { 24 25 const int kFlushPeriodFull = 4; 26 const int kFlushPeriodPartial = kFlushPeriodFull; 27 28 class ResourceUpdateControllerTest; 29 30 class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D { 31 public: 32 explicit WebGraphicsContext3DForUploadTest(ResourceUpdateControllerTest* test) 33 : test_(test) {} 34 35 virtual void flush() OVERRIDE; 36 virtual void shallowFlushCHROMIUM() OVERRIDE; 37 virtual void texSubImage2D(GLenum target, 38 GLint level, 39 GLint xoffset, 40 GLint yoffset, 41 GLsizei width, 42 GLsizei height, 43 GLenum format, 44 GLenum type, 45 const void* pixels) OVERRIDE; 46 47 virtual void getQueryObjectuivEXT(GLuint id, GLenum pname, GLuint* value) 48 OVERRIDE; 49 50 private: 51 ResourceUpdateControllerTest* test_; 52 }; 53 54 class ResourceUpdateControllerTest : public Test { 55 public: 56 ResourceUpdateControllerTest() 57 : proxy_(), 58 queue_(make_scoped_ptr(new ResourceUpdateQueue)), 59 resource_manager_(PrioritizedResourceManager::Create(&proxy_)), 60 query_results_available_(0), 61 full_upload_count_expected_(0), 62 partial_count_expected_(0), 63 total_upload_count_expected_(0), 64 max_upload_count_per_update_(0), 65 num_consecutive_flushes_(0), 66 num_dangling_uploads_(0), 67 num_total_uploads_(0), 68 num_total_flushes_(0) {} 69 70 virtual ~ResourceUpdateControllerTest() { 71 DebugScopedSetImplThreadAndMainThreadBlocked 72 impl_thread_and_main_thread_blocked(&proxy_); 73 resource_manager_->ClearAllMemory(resource_provider_.get()); 74 } 75 76 public: 77 void OnFlush() { 78 // Check for back-to-back flushes. 79 EXPECT_EQ(0, num_consecutive_flushes_) << "Back-to-back flushes detected."; 80 81 num_dangling_uploads_ = 0; 82 num_consecutive_flushes_++; 83 num_total_flushes_++; 84 } 85 86 void OnUpload() { 87 // Check for too many consecutive uploads 88 if (num_total_uploads_ < full_upload_count_expected_) { 89 EXPECT_LT(num_dangling_uploads_, kFlushPeriodFull) 90 << "Too many consecutive full uploads detected."; 91 } else { 92 EXPECT_LT(num_dangling_uploads_, kFlushPeriodPartial) 93 << "Too many consecutive partial uploads detected."; 94 } 95 96 num_consecutive_flushes_ = 0; 97 num_dangling_uploads_++; 98 num_total_uploads_++; 99 } 100 101 bool IsQueryResultAvailable() { 102 if (!query_results_available_) 103 return false; 104 105 query_results_available_--; 106 return true; 107 } 108 109 protected: 110 virtual void SetUp() { 111 bitmap_.allocN32Pixels(300, 150); 112 113 for (int i = 0; i < 4; i++) { 114 textures_[i] = PrioritizedResource::Create(resource_manager_.get(), 115 gfx::Size(300, 150), 116 RGBA_8888); 117 textures_[i]-> 118 set_request_priority(PriorityCalculator::VisiblePriority(true)); 119 } 120 resource_manager_->PrioritizeTextures(); 121 122 output_surface_ = FakeOutputSurface::Create3d( 123 scoped_ptr<TestWebGraphicsContext3D>( 124 new WebGraphicsContext3DForUploadTest(this))); 125 CHECK(output_surface_->BindToClient(&output_surface_client_)); 126 127 shared_bitmap_manager_.reset(new TestSharedBitmapManager()); 128 resource_provider_ = ResourceProvider::Create(output_surface_.get(), 129 shared_bitmap_manager_.get(), 130 NULL, 131 0, 132 false, 133 1, 134 false); 135 } 136 137 void AppendFullUploadsOfIndexedTextureToUpdateQueue(int count, 138 int texture_index) { 139 full_upload_count_expected_ += count; 140 total_upload_count_expected_ += count; 141 142 const gfx::Rect rect(0, 0, 300, 150); 143 const ResourceUpdate upload = ResourceUpdate::Create( 144 textures_[texture_index].get(), &bitmap_, rect, rect, gfx::Vector2d()); 145 for (int i = 0; i < count; i++) 146 queue_->AppendFullUpload(upload); 147 } 148 149 void AppendFullUploadsToUpdateQueue(int count) { 150 AppendFullUploadsOfIndexedTextureToUpdateQueue(count, 0); 151 } 152 153 void AppendPartialUploadsOfIndexedTextureToUpdateQueue(int count, 154 int texture_index) { 155 partial_count_expected_ += count; 156 total_upload_count_expected_ += count; 157 158 const gfx::Rect rect(0, 0, 100, 100); 159 const ResourceUpdate upload = ResourceUpdate::Create( 160 textures_[texture_index].get(), &bitmap_, rect, rect, gfx::Vector2d()); 161 for (int i = 0; i < count; i++) 162 queue_->AppendPartialUpload(upload); 163 } 164 165 void AppendPartialUploadsToUpdateQueue(int count) { 166 AppendPartialUploadsOfIndexedTextureToUpdateQueue(count, 0); 167 } 168 169 void SetMaxUploadCountPerUpdate(int count) { 170 max_upload_count_per_update_ = count; 171 } 172 173 void UpdateTextures() { 174 DebugScopedSetImplThreadAndMainThreadBlocked 175 impl_thread_and_main_thread_blocked(&proxy_); 176 scoped_ptr<ResourceUpdateController> update_controller = 177 ResourceUpdateController::Create(NULL, 178 proxy_.ImplThreadTaskRunner(), 179 queue_.Pass(), 180 resource_provider_.get()); 181 update_controller->Finalize(); 182 } 183 184 void MakeQueryResultAvailable() { query_results_available_++; } 185 186 protected: 187 // Classes required to interact and test the ResourceUpdateController 188 FakeProxy proxy_; 189 FakeOutputSurfaceClient output_surface_client_; 190 scoped_ptr<OutputSurface> output_surface_; 191 scoped_ptr<SharedBitmapManager> shared_bitmap_manager_; 192 scoped_ptr<ResourceProvider> resource_provider_; 193 scoped_ptr<ResourceUpdateQueue> queue_; 194 scoped_ptr<PrioritizedResource> textures_[4]; 195 scoped_ptr<PrioritizedResourceManager> resource_manager_; 196 SkBitmap bitmap_; 197 int query_results_available_; 198 199 // Properties / expectations of this test 200 int full_upload_count_expected_; 201 int partial_count_expected_; 202 int total_upload_count_expected_; 203 int max_upload_count_per_update_; 204 205 // Dynamic properties of this test 206 int num_consecutive_flushes_; 207 int num_dangling_uploads_; 208 int num_total_uploads_; 209 int num_total_flushes_; 210 }; 211 212 void WebGraphicsContext3DForUploadTest::flush() { test_->OnFlush(); } 213 214 void WebGraphicsContext3DForUploadTest::shallowFlushCHROMIUM() { 215 test_->OnFlush(); 216 } 217 218 void WebGraphicsContext3DForUploadTest::texSubImage2D(GLenum target, 219 GLint level, 220 GLint xoffset, 221 GLint yoffset, 222 GLsizei width, 223 GLsizei height, 224 GLenum format, 225 GLenum type, 226 const void* pixels) { 227 test_->OnUpload(); 228 } 229 230 void WebGraphicsContext3DForUploadTest::getQueryObjectuivEXT(GLuint id, 231 GLenum pname, 232 GLuint* params) { 233 if (pname == GL_QUERY_RESULT_AVAILABLE_EXT) 234 *params = test_->IsQueryResultAvailable(); 235 } 236 237 // ZERO UPLOADS TESTS 238 TEST_F(ResourceUpdateControllerTest, ZeroUploads) { 239 AppendFullUploadsToUpdateQueue(0); 240 AppendPartialUploadsToUpdateQueue(0); 241 UpdateTextures(); 242 243 EXPECT_EQ(0, num_total_flushes_); 244 EXPECT_EQ(0, num_total_uploads_); 245 } 246 247 // ONE UPLOAD TESTS 248 TEST_F(ResourceUpdateControllerTest, OneFullUpload) { 249 AppendFullUploadsToUpdateQueue(1); 250 AppendPartialUploadsToUpdateQueue(0); 251 UpdateTextures(); 252 253 EXPECT_EQ(1, num_total_flushes_); 254 EXPECT_EQ(1, num_total_uploads_); 255 EXPECT_EQ(0, num_dangling_uploads_) 256 << "Last upload wasn't followed by a flush."; 257 } 258 259 TEST_F(ResourceUpdateControllerTest, OnePartialUpload) { 260 AppendFullUploadsToUpdateQueue(0); 261 AppendPartialUploadsToUpdateQueue(1); 262 UpdateTextures(); 263 264 EXPECT_EQ(1, num_total_flushes_); 265 EXPECT_EQ(1, num_total_uploads_); 266 EXPECT_EQ(0, num_dangling_uploads_) 267 << "Last upload wasn't followed by a flush."; 268 } 269 270 TEST_F(ResourceUpdateControllerTest, OneFullOnePartialUpload) { 271 AppendFullUploadsToUpdateQueue(1); 272 AppendPartialUploadsToUpdateQueue(1); 273 UpdateTextures(); 274 275 EXPECT_EQ(1, num_total_flushes_); 276 EXPECT_EQ(2, num_total_uploads_); 277 EXPECT_EQ(0, num_dangling_uploads_) 278 << "Last upload wasn't followed by a flush."; 279 } 280 281 // This class of tests upload a number of textures that is a multiple 282 // of the flush period. 283 const int full_upload_flush_multipler = 7; 284 const int full_count = full_upload_flush_multipler * kFlushPeriodFull; 285 286 const int partial_upload_flush_multipler = 11; 287 const int partial_count = 288 partial_upload_flush_multipler * kFlushPeriodPartial; 289 290 TEST_F(ResourceUpdateControllerTest, ManyFullUploads) { 291 AppendFullUploadsToUpdateQueue(full_count); 292 AppendPartialUploadsToUpdateQueue(0); 293 UpdateTextures(); 294 295 EXPECT_EQ(full_upload_flush_multipler, num_total_flushes_); 296 EXPECT_EQ(full_count, num_total_uploads_); 297 EXPECT_EQ(0, num_dangling_uploads_) 298 << "Last upload wasn't followed by a flush."; 299 } 300 301 TEST_F(ResourceUpdateControllerTest, ManyPartialUploads) { 302 AppendFullUploadsToUpdateQueue(0); 303 AppendPartialUploadsToUpdateQueue(partial_count); 304 UpdateTextures(); 305 306 EXPECT_EQ(partial_upload_flush_multipler, num_total_flushes_); 307 EXPECT_EQ(partial_count, num_total_uploads_); 308 EXPECT_EQ(0, num_dangling_uploads_) 309 << "Last upload wasn't followed by a flush."; 310 } 311 312 TEST_F(ResourceUpdateControllerTest, ManyFullManyPartialUploads) { 313 AppendFullUploadsToUpdateQueue(full_count); 314 AppendPartialUploadsToUpdateQueue(partial_count); 315 UpdateTextures(); 316 317 EXPECT_EQ(full_upload_flush_multipler + partial_upload_flush_multipler, 318 num_total_flushes_); 319 EXPECT_EQ(full_count + partial_count, num_total_uploads_); 320 EXPECT_EQ(0, num_dangling_uploads_) 321 << "Last upload wasn't followed by a flush."; 322 } 323 324 class FakeResourceUpdateControllerClient 325 : public ResourceUpdateControllerClient { 326 public: 327 FakeResourceUpdateControllerClient() { Reset(); } 328 void Reset() { ready_to_finalize_called_ = false; } 329 bool ReadyToFinalizeCalled() const { return ready_to_finalize_called_; } 330 331 virtual void ReadyToFinalizeTextureUpdates() OVERRIDE { 332 ready_to_finalize_called_ = true; 333 } 334 335 protected: 336 bool ready_to_finalize_called_; 337 }; 338 339 class FakeResourceUpdateController : public ResourceUpdateController { 340 public: 341 static scoped_ptr<FakeResourceUpdateController> Create( 342 ResourceUpdateControllerClient* client, 343 base::TestSimpleTaskRunner* task_runner, 344 scoped_ptr<ResourceUpdateQueue> queue, 345 ResourceProvider* resource_provider) { 346 return make_scoped_ptr(new FakeResourceUpdateController( 347 client, task_runner, queue.Pass(), resource_provider)); 348 } 349 350 void SetNow(base::TimeTicks time) { now_ = time; } 351 base::TimeTicks Now() const { return now_; } 352 void SetUpdateTextureTime(base::TimeDelta time) { 353 update_textures_time_ = time; 354 } 355 virtual base::TimeTicks UpdateMoreTexturesCompletionTime() OVERRIDE { 356 size_t total_updates = 357 resource_provider_->NumBlockingUploads() + update_more_textures_size_; 358 return now_ + total_updates * update_textures_time_; 359 } 360 void SetUpdateMoreTexturesSize(size_t size) { 361 update_more_textures_size_ = size; 362 } 363 virtual size_t UpdateMoreTexturesSize() const OVERRIDE { 364 return update_more_textures_size_; 365 } 366 367 protected: 368 FakeResourceUpdateController(ResourceUpdateControllerClient* client, 369 base::TestSimpleTaskRunner* task_runner, 370 scoped_ptr<ResourceUpdateQueue> queue, 371 ResourceProvider* resource_provider) 372 : ResourceUpdateController( 373 client, task_runner, queue.Pass(), resource_provider), 374 resource_provider_(resource_provider), 375 update_more_textures_size_(0) {} 376 377 ResourceProvider* resource_provider_; 378 base::TimeTicks now_; 379 base::TimeDelta update_textures_time_; 380 size_t update_more_textures_size_; 381 }; 382 383 static void RunPendingTask(base::TestSimpleTaskRunner* task_runner, 384 FakeResourceUpdateController* controller) { 385 EXPECT_TRUE(task_runner->HasPendingTask()); 386 controller->SetNow(controller->Now() + task_runner->NextPendingTaskDelay()); 387 task_runner->RunPendingTasks(); 388 } 389 390 TEST_F(ResourceUpdateControllerTest, UpdateMoreTextures) { 391 FakeResourceUpdateControllerClient client; 392 scoped_refptr<base::TestSimpleTaskRunner> task_runner = 393 new base::TestSimpleTaskRunner; 394 395 SetMaxUploadCountPerUpdate(1); 396 AppendFullUploadsToUpdateQueue(3); 397 AppendPartialUploadsToUpdateQueue(0); 398 399 DebugScopedSetImplThreadAndMainThreadBlocked 400 impl_thread_and_main_thread_blocked(&proxy_); 401 scoped_ptr<FakeResourceUpdateController> controller( 402 FakeResourceUpdateController::Create(&client, 403 task_runner.get(), 404 queue_.Pass(), 405 resource_provider_.get())); 406 407 controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1)); 408 controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100)); 409 controller->SetUpdateMoreTexturesSize(1); 410 // Not enough time for any updates. 411 controller->PerformMoreUpdates(controller->Now() + 412 base::TimeDelta::FromMilliseconds(90)); 413 EXPECT_FALSE(task_runner->HasPendingTask()); 414 415 controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100)); 416 controller->SetUpdateMoreTexturesSize(1); 417 // Only enough time for 1 update. 418 controller->PerformMoreUpdates(controller->Now() + 419 base::TimeDelta::FromMilliseconds(120)); 420 EXPECT_FALSE(task_runner->HasPendingTask()); 421 EXPECT_EQ(1, num_total_uploads_); 422 423 // Complete one upload. 424 MakeQueryResultAvailable(); 425 426 controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100)); 427 controller->SetUpdateMoreTexturesSize(1); 428 // Enough time for 2 updates. 429 controller->PerformMoreUpdates(controller->Now() + 430 base::TimeDelta::FromMilliseconds(220)); 431 RunPendingTask(task_runner.get(), controller.get()); 432 EXPECT_FALSE(task_runner->HasPendingTask()); 433 EXPECT_TRUE(client.ReadyToFinalizeCalled()); 434 EXPECT_EQ(3, num_total_uploads_); 435 } 436 437 TEST_F(ResourceUpdateControllerTest, NoMoreUpdates) { 438 FakeResourceUpdateControllerClient client; 439 scoped_refptr<base::TestSimpleTaskRunner> task_runner = 440 new base::TestSimpleTaskRunner; 441 442 SetMaxUploadCountPerUpdate(1); 443 AppendFullUploadsToUpdateQueue(2); 444 AppendPartialUploadsToUpdateQueue(0); 445 446 DebugScopedSetImplThreadAndMainThreadBlocked 447 impl_thread_and_main_thread_blocked(&proxy_); 448 scoped_ptr<FakeResourceUpdateController> controller( 449 FakeResourceUpdateController::Create(&client, 450 task_runner.get(), 451 queue_.Pass(), 452 resource_provider_.get())); 453 454 controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1)); 455 controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100)); 456 controller->SetUpdateMoreTexturesSize(1); 457 // Enough time for 3 updates but only 2 necessary. 458 controller->PerformMoreUpdates(controller->Now() + 459 base::TimeDelta::FromMilliseconds(310)); 460 RunPendingTask(task_runner.get(), controller.get()); 461 EXPECT_FALSE(task_runner->HasPendingTask()); 462 EXPECT_TRUE(client.ReadyToFinalizeCalled()); 463 EXPECT_EQ(2, num_total_uploads_); 464 465 client.Reset(); 466 controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100)); 467 controller->SetUpdateMoreTexturesSize(1); 468 // Enough time for updates but no more updates left. 469 controller->PerformMoreUpdates(controller->Now() + 470 base::TimeDelta::FromMilliseconds(310)); 471 472 // ReadyToFinalizeTextureUpdates should only be called once. 473 EXPECT_FALSE(task_runner->HasPendingTask()); 474 EXPECT_FALSE(client.ReadyToFinalizeCalled()); 475 EXPECT_EQ(2, num_total_uploads_); 476 } 477 478 TEST_F(ResourceUpdateControllerTest, UpdatesCompleteInFiniteTime) { 479 FakeResourceUpdateControllerClient client; 480 scoped_refptr<base::TestSimpleTaskRunner> task_runner = 481 new base::TestSimpleTaskRunner; 482 483 SetMaxUploadCountPerUpdate(1); 484 AppendFullUploadsToUpdateQueue(2); 485 AppendPartialUploadsToUpdateQueue(0); 486 487 DebugScopedSetImplThreadAndMainThreadBlocked 488 impl_thread_and_main_thread_blocked(&proxy_); 489 scoped_ptr<FakeResourceUpdateController> controller( 490 FakeResourceUpdateController::Create(&client, 491 task_runner.get(), 492 queue_.Pass(), 493 resource_provider_.get())); 494 495 controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1)); 496 controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(500)); 497 controller->SetUpdateMoreTexturesSize(1); 498 499 for (int i = 0; i < 100; i++) { 500 if (client.ReadyToFinalizeCalled()) 501 break; 502 503 // Not enough time for any updates. 504 controller->PerformMoreUpdates(controller->Now() + 505 base::TimeDelta::FromMilliseconds(400)); 506 507 if (task_runner->HasPendingTask()) 508 RunPendingTask(task_runner.get(), controller.get()); 509 } 510 511 EXPECT_FALSE(task_runner->HasPendingTask()); 512 EXPECT_TRUE(client.ReadyToFinalizeCalled()); 513 EXPECT_EQ(2, num_total_uploads_); 514 } 515 516 } // namespace 517 } // namespace cc 518