普通文本  |  597行  |  18.22 KB

// 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