// 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/test/test_simple_task_runner.h" #include "base/time/time.h" #include "cc/debug/lap_timer.h" #include "cc/output/context_provider.h" #include "cc/resources/bitmap_raster_worker_pool.h" #include "cc/resources/gpu_raster_worker_pool.h" #include "cc/resources/one_copy_raster_worker_pool.h" #include "cc/resources/pixel_buffer_raster_worker_pool.h" #include "cc/resources/raster_buffer.h" #include "cc/resources/rasterizer.h" #include "cc/resources/resource_pool.h" #include "cc/resources/resource_provider.h" #include "cc/resources/scoped_resource.h" #include "cc/resources/zero_copy_raster_worker_pool.h" #include "cc/test/fake_output_surface.h" #include "cc/test/fake_output_surface_client.h" #include "cc/test/test_context_support.h" #include "cc/test/test_gpu_memory_buffer_manager.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: GLuint CreateImageCHROMIUM(ClientBuffer buffer, GLsizei width, GLsizei height, GLenum internalformat) override { return 1u; } void GenBuffers(GLsizei n, GLuint* buffers) override { for (GLsizei i = 0; i < n; ++i) buffers[i] = 1u; } void GenTextures(GLsizei n, GLuint* textures) override { for (GLsizei i = 0; i < n; ++i) textures[i] = 1u; } void GetIntegerv(GLenum pname, GLint* params) override { if (pname == GL_MAX_TEXTURE_SIZE) *params = INT_MAX; } void GenQueriesEXT(GLsizei n, GLuint* queries) override { for (GLsizei i = 0; i < n; ++i) queries[i] = 1u; } void GetQueryObjectuivEXT(GLuint query, GLenum pname, GLuint* params) override { if (pname == GL_QUERY_RESULT_AVAILABLE_EXT) *params = 1; } }; class PerfContextProvider : public ContextProvider { public: PerfContextProvider() : context_gl_(new PerfGLES2Interface) {} bool BindToCurrentThread() override { return true; } Capabilities ContextCapabilities() override { Capabilities capabilities; capabilities.gpu.image = true; capabilities.gpu.sync_query = true; return capabilities; } gpu::gles2::GLES2Interface* ContextGL() override { return context_gl_.get(); } gpu::ContextSupport* ContextSupport() override { return &support_; } class GrContext* GrContext() override { return NULL; } bool IsContextLost() override { return false; } void VerifyContexts() override {} void DeleteCachedResources() override {} bool DestroyedOnMainThread() override { return false; } void SetLostContextCallback(const LostContextCallback& cb) override {} void SetMemoryPolicyChangedCallback( const MemoryPolicyChangedCallback& cb) override {} private: ~PerfContextProvider() override {} scoped_ptr context_gl_; TestContextSupport support_; }; enum RasterWorkerPoolType { RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER, RASTER_WORKER_POOL_TYPE_ZERO_COPY, RASTER_WORKER_POOL_TYPE_ONE_COPY, RASTER_WORKER_POOL_TYPE_GPU, RASTER_WORKER_POOL_TYPE_BITMAP }; static const int kTimeLimitMillis = 2000; static const int kWarmupRuns = 5; static const int kTimeCheckInterval = 10; class PerfImageDecodeTaskImpl : public ImageDecodeTask { public: PerfImageDecodeTaskImpl() {} // Overridden from Task: void RunOnWorkerThread() override {} // Overridden from RasterizerTask: void ScheduleOnOriginThread(RasterizerTaskClient* client) override {} void CompleteOnOriginThread(RasterizerTaskClient* client) override {} void RunReplyOnOriginThread() override { Reset(); } void Reset() { did_run_ = false; did_complete_ = false; } protected: ~PerfImageDecodeTaskImpl() override {} 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: void RunOnWorkerThread() override {} // Overridden from RasterizerTask: void ScheduleOnOriginThread(RasterizerTaskClient* client) override { raster_buffer_ = client->AcquireBufferForRaster(resource()); } void CompleteOnOriginThread(RasterizerTaskClient* client) override { client->ReleaseBufferForRaster(raster_buffer_.Pass()); } void RunReplyOnOriginThread() override { Reset(); } void Reset() { did_run_ = false; did_complete_ = false; } protected: ~PerfRasterTaskImpl() override {} private: scoped_ptr resource_; scoped_ptr raster_buffer_; DISALLOW_COPY_AND_ASSIGN(PerfRasterTaskImpl); }; class RasterWorkerPoolPerfTestBase { public: typedef std::vector> RasterTaskVector; enum NamedTaskSet { REQUIRED_FOR_ACTIVATION = 0, ALL = 1 }; RasterWorkerPoolPerfTestBase() : context_provider_(make_scoped_refptr(new PerfContextProvider)), task_runner_(new base::TestSimpleTaskRunner), task_graph_runner_(new TaskGraphRunner), timer_(kWarmupRuns, base::TimeDelta::FromMilliseconds(kTimeLimitMillis), kTimeCheckInterval) {} 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::TextureHintImmutable, 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; TaskSetCollection task_set_collection; task_set_collection[ALL] = true; task_set_collection[REQUIRED_FOR_ACTIVATION] = required_for_activation; queue->items.push_back( RasterTaskQueue::Item(raster_tasks[i].get(), task_set_collection)); } } protected: scoped_refptr context_provider_; FakeOutputSurfaceClient output_surface_client_; scoped_ptr output_surface_; scoped_ptr resource_provider_; scoped_refptr task_runner_; scoped_ptr task_graph_runner_; LapTimer timer_; }; class RasterWorkerPoolPerfTest : public RasterWorkerPoolPerfTestBase, public testing::TestWithParam, public RasterizerClient { public: // Overridden from testing::Test: virtual void SetUp() override { switch (GetParam()) { case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER: Create3dOutputSurfaceAndResourceProvider(); raster_worker_pool_ = PixelBufferRasterWorkerPool::Create( task_runner_.get(), task_graph_runner_.get(), context_provider_.get(), resource_provider_.get(), std::numeric_limits::max()); break; case RASTER_WORKER_POOL_TYPE_ZERO_COPY: Create3dOutputSurfaceAndResourceProvider(); raster_worker_pool_ = ZeroCopyRasterWorkerPool::Create(task_runner_.get(), task_graph_runner_.get(), resource_provider_.get()); break; case RASTER_WORKER_POOL_TYPE_ONE_COPY: Create3dOutputSurfaceAndResourceProvider(); staging_resource_pool_ = ResourcePool::Create( resource_provider_.get(), GL_TEXTURE_2D, RGBA_8888); raster_worker_pool_ = OneCopyRasterWorkerPool::Create(task_runner_.get(), task_graph_runner_.get(), context_provider_.get(), resource_provider_.get(), staging_resource_pool_.get()); break; case RASTER_WORKER_POOL_TYPE_GPU: Create3dOutputSurfaceAndResourceProvider(); raster_worker_pool_ = GpuRasterWorkerPool::Create(task_runner_.get(), context_provider_.get(), resource_provider_.get(), false); break; case RASTER_WORKER_POOL_TYPE_BITMAP: CreateSoftwareOutputSurfaceAndResourceProvider(); raster_worker_pool_ = BitmapRasterWorkerPool::Create(task_runner_.get(), task_graph_runner_.get(), resource_provider_.get()); break; } DCHECK(raster_worker_pool_); raster_worker_pool_->AsRasterizer()->SetClient(this); } virtual void TearDown() override { raster_worker_pool_->AsRasterizer()->Shutdown(); raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks(); } // Overriden from RasterizerClient: void DidFinishRunningTasks(TaskSet task_set) override { raster_worker_pool_->AsRasterizer()->CheckForCompletedTasks(); } TaskSetCollection TasksThatShouldBeForcedToComplete() const override { return TaskSetCollection(); } void RunMessageLoopUntilAllTasksHaveCompleted() { task_graph_runner_->RunUntilIdle(); task_runner_->RunUntilIdle(); } 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: void Create3dOutputSurfaceAndResourceProvider() { output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass(); CHECK(output_surface_->BindToClient(&output_surface_client_)); resource_provider_ = ResourceProvider::Create(output_surface_.get(), NULL, &gpu_memory_buffer_manager_, NULL, 0, false, 1).Pass(); } void CreateSoftwareOutputSurfaceAndResourceProvider() { output_surface_ = FakeOutputSurface::CreateSoftware( make_scoped_ptr(new SoftwareOutputDevice)); CHECK(output_surface_->BindToClient(&output_surface_client_)); resource_provider_ = ResourceProvider::Create(output_surface_.get(), &shared_bitmap_manager_, NULL, NULL, 0, false, 1).Pass(); } 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_ZERO_COPY: return std::string("_zero_copy_raster_worker_pool"); case RASTER_WORKER_POOL_TYPE_ONE_COPY: return std::string("_one_copy_raster_worker_pool"); case RASTER_WORKER_POOL_TYPE_GPU: return std::string("_gpu_raster_worker_pool"); case RASTER_WORKER_POOL_TYPE_BITMAP: return std::string("_bitmap_raster_worker_pool"); } NOTREACHED(); return std::string(); } scoped_ptr staging_resource_pool_; scoped_ptr raster_worker_pool_; TestGpuMemoryBufferManager gpu_memory_buffer_manager_; TestSharedBitmapManager shared_bitmap_manager_; }; 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_ZERO_COPY, RASTER_WORKER_POOL_TYPE_ONE_COPY, RASTER_WORKER_POOL_TYPE_GPU, RASTER_WORKER_POOL_TYPE_BITMAP)); class RasterWorkerPoolCommonPerfTest : public RasterWorkerPoolPerfTestBase, public testing::Test { public: // Overridden from testing::Test: virtual void SetUp() override { output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass(); CHECK(output_surface_->BindToClient(&output_surface_client_)); resource_provider_ = ResourceProvider::Create( output_surface_.get(), NULL, NULL, NULL, 0, false, 1).Pass(); } 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