1// Copyright 2013 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/raster_worker_pool.h"
6
7#include <limits>
8#include <vector>
9
10#include "base/cancelable_callback.h"
11#include "cc/resources/bitmap_raster_worker_pool.h"
12#include "cc/resources/gpu_raster_worker_pool.h"
13#include "cc/resources/one_copy_raster_worker_pool.h"
14#include "cc/resources/picture_pile.h"
15#include "cc/resources/picture_pile_impl.h"
16#include "cc/resources/pixel_buffer_raster_worker_pool.h"
17#include "cc/resources/raster_buffer.h"
18#include "cc/resources/rasterizer.h"
19#include "cc/resources/resource_pool.h"
20#include "cc/resources/resource_provider.h"
21#include "cc/resources/scoped_resource.h"
22#include "cc/resources/zero_copy_raster_worker_pool.h"
23#include "cc/test/fake_output_surface.h"
24#include "cc/test/fake_output_surface_client.h"
25#include "cc/test/test_shared_bitmap_manager.h"
26#include "cc/test/test_web_graphics_context_3d.h"
27#include "testing/gtest/include/gtest/gtest.h"
28
29namespace cc {
30namespace {
31
32const size_t kMaxTransferBufferUsageBytes = 10000U;
33// A resource of this dimension^2 * 4 must be greater than the above transfer
34// buffer constant.
35const size_t kLargeResourceDimension = 1000U;
36
37enum RasterWorkerPoolType {
38  RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
39  RASTER_WORKER_POOL_TYPE_ZERO_COPY,
40  RASTER_WORKER_POOL_TYPE_ONE_COPY,
41  RASTER_WORKER_POOL_TYPE_GPU,
42  RASTER_WORKER_POOL_TYPE_BITMAP
43};
44
45class TestRasterTaskImpl : public RasterTask {
46 public:
47  typedef base::Callback<
48      void(const PicturePileImpl::Analysis& analysis, bool was_canceled)> Reply;
49
50  TestRasterTaskImpl(const Resource* resource,
51                     const Reply& reply,
52                     ImageDecodeTask::Vector* dependencies)
53      : RasterTask(resource, dependencies), reply_(reply) {}
54
55  // Overridden from Task:
56  virtual void RunOnWorkerThread() OVERRIDE {
57    skia::RefPtr<SkCanvas> canvas = raster_buffer_->AcquireSkCanvas();
58    DCHECK(canvas);
59    canvas->drawColor(SK_ColorWHITE);
60    raster_buffer_->ReleaseSkCanvas(canvas);
61  }
62
63  // Overridden from RasterizerTask:
64  virtual void ScheduleOnOriginThread(RasterizerTaskClient* client) OVERRIDE {
65    raster_buffer_ = client->AcquireBufferForRaster(resource());
66  }
67  virtual void CompleteOnOriginThread(RasterizerTaskClient* client) OVERRIDE {
68    client->ReleaseBufferForRaster(raster_buffer_.Pass());
69  }
70  virtual void RunReplyOnOriginThread() OVERRIDE {
71    reply_.Run(PicturePileImpl::Analysis(), !HasFinishedRunning());
72  }
73
74 protected:
75  virtual ~TestRasterTaskImpl() {}
76
77 private:
78  const Reply reply_;
79  scoped_ptr<RasterBuffer> raster_buffer_;
80
81  DISALLOW_COPY_AND_ASSIGN(TestRasterTaskImpl);
82};
83
84class BlockingTestRasterTaskImpl : public TestRasterTaskImpl {
85 public:
86  BlockingTestRasterTaskImpl(const Resource* resource,
87                             const Reply& reply,
88                             base::Lock* lock,
89                             ImageDecodeTask::Vector* dependencies)
90      : TestRasterTaskImpl(resource, reply, dependencies), lock_(lock) {}
91
92  // Overridden from Task:
93  virtual void RunOnWorkerThread() OVERRIDE {
94    base::AutoLock lock(*lock_);
95    TestRasterTaskImpl::RunOnWorkerThread();
96  }
97
98  // Overridden from RasterizerTask:
99  virtual void RunReplyOnOriginThread() OVERRIDE {}
100
101 protected:
102  virtual ~BlockingTestRasterTaskImpl() {}
103
104 private:
105  base::Lock* lock_;
106
107  DISALLOW_COPY_AND_ASSIGN(BlockingTestRasterTaskImpl);
108};
109
110class RasterWorkerPoolTest
111    : public testing::TestWithParam<RasterWorkerPoolType>,
112      public RasterizerClient {
113 public:
114  struct RasterTaskResult {
115    unsigned id;
116    bool canceled;
117  };
118
119  typedef std::vector<scoped_refptr<RasterTask> > RasterTaskVector;
120
121  enum NamedTaskSet { REQUIRED_FOR_ACTIVATION = 0, ALL = 1 };
122
123  RasterWorkerPoolTest()
124      : context_provider_(TestContextProvider::Create()),
125        timeout_seconds_(5),
126        timed_out_(false) {}
127
128  // Overridden from testing::Test:
129  virtual void SetUp() OVERRIDE {
130    switch (GetParam()) {
131      case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER:
132        Create3dOutputSurfaceAndResourceProvider();
133        raster_worker_pool_ = PixelBufferRasterWorkerPool::Create(
134            base::MessageLoopProxy::current().get(),
135            RasterWorkerPool::GetTaskGraphRunner(),
136            context_provider_.get(),
137            resource_provider_.get(),
138            kMaxTransferBufferUsageBytes);
139        break;
140      case RASTER_WORKER_POOL_TYPE_ZERO_COPY:
141        Create3dOutputSurfaceAndResourceProvider();
142        raster_worker_pool_ = ZeroCopyRasterWorkerPool::Create(
143            base::MessageLoopProxy::current().get(),
144            RasterWorkerPool::GetTaskGraphRunner(),
145            resource_provider_.get());
146        break;
147      case RASTER_WORKER_POOL_TYPE_ONE_COPY:
148        Create3dOutputSurfaceAndResourceProvider();
149        staging_resource_pool_ = ResourcePool::Create(
150            resource_provider_.get(), GL_TEXTURE_2D, RGBA_8888);
151        raster_worker_pool_ = OneCopyRasterWorkerPool::Create(
152            base::MessageLoopProxy::current().get(),
153            RasterWorkerPool::GetTaskGraphRunner(),
154            context_provider_.get(),
155            resource_provider_.get(),
156            staging_resource_pool_.get());
157        break;
158      case RASTER_WORKER_POOL_TYPE_GPU:
159        Create3dOutputSurfaceAndResourceProvider();
160        raster_worker_pool_ =
161            GpuRasterWorkerPool::Create(base::MessageLoopProxy::current().get(),
162                                        context_provider_.get(),
163                                        resource_provider_.get());
164        break;
165      case RASTER_WORKER_POOL_TYPE_BITMAP:
166        CreateSoftwareOutputSurfaceAndResourceProvider();
167        raster_worker_pool_ = BitmapRasterWorkerPool::Create(
168            base::MessageLoopProxy::current().get(),
169            RasterWorkerPool::GetTaskGraphRunner(),
170            resource_provider_.get());
171        break;
172    }
173
174    DCHECK(raster_worker_pool_);
175    raster_worker_pool_->AsRasterizer()->SetClient(this);
176  }
177  virtual void TearDown() OVERRIDE {
178    raster_worker_pool_->AsRasterizer()->Shutdown();
179    raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks();
180  }
181
182  // Overriden from RasterWorkerPoolClient:
183  virtual void DidFinishRunningTasks(TaskSet task_set) OVERRIDE {
184    if (task_set == ALL) {
185      raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks();
186      base::MessageLoop::current()->Quit();
187    }
188  }
189  virtual TaskSetCollection TasksThatShouldBeForcedToComplete() const OVERRIDE {
190    return TaskSetCollection();
191  }
192
193  void RunMessageLoopUntilAllTasksHaveCompleted() {
194    if (timeout_seconds_) {
195      timeout_.Reset(
196          base::Bind(&RasterWorkerPoolTest::OnTimeout, base::Unretained(this)));
197      base::MessageLoopProxy::current()->PostDelayedTask(
198          FROM_HERE,
199          timeout_.callback(),
200          base::TimeDelta::FromSeconds(timeout_seconds_));
201    }
202
203    base::MessageLoop::current()->Run();
204
205    timeout_.Cancel();
206
207    ASSERT_FALSE(timed_out_) << "Test timed out";
208  }
209
210  void ScheduleTasks() {
211    RasterTaskQueue queue;
212
213    for (RasterTaskVector::const_iterator it = tasks_.begin();
214         it != tasks_.end();
215         ++it) {
216      TaskSetCollection task_sets;
217      task_sets[ALL] = true;
218      queue.items.push_back(RasterTaskQueue::Item(it->get(), task_sets));
219    }
220
221    raster_worker_pool_->AsRasterizer()->ScheduleTasks(&queue);
222  }
223
224  void AppendTask(unsigned id, const gfx::Size& size) {
225    scoped_ptr<ScopedResource> resource(
226        ScopedResource::Create(resource_provider_.get()));
227    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
228    const Resource* const_resource = resource.get();
229
230    ImageDecodeTask::Vector empty;
231    tasks_.push_back(new TestRasterTaskImpl(
232        const_resource,
233        base::Bind(&RasterWorkerPoolTest::OnTaskCompleted,
234                   base::Unretained(this),
235                   base::Passed(&resource),
236                   id),
237        &empty));
238  }
239
240  void AppendTask(unsigned id) { AppendTask(id, gfx::Size(1, 1)); }
241
242  void AppendBlockingTask(unsigned id, base::Lock* lock) {
243    const gfx::Size size(1, 1);
244
245    scoped_ptr<ScopedResource> resource(
246        ScopedResource::Create(resource_provider_.get()));
247    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
248    const Resource* const_resource = resource.get();
249
250    ImageDecodeTask::Vector empty;
251    tasks_.push_back(new BlockingTestRasterTaskImpl(
252        const_resource,
253        base::Bind(&RasterWorkerPoolTest::OnTaskCompleted,
254                   base::Unretained(this),
255                   base::Passed(&resource),
256                   id),
257        lock,
258        &empty));
259  }
260
261  const std::vector<RasterTaskResult>& completed_tasks() const {
262    return completed_tasks_;
263  }
264
265 private:
266  void Create3dOutputSurfaceAndResourceProvider() {
267    output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass();
268    CHECK(output_surface_->BindToClient(&output_surface_client_));
269    TestWebGraphicsContext3D* context3d = context_provider_->TestContext3d();
270    context3d->set_support_sync_query(true);
271    resource_provider_ =
272        ResourceProvider::Create(
273            output_surface_.get(), NULL, NULL, 0, false, 1, false).Pass();
274  }
275
276  void CreateSoftwareOutputSurfaceAndResourceProvider() {
277    output_surface_ = FakeOutputSurface::CreateSoftware(
278        make_scoped_ptr(new SoftwareOutputDevice));
279    CHECK(output_surface_->BindToClient(&output_surface_client_));
280    resource_provider_ = ResourceProvider::Create(output_surface_.get(),
281                                                  &shared_bitmap_manager_,
282                                                  NULL,
283                                                  0,
284                                                  false,
285                                                  1,
286                                                  false).Pass();
287  }
288
289  void OnTaskCompleted(scoped_ptr<ScopedResource> resource,
290                       unsigned id,
291                       const PicturePileImpl::Analysis& analysis,
292                       bool was_canceled) {
293    RasterTaskResult result;
294    result.id = id;
295    result.canceled = was_canceled;
296    completed_tasks_.push_back(result);
297  }
298
299  void OnTimeout() {
300    timed_out_ = true;
301    base::MessageLoop::current()->Quit();
302  }
303
304 protected:
305  scoped_refptr<TestContextProvider> context_provider_;
306  FakeOutputSurfaceClient output_surface_client_;
307  scoped_ptr<FakeOutputSurface> output_surface_;
308  scoped_ptr<ResourceProvider> resource_provider_;
309  scoped_ptr<ResourcePool> staging_resource_pool_;
310  scoped_ptr<RasterWorkerPool> raster_worker_pool_;
311  TestSharedBitmapManager shared_bitmap_manager_;
312  base::CancelableClosure timeout_;
313  int timeout_seconds_;
314  bool timed_out_;
315  RasterTaskVector tasks_;
316  std::vector<RasterTaskResult> completed_tasks_;
317};
318
319TEST_P(RasterWorkerPoolTest, Basic) {
320  AppendTask(0u);
321  AppendTask(1u);
322  ScheduleTasks();
323
324  RunMessageLoopUntilAllTasksHaveCompleted();
325
326  ASSERT_EQ(2u, completed_tasks().size());
327  EXPECT_FALSE(completed_tasks()[0].canceled);
328  EXPECT_FALSE(completed_tasks()[1].canceled);
329}
330
331TEST_P(RasterWorkerPoolTest, FailedMapResource) {
332  if (GetParam() == RASTER_WORKER_POOL_TYPE_BITMAP)
333    return;
334
335  TestWebGraphicsContext3D* context3d = context_provider_->TestContext3d();
336  context3d->set_times_map_image_chromium_succeeds(0);
337  context3d->set_times_map_buffer_chromium_succeeds(0);
338  AppendTask(0u);
339  ScheduleTasks();
340
341  RunMessageLoopUntilAllTasksHaveCompleted();
342
343  ASSERT_EQ(1u, completed_tasks().size());
344  EXPECT_FALSE(completed_tasks()[0].canceled);
345}
346
347// This test checks that replacing a pending raster task with another does
348// not prevent the DidFinishRunningTasks notification from being sent.
349TEST_P(RasterWorkerPoolTest, FalseThrottling) {
350  base::Lock lock;
351
352  // Schedule a task that is prevented from completing with a lock.
353  lock.Acquire();
354  AppendBlockingTask(0u, &lock);
355  ScheduleTasks();
356
357  // Schedule another task to replace the still-pending task. Because the old
358  // task is not a throttled task in the new task set, it should not prevent
359  // DidFinishRunningTasks from getting signaled.
360  RasterTaskVector tasks;
361  tasks.swap(tasks_);
362  AppendTask(1u);
363  ScheduleTasks();
364
365  // Unblock the first task to allow the second task to complete.
366  lock.Release();
367
368  RunMessageLoopUntilAllTasksHaveCompleted();
369}
370
371TEST_P(RasterWorkerPoolTest, LargeResources) {
372  gfx::Size size(kLargeResourceDimension, kLargeResourceDimension);
373
374  {
375    // Verify a resource of this size is larger than the transfer buffer.
376    scoped_ptr<ScopedResource> resource(
377        ScopedResource::Create(resource_provider_.get()));
378    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
379    EXPECT_GE(resource->bytes(), kMaxTransferBufferUsageBytes);
380  }
381
382  AppendTask(0u, size);
383  AppendTask(1u, size);
384  AppendTask(2u, size);
385  ScheduleTasks();
386
387  // This will time out if a resource that is larger than the throttle limit
388  // never gets scheduled.
389  RunMessageLoopUntilAllTasksHaveCompleted();
390}
391
392INSTANTIATE_TEST_CASE_P(RasterWorkerPoolTests,
393                        RasterWorkerPoolTest,
394                        ::testing::Values(RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
395                                          RASTER_WORKER_POOL_TYPE_ZERO_COPY,
396                                          RASTER_WORKER_POOL_TYPE_ONE_COPY,
397                                          RASTER_WORKER_POOL_TYPE_GPU,
398                                          RASTER_WORKER_POOL_TYPE_BITMAP));
399
400}  // namespace
401}  // namespace cc
402