mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
* Take SUPPORT_FRACTIONAL_TRANSLATION into account when diffing layers * Store cull_rect in device coordinates This removes the need to update cull rect coordinates when overriding transform and is also consistent with how Layer::needs_painting() works. * Use original transform for culling, but overriden transform to get paint coordinates. This is to replicate the paint process during diffing, where layers are culled first and only then after paint the matrix is overriden. * Rewrite AddLayerBounds based on @flar's suggestions
182 lines
5.9 KiB
C++
182 lines
5.9 KiB
C++
// 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 {
|
|
|
|
#ifdef FLUTTER_ENABLE_DIFF_CONTEXT
|
|
|
|
DiffContext::DiffContext(SkISize frame_size,
|
|
double frame_device_pixel_ratio,
|
|
PaintRegionMap& this_frame_paint_region_map,
|
|
const PaintRegionMap& last_frame_paint_region_map)
|
|
: rects_(std::make_shared<std::vector<SkRect>>()),
|
|
frame_size_(frame_size),
|
|
frame_device_pixel_ratio_(frame_device_pixel_ratio),
|
|
this_frame_paint_region_map_(this_frame_paint_region_map),
|
|
last_frame_paint_region_map_(last_frame_paint_region_map) {}
|
|
|
|
void DiffContext::BeginSubtree() {
|
|
state_stack_.push_back(state_);
|
|
state_.rect_index_ = rects_->size();
|
|
if (state_.transform_override) {
|
|
state_.transform = *state_.transform_override;
|
|
state_.transform_override = std::nullopt;
|
|
}
|
|
}
|
|
|
|
void DiffContext::EndSubtree() {
|
|
FML_DCHECK(!state_stack_.empty());
|
|
state_ = std::move(state_stack_.back());
|
|
state_stack_.pop_back();
|
|
}
|
|
|
|
DiffContext::State::State()
|
|
: dirty(false), cull_rect(kGiantRect), rect_index_(0) {}
|
|
|
|
void DiffContext::PushTransform(const SkMatrix& transform) {
|
|
state_.transform.preConcat(transform);
|
|
}
|
|
|
|
void DiffContext::SetTransform(const SkMatrix& transform) {
|
|
state_.transform_override = transform;
|
|
}
|
|
|
|
Damage DiffContext::ComputeDamage(
|
|
const SkIRect& accumulated_buffer_damage) const {
|
|
SkRect buffer_damage = SkRect::Make(accumulated_buffer_damage);
|
|
buffer_damage.join(damage_);
|
|
SkRect frame_damage(damage_);
|
|
|
|
for (const auto& r : readbacks_) {
|
|
SkRect rect = SkRect::Make(r.rect);
|
|
if (rect.intersects(frame_damage)) {
|
|
frame_damage.join(rect);
|
|
}
|
|
if (rect.intersects(buffer_damage)) {
|
|
buffer_damage.join(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);
|
|
return res;
|
|
}
|
|
|
|
bool DiffContext::PushCullRect(const SkRect& clip) {
|
|
SkRect cull_rect = state_.transform.mapRect(clip);
|
|
return state_.cull_rect.intersect(cull_rect);
|
|
}
|
|
|
|
SkRect DiffContext::GetCullRect() const {
|
|
SkMatrix inverse_transform;
|
|
// Perspective projections don't produce rectangles that are useful for
|
|
// culling for some reason.
|
|
if (!state_.transform.hasPerspective() &&
|
|
state_.transform.invert(&inverse_transform)) {
|
|
return inverse_transform.mapRect(state_.cull_rect);
|
|
} else {
|
|
return kGiantRect;
|
|
}
|
|
}
|
|
|
|
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::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 = state_.transform.mapRect(rect);
|
|
if (transformed_rect.intersects(state_.cull_rect)) {
|
|
auto paint_rect = state_.transform_override
|
|
? state_.transform_override->mapRect(rect)
|
|
: transformed_rect;
|
|
rects_->push_back(paint_rect);
|
|
if (IsSubtreeDirty()) {
|
|
AddDamage(paint_rect);
|
|
}
|
|
}
|
|
}
|
|
|
|
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& rect) {
|
|
Readback readback;
|
|
readback.rect = rect;
|
|
readback.position = rects_->size();
|
|
// Push empty rect as a placeholder for position in current subtree
|
|
rects_->push_back(SkRect::MakeEmpty());
|
|
readbacks_.push_back(std::move(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);
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
#endif // FLUTTER_ENABLE_DIFF_CONTEXT
|
|
|
|
} // namespace flutter
|