// 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/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"
#include "third_party/skia/include/core/SkBitmap.h"
namespace cc {
namespace {
// Subclass of Allocator that takes a suitably allocated pointer and uses
// it as the pixel memory for the bitmap.
class IdentityAllocator : public SkBitmap::Allocator {
public:
explicit IdentityAllocator(void* buffer) : buffer_(buffer) {}
virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE {
dst->setPixels(buffer_);
return true;
}
private:
void* buffer_;
};
// 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,
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),
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);
picture_clone->AnalyzeInRect(
content_rect_, contents_scale_, &analysis_, rendering_stats_);
// Record the solid color prediction.
UMA_HISTOGRAM_BOOLEAN("Renderer4.SolidColorTilesAnalyzed",
analysis_.is_solid_color);
// Clear the flag if we're not using the estimator.
analysis_.is_solid_color &= kUseColorEstimator;
}
bool RunRasterOnThread(unsigned thread_index,
void* buffer,
gfx::Size size,
int stride) {
TRACE_EVENT2(
"cc", "RasterWorkerPoolTaskImpl::RunRasterOnThread",
"data",
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(buffer);
if (analysis_.is_solid_color)
return false;
PicturePileImpl* picture_clone =
picture_pile_->GetCloneForDrawingOnThread(thread_index);
SkBitmap bitmap;
switch (resource()->format()) {
case RGBA_4444:
// Use the default stride if we will eventually convert this
// bitmap to 4444.
bitmap.setConfig(SkBitmap::kARGB_8888_Config,
size.width(),
size.height());
bitmap.allocPixels();
break;
case RGBA_8888:
case BGRA_8888:
bitmap.setConfig(SkBitmap::kARGB_8888_Config,
size.width(),
size.height(),
stride);
bitmap.setPixels(buffer);
break;
case LUMINANCE_8:
case RGB_565:
case ETC1:
NOTREACHED();
break;
}
SkBitmapDevice device(bitmap);
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());
base::TimeDelta prev_rasterize_time =
rendering_stats_->impl_thread_rendering_stats().rasterize_time;
// Only record rasterization time for highres tiles, because
// lowres tiles are not required for activation and therefore
// introduce noise in the measurement (sometimes they get rasterized
// before we draw and sometimes they aren't)
if (tile_resolution_ == HIGH_RESOLUTION) {
picture_clone->RasterToBitmap(
&canvas, content_rect_, contents_scale_, rendering_stats_);
} else {
picture_clone->RasterToBitmap(
&canvas, content_rect_, contents_scale_, NULL);
}
if (rendering_stats_->record_rendering_stats()) {
base::TimeDelta current_rasterize_time =
rendering_stats_->impl_thread_rendering_stats().rasterize_time;
HISTOGRAM_CUSTOM_COUNTS(
"Renderer4.PictureRasterTimeUS",
(current_rasterize_time - prev_rasterize_time).InMicroseconds(),
0,
100000,
100);
}
ChangeBitmapConfigIfNeeded(bitmap, buffer);
return true;
}
// Overridden from internal::RasterWorkerPoolTask:
virtual bool RunOnWorkerThread(unsigned thread_index,
void* buffer,
gfx::Size size,
int stride)
OVERRIDE {
RunAnalysisOnThread(thread_index);
return RunRasterOnThread(thread_index, buffer, size, stride);
}
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->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>();
}
void ChangeBitmapConfigIfNeeded(const SkBitmap& bitmap,
void* buffer) {
TRACE_EVENT0("cc", "RasterWorkerPoolTaskImpl::ChangeBitmapConfigIfNeeded");
SkBitmap::Config config = SkBitmapConfig(resource()->format());
if (bitmap.getConfig() != config) {
SkBitmap bitmap_dest;
IdentityAllocator allocator(buffer);
bitmap.copyTo(&bitmap_dest, config, &allocator);
// TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
// bitmap data. This check will be removed once crbug.com/293728 is fixed.
CHECK_EQ(0u, bitmap_dest.rowBytes() % 4);
}
}
PicturePileImpl::Analysis analysis_;
scoped_refptr<PicturePileImpl> picture_pile_;
gfx::Rect content_rect_;
float contents_scale_;
RasterMode raster_mode_;
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_(skia::SharePtr(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::ScopedImageDecodeTask image_decode_task(
pixel_ref_.get());
pixel_ref_->Decode();
}
virtual void CompleteOnOriginThread() OVERRIDE {
reply_.Run(!HasFinishedRunning());
}
protected:
virtual ~ImageDecodeWorkerPoolTaskImpl() {}
private:
skia::RefPtr<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,
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,
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