// 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/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/strings/stringprintf.h" #include "base/threading/simple_thread.h" #include "cc/base/scoped_ptr_deque.h" #include "cc/resources/raster_source.h" #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" namespace cc { namespace { 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()); } } ~RasterTaskGraphRunner() override { NOTREACHED(); } private: // Overridden from base::DelegateSimpleThread::Delegate: void Run() override { TaskGraphRunner::Run(); } ScopedPtrDeque workers_; }; 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: void RunOnWorkerThread() override { TRACE_EVENT0("cc", "RasterFinishedTaskImpl::RunOnWorkerThread"); RasterFinished(); } // Overridden from RasterizerTask: void ScheduleOnOriginThread(RasterizerTaskClient* client) override {} void CompleteOnOriginThread(RasterizerTaskClient* client) override {} void RunReplyOnOriginThread() override {} protected: ~RasterFinishedTaskImpl() override {} 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); }; } // namespace // 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 = 1u; unsigned RasterWorkerPool::kRasterTaskPriorityBase = 2u; 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 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 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); } static bool IsSupportedPlaybackToMemoryFormat(ResourceFormat format) { switch (format) { case RGBA_4444: case RGBA_8888: case BGRA_8888: return true; case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: return false; } NOTREACHED(); return false; } // static void RasterWorkerPool::PlaybackToMemory(void* memory, ResourceFormat format, const gfx::Size& size, int stride, const RasterSource* raster_source, const gfx::Rect& rect, float scale) { DCHECK(IsSupportedPlaybackToMemoryFormat(format)) << format; // Uses kPremul_SkAlphaType since the result is not known to be opaque. SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType); SkColorType buffer_color_type = ResourceFormatToSkColorType(format); bool needs_copy = buffer_color_type != info.colorType(); // TODO(danakj): Make a SkSurfaceProps with an SkPixelGeometry to enable or // disable LCD text. // TODO(danakj): Disable LCD text on Mac during layout tests: // https://cs.chromium.org#chromium/src/third_party/WebKit/Source/platform/fonts/mac/FontPlatformDataMac.mm&l=55 // TODO(danakj): On Windows when LCD text is disabled, ask skia to draw LCD // text offscreen and downsample it to AA text. // https://cs.chromium.org#chromium/src/third_party/WebKit/Source/platform/fonts/win/FontPlatformDataWin.cpp&l=86 SkSurfaceProps* surface_props = nullptr; if (!stride) stride = info.minRowBytes(); if (!needs_copy) { skia::RefPtr surface = skia::AdoptRef( SkSurface::NewRasterDirect(info, memory, stride, surface_props)); skia::RefPtr canvas = skia::SharePtr(surface->getCanvas()); raster_source->PlaybackToCanvas(canvas.get(), rect, scale); return; } skia::RefPtr surface = skia::AdoptRef(SkSurface::NewRaster(info, surface_props)); skia::RefPtr canvas = skia::SharePtr(surface->getCanvas()); raster_source->PlaybackToCanvas(canvas.get(), rect, scale); SkImageInfo dst_info = info; dst_info.fColorType = buffer_color_type; // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the // bitmap data. There will be no need to call SkAlign4 once crbug.com/293728 // is fixed. const size_t dst_row_bytes = SkAlign4(dst_info.minRowBytes()); DCHECK_EQ(0u, dst_row_bytes % 4); bool success = canvas->readPixels(dst_info, memory, dst_row_bytes, 0, 0); DCHECK_EQ(true, success); } } // namespace cc