[go: nahoru, domu]

blob: 3e83f612e1b9a6bae3479b79b0e673e1202e0a27 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/paint/paint_op_buffer_serializer.h"
#include <limits>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/clear_for_opaque_raster.h"
#include "cc/paint/paint_op_buffer_iterator.h"
#include "cc/paint/scoped_raster_flags.h"
#include "skia/ext/legacy_display_globals.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "ui/gfx/geometry/skia_conversions.h"
namespace cc {
namespace {
PlaybackParams MakeParams(const SkCanvas* canvas) {
// We don't use an ImageProvider here since the ops are played onto a no-draw
// canvas for state tracking and don't need decoded images.
PlaybackParams params(nullptr, canvas->getLocalToDevice());
params.is_analyzing = true;
return params;
}
std::unique_ptr<SkCanvas> MakeAnalysisCanvas(
const PaintOp::SerializeOptions& options) {
// Use half of the max int as the extent for the SkNoDrawCanvas. The correct
// clip is applied to the canvas during serialization.
const int kMaxExtent = std::numeric_limits<int>::max() >> 1;
return options.strike_server
? options.strike_server->makeAnalysisCanvas(
kMaxExtent, kMaxExtent,
skia::LegacyDisplayGlobals::ComputeSurfaceProps(
options.can_use_lcd_text),
options.color_space,
options.context_supports_distance_field_text)
: std::make_unique<SkNoDrawCanvas>(kMaxExtent, kMaxExtent);
}
} // namespace
PaintOpBufferSerializer::PaintOpBufferSerializer(
SerializeCallback serialize_cb,
const PaintOp::SerializeOptions& options)
: serialize_cb_(std::move(serialize_cb)), options_(options) {
DCHECK(serialize_cb_);
}
PaintOpBufferSerializer::~PaintOpBufferSerializer() = default;
void PaintOpBufferSerializer::Serialize(const PaintOpBuffer& buffer,
const std::vector<size_t>* offsets,
const Preamble& preamble) {
std::unique_ptr<SkCanvas> canvas = MakeAnalysisCanvas(options_);
// These PlaybackParams use the initial (identity) canvas matrix, as they are
// only used for serializing the preamble and the initial save / final restore
// SerializeBuffer will create its own PlaybackParams based on the
// post-preamble canvas.
PlaybackParams params = MakeParams(canvas.get());
int saveCount = canvas->getSaveCount();
Save(canvas.get(), params);
SerializePreamble(canvas.get(), preamble, params);
SerializeBuffer(canvas.get(), buffer, offsets);
RestoreToCount(canvas.get(), saveCount, params);
}
void PaintOpBufferSerializer::Serialize(const PaintOpBuffer& buffer) {
std::unique_ptr<SkCanvas> canvas = MakeAnalysisCanvas(options_);
SerializeBuffer(canvas.get(), buffer, nullptr);
}
void PaintOpBufferSerializer::Serialize(const PaintOpBuffer& buffer,
const gfx::Rect& playback_rect,
const gfx::SizeF& post_scale) {
std::unique_ptr<SkCanvas> canvas = MakeAnalysisCanvas(options_);
PlaybackParams params = MakeParams(canvas.get());
// TODO(khushalsagar): remove this clip rect if it's not needed.
if (!playback_rect.IsEmpty()) {
ClipRectOp clip_op(gfx::RectToSkRect(playback_rect), SkClipOp::kIntersect,
false);
SerializeOp(canvas.get(), clip_op, nullptr, params);
}
if (post_scale.width() != 1.f || post_scale.height() != 1.f) {
ScaleOp scale_op(post_scale.width(), post_scale.height());
SerializeOp(canvas.get(), scale_op, nullptr, params);
}
SerializeBuffer(canvas.get(), buffer, nullptr);
}
// This function needs to have the exact same behavior as
// RasterSource::ClearForOpaqueRaster.
void PaintOpBufferSerializer::ClearForOpaqueRaster(
SkCanvas* canvas,
const Preamble& preamble,
const PlaybackParams& params) {
gfx::Rect outer_rect;
gfx::Rect inner_rect;
if (!CalculateClearForOpaqueRasterRects(
preamble.post_translation, preamble.post_scale, preamble.content_size,
preamble.full_raster_rect, preamble.playback_rect, outer_rect,
inner_rect))
return;
Save(canvas, params);
ClipRectOp outer_clip_op(gfx::RectToSkRect(outer_rect), SkClipOp::kIntersect,
false);
SerializeOp(canvas, outer_clip_op, nullptr, params);
if (!inner_rect.IsEmpty()) {
ClipRectOp inner_clip_op(gfx::RectToSkRect(inner_rect),
SkClipOp::kDifference, false);
SerializeOp(canvas, inner_clip_op, nullptr, params);
}
DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc);
SerializeOp(canvas, clear_op, nullptr, params);
RestoreToCount(canvas, 1, params);
}
void PaintOpBufferSerializer::SerializePreamble(SkCanvas* canvas,
const Preamble& preamble,
const PlaybackParams& params) {
DCHECK(preamble.full_raster_rect.Contains(preamble.playback_rect))
<< "full: " << preamble.full_raster_rect.ToString()
<< ", playback: " << preamble.playback_rect.ToString();
// NOTE: The following code should be kept consistent with
// RasterSource::PlaybackToCanvas().
bool is_partial_raster = preamble.full_raster_rect != preamble.playback_rect;
if (!preamble.requires_clear) {
ClearForOpaqueRaster(canvas, preamble, params);
} else if (!is_partial_raster) {
// If rastering the entire tile, clear to transparent pre-clip. This is so
// that any external texels outside of the playback rect also get cleared.
// There's not enough information at this point to know if this texture is
// being reused from another tile, so the external texels could have been
// cleared to some wrong value.
DrawColorOp clear(SkColors::kTransparent, SkBlendMode::kSrc);
SerializeOp(canvas, clear, nullptr, params);
}
if (!preamble.full_raster_rect.OffsetFromOrigin().IsZero()) {
TranslateOp translate_op(-preamble.full_raster_rect.x(),
-preamble.full_raster_rect.y());
SerializeOp(canvas, translate_op, nullptr, params);
}
if (!preamble.playback_rect.IsEmpty()) {
ClipRectOp clip_op(gfx::RectToSkRect(preamble.playback_rect),
SkClipOp::kIntersect, false);
SerializeOp(canvas, clip_op, nullptr, params);
}
if (!preamble.post_translation.IsZero()) {
TranslateOp translate_op(preamble.post_translation.x(),
preamble.post_translation.y());
SerializeOp(canvas, translate_op, nullptr, params);
}
if (preamble.post_scale.x() != 1.f || preamble.post_scale.y() != 1.f) {
ScaleOp scale_op(preamble.post_scale.x(), preamble.post_scale.y());
SerializeOp(canvas, scale_op, nullptr, params);
}
// If tile is transparent and this is partial raster, just clear the
// section that is being rastered. If this is opaque, trust the raster
// to write all the pixels inside of the full_raster_rect.
if (preamble.requires_clear && is_partial_raster) {
DrawColorOp clear_op(SkColors::kTransparent, SkBlendMode::kSrc);
SerializeOp(canvas, clear_op, nullptr, params);
}
}
bool PaintOpBufferSerializer::WillSerializeNextOp(const PaintOp& op,
SkCanvas* canvas,
const PlaybackParams& params,
uint8_t alpha) {
// Skip ops outside the current clip if they have images. This saves
// performing an unnecessary expensive decode.
bool skip_op = PaintOp::OpHasDiscardableImages(op) &&
PaintOp::QuickRejectDraw(op, canvas);
// Skip text ops if there is no SkStrikeServer.
skip_op |=
op.GetType() == PaintOpType::DrawTextBlob && !options_.strike_server;
if (skip_op)
return true;
if (op.GetType() == PaintOpType::DrawRecord) {
int save_count = canvas->getSaveCount();
Save(canvas, params);
SerializeBuffer(
canvas, static_cast<const DrawRecordOp&>(op).record.buffer(), nullptr);
RestoreToCount(canvas, save_count, params);
return true;
}
if (op.GetType() == PaintOpType::DrawImageRect &&
static_cast<const DrawImageRectOp&>(op).image.IsPaintWorklet()) {
DCHECK(options_.image_provider);
const DrawImageRectOp& draw_op = static_cast<const DrawImageRectOp&>(op);
ImageProvider::ScopedResult result =
options_.image_provider->GetRasterContent(DrawImage(draw_op.image));
if (!result || !result.paint_record())
return true;
int save_count = canvas->getSaveCount();
Save(canvas, params);
// The following ops are copying the canvas's ops from
// DrawImageRectOp::RasterWithFlags.
SkM44 trans = SkM44(SkMatrix::RectToRect(draw_op.src, draw_op.dst));
ConcatOp concat_op(trans);
bool success = SerializeOp(canvas, concat_op, nullptr, params);
if (!success)
return false;
ClipRectOp clip_rect_op(draw_op.src, SkClipOp::kIntersect, false);
success = SerializeOp(canvas, clip_rect_op, nullptr, params);
if (!success)
return false;
// In DrawImageRectOp::RasterWithFlags, the save layer uses the
// flags_to_serialize or default(null) flags. At this point in the
// serialization, flags_to_serialize is always null as well.
SaveLayerOp save_layer_op(&draw_op.src, nullptr);
success = SerializeOpWithFlags(canvas, save_layer_op, params, 255);
if (!success)
return false;
SerializeBuffer(canvas, result.paint_record()->buffer(), nullptr);
RestoreToCount(canvas, save_count, params);
return true;
} else {
if (op.IsPaintOpWithFlags()) {
return SerializeOpWithFlags(
canvas, static_cast<const PaintOpWithFlags&>(op), params, alpha);
} else {
return SerializeOp(canvas, op, nullptr, params);
}
}
}
void PaintOpBufferSerializer::SerializeBuffer(
SkCanvas* canvas,
const PaintOpBuffer& buffer,
const std::vector<size_t>* offsets) {
// This updates the original_ctm to reflect the canvas transformation at
// start of this call to SerializeBuffer.
PlaybackParams params = MakeParams(canvas);
for (PaintOpBuffer::PlaybackFoldingIterator iter(buffer, offsets); iter;
++iter) {
const PaintOp& op = *iter;
if (!WillSerializeNextOp(op, canvas, params, iter.alpha())) {
return;
}
}
}
bool PaintOpBufferSerializer::SerializeOpWithFlags(
SkCanvas* canvas,
const PaintOpWithFlags& flags_op,
const PlaybackParams& params,
uint8_t alpha) {
// We use a null |image_provider| here because images are decoded during
// serialization.
const ScopedRasterFlags scoped_flags(
&flags_op.flags, nullptr, canvas->getTotalMatrix(),
options_.max_texture_size, alpha / 255.0f);
const PaintFlags* flags_to_serialize = scoped_flags.flags();
if (!flags_to_serialize)
return true;
return SerializeOp(canvas, flags_op, flags_to_serialize, params);
}
bool PaintOpBufferSerializer::SerializeOp(SkCanvas* canvas,
const PaintOp& op,
const PaintFlags* flags_to_serialize,
const PlaybackParams& params) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"PaintOpBufferSerializer::SerializeOp", "op",
PaintOpTypeToString(op.GetType()));
if (!valid_)
return false;
// Playback on analysis canvas first to make sure the canvas transform is set
// correctly for analysis of records in filters.
PlaybackOnAnalysisCanvas(canvas, op, flags_to_serialize, params);
size_t bytes =
serialize_cb_.Run(op, options_, flags_to_serialize,
canvas->getLocalToDevice(), params.original_ctm);
if (!bytes) {
valid_ = false;
return false;
}
DCHECK_GE(bytes, 4u);
DCHECK_EQ(bytes % PaintOpBuffer::kPaintOpAlign, 0u);
return true;
}
void PaintOpBufferSerializer::PlaybackOnAnalysisCanvas(
SkCanvas* canvas,
const PaintOp& op,
const PaintFlags* flags_to_serialize,
const PlaybackParams& params) {
// Only 2 types of ops need to played on the analysis canvas.
// 1) Non-draw ops which affect the transform/clip state on the canvas, since
// we need the correct ctm at which text and images will be rasterized, and
// the clip rect so we can skip sending data for ops which will not be
// rasterized.
// 2) DrawTextBlob ops since they need to be analyzed by the cache diff canvas
// to serialize/lock the requisite glyphs for this op.
if (op.IsDrawOp() && op.GetType() != PaintOpType::DrawTextBlob)
return;
if (op.IsPaintOpWithFlags() && flags_to_serialize) {
static_cast<const PaintOpWithFlags&>(op).RasterWithFlags(
canvas, flags_to_serialize, params);
} else {
op.Raster(canvas, params);
}
}
void PaintOpBufferSerializer::Save(SkCanvas* canvas,
const PlaybackParams& params) {
SaveOp save_op;
SerializeOp(canvas, save_op, nullptr, params);
}
void PaintOpBufferSerializer::RestoreToCount(SkCanvas* canvas,
int count,
const PlaybackParams& params) {
RestoreOp restore_op;
while (canvas->getSaveCount() > count) {
if (!SerializeOp(canvas, restore_op, nullptr, params))
return;
}
}
SimpleBufferSerializer::SimpleBufferSerializer(
void* memory,
size_t size,
const PaintOp::SerializeOptions& options)
: PaintOpBufferSerializer(
base::BindRepeating(&SimpleBufferSerializer::SerializeToMemory,
base::Unretained(this)),
options),
memory_(memory),
total_(size) {}
SimpleBufferSerializer::~SimpleBufferSerializer() = default;
size_t SimpleBufferSerializer::SerializeToMemory(
const PaintOp& op,
const PaintOp::SerializeOptions& options,
const PaintFlags* flags_to_serialize,
const SkM44& current_ctm,
const SkM44& original_ctm) {
if (written_ == total_)
return 0u;
size_t bytes =
op.Serialize(static_cast<char*>(memory_) + written_, total_ - written_,
options, flags_to_serialize, current_ctm, original_ctm);
if (!bytes)
return 0u;
written_ += bytes;
DCHECK_GE(total_, written_);
return bytes;
}
} // namespace cc