mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
add pixel snapping conditional on presence of raster cache (flutter/engine#35981)
This commit is contained in:
parent
ca593fb04c
commit
c4d3db0133
@ -11,14 +11,16 @@
|
||||
namespace flutter {
|
||||
|
||||
std::optional<SkRect> FrameDamage::ComputeClipRect(
|
||||
flutter::LayerTree& layer_tree) {
|
||||
flutter::LayerTree& layer_tree,
|
||||
bool has_raster_cache) {
|
||||
if (layer_tree.root_layer()) {
|
||||
PaintRegionMap empty_paint_region_map;
|
||||
DiffContext context(layer_tree.frame_size(),
|
||||
layer_tree.device_pixel_ratio(),
|
||||
layer_tree.paint_region_map(),
|
||||
prev_layer_tree_ ? prev_layer_tree_->paint_region_map()
|
||||
: empty_paint_region_map);
|
||||
: empty_paint_region_map,
|
||||
has_raster_cache);
|
||||
context.PushCullRect(SkRect::MakeIWH(layer_tree.frame_size().width(),
|
||||
layer_tree.frame_size().height()));
|
||||
{
|
||||
@ -119,7 +121,9 @@ RasterStatus CompositorContext::ScopedFrame::Raster(
|
||||
TRACE_EVENT0("flutter", "CompositorContext::ScopedFrame::Raster");
|
||||
|
||||
std::optional<SkRect> clip_rect =
|
||||
frame_damage ? frame_damage->ComputeClipRect(layer_tree) : std::nullopt;
|
||||
frame_damage
|
||||
? frame_damage->ComputeClipRect(layer_tree, !ignore_raster_cache)
|
||||
: std::nullopt;
|
||||
|
||||
bool root_needs_readback = layer_tree.Preroll(
|
||||
*this, ignore_raster_cache, clip_rect ? *clip_rect : kGiantRect);
|
||||
|
||||
@ -81,7 +81,8 @@ class FrameDamage {
|
||||
// If previous layer tree is not specified, clip rect will be nullopt,
|
||||
// but the paint region of layer_tree will be calculated so that it can be
|
||||
// used for diffing of subsequent frames.
|
||||
std::optional<SkRect> ComputeClipRect(flutter::LayerTree& layer_tree);
|
||||
std::optional<SkRect> ComputeClipRect(flutter::LayerTree& layer_tree,
|
||||
bool has_raster_cache);
|
||||
|
||||
// See Damage::frame_damage.
|
||||
std::optional<SkIRect> GetFrameDamage() const {
|
||||
|
||||
@ -10,12 +10,14 @@ namespace flutter {
|
||||
DiffContext::DiffContext(SkISize frame_size,
|
||||
double frame_device_pixel_ratio,
|
||||
PaintRegionMap& this_frame_paint_region_map,
|
||||
const PaintRegionMap& last_frame_paint_region_map)
|
||||
const PaintRegionMap& last_frame_paint_region_map,
|
||||
bool has_raster_cache)
|
||||
: 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) {}
|
||||
last_frame_paint_region_map_(last_frame_paint_region_map),
|
||||
has_raster_cache_(has_raster_cache) {}
|
||||
|
||||
void DiffContext::BeginSubtree() {
|
||||
state_stack_.push_back(state_);
|
||||
|
||||
@ -45,7 +45,8 @@ class DiffContext {
|
||||
explicit DiffContext(SkISize frame_size,
|
||||
double device_pixel_aspect_ratio,
|
||||
PaintRegionMap& this_frame_paint_region_map,
|
||||
const PaintRegionMap& last_frame_paint_region_map);
|
||||
const PaintRegionMap& last_frame_paint_region_map,
|
||||
bool has_raster_cache);
|
||||
|
||||
// Starts a new subtree.
|
||||
void BeginSubtree();
|
||||
@ -156,6 +157,11 @@ class DiffContext {
|
||||
// frame layer tree.
|
||||
PaintRegion GetOldLayerPaintRegion(const Layer* layer) const;
|
||||
|
||||
// Whether or not a raster cache is being used. If so, we must snap
|
||||
// all transformations to physical pixels if the layer may be raster
|
||||
// cached.
|
||||
bool has_raster_cache() const { return has_raster_cache_; }
|
||||
|
||||
class Statistics {
|
||||
public:
|
||||
// Picture replaced by different picture
|
||||
@ -223,6 +229,7 @@ class DiffContext {
|
||||
|
||||
PaintRegionMap& this_frame_paint_region_map_;
|
||||
const PaintRegionMap& last_frame_paint_region_map_;
|
||||
bool has_raster_cache_;
|
||||
|
||||
void AddDamage(const SkRect& rect);
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "flutter/flow/layers/cacheable_layer.h"
|
||||
#include "flutter/flow/layers/container_layer.h"
|
||||
#include "flutter/flow/paint_utils.h"
|
||||
#include "flutter/flow/raster_cache_util.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
@ -32,6 +33,10 @@ class ClipShapeLayer : public CacheableContainerLayer {
|
||||
context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer));
|
||||
}
|
||||
}
|
||||
if (UsesSaveLayer() && context->has_raster_cache()) {
|
||||
context->SetTransform(
|
||||
RasterCacheUtil::GetIntegralTransCTM(context->GetTransform()));
|
||||
}
|
||||
if (context->PushCullRect(clip_shape_bounds())) {
|
||||
DiffChildren(context, prev);
|
||||
}
|
||||
@ -45,12 +50,16 @@ class ClipShapeLayer : public CacheableContainerLayer {
|
||||
if (!context->cull_rect.intersect(clip_shape_bounds())) {
|
||||
context->cull_rect.setEmpty();
|
||||
}
|
||||
SkMatrix child_matrix = matrix;
|
||||
if (context->raster_cache && uses_save_layer) {
|
||||
child_matrix = RasterCacheUtil::GetIntegralTransCTM(child_matrix);
|
||||
}
|
||||
// We can use the raster_cache for children only when the use_save_layer is
|
||||
// true so if use_save_layer is false we pass the layer_raster_item is
|
||||
// nullptr which mean we don't do raster cache logic.
|
||||
AutoCache cache =
|
||||
AutoCache(uses_save_layer ? layer_raster_cache_item_.get() : nullptr,
|
||||
context, matrix);
|
||||
context, child_matrix);
|
||||
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
|
||||
@ -89,6 +98,9 @@ class ClipShapeLayer : public CacheableContainerLayer {
|
||||
|
||||
AutoCachePaint cache_paint(context);
|
||||
if (context.raster_cache) {
|
||||
context.internal_nodes_canvas->setMatrix(
|
||||
RasterCacheUtil::GetIntegralTransCTM(
|
||||
context.leaf_nodes_canvas->getTotalMatrix()));
|
||||
if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -27,6 +27,11 @@ void ColorFilterLayer::Diff(DiffContext* context, const Layer* old_layer) {
|
||||
}
|
||||
}
|
||||
|
||||
if (context->has_raster_cache()) {
|
||||
context->SetTransform(
|
||||
RasterCacheUtil::GetIntegralTransCTM(context->GetTransform()));
|
||||
}
|
||||
|
||||
DiffChildren(context, prev);
|
||||
|
||||
context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
|
||||
@ -38,7 +43,12 @@ void ColorFilterLayer::Preroll(PrerollContext* context,
|
||||
Layer::AutoPrerollSaveLayerState::Create(context);
|
||||
AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, matrix);
|
||||
|
||||
ContainerLayer::Preroll(context, matrix);
|
||||
SkMatrix child_matrix = matrix;
|
||||
if (context->raster_cache) {
|
||||
child_matrix = RasterCacheUtil::GetIntegralTransCTM(child_matrix);
|
||||
}
|
||||
|
||||
ContainerLayer::Preroll(context, child_matrix);
|
||||
// We always use a saveLayer (or a cached rendering), so we
|
||||
// can always apply opacity in those cases.
|
||||
context->subtree_can_inherit_opacity = true;
|
||||
@ -49,6 +59,9 @@ void ColorFilterLayer::Paint(PaintContext& context) const {
|
||||
FML_DCHECK(needs_painting(context));
|
||||
|
||||
if (context.raster_cache) {
|
||||
context.internal_nodes_canvas->setMatrix(
|
||||
RasterCacheUtil::GetIntegralTransCTM(
|
||||
context.leaf_nodes_canvas->getTotalMatrix()));
|
||||
AutoCachePaint cache_paint(context);
|
||||
if (layer_raster_cache_item_->IsCacheChildren()) {
|
||||
cache_paint.setColorFilter(filter_.get());
|
||||
|
||||
@ -52,6 +52,10 @@ void DisplayListLayer::Diff(DiffContext* context, const Layer* old_layer) {
|
||||
#endif
|
||||
}
|
||||
context->PushTransform(SkMatrix::Translate(offset_.x(), offset_.y()));
|
||||
if (context->has_raster_cache()) {
|
||||
context->SetTransform(
|
||||
RasterCacheUtil::GetIntegralTransCTM(context->GetTransform()));
|
||||
}
|
||||
context->AddLayerBounds(display_list()->bounds());
|
||||
context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
|
||||
}
|
||||
@ -95,9 +99,13 @@ void DisplayListLayer::Preroll(PrerollContext* context,
|
||||
const SkMatrix& matrix) {
|
||||
TRACE_EVENT0("flutter", "DisplayListLayer::Preroll");
|
||||
DisplayList* disp_list = display_list();
|
||||
SkMatrix child_matrix = matrix;
|
||||
if (context->raster_cache) {
|
||||
child_matrix = RasterCacheUtil::GetIntegralTransCTM(child_matrix);
|
||||
}
|
||||
|
||||
AutoCache cache =
|
||||
AutoCache(display_list_raster_cache_item_.get(), context, matrix);
|
||||
AutoCache(display_list_raster_cache_item_.get(), context, child_matrix);
|
||||
if (disp_list->can_apply_group_opacity()) {
|
||||
context->subtree_can_inherit_opacity = true;
|
||||
}
|
||||
@ -111,6 +119,10 @@ void DisplayListLayer::Paint(PaintContext& context) const {
|
||||
|
||||
SkAutoCanvasRestore save(context.leaf_nodes_canvas, true);
|
||||
context.leaf_nodes_canvas->translate(offset_.x(), offset_.y());
|
||||
if (context.raster_cache) {
|
||||
context.leaf_nodes_canvas->setMatrix(RasterCacheUtil::GetIntegralTransCTM(
|
||||
context.leaf_nodes_canvas->getTotalMatrix()));
|
||||
}
|
||||
|
||||
if (context.raster_cache && display_list_raster_cache_item_) {
|
||||
AutoCachePaint cache_paint(context);
|
||||
|
||||
@ -325,10 +325,25 @@ TEST_F(DisplayListLayerDiffTest, FractionalTranslation) {
|
||||
tree1.root()->Add(
|
||||
CreateDisplayListLayer(display_list, SkPoint::Make(0.5, 0.5)));
|
||||
|
||||
auto damage = DiffLayerTree(tree1, MockLayerTree());
|
||||
auto damage =
|
||||
DiffLayerTree(tree1, MockLayerTree(), SkIRect::MakeEmpty(), 0, 0,
|
||||
/*use_raster_cache=*/false);
|
||||
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 61, 61));
|
||||
}
|
||||
|
||||
TEST_F(DisplayListLayerDiffTest, FractionalTranslationWithRasterCache) {
|
||||
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
|
||||
|
||||
MockLayerTree tree1;
|
||||
tree1.root()->Add(
|
||||
CreateDisplayListLayer(display_list, SkPoint::Make(0.5, 0.5)));
|
||||
|
||||
auto damage =
|
||||
DiffLayerTree(tree1, MockLayerTree(), SkIRect::MakeEmpty(), 0, 0,
|
||||
/*use_raster_cache=*/true);
|
||||
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(11, 11, 61, 61));
|
||||
}
|
||||
|
||||
TEST_F(DisplayListLayerDiffTest, DisplayListCompare) {
|
||||
MockLayerTree tree1;
|
||||
auto display_list1 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
|
||||
|
||||
@ -25,6 +25,11 @@ void ImageFilterLayer::Diff(DiffContext* context, const Layer* old_layer) {
|
||||
}
|
||||
}
|
||||
|
||||
if (context->has_raster_cache()) {
|
||||
context->SetTransform(
|
||||
RasterCacheUtil::GetIntegralTransCTM(context->GetTransform()));
|
||||
}
|
||||
|
||||
if (filter_) {
|
||||
auto filter = filter_->makeWithLocalMatrix(context->GetTransform());
|
||||
if (filter) {
|
||||
@ -51,7 +56,11 @@ void ImageFilterLayer::Preroll(PrerollContext* context,
|
||||
AutoCache cache = AutoCache(layer_raster_cache_item_.get(), context, matrix);
|
||||
|
||||
SkRect child_bounds = SkRect::MakeEmpty();
|
||||
PrerollChildren(context, matrix, &child_bounds);
|
||||
SkMatrix child_matrix = matrix;
|
||||
if (context->raster_cache) {
|
||||
child_matrix = RasterCacheUtil::GetIntegralTransCTM(child_matrix);
|
||||
}
|
||||
PrerollChildren(context, child_matrix, &child_bounds);
|
||||
|
||||
// We always paint with a saveLayer (or a cached rendering),
|
||||
// so we can always apply opacity in any of those cases.
|
||||
@ -74,7 +83,7 @@ void ImageFilterLayer::Preroll(PrerollContext* context,
|
||||
// So in here we reset the LayerRasterCacheItem cache state.
|
||||
layer_raster_cache_item_->MarkNotCacheChildren();
|
||||
|
||||
transformed_filter_ = filter_->makeWithLocalMatrix(matrix);
|
||||
transformed_filter_ = filter_->makeWithLocalMatrix(child_matrix);
|
||||
if (transformed_filter_) {
|
||||
layer_raster_cache_item_->MarkCacheChildren();
|
||||
}
|
||||
@ -86,6 +95,9 @@ void ImageFilterLayer::Paint(PaintContext& context) const {
|
||||
|
||||
AutoCachePaint cache_paint(context);
|
||||
if (context.raster_cache) {
|
||||
context.internal_nodes_canvas->setMatrix(
|
||||
RasterCacheUtil::GetIntegralTransCTM(
|
||||
context.leaf_nodes_canvas->getTotalMatrix()));
|
||||
if (layer_raster_cache_item_->IsCacheChildren()) {
|
||||
cache_paint.setImageFilter(transformed_filter_.get());
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "flutter/flow/layers/opacity_layer.h"
|
||||
|
||||
#include "flutter/flow/layers/cacheable_layer.h"
|
||||
#include "flutter/flow/raster_cache_util.h"
|
||||
#include "third_party/skia/include/core/SkPaint.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -27,6 +28,10 @@ void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) {
|
||||
}
|
||||
}
|
||||
context->PushTransform(SkMatrix::Translate(offset_.fX, offset_.fY));
|
||||
if (context->has_raster_cache()) {
|
||||
context->SetTransform(
|
||||
RasterCacheUtil::GetIntegralTransCTM(context->GetTransform()));
|
||||
}
|
||||
DiffChildren(context, prev);
|
||||
context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
|
||||
}
|
||||
@ -85,6 +90,11 @@ void OpacityLayer::Paint(PaintContext& context) const {
|
||||
|
||||
SkAutoCanvasRestore save(context.internal_nodes_canvas, true);
|
||||
context.internal_nodes_canvas->translate(offset_.fX, offset_.fY);
|
||||
if (context.raster_cache) {
|
||||
context.internal_nodes_canvas->setMatrix(
|
||||
RasterCacheUtil::GetIntegralTransCTM(
|
||||
context.leaf_nodes_canvas->getTotalMatrix()));
|
||||
}
|
||||
|
||||
SkScalar inherited_opacity = context.inherited_opacity;
|
||||
SkScalar subtree_opacity = opacity() * inherited_opacity;
|
||||
|
||||
@ -644,9 +644,23 @@ TEST_F(OpacityLayerDiffTest, FractionalTranslation) {
|
||||
MockLayerTree tree1;
|
||||
tree1.root()->Add(layer);
|
||||
|
||||
auto damage = DiffLayerTree(tree1, MockLayerTree());
|
||||
auto damage = DiffLayerTree(tree1, MockLayerTree(), SkIRect::MakeEmpty(), 0,
|
||||
0, /*use_raster_cache=*/false);
|
||||
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 61, 61));
|
||||
}
|
||||
|
||||
TEST_F(OpacityLayerDiffTest, FractionalTranslationWithRasterCache) {
|
||||
auto picture = CreateDisplayListLayer(
|
||||
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1));
|
||||
auto layer = CreateOpacityLater({picture}, 128, SkPoint::Make(0.5, 0.5));
|
||||
|
||||
MockLayerTree tree1;
|
||||
tree1.root()->Add(layer);
|
||||
|
||||
auto damage = DiffLayerTree(tree1, MockLayerTree(), SkIRect::MakeEmpty(), 0,
|
||||
0, /*use_raster_cache=*/true);
|
||||
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(11, 11, 61, 61));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -26,6 +26,10 @@ void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) {
|
||||
context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer));
|
||||
}
|
||||
}
|
||||
if (context->has_raster_cache()) {
|
||||
context->SetTransform(
|
||||
RasterCacheUtil::GetIntegralTransCTM(context->GetTransform()));
|
||||
}
|
||||
DiffChildren(context, prev);
|
||||
|
||||
context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
|
||||
@ -49,6 +53,12 @@ void ShaderMaskLayer::Paint(PaintContext& context) const {
|
||||
|
||||
AutoCachePaint cache_paint(context);
|
||||
|
||||
if (context.raster_cache) {
|
||||
context.internal_nodes_canvas->setMatrix(
|
||||
RasterCacheUtil::GetIntegralTransCTM(
|
||||
context.leaf_nodes_canvas->getTotalMatrix()));
|
||||
}
|
||||
|
||||
if (context.raster_cache) {
|
||||
if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) {
|
||||
return;
|
||||
|
||||
@ -32,38 +32,14 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const {
|
||||
TRACE_EVENT0("flutter", "RasterCacheResult::draw");
|
||||
SkAutoCanvasRestore auto_restore(&canvas, true);
|
||||
|
||||
SkRect bounds =
|
||||
RasterCacheUtil::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix());
|
||||
#ifndef NDEBUG
|
||||
// The image dimensions should always be larger than the device bounds and
|
||||
// smaller than the device bounds plus one pixel, at the same time, we must
|
||||
// introduce epsilon to solve the round-off error. The value of epsilon is
|
||||
// 1/512, which represents half of an AA sample.
|
||||
float epsilon = 1 / 512.0;
|
||||
FML_DCHECK(image_->dimensions().width() - bounds.width() > -epsilon &&
|
||||
image_->dimensions().height() - bounds.height() > -epsilon &&
|
||||
image_->dimensions().width() - bounds.width() < 1 + epsilon &&
|
||||
image_->dimensions().height() - bounds.height() < 1 + epsilon);
|
||||
#endif
|
||||
SkRect bounds = RasterCacheUtil::GetRoundedOutDeviceBounds(
|
||||
logical_rect_, canvas.getTotalMatrix());
|
||||
FML_DCHECK(std::abs(bounds.width() - image_->dimensions().width()) <= 1 &&
|
||||
std::abs(bounds.height() - image_->dimensions().height()) <= 1);
|
||||
canvas.resetMatrix();
|
||||
flow_.Step();
|
||||
|
||||
bool exceeds_bounds = bounds.fLeft + image_->dimensions().width() >
|
||||
SkScalarCeilToScalar(bounds.fRight) ||
|
||||
bounds.fTop + image_->dimensions().height() >
|
||||
SkScalarCeilToScalar(bounds.fBottom);
|
||||
|
||||
// Make sure raster cache doesn't bleed to physical pixels outside of
|
||||
// original bounds. https://github.com/flutter/flutter/issues/110002
|
||||
if (exceeds_bounds) {
|
||||
canvas.save();
|
||||
canvas.clipRect(SkRect::Make(bounds.roundOut()));
|
||||
}
|
||||
canvas.drawImage(image_, bounds.fLeft, bounds.fTop, SkSamplingOptions(),
|
||||
paint);
|
||||
if (exceeds_bounds) {
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
RasterCache::RasterCache(size_t access_threshold,
|
||||
@ -80,14 +56,12 @@ std::unique_ptr<RasterCacheResult> RasterCache::Rasterize(
|
||||
const {
|
||||
TRACE_EVENT0("flutter", "RasterCachePopulate");
|
||||
|
||||
SkRect dest_rect =
|
||||
RasterCacheUtil::GetDeviceBounds(context.logical_rect, context.matrix);
|
||||
// we always round out here so that the texture is integer sized.
|
||||
int width = SkScalarCeilToInt(dest_rect.width());
|
||||
int height = SkScalarCeilToInt(dest_rect.height());
|
||||
SkRect dest_rect = RasterCacheUtil::GetRoundedOutDeviceBounds(
|
||||
context.logical_rect, context.matrix);
|
||||
|
||||
const SkImageInfo image_info = SkImageInfo::MakeN32Premul(
|
||||
width, height, sk_ref_sp(context.dst_color_space));
|
||||
const SkImageInfo image_info =
|
||||
SkImageInfo::MakeN32Premul(dest_rect.width(), dest_rect.height(),
|
||||
sk_ref_sp(context.dst_color_space));
|
||||
|
||||
sk_sp<SkSurface> surface =
|
||||
context.gr_context ? SkSurface::MakeRenderTarget(
|
||||
@ -139,7 +113,8 @@ bool RasterCache::UpdateCacheEntry(
|
||||
int RasterCache::MarkSeen(const RasterCacheKeyID& id,
|
||||
const SkMatrix& matrix,
|
||||
bool visible) const {
|
||||
RasterCacheKey key = RasterCacheKey(id, matrix);
|
||||
RasterCacheKey key =
|
||||
RasterCacheKey(id, RasterCacheUtil::GetIntegralTransCTM(matrix));
|
||||
Entry& entry = cache_[key];
|
||||
entry.encountered_this_frame = true;
|
||||
entry.visible_this_frame = visible;
|
||||
@ -151,7 +126,8 @@ int RasterCache::MarkSeen(const RasterCacheKeyID& id,
|
||||
|
||||
int RasterCache::GetAccessCount(const RasterCacheKeyID& id,
|
||||
const SkMatrix& matrix) const {
|
||||
RasterCacheKey key = RasterCacheKey(id, matrix);
|
||||
RasterCacheKey key =
|
||||
RasterCacheKey(id, RasterCacheUtil::GetIntegralTransCTM(matrix));
|
||||
auto entry = cache_.find(key);
|
||||
if (entry != cache_.cend()) {
|
||||
return entry->second.accesses_since_visible;
|
||||
@ -161,7 +137,8 @@ int RasterCache::GetAccessCount(const RasterCacheKeyID& id,
|
||||
|
||||
bool RasterCache::HasEntry(const RasterCacheKeyID& id,
|
||||
const SkMatrix& matrix) const {
|
||||
RasterCacheKey key = RasterCacheKey(id, matrix);
|
||||
RasterCacheKey key =
|
||||
RasterCacheKey(id, RasterCacheUtil::GetIntegralTransCTM(matrix));
|
||||
if (cache_.find(key) != cache_.cend()) {
|
||||
return true;
|
||||
}
|
||||
@ -171,7 +148,8 @@ bool RasterCache::HasEntry(const RasterCacheKeyID& id,
|
||||
bool RasterCache::Draw(const RasterCacheKeyID& id,
|
||||
SkCanvas& canvas,
|
||||
const SkPaint* paint) const {
|
||||
auto it = cache_.find(RasterCacheKey(id, canvas.getTotalMatrix()));
|
||||
auto it = cache_.find(RasterCacheKey(
|
||||
id, RasterCacheUtil::GetIntegralTransCTM(canvas.getTotalMatrix())));
|
||||
if (it == cache_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -787,67 +787,5 @@ TEST_F(RasterCacheTest, RasterCacheKeyID_LayerChildrenIds) {
|
||||
ASSERT_EQ(ids, expected_ids);
|
||||
}
|
||||
|
||||
TEST_F(RasterCacheTest, RasterCacheBleedingNoClipNeeded) {
|
||||
SkImageInfo info =
|
||||
SkImageInfo::MakeN32(40, 40, SkAlphaType::kOpaque_SkAlphaType);
|
||||
|
||||
auto image = SkImage::MakeRasterData(
|
||||
info, SkData::MakeUninitialized(40 * 40 * 4), 40 * 4);
|
||||
auto canvas = MockCanvas();
|
||||
canvas.setMatrix(SkMatrix::Scale(2, 2));
|
||||
// Drawing cached image does not exceeds physical pixels of the original
|
||||
// bounds and does not need to be clipped.
|
||||
auto cache_result =
|
||||
RasterCacheResult(image, SkRect::MakeXYWH(100.3, 100.3, 20, 20), "");
|
||||
auto paint = SkPaint();
|
||||
cache_result.draw(canvas, &paint);
|
||||
|
||||
EXPECT_EQ(canvas.draw_calls(),
|
||||
std::vector({
|
||||
MockCanvas::DrawCall{
|
||||
0, MockCanvas::SetMatrixData{SkM44::Scale(2, 2)}},
|
||||
MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
|
||||
MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}},
|
||||
MockCanvas::DrawCall{
|
||||
1, MockCanvas::DrawImageData{image, 200.6, 200.6,
|
||||
SkSamplingOptions(), paint}},
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}},
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(RasterCacheTest, RasterCacheBleedingClipNeeded) {
|
||||
SkImageInfo info =
|
||||
SkImageInfo::MakeN32(40, 40, SkAlphaType::kOpaque_SkAlphaType);
|
||||
|
||||
auto image = SkImage::MakeRasterData(
|
||||
info, SkData::MakeUninitialized(40 * 40 * 4), 40 * 4);
|
||||
auto canvas = MockCanvas();
|
||||
canvas.setMatrix(SkMatrix::Scale(2, 2));
|
||||
|
||||
auto cache_result =
|
||||
RasterCacheResult(image, SkRect::MakeXYWH(100.3, 100.3, 19.6, 19.6), "");
|
||||
auto paint = SkPaint();
|
||||
cache_result.draw(canvas, &paint);
|
||||
|
||||
EXPECT_EQ(
|
||||
canvas.draw_calls(),
|
||||
std::vector({
|
||||
MockCanvas::DrawCall{0,
|
||||
MockCanvas::SetMatrixData{SkM44::Scale(2, 2)}},
|
||||
MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
|
||||
MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}},
|
||||
MockCanvas::DrawCall{1, MockCanvas::SaveData{2}},
|
||||
MockCanvas::DrawCall{
|
||||
2, MockCanvas::ClipRectData{SkRect::MakeLTRB(200, 200, 240, 240),
|
||||
SkClipOp::kIntersect,
|
||||
MockCanvas::kHard_ClipEdgeStyle}},
|
||||
MockCanvas::DrawCall{
|
||||
2, MockCanvas::DrawImageData{image, 200.6, 200.6,
|
||||
SkSamplingOptions(), paint}},
|
||||
MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}},
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -54,6 +54,39 @@ struct RasterCacheUtil {
|
||||
ctm.mapRect(&device_rect, rect);
|
||||
return device_rect;
|
||||
}
|
||||
|
||||
static SkRect GetRoundedOutDeviceBounds(const SkRect& rect,
|
||||
const SkMatrix& ctm) {
|
||||
SkRect device_rect;
|
||||
ctm.mapRect(&device_rect, rect);
|
||||
device_rect.roundOut(&device_rect);
|
||||
return device_rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snap the translation components of the matrix to integers.
|
||||
*
|
||||
* The snapping will only happen if the matrix only has scale and translation
|
||||
* transformations. This is used, along with GetRoundedOutDeviceBounds, to
|
||||
* ensure that the textures drawn by the raster cache are exactly aligned to
|
||||
* physical pixels. Any layers that participate in raster caching must align
|
||||
* themselves to physical pixels even when not cached to prevent a change in
|
||||
* apparent location if caching is later applied.
|
||||
*
|
||||
* @param ctm the current transformation matrix.
|
||||
* @return SkMatrix the snapped transformation matrix.
|
||||
*/
|
||||
static SkMatrix GetIntegralTransCTM(const SkMatrix& ctm) {
|
||||
// Avoid integral snapping if the matrix has complex transformation to avoid
|
||||
// the artifact observed in https://github.com/flutter/flutter/issues/41654.
|
||||
if (!ctm.isScaleTranslate()) {
|
||||
return ctm;
|
||||
}
|
||||
SkMatrix result = ctm;
|
||||
result[SkMatrix::kMTransX] = SkScalarRoundToScalar(ctm.getTranslateX());
|
||||
result[SkMatrix::kMTransY] = SkScalarRoundToScalar(ctm.getTranslateY());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -17,11 +17,12 @@ Damage DiffContextTest::DiffLayerTree(MockLayerTree& layer_tree,
|
||||
const MockLayerTree& old_layer_tree,
|
||||
const SkIRect& additional_damage,
|
||||
int horizontal_clip_alignment,
|
||||
int vertical_clip_alignment) {
|
||||
int vertical_clip_alignment,
|
||||
bool use_raster_cache) {
|
||||
FML_CHECK(layer_tree.size() == old_layer_tree.size());
|
||||
|
||||
DiffContext dc(layer_tree.size(), 1, layer_tree.paint_region_map(),
|
||||
old_layer_tree.paint_region_map());
|
||||
old_layer_tree.paint_region_map(), use_raster_cache);
|
||||
dc.PushCullRect(
|
||||
SkRect::MakeIWH(layer_tree.size().width(), layer_tree.size().height()));
|
||||
layer_tree.root()->Diff(&dc, old_layer_tree.root());
|
||||
|
||||
@ -39,7 +39,8 @@ class DiffContextTest : public ThreadTest {
|
||||
const MockLayerTree& old_layer_tree,
|
||||
const SkIRect& additional_damage = SkIRect::MakeEmpty(),
|
||||
int horizontal_clip_alignment = 0,
|
||||
int vertical_alignment = 0);
|
||||
int vertical_alignment = 0,
|
||||
bool use_raster_cache = true);
|
||||
|
||||
// Create display list consisting of filled rect with given color; Being able
|
||||
// to specify different color is useful to test deep comparison of pictures
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user