// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cc/resources/raster_worker_pool.h" #include "base/time/time.h" #include "cc/output/context_provider.h" #include "cc/resources/direct_raster_worker_pool.h" #include "cc/resources/image_raster_worker_pool.h" #include "cc/resources/pixel_buffer_raster_worker_pool.h" #include "cc/resources/rasterizer.h" #include "cc/resources/resource_provider.h" #include "cc/resources/scoped_resource.h" #include "cc/test/fake_output_surface.h" #include "cc/test/fake_output_surface_client.h" #include "cc/test/lap_timer.h" #include "cc/test/test_context_support.h" #include "cc/test/test_shared_bitmap_manager.h" #include "cc/test/test_web_graphics_context_3d.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_test.h" #include "third_party/khronos/GLES2/gl2.h" namespace cc { namespace { class PerfGLES2Interface : public gpu::gles2::GLES2InterfaceStub { // Overridden from gpu::gles2::GLES2Interface: virtual GLuint CreateImageCHROMIUM(GLsizei width, GLsizei height, GLenum internalformat) OVERRIDE { return 1u; } virtual void GenBuffers(GLsizei n, GLuint* buffers) OVERRIDE { for (GLsizei i = 0; i < n; ++i) buffers[i] = 1u; } virtual void GenTextures(GLsizei n, GLuint* textures) OVERRIDE { for (GLsizei i = 0; i < n; ++i) textures[i] = 1u; } virtual void GetIntegerv(GLenum pname, GLint* params) OVERRIDE { if (pname == GL_MAX_TEXTURE_SIZE) *params = INT_MAX; } }; class PerfContextProvider : public ContextProvider { public: PerfContextProvider() : context_gl_(new PerfGLES2Interface) {} virtual bool BindToCurrentThread() OVERRIDE { return true; } virtual Capabilities ContextCapabilities() OVERRIDE { return Capabilities(); } virtual gpu::gles2::GLES2Interface* ContextGL() OVERRIDE { return context_gl_.get(); } virtual gpu::ContextSupport* ContextSupport() OVERRIDE { return &support_; } virtual class GrContext* GrContext() OVERRIDE { return NULL; } virtual bool IsContextLost() OVERRIDE { return false; } virtual void VerifyContexts() OVERRIDE {} virtual void DeleteCachedResources() OVERRIDE {} virtual bool DestroyedOnMainThread() OVERRIDE { return false; } virtual void SetLostContextCallback(const LostContextCallback& cb) OVERRIDE {} virtual void SetMemoryPolicyChangedCallback( const MemoryPolicyChangedCallback& cb) OVERRIDE {} private: virtual ~PerfContextProvider() {} scoped_ptr context_gl_; TestContextSupport support_; }; enum RasterWorkerPoolType { RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER, RASTER_WORKER_POOL_TYPE_IMAGE, RASTER_WORKER_POOL_TYPE_DIRECT }; static const int kTimeLimitMillis = 2000; static const int kWarmupRuns = 5; static const int kTimeCheckInterval = 10; class PerfImageDecodeTaskImpl : public ImageDecodeTask { public: PerfImageDecodeTaskImpl() {} // Overridden from Task: virtual void RunOnWorkerThread() OVERRIDE {} // Overridden from RasterizerTask: virtual void ScheduleOnOriginThread(RasterizerTaskClient* client) OVERRIDE {} virtual void CompleteOnOriginThread(RasterizerTaskClient* client) OVERRIDE {} virtual void RunReplyOnOriginThread() OVERRIDE { Reset(); } void Reset() { did_run_ = false; did_complete_ = false; } protected: virtual ~PerfImageDecodeTaskImpl() {} private: DISALLOW_COPY_AND_ASSIGN(PerfImageDecodeTaskImpl); }; class PerfRasterTaskImpl : public RasterTask { public: PerfRasterTaskImpl(scoped_ptr resource, ImageDecodeTask::Vector* dependencies) : RasterTask(resource.get(), dependencies), resource_(resource.Pass()) {} // Overridden from Task: virtual void RunOnWorkerThread() OVERRIDE {} // Overridden from RasterizerTask: virtual void ScheduleOnOriginThread(RasterizerTaskClient* client) OVERRIDE { client->AcquireCanvasForRaster(this); } virtual void CompleteOnOriginThread(RasterizerTaskClient* client) OVERRIDE { client->ReleaseCanvasForRaster(this); } virtual void RunReplyOnOriginThread() OVERRIDE { Reset(); } void Reset() { did_run_ = false; did_complete_ = false; } protected: virtual ~PerfRasterTaskImpl() {} private: scoped_ptr resource_; DISALLOW_COPY_AND_ASSIGN(PerfRasterTaskImpl); }; class RasterWorkerPoolPerfTestBase { public: typedef std::vector > RasterTaskVector; RasterWorkerPoolPerfTestBase() : context_provider_(make_scoped_refptr(new PerfContextProvider)), task_graph_runner_(new TaskGraphRunner), timer_(kWarmupRuns, base::TimeDelta::FromMilliseconds(kTimeLimitMillis), kTimeCheckInterval) { output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass(); CHECK(output_surface_->BindToClient(&output_surface_client_)); shared_bitmap_manager_.reset(new TestSharedBitmapManager()); resource_provider_ = ResourceProvider::Create( output_surface_.get(), shared_bitmap_manager_.get(), 0, false, 1) .Pass(); } void CreateImageDecodeTasks(unsigned num_image_decode_tasks, ImageDecodeTask::Vector* image_decode_tasks) { for (unsigned i = 0; i < num_image_decode_tasks; ++i) image_decode_tasks->push_back(new PerfImageDecodeTaskImpl); } void CreateRasterTasks(unsigned num_raster_tasks, const ImageDecodeTask::Vector& image_decode_tasks, RasterTaskVector* raster_tasks) { const gfx::Size size(1, 1); for (unsigned i = 0; i < num_raster_tasks; ++i) { scoped_ptr resource( ScopedResource::Create(resource_provider_.get())); resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888); ImageDecodeTask::Vector dependencies = image_decode_tasks; raster_tasks->push_back( new PerfRasterTaskImpl(resource.Pass(), &dependencies)); } } void BuildRasterTaskQueue(RasterTaskQueue* queue, const RasterTaskVector& raster_tasks) { for (size_t i = 0u; i < raster_tasks.size(); ++i) { bool required_for_activation = (i % 2) == 0; queue->items.push_back(RasterTaskQueue::Item(raster_tasks[i].get(), required_for_activation)); queue->required_for_activation_count += required_for_activation; } } protected: scoped_refptr context_provider_; FakeOutputSurfaceClient output_surface_client_; scoped_ptr output_surface_; scoped_ptr shared_bitmap_manager_; scoped_ptr resource_provider_; scoped_ptr task_graph_runner_; LapTimer timer_; }; class RasterWorkerPoolPerfTest : public RasterWorkerPoolPerfTestBase, public testing::TestWithParam, public RasterizerClient { public: RasterWorkerPoolPerfTest() { switch (GetParam()) { case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER: raster_worker_pool_ = PixelBufferRasterWorkerPool::Create( base::MessageLoopProxy::current().get(), task_graph_runner_.get(), resource_provider_.get(), std::numeric_limits::max()); break; case RASTER_WORKER_POOL_TYPE_IMAGE: raster_worker_pool_ = ImageRasterWorkerPool::Create( base::MessageLoopProxy::current().get(), task_graph_runner_.get(), resource_provider_.get()); break; case RASTER_WORKER_POOL_TYPE_DIRECT: raster_worker_pool_ = DirectRasterWorkerPool::Create( base::MessageLoopProxy::current().get(), resource_provider_.get(), context_provider_.get()); break; } DCHECK(raster_worker_pool_); raster_worker_pool_->AsRasterizer()->SetClient(this); } // Overridden from testing::Test: virtual void TearDown() OVERRIDE { raster_worker_pool_->AsRasterizer()->Shutdown(); raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks(); } // Overriden from RasterizerClient: virtual bool ShouldForceTasksRequiredForActivationToComplete() const OVERRIDE { return false; } virtual void DidFinishRunningTasks() OVERRIDE { raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks(); base::MessageLoop::current()->Quit(); } virtual void DidFinishRunningTasksRequiredForActivation() OVERRIDE {} void RunMessageLoopUntilAllTasksHaveCompleted() { task_graph_runner_->RunUntilIdle(); base::MessageLoop::current()->Run(); } void RunScheduleTasksTest(const std::string& test_name, unsigned num_raster_tasks, unsigned num_image_decode_tasks) { ImageDecodeTask::Vector image_decode_tasks; RasterTaskVector raster_tasks; CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks); CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks); // Avoid unnecessary heap allocations by reusing the same queue. RasterTaskQueue queue; timer_.Reset(); do { queue.Reset(); BuildRasterTaskQueue(&queue, raster_tasks); raster_worker_pool_->AsRasterizer()->ScheduleTasks(&queue); raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks(); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); RasterTaskQueue empty; raster_worker_pool_->AsRasterizer()->ScheduleTasks(&empty); RunMessageLoopUntilAllTasksHaveCompleted(); perf_test::PrintResult("schedule_tasks", TestModifierString(), test_name, timer_.LapsPerSecond(), "runs/s", true); } void RunScheduleAlternateTasksTest(const std::string& test_name, unsigned num_raster_tasks, unsigned num_image_decode_tasks) { const size_t kNumVersions = 2; ImageDecodeTask::Vector image_decode_tasks[kNumVersions]; RasterTaskVector raster_tasks[kNumVersions]; for (size_t i = 0; i < kNumVersions; ++i) { CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks[i]); CreateRasterTasks( num_raster_tasks, image_decode_tasks[i], &raster_tasks[i]); } // Avoid unnecessary heap allocations by reusing the same queue. RasterTaskQueue queue; size_t count = 0; timer_.Reset(); do { queue.Reset(); BuildRasterTaskQueue(&queue, raster_tasks[count % kNumVersions]); raster_worker_pool_->AsRasterizer()->ScheduleTasks(&queue); raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks(); ++count; timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); RasterTaskQueue empty; raster_worker_pool_->AsRasterizer()->ScheduleTasks(&empty); RunMessageLoopUntilAllTasksHaveCompleted(); perf_test::PrintResult("schedule_alternate_tasks", TestModifierString(), test_name, timer_.LapsPerSecond(), "runs/s", true); } void RunScheduleAndExecuteTasksTest(const std::string& test_name, unsigned num_raster_tasks, unsigned num_image_decode_tasks) { ImageDecodeTask::Vector image_decode_tasks; RasterTaskVector raster_tasks; CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks); CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks); // Avoid unnecessary heap allocations by reusing the same queue. RasterTaskQueue queue; timer_.Reset(); do { queue.Reset(); BuildRasterTaskQueue(&queue, raster_tasks); raster_worker_pool_->AsRasterizer()->ScheduleTasks(&queue); RunMessageLoopUntilAllTasksHaveCompleted(); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); RasterTaskQueue empty; raster_worker_pool_->AsRasterizer()->ScheduleTasks(&empty); RunMessageLoopUntilAllTasksHaveCompleted(); perf_test::PrintResult("schedule_and_execute_tasks", TestModifierString(), test_name, timer_.LapsPerSecond(), "runs/s", true); } private: std::string TestModifierString() const { switch (GetParam()) { case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER: return std::string("_pixel_raster_worker_pool"); case RASTER_WORKER_POOL_TYPE_IMAGE: return std::string("_image_raster_worker_pool"); case RASTER_WORKER_POOL_TYPE_DIRECT: return std::string("_direct_raster_worker_pool"); } NOTREACHED(); return std::string(); } scoped_ptr raster_worker_pool_; }; TEST_P(RasterWorkerPoolPerfTest, ScheduleTasks) { RunScheduleTasksTest("1_0", 1, 0); RunScheduleTasksTest("32_0", 32, 0); RunScheduleTasksTest("1_1", 1, 1); RunScheduleTasksTest("32_1", 32, 1); RunScheduleTasksTest("1_4", 1, 4); RunScheduleTasksTest("32_4", 32, 4); } TEST_P(RasterWorkerPoolPerfTest, ScheduleAlternateTasks) { RunScheduleAlternateTasksTest("1_0", 1, 0); RunScheduleAlternateTasksTest("32_0", 32, 0); RunScheduleAlternateTasksTest("1_1", 1, 1); RunScheduleAlternateTasksTest("32_1", 32, 1); RunScheduleAlternateTasksTest("1_4", 1, 4); RunScheduleAlternateTasksTest("32_4", 32, 4); } TEST_P(RasterWorkerPoolPerfTest, ScheduleAndExecuteTasks) { RunScheduleAndExecuteTasksTest("1_0", 1, 0); RunScheduleAndExecuteTasksTest("32_0", 32, 0); RunScheduleAndExecuteTasksTest("1_1", 1, 1); RunScheduleAndExecuteTasksTest("32_1", 32, 1); RunScheduleAndExecuteTasksTest("1_4", 1, 4); RunScheduleAndExecuteTasksTest("32_4", 32, 4); } INSTANTIATE_TEST_CASE_P(RasterWorkerPoolPerfTests, RasterWorkerPoolPerfTest, ::testing::Values(RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER, RASTER_WORKER_POOL_TYPE_IMAGE, RASTER_WORKER_POOL_TYPE_DIRECT)); class RasterWorkerPoolCommonPerfTest : public RasterWorkerPoolPerfTestBase, public testing::Test { public: void RunBuildRasterTaskQueueTest(const std::string& test_name, unsigned num_raster_tasks, unsigned num_image_decode_tasks) { ImageDecodeTask::Vector image_decode_tasks; RasterTaskVector raster_tasks; CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks); CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks); // Avoid unnecessary heap allocations by reusing the same queue. RasterTaskQueue queue; timer_.Reset(); do { queue.Reset(); BuildRasterTaskQueue(&queue, raster_tasks); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); perf_test::PrintResult("build_raster_task_queue", "", test_name, timer_.LapsPerSecond(), "runs/s", true); } }; TEST_F(RasterWorkerPoolCommonPerfTest, BuildRasterTaskQueue) { RunBuildRasterTaskQueueTest("1_0", 1, 0); RunBuildRasterTaskQueueTest("32_0", 32, 0); RunBuildRasterTaskQueueTest("1_1", 1, 1); RunBuildRasterTaskQueueTest("32_1", 32, 1); RunBuildRasterTaskQueueTest("1_4", 1, 4); RunBuildRasterTaskQueueTest("32_4", 32, 4); } } // namespace } // namespace cc