blob: 8bfec18230aa05c2221cabe889a4639900c39eee [file] [log] [blame]
// Copyright 2013 The Flutter 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 "flutter/flow/diff_context.h"
#include "flutter/flow/layers/layer.h"
namespace flutter {
DiffContext::DiffContext(SkISize frame_size,
PaintRegionMap& this_frame_paint_region_map,
const PaintRegionMap& last_frame_paint_region_map,
bool has_raster_cache,
bool impeller_enabled)
: clip_tracker_(DisplayListMatrixClipTracker(kGiantRect, SkMatrix::I())),
rects_(std::make_shared<std::vector<SkRect>>()),
frame_size_(frame_size),
this_frame_paint_region_map_(this_frame_paint_region_map),
last_frame_paint_region_map_(last_frame_paint_region_map),
has_raster_cache_(has_raster_cache),
impeller_enabled_(impeller_enabled) {}
void DiffContext::BeginSubtree() {
state_stack_.push_back(state_);
bool had_integral_transform = state_.integral_transform;
state_.rect_index = rects_->size();
state_.has_filter_bounds_adjustment = false;
state_.has_texture = false;
state_.integral_transform = false;
state_.clip_tracker_save_count = clip_tracker_.getSaveCount();
clip_tracker_.save();
if (had_integral_transform) {
MakeCurrentTransformIntegral();
}
}
void DiffContext::EndSubtree() {
FML_DCHECK(!state_stack_.empty());
if (state_.has_filter_bounds_adjustment) {
filter_bounds_adjustment_stack_.pop_back();
}
clip_tracker_.restoreToCount(state_.clip_tracker_save_count);
state_ = state_stack_.back();
state_stack_.pop_back();
}
DiffContext::State::State() {}
void DiffContext::PushTransform(const SkMatrix& transform) {
clip_tracker_.transform(transform);
}
void DiffContext::PushTransform(const SkM44& transform) {
clip_tracker_.transform(transform);
}
void DiffContext::MakeCurrentTransformIntegral() {
// TODO(knopp): This is duplicated from LayerStack. Maybe should be part of
// clip tracker?
if (clip_tracker_.using_4x4_matrix()) {
SkM44 integral;
if (RasterCacheUtil::ComputeIntegralTransCTM(clip_tracker_.matrix_4x4(),
&integral)) {
clip_tracker_.setTransform(integral);
}
} else {
SkMatrix integral;
if (RasterCacheUtil::ComputeIntegralTransCTM(clip_tracker_.matrix_3x3(),
&integral)) {
clip_tracker_.setTransform(integral);
}
}
}
void DiffContext::PushFilterBoundsAdjustment(
const FilterBoundsAdjustment& filter) {
FML_DCHECK(state_.has_filter_bounds_adjustment == false);
state_.has_filter_bounds_adjustment = true;
filter_bounds_adjustment_stack_.push_back(filter);
}
SkRect DiffContext::ApplyFilterBoundsAdjustment(SkRect rect) const {
// Apply filter bounds adjustment in reverse order
for (auto i = filter_bounds_adjustment_stack_.rbegin();
i != filter_bounds_adjustment_stack_.rend(); ++i) {
rect = (*i)(rect);
}
return rect;
}
void DiffContext::AlignRect(SkIRect& rect,
int horizontal_alignment,
int vertical_alignment) const {
auto top = rect.top();
auto left = rect.left();
auto right = rect.right();
auto bottom = rect.bottom();
if (top % vertical_alignment != 0) {
top -= top % vertical_alignment;
}
if (left % horizontal_alignment != 0) {
left -= left % horizontal_alignment;
}
if (right % horizontal_alignment != 0) {
right += horizontal_alignment - right % horizontal_alignment;
}
if (bottom % vertical_alignment != 0) {
bottom += vertical_alignment - bottom % vertical_alignment;
}
right = std::min(right, frame_size_.width());
bottom = std::min(bottom, frame_size_.height());
rect = SkIRect::MakeLTRB(left, top, right, bottom);
}
Damage DiffContext::ComputeDamage(const SkIRect& accumulated_buffer_damage,
int horizontal_clip_alignment,
int vertical_clip_alignment) const {
SkRect buffer_damage = SkRect::Make(accumulated_buffer_damage);
buffer_damage.join(damage_);
SkRect frame_damage(damage_);
for (const auto& r : readbacks_) {
SkRect paint_rect = SkRect::Make(r.paint_rect);
SkRect readback_rect = SkRect::Make(r.readback_rect);
// Changes either in readback or paint rect require repainting both readback
// and paint rect.
if (paint_rect.intersects(frame_damage) ||
readback_rect.intersects(frame_damage)) {
frame_damage.join(readback_rect);
frame_damage.join(paint_rect);
buffer_damage.join(readback_rect);
buffer_damage.join(paint_rect);
}
}
Damage res;
buffer_damage.roundOut(&res.buffer_damage);
frame_damage.roundOut(&res.frame_damage);
SkIRect frame_clip = SkIRect::MakeSize(frame_size_);
res.buffer_damage.intersect(frame_clip);
res.frame_damage.intersect(frame_clip);
if (horizontal_clip_alignment > 1 || vertical_clip_alignment > 1) {
AlignRect(res.buffer_damage, horizontal_clip_alignment,
vertical_clip_alignment);
AlignRect(res.frame_damage, horizontal_clip_alignment,
vertical_clip_alignment);
}
return res;
}
SkRect DiffContext::MapRect(const SkRect& rect) {
SkRect mapped_rect(rect);
clip_tracker_.mapRect(&mapped_rect);
return mapped_rect;
}
bool DiffContext::PushCullRect(const SkRect& clip) {
clip_tracker_.clipRect(clip, DlCanvas::ClipOp::kIntersect, false);
return !clip_tracker_.device_cull_rect().isEmpty();
}
SkMatrix DiffContext::GetTransform3x3() const {
return clip_tracker_.matrix_3x3();
}
SkRect DiffContext::GetCullRect() const {
return clip_tracker_.local_cull_rect();
}
void DiffContext::MarkSubtreeDirty(const PaintRegion& previous_paint_region) {
FML_DCHECK(!IsSubtreeDirty());
if (previous_paint_region.is_valid()) {
AddDamage(previous_paint_region);
}
state_.dirty = true;
}
void DiffContext::MarkSubtreeDirty(const SkRect& previous_paint_region) {
FML_DCHECK(!IsSubtreeDirty());
AddDamage(previous_paint_region);
state_.dirty = true;
}
void DiffContext::AddLayerBounds(const SkRect& rect) {
// During painting we cull based on non-overriden transform and then
// override the transform right before paint. Do the same thing here to get
// identical paint rect.
auto transformed_rect = ApplyFilterBoundsAdjustment(MapRect(rect));
if (transformed_rect.intersects(clip_tracker_.device_cull_rect())) {
if (state_.integral_transform) {
clip_tracker_.save();
MakeCurrentTransformIntegral();
transformed_rect = ApplyFilterBoundsAdjustment(MapRect(rect));
clip_tracker_.restore();
}
rects_->push_back(transformed_rect);
if (IsSubtreeDirty()) {
AddDamage(transformed_rect);
}
}
}
void DiffContext::MarkSubtreeHasTextureLayer() {
// Set the has_texture flag on current state and all parent states. That
// way we'll know that we can't skip diff for retained layers because
// they contain a TextureLayer.
for (auto& state : state_stack_) {
state.has_texture = true;
}
state_.has_texture = true;
}
void DiffContext::AddExistingPaintRegion(const PaintRegion& region) {
// Adding paint region for retained layer implies that current subtree is not
// dirty, so we know, for example, that the inherited transforms must match
FML_DCHECK(!IsSubtreeDirty());
if (region.is_valid()) {
rects_->insert(rects_->end(), region.begin(), region.end());
}
}
void DiffContext::AddReadbackRegion(const SkIRect& paint_rect,
const SkIRect& readback_rect) {
Readback readback;
readback.paint_rect = paint_rect;
readback.readback_rect = readback_rect;
readback.position = rects_->size();
// Push empty rect as a placeholder for position in current subtree
rects_->push_back(SkRect::MakeEmpty());
readbacks_.push_back(readback);
}
PaintRegion DiffContext::CurrentSubtreeRegion() const {
bool has_readback = std::any_of(
readbacks_.begin(), readbacks_.end(),
[&](const Readback& r) { return r.position >= state_.rect_index; });
return PaintRegion(rects_, state_.rect_index, rects_->size(), has_readback,
state_.has_texture);
}
void DiffContext::AddDamage(const PaintRegion& damage) {
FML_DCHECK(damage.is_valid());
for (const auto& r : damage) {
damage_.join(r);
}
}
void DiffContext::AddDamage(const SkRect& rect) {
damage_.join(rect);
}
void DiffContext::SetLayerPaintRegion(const Layer* layer,
const PaintRegion& region) {
this_frame_paint_region_map_[layer->unique_id()] = region;
}
PaintRegion DiffContext::GetOldLayerPaintRegion(const Layer* layer) const {
auto i = last_frame_paint_region_map_.find(layer->unique_id());
if (i != last_frame_paint_region_map_.end()) {
return i->second;
} else {
// This is valid when Layer::PreservePaintRegion is called for retained
// layer with zero sized parent clip (these layers are not diffed)
return PaintRegion();
}
}
void DiffContext::Statistics::LogStatistics() {
#if !FLUTTER_RELEASE
FML_TRACE_COUNTER("flutter", "DiffContext", reinterpret_cast<int64_t>(this),
"NewPictures", new_pictures_, "PicturesTooComplexToCompare",
pictures_too_complex_to_compare_, "DeepComparePictures",
deep_compare_pictures_, "SameInstancePictures",
same_instance_pictures_,
"DifferentInstanceButEqualPictures",
different_instance_but_equal_pictures_);
#endif // !FLUTTER_RELEASE
}
} // namespace flutter