blob: 1ab4da244fa78b6a5bb8fd740d3e2e37be8f48d3 [file] [log] [blame]
// Copyright 2014 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/raster/raster_source.h"
#include <stddef.h>
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/math_util.h"
#include "cc/base/region.h"
#include "cc/debug/debug_colors.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/skia_paint_canvas.h"
#include "components/viz/common/traced_value.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "ui/gfx/geometry/axis_transform2d.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
namespace {
// These enum values are persisted to logs and must never by renumbered or
// reused.
enum class RasterSourceClearType {
kNone = 0,
kFull = 1,
kBorder = 2,
kCount = 3
};
void TrackRasterSourceNeededClear(RasterSourceClearType clear_type) {
UMA_HISTOGRAM_ENUMERATION("Renderer4.RasterSourceClearType", clear_type,
RasterSourceClearType::kCount);
}
} // namespace
RasterSource::RasterSource(const RecordingSource* other)
: display_list_(other->display_list_),
painter_reported_memory_usage_(other->painter_reported_memory_usage_),
background_color_(other->background_color_),
requires_clear_(other->requires_clear_),
is_solid_color_(other->is_solid_color_),
solid_color_(other->solid_color_),
recorded_viewport_(other->recorded_viewport_),
size_(other->size_),
slow_down_raster_scale_factor_for_debug_(
other->slow_down_raster_scale_factor_for_debug_),
recording_scale_factor_(other->recording_scale_factor_) {}
RasterSource::~RasterSource() = default;
void RasterSource::ClearForOpaqueRaster(
SkCanvas* raster_canvas,
const gfx::Size& content_size,
const gfx::Rect& canvas_bitmap_rect,
const gfx::Rect& canvas_playback_rect) const {
// The last texel of this content is not guaranteed to be fully opaque, so
// inset by one to generate the fully opaque coverage rect. This rect is
// in device space.
SkIRect coverage_device_rect =
SkIRect::MakeWH(content_size.width() - canvas_bitmap_rect.x() - 1,
content_size.height() - canvas_bitmap_rect.y() - 1);
// If not fully covered, we need to clear one texel inside the coverage
// rect (because of blending during raster) and one texel outside the canvas
// bitmap rect (because of bilinear filtering during draw). See comments
// in RasterSource.
SkIRect device_column = SkIRect::MakeXYWH(coverage_device_rect.right(), 0, 2,
coverage_device_rect.bottom());
// row includes the corner, column excludes it.
SkIRect device_row = SkIRect::MakeXYWH(0, coverage_device_rect.bottom(),
coverage_device_rect.right() + 2, 2);
bool right_edge = content_size.width() == canvas_playback_rect.right();
bool bottom_edge = content_size.height() == canvas_playback_rect.bottom();
// If the playback rect is touching either edge of the content rect
// extend it by one pixel to include the extra texel outside the canvas
// bitmap rect that was added to device column and row above.
SkIRect playback_device_rect =
SkIRect::MakeXYWH(canvas_playback_rect.x() - canvas_bitmap_rect.x(),
canvas_playback_rect.y() - canvas_bitmap_rect.y(),
canvas_playback_rect.width() + (right_edge ? 1 : 0),
canvas_playback_rect.height() + (bottom_edge ? 1 : 0));
// Intersect the device column and row with the playback rect and only
// clear inside of that rect if needed.
RasterSourceClearType clear_type = RasterSourceClearType::kNone;
if (device_column.intersect(playback_device_rect)) {
clear_type = RasterSourceClearType::kBorder;
raster_canvas->save();
raster_canvas->clipRect(SkRect::Make(device_column), SkClipOp::kIntersect,
false);
raster_canvas->drawColor(background_color_, SkBlendMode::kSrc);
raster_canvas->restore();
}
if (device_row.intersect(playback_device_rect)) {
clear_type = RasterSourceClearType::kBorder;
raster_canvas->save();
raster_canvas->clipRect(SkRect::Make(device_row), SkClipOp::kIntersect,
false);
raster_canvas->drawColor(background_color_, SkBlendMode::kSrc);
raster_canvas->restore();
}
TrackRasterSourceNeededClear(clear_type);
}
void RasterSource::PlaybackToCanvas(
SkCanvas* input_canvas,
const gfx::ColorSpace& target_color_space,
const gfx::Size& content_size,
const gfx::Rect& canvas_bitmap_rect,
const gfx::Rect& canvas_playback_rect,
const gfx::AxisTransform2d& raster_transform,
const PlaybackSettings& settings) const {
SkIRect raster_bounds = gfx::RectToSkIRect(canvas_bitmap_rect);
if (!canvas_playback_rect.IsEmpty() &&
!raster_bounds.intersect(gfx::RectToSkIRect(canvas_playback_rect)))
return;
// Treat all subnormal values as zero for performance.
ScopedSubnormalFloatDisabler disabler;
// TODO(enne): color transform needs to be replicated in gles2_cmd_decoder
SkCanvas* raster_canvas = input_canvas;
std::unique_ptr<SkCanvas> color_transform_canvas;
if (target_color_space.IsValid()) {
color_transform_canvas = SkCreateColorSpaceXformCanvas(
input_canvas, target_color_space.ToSkColorSpace());
raster_canvas = color_transform_canvas.get();
}
bool is_partial_raster = canvas_bitmap_rect != canvas_playback_rect;
if (!requires_clear_) {
// Clear opaque raster sources. Opaque rasters sources guarantee that all
// pixels inside the opaque region are painted. However, due to scaling
// it's possible that the last row and column might include pixels that
// are not painted. Because this raster source is required to be opaque,
// we may need to do extra clearing outside of the clip. This needs to
// be done for both full and partial raster.
ClearForOpaqueRaster(raster_canvas, content_size, canvas_bitmap_rect,
canvas_playback_rect);
} else if (!is_partial_raster) {
// For non-opaque raster sources that are rastering the full tile,
// just clear the entire canvas (even if stretches past the canvas
// bitmap rect) as it's cheap to do so.
TrackRasterSourceNeededClear(RasterSourceClearType::kFull);
raster_canvas->clear(SK_ColorTRANSPARENT);
}
raster_canvas->save();
raster_canvas->translate(-canvas_bitmap_rect.x(), -canvas_bitmap_rect.y());
raster_canvas->clipRect(SkRect::Make(raster_bounds));
raster_canvas->translate(raster_transform.translation().x(),
raster_transform.translation().y());
raster_canvas->scale(raster_transform.scale() / recording_scale_factor_,
raster_transform.scale() / recording_scale_factor_);
if (is_partial_raster && requires_clear_) {
// TODO(enne): Should this be considered a partial clear?
TrackRasterSourceNeededClear(RasterSourceClearType::kFull);
// Because Skia treats painted regions as transparent by default, we don't
// need to clear outside of the playback rect in the same way that
// ClearForOpaqueRaster must handle.
raster_canvas->clear(SK_ColorTRANSPARENT);
}
PlaybackToCanvas(raster_canvas, settings.image_provider,
settings.paint_worklet_image_provider);
raster_canvas->restore();
}
void RasterSource::PlaybackToCanvas(
SkCanvas* raster_canvas,
ImageProvider* image_provider,
PaintWorkletImageProvider* paint_worklet_image_provider) const {
// TODO(enne): Temporary CHECK debugging for http://crbug.com/823835
CHECK(display_list_.get());
int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
for (int i = 0; i < repeat_count; ++i)
display_list_->Raster(raster_canvas, image_provider,
paint_worklet_image_provider);
}
sk_sp<SkPicture> RasterSource::GetFlattenedPicture() {
TRACE_EVENT0("cc", "RasterSource::GetFlattenedPicture");
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(size_.width(), size_.height());
if (!size_.IsEmpty()) {
canvas->clear(SK_ColorTRANSPARENT);
PlaybackToCanvas(canvas, nullptr, nullptr);
}
return recorder.finishRecordingAsPicture();
}
size_t RasterSource::GetMemoryUsage() const {
if (!display_list_)
return 0;
return display_list_->BytesUsed() + painter_reported_memory_usage_;
}
bool RasterSource::PerformSolidColorAnalysis(gfx::Rect layer_rect,
SkColor* color) const {
TRACE_EVENT0("cc", "RasterSource::PerformSolidColorAnalysis");
layer_rect.Intersect(gfx::Rect(size_));
layer_rect = gfx::ScaleToRoundedRect(layer_rect, recording_scale_factor_);
return display_list_->GetColorIfSolidInRect(layer_rect, color);
}
void RasterSource::GetDiscardableImagesInRect(
const gfx::Rect& layer_rect,
std::vector<const DrawImage*>* images) const {
DCHECK_EQ(0u, images->size());
display_list_->discardable_image_map().GetDiscardableImagesInRect(layer_rect,
images);
}
base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
RasterSource::TakeDecodingModeMap() {
return display_list_->TakeDecodingModeMap();
}
bool RasterSource::CoversRect(const gfx::Rect& layer_rect) const {
if (size_.IsEmpty())
return false;
gfx::Rect bounded_rect = layer_rect;
bounded_rect.Intersect(gfx::Rect(size_));
return recorded_viewport_.Contains(bounded_rect);
}
gfx::Size RasterSource::GetSize() const {
return size_;
}
gfx::Size RasterSource::GetContentSize(float content_scale) const {
return gfx::ScaleToCeiledSize(GetSize(), content_scale);
}
bool RasterSource::IsSolidColor() const {
return is_solid_color_;
}
SkColor RasterSource::GetSolidColor() const {
DCHECK(IsSolidColor());
return solid_color_;
}
bool RasterSource::HasRecordings() const {
return !!display_list_.get();
}
gfx::Rect RasterSource::RecordedViewport() const {
return recorded_viewport_;
}
void RasterSource::AsValueInto(base::trace_event::TracedValue* array) const {
if (display_list_.get())
viz::TracedValue::AppendIDRef(display_list_.get(), array);
}
void RasterSource::DidBeginTracing() {
if (display_list_.get())
display_list_->EmitTraceSnapshot();
}
RasterSource::PlaybackSettings::PlaybackSettings() = default;
RasterSource::PlaybackSettings::PlaybackSettings(const PlaybackSettings&) =
default;
RasterSource::PlaybackSettings::PlaybackSettings(PlaybackSettings&&) = default;
RasterSource::PlaybackSettings::~PlaybackSettings() = default;
} // namespace cc