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
20using testing::Test;
21
22namespace cc {
23namespace {
24
25const int kFlushPeriodFull = 4;
26const int kFlushPeriodPartial = kFlushPeriodFull;
27
28class ResourceUpdateControllerTest;
29
30class 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
54class 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
212void WebGraphicsContext3DForUploadTest::flush() { test_->OnFlush(); }
213
214void WebGraphicsContext3DForUploadTest::shallowFlushCHROMIUM() {
215  test_->OnFlush();
216}
217
218void 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
230void 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
238TEST_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
248TEST_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
259TEST_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
270TEST_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.
283const int full_upload_flush_multipler = 7;
284const int full_count = full_upload_flush_multipler * kFlushPeriodFull;
285
286const int partial_upload_flush_multipler = 11;
287const int partial_count =
288    partial_upload_flush_multipler * kFlushPeriodPartial;
289
290TEST_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
301TEST_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
312TEST_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
324class 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
339class 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
383static 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
390TEST_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
437TEST_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
478TEST_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