Allow raster caching any layer subtree (flutter/engine#6442)
We first test this with OpacityLayer. This test alone (without retained rendering) should have ~30% speedup as we'll have fewer render target switches by snapshoting in the Preroll instead of saveLayer in the Paint. In my local flutter_gallery transition perf tests, the average frame time drops from ~16ms to ~12ms. https://github.com/flutter/flutter/issues/21756
This commit is contained in:
@@ -63,7 +63,7 @@ CompositorContext::ScopedFrame::~ScopedFrame() {
|
||||
bool CompositorContext::ScopedFrame::Raster(flow::LayerTree& layer_tree,
|
||||
bool ignore_raster_cache) {
|
||||
layer_tree.Preroll(*this, ignore_raster_cache);
|
||||
layer_tree.Paint(*this);
|
||||
layer_tree.Paint(*this, ignore_raster_cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,8 @@ std::ostream& operator<<(std::ostream& os, const flow::MatrixDecomposition& m) {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const SkMatrix& m) {
|
||||
SkString string;
|
||||
string.printf(
|
||||
"[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
|
||||
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
|
||||
string.printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
|
||||
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
|
||||
os << string.c_str();
|
||||
return os;
|
||||
}
|
||||
@@ -72,8 +71,9 @@ std::ostream& operator<<(std::ostream& os, const SkPoint& r) {
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const flow::RasterCacheKey& k) {
|
||||
os << "Picture: " << k.picture_id() << " matrix: " << k.matrix();
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const flow::PictureRasterCacheKey& k) {
|
||||
os << "Picture: " << k.id() << " matrix: " << k.matrix();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#define DEF_PRINTER(x) std::ostream& operator<<(std::ostream&, const x&);
|
||||
|
||||
DEF_PRINTER(flow::MatrixDecomposition);
|
||||
DEF_PRINTER(flow::RasterCacheKey);
|
||||
DEF_PRINTER(flow::PictureRasterCacheKey);
|
||||
DEF_PRINTER(SkISize);
|
||||
DEF_PRINTER(SkMatrix);
|
||||
DEF_PRINTER(SkMatrix44);
|
||||
|
||||
@@ -40,6 +40,19 @@ enum Clip { none, hardEdge, antiAlias, antiAliasWithSaveLayer };
|
||||
|
||||
class ContainerLayer;
|
||||
|
||||
struct PrerollContext {
|
||||
RasterCache* raster_cache;
|
||||
GrContext* gr_context;
|
||||
SkColorSpace* dst_color_space;
|
||||
SkRect child_paint_bounds;
|
||||
|
||||
// The following allows us to paint in the end of subtree preroll
|
||||
const Stopwatch& frame_time;
|
||||
const Stopwatch& engine_time;
|
||||
TextureRegistry& texture_registry;
|
||||
const bool checkerboard_offscreen_layers;
|
||||
};
|
||||
|
||||
// Represents a single composited layer. Created on the UI thread but then
|
||||
// subquently used on the Rasterizer thread.
|
||||
class Layer {
|
||||
@@ -47,13 +60,6 @@ class Layer {
|
||||
Layer();
|
||||
virtual ~Layer();
|
||||
|
||||
struct PrerollContext {
|
||||
RasterCache* raster_cache;
|
||||
GrContext* gr_context;
|
||||
SkColorSpace* dst_color_space;
|
||||
SkRect child_paint_bounds;
|
||||
};
|
||||
|
||||
virtual void Preroll(PrerollContext* context, const SkMatrix& matrix);
|
||||
|
||||
struct PaintContext {
|
||||
@@ -61,6 +67,7 @@ class Layer {
|
||||
const Stopwatch& frame_time;
|
||||
const Stopwatch& engine_time;
|
||||
TextureRegistry& texture_registry;
|
||||
const RasterCache* raster_cache;
|
||||
const bool checkerboard_offscreen_layers;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,12 +25,15 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
|
||||
frame.canvas() ? frame.canvas()->imageInfo().colorSpace() : nullptr;
|
||||
frame.context().raster_cache().SetCheckboardCacheImages(
|
||||
checkerboard_raster_cache_images_);
|
||||
Layer::PrerollContext context = {
|
||||
PrerollContext context = {
|
||||
ignore_raster_cache ? nullptr : &frame.context().raster_cache(),
|
||||
frame.gr_context(),
|
||||
color_space,
|
||||
SkRect::MakeEmpty(),
|
||||
};
|
||||
frame.context().frame_time(),
|
||||
frame.context().engine_time(),
|
||||
frame.context().texture_registry(),
|
||||
checkerboard_offscreen_layers_};
|
||||
|
||||
root_layer_->Preroll(&context, frame.root_surface_transformation());
|
||||
}
|
||||
@@ -60,15 +63,16 @@ void LayerTree::UpdateScene(SceneUpdateContext& context,
|
||||
}
|
||||
#endif
|
||||
|
||||
void LayerTree::Paint(CompositorContext::ScopedFrame& frame) const {
|
||||
void LayerTree::Paint(CompositorContext::ScopedFrame& frame,
|
||||
bool ignore_raster_cache) const {
|
||||
TRACE_EVENT0("flutter", "LayerTree::Paint");
|
||||
Layer::PaintContext context = {
|
||||
*frame.canvas(), //
|
||||
frame.context().frame_time(), //
|
||||
frame.context().engine_time(), //
|
||||
frame.context().texture_registry(), //
|
||||
checkerboard_offscreen_layers_ //
|
||||
};
|
||||
*frame.canvas(),
|
||||
frame.context().frame_time(),
|
||||
frame.context().engine_time(),
|
||||
frame.context().texture_registry(),
|
||||
ignore_raster_cache ? nullptr : &frame.context().raster_cache(),
|
||||
checkerboard_offscreen_layers_};
|
||||
|
||||
if (root_layer_->needs_painting())
|
||||
root_layer_->Paint(context);
|
||||
@@ -84,24 +88,29 @@ sk_sp<SkPicture> LayerTree::Flatten(const SkRect& bounds) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Layer::PrerollContext preroll_context{
|
||||
nullptr, // raster_cache (don't consult the cache)
|
||||
nullptr, // gr_context (used for the raster cache)
|
||||
nullptr, // SkColorSpace* dst_color_space
|
||||
SkRect::MakeEmpty(), // SkRect child_paint_bounds
|
||||
};
|
||||
|
||||
const Stopwatch unused_stopwatch;
|
||||
TextureRegistry unused_texture_registry;
|
||||
SkMatrix root_surface_transformation;
|
||||
// No root surface transformation. So assume identity.
|
||||
root_surface_transformation.reset();
|
||||
|
||||
PrerollContext preroll_context{
|
||||
nullptr, // raster_cache (don't consult the cache)
|
||||
nullptr, // gr_context (used for the raster cache)
|
||||
nullptr, // SkColorSpace* dst_color_space
|
||||
SkRect::MakeEmpty(), // SkRect child_paint_bounds
|
||||
unused_stopwatch, // frame time (dont care)
|
||||
unused_stopwatch, // engine time (dont care)
|
||||
unused_texture_registry, // texture registry (not supported)
|
||||
false, // checkerboard_offscreen_layers
|
||||
};
|
||||
|
||||
Layer::PaintContext paint_context = {
|
||||
*canvas, // canvas
|
||||
unused_stopwatch, // frame time (dont care)
|
||||
unused_stopwatch, // engine time (dont care)
|
||||
unused_texture_registry, // texture registry (not supported)
|
||||
nullptr, // raster cache
|
||||
false // checkerboard offscreen layers
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ class LayerTree {
|
||||
scenic::ContainerNode& container);
|
||||
#endif
|
||||
|
||||
void Paint(CompositorContext::ScopedFrame& frame) const;
|
||||
void Paint(CompositorContext::ScopedFrame& frame,
|
||||
bool ignore_raster_cache = false) const;
|
||||
|
||||
sk_sp<SkPicture> Flatten(const SkRect& bounds);
|
||||
|
||||
|
||||
@@ -10,6 +10,18 @@ OpacityLayer::OpacityLayer() = default;
|
||||
|
||||
OpacityLayer::~OpacityLayer() = default;
|
||||
|
||||
void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
ContainerLayer::Preroll(context, matrix);
|
||||
if (context->raster_cache && layers().size() == 1) {
|
||||
std::shared_ptr<Layer> child = layers()[0];
|
||||
SkMatrix ctm = matrix;
|
||||
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
|
||||
ctm = RasterCache::GetIntegralTransCTM(ctm);
|
||||
#endif
|
||||
context->raster_cache->Prepare(context, child, ctm);
|
||||
}
|
||||
}
|
||||
|
||||
void OpacityLayer::Paint(PaintContext& context) const {
|
||||
TRACE_EVENT0("flutter", "OpacityLayer::Paint");
|
||||
FML_DCHECK(needs_painting());
|
||||
@@ -17,7 +29,23 @@ void OpacityLayer::Paint(PaintContext& context) const {
|
||||
SkPaint paint;
|
||||
paint.setAlpha(alpha_);
|
||||
|
||||
Layer::AutoSaveLayer save =
|
||||
SkAutoCanvasRestore save(&context.canvas, true);
|
||||
|
||||
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
|
||||
context.canvas.setMatrix(
|
||||
RasterCache::GetIntegralTransCTM(context.canvas.getTotalMatrix()));
|
||||
#endif
|
||||
|
||||
if (layers().size() == 1 && context.raster_cache) {
|
||||
const SkMatrix& ctm = context.canvas.getTotalMatrix();
|
||||
RasterCacheResult child_cache = context.raster_cache->Get(layers()[0], ctm);
|
||||
if (child_cache.is_valid()) {
|
||||
child_cache.draw(context.canvas, &paint);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Layer::AutoSaveLayer save_layer =
|
||||
Layer::AutoSaveLayer::Create(context, paint_bounds(), &paint);
|
||||
PaintChildren(context);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ class OpacityLayer : public ContainerLayer {
|
||||
|
||||
void set_alpha(int alpha) { alpha_ = alpha; }
|
||||
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
// TODO(chinmaygarde): Once MZ-139 is addressed, introduce a new node in the
|
||||
|
||||
@@ -21,11 +21,8 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
|
||||
ctm = RasterCache::GetIntegralTransCTM(ctm);
|
||||
#endif
|
||||
raster_cache_result_ = cache->GetPrerolledImage(
|
||||
context->gr_context, sk_picture, ctm, context->dst_color_space,
|
||||
is_complex_, will_change_);
|
||||
} else {
|
||||
raster_cache_result_ = RasterCacheResult();
|
||||
cache->Prepare(context->gr_context, sk_picture, ctm,
|
||||
context->dst_color_space, is_complex_, will_change_);
|
||||
}
|
||||
|
||||
SkRect bounds = sk_picture->cullRect().makeOffset(offset_.x(), offset_.y());
|
||||
@@ -44,11 +41,15 @@ void PictureLayer::Paint(PaintContext& context) const {
|
||||
RasterCache::GetIntegralTransCTM(context.canvas.getTotalMatrix()));
|
||||
#endif
|
||||
|
||||
if (raster_cache_result_.is_valid()) {
|
||||
raster_cache_result_.draw(context.canvas);
|
||||
} else {
|
||||
context.canvas.drawPicture(picture());
|
||||
if (context.raster_cache) {
|
||||
const SkMatrix& ctm = context.canvas.getTotalMatrix();
|
||||
RasterCacheResult result = context.raster_cache->Get(*picture(), ctm);
|
||||
if (result.is_valid()) {
|
||||
result.draw(context.canvas);
|
||||
return;
|
||||
}
|
||||
}
|
||||
context.canvas.drawPicture(picture());
|
||||
}
|
||||
|
||||
} // namespace flow
|
||||
|
||||
@@ -39,7 +39,6 @@ class PictureLayer : public Layer {
|
||||
SkiaGPUObject<SkPicture> picture_;
|
||||
bool is_complex_ = false;
|
||||
bool will_change_ = false;
|
||||
RasterCacheResult raster_cache_result_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PictureLayer);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/flow/layers/layer.h"
|
||||
#include "flutter/flow/paint_utils.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
@@ -17,13 +18,13 @@
|
||||
|
||||
namespace flow {
|
||||
|
||||
void RasterCacheResult::draw(SkCanvas& canvas) const {
|
||||
void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const {
|
||||
SkAutoCanvasRestore auto_restore(&canvas, true);
|
||||
SkIRect bounds =
|
||||
RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix());
|
||||
FML_DCHECK(bounds.size() == image_->dimensions());
|
||||
canvas.resetMatrix();
|
||||
canvas.drawImage(image_, bounds.fLeft, bounds.fTop);
|
||||
canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint);
|
||||
}
|
||||
|
||||
RasterCache::RasterCache(size_t threshold)
|
||||
@@ -77,14 +78,13 @@ static bool IsPictureWorthRasterizing(SkPicture* picture,
|
||||
return picture->approximateOpCount() > 10;
|
||||
}
|
||||
|
||||
RasterCacheResult RasterizePicture(SkPicture* picture,
|
||||
GrContext* context,
|
||||
const SkMatrix& ctm,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool checkerboard) {
|
||||
TRACE_EVENT0("flutter", "RasterCachePopulate");
|
||||
|
||||
const SkRect logical_rect = picture->cullRect();
|
||||
static RasterCacheResult Rasterize(
|
||||
GrContext* context,
|
||||
const SkMatrix& ctm,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool checkerboard,
|
||||
const SkRect& logical_rect,
|
||||
std::function<void(SkCanvas*)> draw_function) {
|
||||
SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm);
|
||||
|
||||
const SkImageInfo image_info =
|
||||
@@ -112,7 +112,7 @@ RasterCacheResult RasterizePicture(SkPicture* picture,
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
canvas->translate(-cache_rect.left(), -cache_rect.top());
|
||||
canvas->concat(ctm);
|
||||
canvas->drawPicture(picture);
|
||||
draw_function(canvas);
|
||||
|
||||
if (checkerboard) {
|
||||
DrawCheckerboard(canvas, logical_rect);
|
||||
@@ -121,6 +121,18 @@ RasterCacheResult RasterizePicture(SkPicture* picture,
|
||||
return {surface->makeImageSnapshot(), logical_rect};
|
||||
}
|
||||
|
||||
RasterCacheResult RasterizePicture(SkPicture* picture,
|
||||
GrContext* context,
|
||||
const SkMatrix& ctm,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool checkerboard) {
|
||||
TRACE_EVENT0("flutter", "RasterCachePopulate");
|
||||
|
||||
return Rasterize(context, ctm, dst_color_space, checkerboard,
|
||||
picture->cullRect(),
|
||||
[=](SkCanvas* canvas) { canvas->drawPicture(picture); });
|
||||
}
|
||||
|
||||
static inline size_t ClampSize(size_t value, size_t min, size_t max) {
|
||||
if (value > max) {
|
||||
return max;
|
||||
@@ -133,16 +145,38 @@ static inline size_t ClampSize(size_t value, size_t min, size_t max) {
|
||||
return value;
|
||||
}
|
||||
|
||||
RasterCacheResult RasterCache::GetPrerolledImage(
|
||||
GrContext* context,
|
||||
SkPicture* picture,
|
||||
const SkMatrix& transformation_matrix,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool is_complex,
|
||||
bool will_change) {
|
||||
void RasterCache::Prepare(PrerollContext* context,
|
||||
std::shared_ptr<Layer> layer,
|
||||
const SkMatrix& ctm) {
|
||||
LayerRasterCacheKey cache_key(layer, ctm);
|
||||
Entry& entry = layer_cache_[cache_key];
|
||||
entry.access_count = ClampSize(entry.access_count + 1, 0, threshold_);
|
||||
entry.used_this_frame = true;
|
||||
if (!entry.image.is_valid()) {
|
||||
entry.image = Rasterize(context->gr_context, ctm, context->dst_color_space,
|
||||
checkerboard_images_, layer->paint_bounds(),
|
||||
[layer, context](SkCanvas* canvas) {
|
||||
Layer::PaintContext paintContext = {
|
||||
*canvas,
|
||||
context->frame_time,
|
||||
context->engine_time,
|
||||
context->texture_registry,
|
||||
context->raster_cache,
|
||||
context->checkerboard_offscreen_layers};
|
||||
layer->Paint(paintContext);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool RasterCache::Prepare(GrContext* context,
|
||||
SkPicture* picture,
|
||||
const SkMatrix& transformation_matrix,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool is_complex,
|
||||
bool will_change) {
|
||||
if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) {
|
||||
// We only deal with pictures that are worthy of rasterization.
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decompose the matrix (once) for all subsequent operations. We want to make
|
||||
@@ -151,46 +185,50 @@ RasterCacheResult RasterCache::GetPrerolledImage(
|
||||
|
||||
if (!matrix.IsValid()) {
|
||||
// The matrix was singular. No point in going further.
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
RasterCacheKey cache_key(*picture, transformation_matrix);
|
||||
PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix);
|
||||
|
||||
Entry& entry = cache_[cache_key];
|
||||
Entry& entry = picture_cache_[cache_key];
|
||||
entry.access_count = ClampSize(entry.access_count + 1, 0, threshold_);
|
||||
entry.used_this_frame = true;
|
||||
|
||||
if (entry.access_count < threshold_ || threshold_ == 0) {
|
||||
// Frame threshold has not yet been reached.
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entry.image.is_valid()) {
|
||||
entry.image = RasterizePicture(picture, context, transformation_matrix,
|
||||
dst_color_space, checkerboard_images_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return entry.image;
|
||||
RasterCacheResult RasterCache::Get(const SkPicture& picture,
|
||||
const SkMatrix& ctm) const {
|
||||
PictureRasterCacheKey cache_key(picture.uniqueID(), ctm);
|
||||
auto it = picture_cache_.find(cache_key);
|
||||
return it == picture_cache_.end() ? RasterCacheResult() : it->second.image;
|
||||
}
|
||||
|
||||
RasterCacheResult RasterCache::Get(std::shared_ptr<Layer> layer,
|
||||
const SkMatrix& ctm) const {
|
||||
LayerRasterCacheKey cache_key(layer, ctm);
|
||||
auto it = layer_cache_.find(cache_key);
|
||||
return it == layer_cache_.end() ? RasterCacheResult() : it->second.image;
|
||||
}
|
||||
|
||||
void RasterCache::SweepAfterFrame() {
|
||||
std::vector<RasterCacheKey::Map<Entry>::iterator> dead;
|
||||
|
||||
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
|
||||
Entry& entry = it->second;
|
||||
if (!entry.used_this_frame) {
|
||||
dead.push_back(it);
|
||||
}
|
||||
entry.used_this_frame = false;
|
||||
}
|
||||
|
||||
for (auto it : dead) {
|
||||
cache_.erase(it);
|
||||
}
|
||||
using PictureCache = PictureRasterCacheKey::Map<Entry>;
|
||||
using LayerCache = LayerRasterCacheKey::Map<Entry>;
|
||||
SweepOneCacheAfterFrame<PictureCache, PictureCache::iterator>(picture_cache_);
|
||||
SweepOneCacheAfterFrame<LayerCache, LayerCache::iterator>(layer_cache_);
|
||||
}
|
||||
|
||||
void RasterCache::Clear() {
|
||||
cache_.clear();
|
||||
picture_cache_.clear();
|
||||
}
|
||||
|
||||
void RasterCache::SetCheckboardCacheImages(bool checkerboard) {
|
||||
|
||||
@@ -28,13 +28,15 @@ class RasterCacheResult {
|
||||
|
||||
bool is_valid() const { return static_cast<bool>(image_); };
|
||||
|
||||
void draw(SkCanvas& canvas) const;
|
||||
void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const;
|
||||
|
||||
private:
|
||||
sk_sp<SkImage> image_;
|
||||
SkRect logical_rect_;
|
||||
};
|
||||
|
||||
struct PrerollContext;
|
||||
|
||||
class RasterCache {
|
||||
public:
|
||||
explicit RasterCache(size_t threshold = 3);
|
||||
@@ -56,12 +58,26 @@ class RasterCache {
|
||||
return result;
|
||||
}
|
||||
|
||||
RasterCacheResult GetPrerolledImage(GrContext* context,
|
||||
SkPicture* picture,
|
||||
const SkMatrix& transformation_matrix,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool is_complex,
|
||||
bool will_change);
|
||||
// Return true if the cache is generated.
|
||||
//
|
||||
// We may return false and not generate the cache if
|
||||
// 1. The picture is not worth rasterizing
|
||||
// 2. The matrix is singular
|
||||
// 3. The picture is accessed too few times
|
||||
bool Prepare(GrContext* context,
|
||||
SkPicture* picture,
|
||||
const SkMatrix& transformation_matrix,
|
||||
SkColorSpace* dst_color_space,
|
||||
bool is_complex,
|
||||
bool will_change);
|
||||
|
||||
void Prepare(PrerollContext* context,
|
||||
std::shared_ptr<Layer> layer,
|
||||
const SkMatrix& ctm);
|
||||
|
||||
RasterCacheResult Get(const SkPicture& picture, const SkMatrix& ctm) const;
|
||||
RasterCacheResult Get(std::shared_ptr<Layer> layer,
|
||||
const SkMatrix& ctm) const;
|
||||
|
||||
void SweepAfterFrame();
|
||||
|
||||
@@ -76,8 +92,26 @@ class RasterCache {
|
||||
RasterCacheResult image;
|
||||
};
|
||||
|
||||
template <class Cache, class Iterator>
|
||||
static void SweepOneCacheAfterFrame(Cache& cache) {
|
||||
std::vector<Iterator> dead;
|
||||
|
||||
for (auto it = cache.begin(); it != cache.end(); ++it) {
|
||||
Entry& entry = it->second;
|
||||
if (!entry.used_this_frame) {
|
||||
dead.push_back(it);
|
||||
}
|
||||
entry.used_this_frame = false;
|
||||
}
|
||||
|
||||
for (auto it : dead) {
|
||||
cache.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
const size_t threshold_;
|
||||
RasterCacheKey::Map<Entry> cache_;
|
||||
PictureRasterCacheKey::Map<Entry> picture_cache_;
|
||||
LayerRasterCacheKey::Map<Entry> layer_cache_;
|
||||
bool checkerboard_images_;
|
||||
fml::WeakPtrFactory<RasterCache> weak_factory_;
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
namespace flow {
|
||||
|
||||
template <typename ID>
|
||||
class RasterCacheKey {
|
||||
public:
|
||||
RasterCacheKey(const SkPicture& picture, const SkMatrix& ctm)
|
||||
: picture_id_(picture.uniqueID()), matrix_(ctm) {
|
||||
RasterCacheKey(ID id, const SkMatrix& ctm) : id_(id), matrix_(ctm) {
|
||||
matrix_[SkMatrix::kMTransX] = SkScalarFraction(ctm.getTranslateX());
|
||||
matrix_[SkMatrix::kMTransY] = SkScalarFraction(ctm.getTranslateY());
|
||||
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
|
||||
@@ -25,19 +25,19 @@ class RasterCacheKey {
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t picture_id() const { return picture_id_; }
|
||||
ID id() const { return id_; }
|
||||
const SkMatrix& matrix() const { return matrix_; }
|
||||
|
||||
struct Hash {
|
||||
std::size_t operator()(RasterCacheKey const& key) const {
|
||||
return key.picture_id_;
|
||||
uint32_t operator()(RasterCacheKey const& key) const {
|
||||
return std::hash<ID>()(key.id_);
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal {
|
||||
constexpr bool operator()(const RasterCacheKey& lhs,
|
||||
const RasterCacheKey& rhs) const {
|
||||
return lhs.picture_id_ == rhs.picture_id_ && lhs.matrix_ == rhs.matrix_;
|
||||
return lhs.id_ == rhs.id_ && lhs.matrix_ == rhs.matrix_;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ class RasterCacheKey {
|
||||
using Map = std::unordered_map<RasterCacheKey, Value, Hash, Equal>;
|
||||
|
||||
private:
|
||||
uint32_t picture_id_;
|
||||
ID id_;
|
||||
|
||||
// ctm where only fractional (0-1) translations are preserved:
|
||||
// matrix_ = ctm;
|
||||
@@ -54,6 +54,13 @@ class RasterCacheKey {
|
||||
SkMatrix matrix_;
|
||||
};
|
||||
|
||||
// The ID is the uint32_t picture uniqueID
|
||||
using PictureRasterCacheKey = RasterCacheKey<uint32_t>;
|
||||
|
||||
class Layer;
|
||||
|
||||
using LayerRasterCacheKey = RasterCacheKey<std::shared_ptr<Layer>>;
|
||||
|
||||
} // namespace flow
|
||||
|
||||
#endif // FLUTTER_FLOW_RASTER_CACHE_KEY_H_
|
||||
|
||||
@@ -33,14 +33,14 @@ TEST(RasterCache, ThresholdIsRespected) {
|
||||
sk_sp<SkImage> image;
|
||||
|
||||
sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 1
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 1
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 2
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 2
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_TRUE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 3
|
||||
ASSERT_TRUE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 3
|
||||
cache.SweepAfterFrame();
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ TEST(RasterCache, ThresholdIsRespectedWhenZero) {
|
||||
sk_sp<SkImage> image;
|
||||
|
||||
sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 1
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 1
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 2
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 2
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 3
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 3
|
||||
cache.SweepAfterFrame();
|
||||
}
|
||||
|
||||
@@ -77,19 +77,19 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) {
|
||||
sk_sp<SkImage> image;
|
||||
|
||||
sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 1
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 1
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 2
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 2
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_TRUE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 3
|
||||
ASSERT_TRUE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 3
|
||||
cache.SweepAfterFrame();
|
||||
ASSERT_TRUE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 4
|
||||
ASSERT_TRUE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 4
|
||||
cache.SweepAfterFrame();
|
||||
cache.SweepAfterFrame(); // Extra frame without a preroll image access.
|
||||
ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(),
|
||||
true, false)); // 5
|
||||
ASSERT_FALSE(cache.Prepare(NULL, picture.get(), matrix, srgb.get(), true,
|
||||
false)); // 5
|
||||
}
|
||||
|
||||
@@ -187,9 +187,12 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) {
|
||||
for (auto& task : paint_tasks_) {
|
||||
FML_DCHECK(task.surface);
|
||||
SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas();
|
||||
Layer::PaintContext context = {*canvas, frame.context().frame_time(),
|
||||
Layer::PaintContext context = {*canvas,
|
||||
frame.context().frame_time(),
|
||||
frame.context().engine_time(),
|
||||
frame.context().texture_registry(), false};
|
||||
frame.context().texture_registry(),
|
||||
&frame.context().raster_cache(),
|
||||
false};
|
||||
canvas->restoreToCount(1);
|
||||
canvas->save();
|
||||
canvas->clear(task.background_color);
|
||||
|
||||
Reference in New Issue
Block a user