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