blob: aced03fcec6c8eb3c5e413d5213f8b635e3b756a [file] [log] [blame]
// 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/json/json_writer.h"
#include "base/metrics/histogram.h"
#include "base/values.h"
#include "cc/debug/benchmark_instrumentation.h"
#include "cc/debug/devtools_instrumentation.h"
#include "cc/debug/traced_value.h"
#include "cc/resources/picture_pile_impl.h"
#include "skia/ext/lazy_pixel_ref.h"
#include "skia/ext/paint_simplifier.h"
namespace cc {
namespace {
// Flag to indicate whether we should try and detect that
// a tile is of solid color.
const bool kUseColorEstimator = true;
class DisableLCDTextFilter : public SkDrawFilter {
public:
// SkDrawFilter interface.
virtual bool filter(SkPaint* paint, SkDrawFilter::Type type) OVERRIDE {
if (type != SkDrawFilter::kText_Type)
return true;
paint->setLCDRenderText(false);
return true;
}
};
class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
public:
RasterWorkerPoolTaskImpl(const Resource* resource,
PicturePileImpl* picture_pile,
gfx::Rect content_rect,
float contents_scale,
RasterMode raster_mode,
bool is_tile_in_pending_tree_now_bin,
TileResolution tile_resolution,
int layer_id,
const void* tile_id,
int source_frame_number,
RenderingStatsInstrumentation* rendering_stats,
const RasterWorkerPool::RasterTask::Reply& reply,
TaskVector* dependencies)
: internal::RasterWorkerPoolTask(resource, dependencies),
picture_pile_(picture_pile),
content_rect_(content_rect),
contents_scale_(contents_scale),
raster_mode_(raster_mode),
is_tile_in_pending_tree_now_bin_(is_tile_in_pending_tree_now_bin),
tile_resolution_(tile_resolution),
layer_id_(layer_id),
tile_id_(tile_id),
source_frame_number_(source_frame_number),
rendering_stats_(rendering_stats),
reply_(reply) {}
void RunAnalysisOnThread(unsigned thread_index) {
TRACE_EVENT1("cc",
"RasterWorkerPoolTaskImpl::RunAnalysisOnThread",
"data",
TracedValue::FromValue(DataAsValue().release()));
DCHECK(picture_pile_.get());
DCHECK(rendering_stats_);
PicturePileImpl* picture_clone =
picture_pile_->GetCloneForDrawingOnThread(thread_index);
DCHECK(picture_clone);
base::TimeTicks start_time = rendering_stats_->StartRecording();
picture_clone->AnalyzeInRect(content_rect_, contents_scale_, &analysis_);
base::TimeDelta duration = rendering_stats_->EndRecording(start_time);
// Record the solid color prediction.
UMA_HISTOGRAM_BOOLEAN("Renderer4.SolidColorTilesAnalyzed",
analysis_.is_solid_color);
rendering_stats_->AddAnalysisResult(duration, analysis_.is_solid_color);
// Clear the flag if we're not using the estimator.
analysis_.is_solid_color &= kUseColorEstimator;
}
bool RunRasterOnThread(SkDevice* device, unsigned thread_index) {
TRACE_EVENT2(
benchmark_instrumentation::kCategory,
benchmark_instrumentation::kRunRasterOnThread,
benchmark_instrumentation::kData,
TracedValue::FromValue(DataAsValue().release()),
"raster_mode",
TracedValue::FromValue(RasterModeAsValue(raster_mode_).release()));
devtools_instrumentation::ScopedLayerTask raster_task(
devtools_instrumentation::kRasterTask, layer_id_);
DCHECK(picture_pile_.get());
DCHECK(device);
if (analysis_.is_solid_color)
return false;
PicturePileImpl* picture_clone =
picture_pile_->GetCloneForDrawingOnThread(thread_index);
SkCanvas canvas(device);
skia::RefPtr<SkDrawFilter> draw_filter;
switch (raster_mode_) {
case LOW_QUALITY_RASTER_MODE:
draw_filter = skia::AdoptRef(new skia::PaintSimplifier);
break;
case HIGH_QUALITY_NO_LCD_RASTER_MODE:
draw_filter = skia::AdoptRef(new DisableLCDTextFilter);
break;
case HIGH_QUALITY_RASTER_MODE:
break;
case NUM_RASTER_MODES:
default:
NOTREACHED();
}
canvas.setDrawFilter(draw_filter.get());
if (rendering_stats_->record_rendering_stats()) {
PicturePileImpl::RasterStats raster_stats;
picture_clone->RasterToBitmap(
&canvas, content_rect_, contents_scale_, &raster_stats);
rendering_stats_->AddRaster(
raster_stats.total_rasterize_time,
raster_stats.best_rasterize_time,
raster_stats.total_pixels_rasterized,
is_tile_in_pending_tree_now_bin_);
HISTOGRAM_CUSTOM_COUNTS(
"Renderer4.PictureRasterTimeUS",
raster_stats.total_rasterize_time.InMicroseconds(),
0,
100000,
100);
} else {
picture_clone->RasterToBitmap(
&canvas, content_rect_, contents_scale_, NULL);
}
return true;
}
// Overridden from internal::RasterWorkerPoolTask:
virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index)
OVERRIDE {
RunAnalysisOnThread(thread_index);
return RunRasterOnThread(device, thread_index);
}
virtual void CompleteOnOriginThread() OVERRIDE {
reply_.Run(analysis_, !HasFinishedRunning() || WasCanceled());
}
protected:
virtual ~RasterWorkerPoolTaskImpl() {}
private:
scoped_ptr<base::Value> DataAsValue() const {
scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
res->Set("tile_id", TracedValue::CreateIDRef(tile_id_).release());
res->SetBoolean("is_tile_in_pending_tree_now_bin",
is_tile_in_pending_tree_now_bin_);
res->Set("resolution", TileResolutionAsValue(tile_resolution_).release());
res->SetInteger("source_frame_number", source_frame_number_);
res->SetInteger("layer_id", layer_id_);
return res.PassAs<base::Value>();
}
PicturePileImpl::Analysis analysis_;
scoped_refptr<PicturePileImpl> picture_pile_;
gfx::Rect content_rect_;
float contents_scale_;
RasterMode raster_mode_;
bool is_tile_in_pending_tree_now_bin_;
TileResolution tile_resolution_;
int layer_id_;
const void* tile_id_;
int source_frame_number_;
RenderingStatsInstrumentation* rendering_stats_;
const RasterWorkerPool::RasterTask::Reply reply_;
DISALLOW_COPY_AND_ASSIGN(RasterWorkerPoolTaskImpl);
};
class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
public:
ImageDecodeWorkerPoolTaskImpl(skia::LazyPixelRef* pixel_ref,
int layer_id,
RenderingStatsInstrumentation* rendering_stats,
const RasterWorkerPool::Task::Reply& reply)
: pixel_ref_(pixel_ref),
layer_id_(layer_id),
rendering_stats_(rendering_stats),
reply_(reply) {}
// Overridden from internal::WorkerPoolTask:
virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
TRACE_EVENT0("cc", "ImageDecodeWorkerPoolTaskImpl::RunOnWorkerThread");
devtools_instrumentation::ScopedLayerTask image_decode_task(
devtools_instrumentation::kImageDecodeTask, layer_id_);
base::TimeTicks start_time = rendering_stats_->StartRecording();
pixel_ref_->Decode();
base::TimeDelta duration = rendering_stats_->EndRecording(start_time);
rendering_stats_->AddDeferredImageDecode(duration);
}
virtual void CompleteOnOriginThread() OVERRIDE {
reply_.Run(!HasFinishedRunning());
}
protected:
virtual ~ImageDecodeWorkerPoolTaskImpl() {}
private:
skia::LazyPixelRef* pixel_ref_;
int layer_id_;
RenderingStatsInstrumentation* rendering_stats_;
const RasterWorkerPool::Task::Reply reply_;
DISALLOW_COPY_AND_ASSIGN(ImageDecodeWorkerPoolTaskImpl);
};
class RasterFinishedWorkerPoolTaskImpl : public internal::WorkerPoolTask {
public:
typedef base::Callback<void(const internal::WorkerPoolTask* source)>
Callback;
RasterFinishedWorkerPoolTaskImpl(
const Callback& on_raster_finished_callback)
: origin_loop_(base::MessageLoopProxy::current().get()),
on_raster_finished_callback_(on_raster_finished_callback) {
}
// Overridden from internal::WorkerPoolTask:
virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
TRACE_EVENT0("cc", "RasterFinishedWorkerPoolTaskImpl::RunOnWorkerThread");
origin_loop_->PostTask(
FROM_HERE,
base::Bind(&RasterFinishedWorkerPoolTaskImpl::RunOnOriginThread,
this));
}
virtual void CompleteOnOriginThread() OVERRIDE {}
private:
virtual ~RasterFinishedWorkerPoolTaskImpl() {}
void RunOnOriginThread() const {
on_raster_finished_callback_.Run(this);
}
scoped_refptr<base::MessageLoopProxy> origin_loop_;
const Callback on_raster_finished_callback_;
DISALLOW_COPY_AND_ASSIGN(RasterFinishedWorkerPoolTaskImpl);
};
const char* kWorkerThreadNamePrefix = "CompositorRaster";
} // namespace
namespace internal {
RasterWorkerPoolTask::RasterWorkerPoolTask(
const Resource* resource, TaskVector* dependencies)
: did_run_(false),
did_complete_(false),
was_canceled_(false),
resource_(resource) {
dependencies_.swap(*dependencies);
}
RasterWorkerPoolTask::~RasterWorkerPoolTask() {
}
void RasterWorkerPoolTask::DidRun(bool was_canceled) {
DCHECK(!did_run_);
did_run_ = true;
was_canceled_ = was_canceled;
}
bool RasterWorkerPoolTask::HasFinishedRunning() const {
return did_run_;
}
bool RasterWorkerPoolTask::WasCanceled() const {
return was_canceled_;
}
void RasterWorkerPoolTask::WillComplete() {
DCHECK(!did_complete_);
}
void RasterWorkerPoolTask::DidComplete() {
DCHECK(!did_complete_);
did_complete_ = true;
}
bool RasterWorkerPoolTask::HasCompleted() const {
return did_complete_;
}
} // namespace internal
RasterWorkerPool::Task::Set::Set() {
}
RasterWorkerPool::Task::Set::~Set() {
}
void RasterWorkerPool::Task::Set::Insert(const Task& task) {
DCHECK(!task.is_null());
tasks_.push_back(task.internal_);
}
RasterWorkerPool::Task::Task() {
}
RasterWorkerPool::Task::Task(internal::WorkerPoolTask* internal)
: internal_(internal) {
}
RasterWorkerPool::Task::~Task() {
}
void RasterWorkerPool::Task::Reset() {
internal_ = NULL;
}
RasterWorkerPool::RasterTask::Queue::Queue() {
}
RasterWorkerPool::RasterTask::Queue::~Queue() {
}
void RasterWorkerPool::RasterTask::Queue::Append(
const RasterTask& task, bool required_for_activation) {
DCHECK(!task.is_null());
tasks_.push_back(task.internal_);
if (required_for_activation)
tasks_required_for_activation_.insert(task.internal_.get());
}
RasterWorkerPool::RasterTask::RasterTask() {
}
RasterWorkerPool::RasterTask::RasterTask(
internal::RasterWorkerPoolTask* internal)
: internal_(internal) {
}
void RasterWorkerPool::RasterTask::Reset() {
internal_ = NULL;
}
RasterWorkerPool::RasterTask::~RasterTask() {
}
// static
RasterWorkerPool::RasterTask RasterWorkerPool::CreateRasterTask(
const Resource* resource,
PicturePileImpl* picture_pile,
gfx::Rect content_rect,
float contents_scale,
RasterMode raster_mode,
bool is_tile_in_pending_tree_now_bin,
TileResolution tile_resolution,
int layer_id,
const void* tile_id,
int source_frame_number,
RenderingStatsInstrumentation* rendering_stats,
const RasterTask::Reply& reply,
Task::Set* dependencies) {
return RasterTask(
new RasterWorkerPoolTaskImpl(resource,
picture_pile,
content_rect,
contents_scale,
raster_mode,
is_tile_in_pending_tree_now_bin,
tile_resolution,
layer_id,
tile_id,
source_frame_number,
rendering_stats,
reply,
&dependencies->tasks_));
}
// static
RasterWorkerPool::Task RasterWorkerPool::CreateImageDecodeTask(
skia::LazyPixelRef* pixel_ref,
int layer_id,
RenderingStatsInstrumentation* stats_instrumentation,
const Task::Reply& reply) {
return Task(new ImageDecodeWorkerPoolTaskImpl(pixel_ref,
layer_id,
stats_instrumentation,
reply));
}
RasterWorkerPool::RasterWorkerPool(ResourceProvider* resource_provider,
size_t num_threads)
: WorkerPool(num_threads, kWorkerThreadNamePrefix),
client_(NULL),
resource_provider_(resource_provider),
weak_ptr_factory_(this) {
}
RasterWorkerPool::~RasterWorkerPool() {
}
void RasterWorkerPool::SetClient(RasterWorkerPoolClient* client) {
client_ = client;
}
void RasterWorkerPool::Shutdown() {
raster_tasks_.clear();
TaskGraph empty;
SetTaskGraph(&empty);
WorkerPool::Shutdown();
weak_ptr_factory_.InvalidateWeakPtrs();
}
void RasterWorkerPool::SetRasterTasks(RasterTask::Queue* queue) {
raster_tasks_.swap(queue->tasks_);
raster_tasks_required_for_activation_.swap(
queue->tasks_required_for_activation_);
}
bool RasterWorkerPool::IsRasterTaskRequiredForActivation(
internal::RasterWorkerPoolTask* task) const {
return
raster_tasks_required_for_activation_.find(task) !=
raster_tasks_required_for_activation_.end();
}
scoped_refptr<internal::WorkerPoolTask>
RasterWorkerPool::CreateRasterFinishedTask() {
return make_scoped_refptr(
new RasterFinishedWorkerPoolTaskImpl(
base::Bind(&RasterWorkerPool::OnRasterFinished,
weak_ptr_factory_.GetWeakPtr())));
}
scoped_refptr<internal::WorkerPoolTask>
RasterWorkerPool::CreateRasterRequiredForActivationFinishedTask() {
return make_scoped_refptr(
new RasterFinishedWorkerPoolTaskImpl(
base::Bind(&RasterWorkerPool::OnRasterRequiredForActivationFinished,
weak_ptr_factory_.GetWeakPtr())));
}
void RasterWorkerPool::OnRasterFinished(
const internal::WorkerPoolTask* source) {
TRACE_EVENT0("cc", "RasterWorkerPool::OnRasterFinished");
// Early out if current |raster_finished_task_| is not the source.
if (source != raster_finished_task_.get())
return;
OnRasterTasksFinished();
}
void RasterWorkerPool::OnRasterRequiredForActivationFinished(
const internal::WorkerPoolTask* source) {
TRACE_EVENT0("cc", "RasterWorkerPool::OnRasterRequiredForActivationFinished");
// Early out if current |raster_required_for_activation_finished_task_|
// is not the source.
if (source != raster_required_for_activation_finished_task_.get())
return;
OnRasterTasksRequiredForActivationFinished();
}
scoped_ptr<base::Value> RasterWorkerPool::ScheduledStateAsValue() const {
scoped_ptr<base::DictionaryValue> scheduled_state(new base::DictionaryValue);
scheduled_state->SetInteger("task_count", raster_tasks_.size());
scheduled_state->SetInteger("task_required_for_activation_count",
raster_tasks_required_for_activation_.size());
return scheduled_state.PassAs<base::Value>();
}
// static
internal::GraphNode* RasterWorkerPool::CreateGraphNodeForTask(
internal::WorkerPoolTask* task,
unsigned priority,
TaskGraph* graph) {
internal::GraphNode* node = new internal::GraphNode(task, priority);
DCHECK(graph->find(task) == graph->end());
graph->set(task, make_scoped_ptr(node));
return node;
}
// static
internal::GraphNode* RasterWorkerPool::CreateGraphNodeForRasterTask(
internal::WorkerPoolTask* raster_task,
const TaskVector& decode_tasks,
unsigned priority,
TaskGraph* graph) {
DCHECK(!raster_task->HasCompleted());
internal::GraphNode* raster_node = CreateGraphNodeForTask(
raster_task, priority, graph);
// Insert image decode tasks.
for (TaskVector::const_iterator it = decode_tasks.begin();
it != decode_tasks.end(); ++it) {
internal::WorkerPoolTask* decode_task = it->get();
// Skip if already decoded.
if (decode_task->HasCompleted())
continue;
raster_node->add_dependency();
// Check if decode task already exists in graph.
GraphNodeMap::iterator decode_it = graph->find(decode_task);
if (decode_it != graph->end()) {
internal::GraphNode* decode_node = decode_it->second;
decode_node->add_dependent(raster_node);
continue;
}
internal::GraphNode* decode_node = CreateGraphNodeForTask(
decode_task, priority, graph);
decode_node->add_dependent(raster_node);
}
return raster_node;
}
} // namespace cc