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