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