// 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 #include "base/atomic_sequence_num.h" #include "base/debug/trace_event_synthetic_delay.h" #include "base/lazy_instance.h" #include "base/strings/stringprintf.h" #include "base/threading/simple_thread.h" #include "base/threading/thread_local.h" #include "cc/base/scoped_ptr_deque.h" namespace cc { namespace { // Synthetic delay for raster tasks that are required for activation. Global to // avoid static initializer on critical path. struct RasterRequiredForActivationSyntheticDelayInitializer { RasterRequiredForActivationSyntheticDelayInitializer() : delay(base::debug::TraceEventSyntheticDelay::Lookup( "cc.RasterRequiredForActivation")) {} base::debug::TraceEventSyntheticDelay* delay; }; static base::LazyInstance g_raster_required_for_activation_delay = LAZY_INSTANCE_INITIALIZER; class RasterTaskGraphRunner : public TaskGraphRunner, public base::DelegateSimpleThread::Delegate { public: RasterTaskGraphRunner() { size_t num_threads = RasterWorkerPool::GetNumRasterThreads(); while (workers_.size() < num_threads) { scoped_ptr worker = make_scoped_ptr(new base::DelegateSimpleThread( this, base::StringPrintf("CompositorRasterWorker%u", static_cast(workers_.size() + 1)) .c_str())); worker->Start(); #if defined(OS_ANDROID) || defined(OS_LINUX) worker->SetThreadPriority(base::kThreadPriority_Background); #endif workers_.push_back(worker.Pass()); } } virtual ~RasterTaskGraphRunner() { NOTREACHED(); } size_t GetPictureCloneIndexForCurrentThread() { // Use index 0 if called on non-raster thread. ThreadLocalState* thread_local_state = current_tls_.Get(); return thread_local_state ? current_tls_.Get()->picture_clone_index : 0; } private: struct ThreadLocalState { explicit ThreadLocalState(size_t picture_clone_index) : picture_clone_index(picture_clone_index) {} size_t picture_clone_index; }; // Overridden from base::DelegateSimpleThread::Delegate: virtual void Run() OVERRIDE { // Use picture clone index 0..num_threads. int picture_clone_index = picture_clone_index_sequence_.GetNext(); DCHECK_LE(0, picture_clone_index); DCHECK_GT(RasterWorkerPool::GetNumRasterThreads(), picture_clone_index); current_tls_.Set(new ThreadLocalState(picture_clone_index)); TaskGraphRunner::Run(); } ScopedPtrDeque workers_; base::AtomicSequenceNumber picture_clone_index_sequence_; base::ThreadLocalPointer current_tls_; }; base::LazyInstance::Leaky g_task_graph_runner = LAZY_INSTANCE_INITIALIZER; const int kDefaultNumRasterThreads = 1; int g_num_raster_threads = 0; class RasterFinishedTaskImpl : public RasterizerTask { public: explicit RasterFinishedTaskImpl( base::SequencedTaskRunner* task_runner, const base::Closure& on_raster_finished_callback) : task_runner_(task_runner), on_raster_finished_callback_(on_raster_finished_callback) {} // Overridden from Task: virtual void RunOnWorkerThread() OVERRIDE { TRACE_EVENT0("cc", "RasterFinishedTaskImpl::RunOnWorkerThread"); RasterFinished(); } // Overridden from RasterizerTask: virtual void ScheduleOnOriginThread(RasterizerTaskClient* client) OVERRIDE {} virtual void CompleteOnOriginThread(RasterizerTaskClient* client) OVERRIDE {} virtual void RunReplyOnOriginThread() OVERRIDE {} protected: virtual ~RasterFinishedTaskImpl() {} void RasterFinished() { task_runner_->PostTask(FROM_HERE, on_raster_finished_callback_); } private: scoped_refptr task_runner_; const base::Closure on_raster_finished_callback_; DISALLOW_COPY_AND_ASSIGN(RasterFinishedTaskImpl); }; class RasterRequiredForActivationFinishedTaskImpl : public RasterFinishedTaskImpl { public: RasterRequiredForActivationFinishedTaskImpl( base::SequencedTaskRunner* task_runner, const base::Closure& on_raster_finished_callback, size_t tasks_required_for_activation_count) : RasterFinishedTaskImpl(task_runner, on_raster_finished_callback), tasks_required_for_activation_count_( tasks_required_for_activation_count) { if (tasks_required_for_activation_count_) { g_raster_required_for_activation_delay.Get().delay->BeginParallel( &activation_delay_end_time_); } } // Overridden from Task: virtual void RunOnWorkerThread() OVERRIDE { TRACE_EVENT0( "cc", "RasterRequiredForActivationFinishedTaskImpl::RunOnWorkerThread"); if (tasks_required_for_activation_count_) { g_raster_required_for_activation_delay.Get().delay->EndParallel( activation_delay_end_time_); } RasterFinished(); } private: virtual ~RasterRequiredForActivationFinishedTaskImpl() {} base::TimeTicks activation_delay_end_time_; const size_t tasks_required_for_activation_count_; DISALLOW_COPY_AND_ASSIGN(RasterRequiredForActivationFinishedTaskImpl); }; } // namespace // This allows an external rasterize on-demand system to run raster tasks // with highest priority using the same task graph runner instance. unsigned RasterWorkerPool::kOnDemandRasterTaskPriority = 0u; // This allows a micro benchmark system to run tasks with highest priority, // since it should finish as quickly as possible. unsigned RasterWorkerPool::kBenchmarkRasterTaskPriority = 0u; // Task priorities that make sure raster finished tasks run before any // remaining raster tasks. unsigned RasterWorkerPool::kRasterFinishedTaskPriority = 2u; unsigned RasterWorkerPool::kRasterRequiredForActivationFinishedTaskPriority = 1u; unsigned RasterWorkerPool::kRasterTaskPriorityBase = 3u; RasterWorkerPool::RasterWorkerPool() {} RasterWorkerPool::~RasterWorkerPool() {} // static void RasterWorkerPool::SetNumRasterThreads(int num_threads) { DCHECK_LT(0, num_threads); DCHECK_EQ(0, g_num_raster_threads); g_num_raster_threads = num_threads; } // static int RasterWorkerPool::GetNumRasterThreads() { if (!g_num_raster_threads) g_num_raster_threads = kDefaultNumRasterThreads; return g_num_raster_threads; } // static TaskGraphRunner* RasterWorkerPool::GetTaskGraphRunner() { return g_task_graph_runner.Pointer(); } // static size_t RasterWorkerPool::GetPictureCloneIndexForCurrentThread() { return g_task_graph_runner.Pointer()->GetPictureCloneIndexForCurrentThread(); } // static scoped_refptr RasterWorkerPool::CreateRasterFinishedTask( base::SequencedTaskRunner* task_runner, const base::Closure& on_raster_finished_callback) { return make_scoped_refptr( new RasterFinishedTaskImpl(task_runner, on_raster_finished_callback)); } // static scoped_refptr RasterWorkerPool::CreateRasterRequiredForActivationFinishedTask( size_t tasks_required_for_activation_count, base::SequencedTaskRunner* task_runner, const base::Closure& on_raster_finished_callback) { return make_scoped_refptr(new RasterRequiredForActivationFinishedTaskImpl( task_runner, on_raster_finished_callback, tasks_required_for_activation_count)); } // static void RasterWorkerPool::ScheduleTasksOnOriginThread(RasterizerTaskClient* client, TaskGraph* graph) { TRACE_EVENT0("cc", "Rasterizer::ScheduleTasksOnOriginThread"); for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin(); it != graph->nodes.end(); ++it) { TaskGraph::Node& node = *it; RasterizerTask* task = static_cast(node.task); if (!task->HasBeenScheduled()) { task->WillSchedule(); task->ScheduleOnOriginThread(client); task->DidSchedule(); } } } // static void RasterWorkerPool::InsertNodeForTask(TaskGraph* graph, RasterizerTask* task, unsigned priority, size_t dependencies) { DCHECK(std::find_if(graph->nodes.begin(), graph->nodes.end(), TaskGraph::Node::TaskComparator(task)) == graph->nodes.end()); graph->nodes.push_back(TaskGraph::Node(task, priority, dependencies)); } // static void RasterWorkerPool::InsertNodesForRasterTask( TaskGraph* graph, RasterTask* raster_task, const ImageDecodeTask::Vector& decode_tasks, unsigned priority) { size_t dependencies = 0u; // Insert image decode tasks. for (ImageDecodeTask::Vector::const_iterator it = decode_tasks.begin(); it != decode_tasks.end(); ++it) { ImageDecodeTask* decode_task = it->get(); // Skip if already decoded. if (decode_task->HasCompleted()) continue; dependencies++; // Add decode task if it doesn't already exists in graph. TaskGraph::Node::Vector::iterator decode_it = std::find_if(graph->nodes.begin(), graph->nodes.end(), TaskGraph::Node::TaskComparator(decode_task)); if (decode_it == graph->nodes.end()) InsertNodeForTask(graph, decode_task, priority, 0u); graph->edges.push_back(TaskGraph::Edge(decode_task, raster_task)); } InsertNodeForTask(graph, raster_task, priority, dependencies); } } // namespace cc