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(
129        output_surface_.get(), shared_bitmap_manager_.get(), 0, false, 1,
130        false);
131  }
132
133  void AppendFullUploadsOfIndexedTextureToUpdateQueue(int count,
134                                                      int texture_index) {
135    full_upload_count_expected_ += count;
136    total_upload_count_expected_ += count;
137
138    const gfx::Rect rect(0, 0, 300, 150);
139    const ResourceUpdate upload = ResourceUpdate::Create(
140        textures_[texture_index].get(), &bitmap_, rect, rect, gfx::Vector2d());
141    for (int i = 0; i < count; i++)
142      queue_->AppendFullUpload(upload);
143  }
144
145  void AppendFullUploadsToUpdateQueue(int count) {
146    AppendFullUploadsOfIndexedTextureToUpdateQueue(count, 0);
147  }
148
149  void AppendPartialUploadsOfIndexedTextureToUpdateQueue(int count,
150                                                         int texture_index) {
151    partial_count_expected_ += count;
152    total_upload_count_expected_ += count;
153
154    const gfx::Rect rect(0, 0, 100, 100);
155    const ResourceUpdate upload = ResourceUpdate::Create(
156        textures_[texture_index].get(), &bitmap_, rect, rect, gfx::Vector2d());
157    for (int i = 0; i < count; i++)
158      queue_->AppendPartialUpload(upload);
159  }
160
161  void AppendPartialUploadsToUpdateQueue(int count) {
162    AppendPartialUploadsOfIndexedTextureToUpdateQueue(count, 0);
163  }
164
165  void SetMaxUploadCountPerUpdate(int count) {
166    max_upload_count_per_update_ = count;
167  }
168
169  void UpdateTextures() {
170    DebugScopedSetImplThreadAndMainThreadBlocked
171    impl_thread_and_main_thread_blocked(&proxy_);
172    scoped_ptr<ResourceUpdateController> update_controller =
173        ResourceUpdateController::Create(NULL,
174                                         proxy_.ImplThreadTaskRunner(),
175                                         queue_.Pass(),
176                                         resource_provider_.get());
177    update_controller->Finalize();
178  }
179
180  void MakeQueryResultAvailable() { query_results_available_++; }
181
182 protected:
183  // Classes required to interact and test the ResourceUpdateController
184  FakeProxy proxy_;
185  FakeOutputSurfaceClient output_surface_client_;
186  scoped_ptr<OutputSurface> output_surface_;
187  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
188  scoped_ptr<ResourceProvider> resource_provider_;
189  scoped_ptr<ResourceUpdateQueue> queue_;
190  scoped_ptr<PrioritizedResource> textures_[4];
191  scoped_ptr<PrioritizedResourceManager> resource_manager_;
192  SkBitmap bitmap_;
193  int query_results_available_;
194
195  // Properties / expectations of this test
196  int full_upload_count_expected_;
197  int partial_count_expected_;
198  int total_upload_count_expected_;
199  int max_upload_count_per_update_;
200
201  // Dynamic properties of this test
202  int num_consecutive_flushes_;
203  int num_dangling_uploads_;
204  int num_total_uploads_;
205  int num_total_flushes_;
206};
207
208void WebGraphicsContext3DForUploadTest::flush() { test_->OnFlush(); }
209
210void WebGraphicsContext3DForUploadTest::shallowFlushCHROMIUM() {
211  test_->OnFlush();
212}
213
214void WebGraphicsContext3DForUploadTest::texSubImage2D(GLenum target,
215                                                      GLint level,
216                                                      GLint xoffset,
217                                                      GLint yoffset,
218                                                      GLsizei width,
219                                                      GLsizei height,
220                                                      GLenum format,
221                                                      GLenum type,
222                                                      const void* pixels) {
223  test_->OnUpload();
224}
225
226void WebGraphicsContext3DForUploadTest::getQueryObjectuivEXT(GLuint id,
227                                                             GLenum pname,
228                                                             GLuint* params) {
229  if (pname == GL_QUERY_RESULT_AVAILABLE_EXT)
230    *params = test_->IsQueryResultAvailable();
231}
232
233// ZERO UPLOADS TESTS
234TEST_F(ResourceUpdateControllerTest, ZeroUploads) {
235  AppendFullUploadsToUpdateQueue(0);
236  AppendPartialUploadsToUpdateQueue(0);
237  UpdateTextures();
238
239  EXPECT_EQ(0, num_total_flushes_);
240  EXPECT_EQ(0, num_total_uploads_);
241}
242
243// ONE UPLOAD TESTS
244TEST_F(ResourceUpdateControllerTest, OneFullUpload) {
245  AppendFullUploadsToUpdateQueue(1);
246  AppendPartialUploadsToUpdateQueue(0);
247  UpdateTextures();
248
249  EXPECT_EQ(1, num_total_flushes_);
250  EXPECT_EQ(1, num_total_uploads_);
251  EXPECT_EQ(0, num_dangling_uploads_)
252      << "Last upload wasn't followed by a flush.";
253}
254
255TEST_F(ResourceUpdateControllerTest, OnePartialUpload) {
256  AppendFullUploadsToUpdateQueue(0);
257  AppendPartialUploadsToUpdateQueue(1);
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
266TEST_F(ResourceUpdateControllerTest, OneFullOnePartialUpload) {
267  AppendFullUploadsToUpdateQueue(1);
268  AppendPartialUploadsToUpdateQueue(1);
269  UpdateTextures();
270
271  EXPECT_EQ(1, num_total_flushes_);
272  EXPECT_EQ(2, num_total_uploads_);
273  EXPECT_EQ(0, num_dangling_uploads_)
274      << "Last upload wasn't followed by a flush.";
275}
276
277// This class of tests upload a number of textures that is a multiple
278// of the flush period.
279const int full_upload_flush_multipler = 7;
280const int full_count = full_upload_flush_multipler * kFlushPeriodFull;
281
282const int partial_upload_flush_multipler = 11;
283const int partial_count =
284    partial_upload_flush_multipler * kFlushPeriodPartial;
285
286TEST_F(ResourceUpdateControllerTest, ManyFullUploads) {
287  AppendFullUploadsToUpdateQueue(full_count);
288  AppendPartialUploadsToUpdateQueue(0);
289  UpdateTextures();
290
291  EXPECT_EQ(full_upload_flush_multipler, num_total_flushes_);
292  EXPECT_EQ(full_count, num_total_uploads_);
293  EXPECT_EQ(0, num_dangling_uploads_)
294      << "Last upload wasn't followed by a flush.";
295}
296
297TEST_F(ResourceUpdateControllerTest, ManyPartialUploads) {
298  AppendFullUploadsToUpdateQueue(0);
299  AppendPartialUploadsToUpdateQueue(partial_count);
300  UpdateTextures();
301
302  EXPECT_EQ(partial_upload_flush_multipler, num_total_flushes_);
303  EXPECT_EQ(partial_count, num_total_uploads_);
304  EXPECT_EQ(0, num_dangling_uploads_)
305      << "Last upload wasn't followed by a flush.";
306}
307
308TEST_F(ResourceUpdateControllerTest, ManyFullManyPartialUploads) {
309  AppendFullUploadsToUpdateQueue(full_count);
310  AppendPartialUploadsToUpdateQueue(partial_count);
311  UpdateTextures();
312
313  EXPECT_EQ(full_upload_flush_multipler + partial_upload_flush_multipler,
314            num_total_flushes_);
315  EXPECT_EQ(full_count + partial_count, num_total_uploads_);
316  EXPECT_EQ(0, num_dangling_uploads_)
317      << "Last upload wasn't followed by a flush.";
318}
319
320class FakeResourceUpdateControllerClient
321    : public ResourceUpdateControllerClient {
322 public:
323  FakeResourceUpdateControllerClient() { Reset(); }
324  void Reset() { ready_to_finalize_called_ = false; }
325  bool ReadyToFinalizeCalled() const { return ready_to_finalize_called_; }
326
327  virtual void ReadyToFinalizeTextureUpdates() OVERRIDE {
328    ready_to_finalize_called_ = true;
329  }
330
331 protected:
332  bool ready_to_finalize_called_;
333};
334
335class FakeResourceUpdateController : public ResourceUpdateController {
336 public:
337  static scoped_ptr<FakeResourceUpdateController> Create(
338      ResourceUpdateControllerClient* client,
339      base::TestSimpleTaskRunner* task_runner,
340      scoped_ptr<ResourceUpdateQueue> queue,
341      ResourceProvider* resource_provider) {
342    return make_scoped_ptr(new FakeResourceUpdateController(
343        client, task_runner, queue.Pass(), resource_provider));
344  }
345
346  void SetNow(base::TimeTicks time) { now_ = time; }
347  base::TimeTicks Now() const { return now_; }
348  void SetUpdateTextureTime(base::TimeDelta time) {
349    update_textures_time_ = time;
350  }
351  virtual base::TimeTicks UpdateMoreTexturesCompletionTime() OVERRIDE {
352    size_t total_updates =
353        resource_provider_->NumBlockingUploads() + update_more_textures_size_;
354    return now_ + total_updates * update_textures_time_;
355  }
356  void SetUpdateMoreTexturesSize(size_t size) {
357    update_more_textures_size_ = size;
358  }
359  virtual size_t UpdateMoreTexturesSize() const OVERRIDE {
360    return update_more_textures_size_;
361  }
362
363 protected:
364  FakeResourceUpdateController(ResourceUpdateControllerClient* client,
365                               base::TestSimpleTaskRunner* task_runner,
366                               scoped_ptr<ResourceUpdateQueue> queue,
367                               ResourceProvider* resource_provider)
368      : ResourceUpdateController(
369          client, task_runner, queue.Pass(), resource_provider),
370        resource_provider_(resource_provider),
371        update_more_textures_size_(0) {}
372
373  ResourceProvider* resource_provider_;
374  base::TimeTicks now_;
375  base::TimeDelta update_textures_time_;
376  size_t update_more_textures_size_;
377};
378
379static void RunPendingTask(base::TestSimpleTaskRunner* task_runner,
380                           FakeResourceUpdateController* controller) {
381  EXPECT_TRUE(task_runner->HasPendingTask());
382  controller->SetNow(controller->Now() + task_runner->NextPendingTaskDelay());
383  task_runner->RunPendingTasks();
384}
385
386TEST_F(ResourceUpdateControllerTest, UpdateMoreTextures) {
387  FakeResourceUpdateControllerClient client;
388  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
389      new base::TestSimpleTaskRunner;
390
391  SetMaxUploadCountPerUpdate(1);
392  AppendFullUploadsToUpdateQueue(3);
393  AppendPartialUploadsToUpdateQueue(0);
394
395  DebugScopedSetImplThreadAndMainThreadBlocked
396  impl_thread_and_main_thread_blocked(&proxy_);
397  scoped_ptr<FakeResourceUpdateController> controller(
398      FakeResourceUpdateController::Create(&client,
399                                           task_runner.get(),
400                                           queue_.Pass(),
401                                           resource_provider_.get()));
402
403  controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1));
404  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
405  controller->SetUpdateMoreTexturesSize(1);
406  // Not enough time for any updates.
407  controller->PerformMoreUpdates(controller->Now() +
408                                 base::TimeDelta::FromMilliseconds(90));
409  EXPECT_FALSE(task_runner->HasPendingTask());
410
411  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
412  controller->SetUpdateMoreTexturesSize(1);
413  // Only enough time for 1 update.
414  controller->PerformMoreUpdates(controller->Now() +
415                                 base::TimeDelta::FromMilliseconds(120));
416  EXPECT_FALSE(task_runner->HasPendingTask());
417  EXPECT_EQ(1, num_total_uploads_);
418
419  // Complete one upload.
420  MakeQueryResultAvailable();
421
422  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
423  controller->SetUpdateMoreTexturesSize(1);
424  // Enough time for 2 updates.
425  controller->PerformMoreUpdates(controller->Now() +
426                                 base::TimeDelta::FromMilliseconds(220));
427  RunPendingTask(task_runner.get(), controller.get());
428  EXPECT_FALSE(task_runner->HasPendingTask());
429  EXPECT_TRUE(client.ReadyToFinalizeCalled());
430  EXPECT_EQ(3, num_total_uploads_);
431}
432
433TEST_F(ResourceUpdateControllerTest, NoMoreUpdates) {
434  FakeResourceUpdateControllerClient client;
435  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
436      new base::TestSimpleTaskRunner;
437
438  SetMaxUploadCountPerUpdate(1);
439  AppendFullUploadsToUpdateQueue(2);
440  AppendPartialUploadsToUpdateQueue(0);
441
442  DebugScopedSetImplThreadAndMainThreadBlocked
443  impl_thread_and_main_thread_blocked(&proxy_);
444  scoped_ptr<FakeResourceUpdateController> controller(
445      FakeResourceUpdateController::Create(&client,
446                                           task_runner.get(),
447                                           queue_.Pass(),
448                                           resource_provider_.get()));
449
450  controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1));
451  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
452  controller->SetUpdateMoreTexturesSize(1);
453  // Enough time for 3 updates but only 2 necessary.
454  controller->PerformMoreUpdates(controller->Now() +
455                                 base::TimeDelta::FromMilliseconds(310));
456  RunPendingTask(task_runner.get(), controller.get());
457  EXPECT_FALSE(task_runner->HasPendingTask());
458  EXPECT_TRUE(client.ReadyToFinalizeCalled());
459  EXPECT_EQ(2, num_total_uploads_);
460
461  client.Reset();
462  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
463  controller->SetUpdateMoreTexturesSize(1);
464  // Enough time for updates but no more updates left.
465  controller->PerformMoreUpdates(controller->Now() +
466                                 base::TimeDelta::FromMilliseconds(310));
467
468  // ReadyToFinalizeTextureUpdates should only be called once.
469  EXPECT_FALSE(task_runner->HasPendingTask());
470  EXPECT_FALSE(client.ReadyToFinalizeCalled());
471  EXPECT_EQ(2, num_total_uploads_);
472}
473
474TEST_F(ResourceUpdateControllerTest, UpdatesCompleteInFiniteTime) {
475  FakeResourceUpdateControllerClient client;
476  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
477      new base::TestSimpleTaskRunner;
478
479  SetMaxUploadCountPerUpdate(1);
480  AppendFullUploadsToUpdateQueue(2);
481  AppendPartialUploadsToUpdateQueue(0);
482
483  DebugScopedSetImplThreadAndMainThreadBlocked
484  impl_thread_and_main_thread_blocked(&proxy_);
485  scoped_ptr<FakeResourceUpdateController> controller(
486      FakeResourceUpdateController::Create(&client,
487                                           task_runner.get(),
488                                           queue_.Pass(),
489                                           resource_provider_.get()));
490
491  controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1));
492  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(500));
493  controller->SetUpdateMoreTexturesSize(1);
494
495  for (int i = 0; i < 100; i++) {
496    if (client.ReadyToFinalizeCalled())
497      break;
498
499    // Not enough time for any updates.
500    controller->PerformMoreUpdates(controller->Now() +
501                                   base::TimeDelta::FromMilliseconds(400));
502
503    if (task_runner->HasPendingTask())
504      RunPendingTask(task_runner.get(), controller.get());
505  }
506
507  EXPECT_FALSE(task_runner->HasPendingTask());
508  EXPECT_TRUE(client.ReadyToFinalizeCalled());
509  EXPECT_EQ(2, num_total_uploads_);
510}
511
512}  // namespace
513}  // namespace cc
514