2018-11-07 12:24:35 -08:00
|
|
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
2016-01-08 01:53:36 -08:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
2016-08-09 12:48:29 -07:00
|
|
|
#include "flutter/flow/raster_cache.h"
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2016-11-03 13:59:57 -07:00
|
|
|
#include <vector>
|
|
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
#include "flutter/flow/layers/layer.h"
|
2017-05-11 15:00:16 -07:00
|
|
|
#include "flutter/flow/paint_utils.h"
|
2018-07-26 12:49:34 -07:00
|
|
|
#include "flutter/fml/logging.h"
|
2018-07-25 13:20:48 -07:00
|
|
|
#include "flutter/fml/trace_event.h"
|
2016-01-12 19:59:18 -08:00
|
|
|
#include "third_party/skia/include/core/SkCanvas.h"
|
2016-01-08 01:53:36 -08:00
|
|
|
#include "third_party/skia/include/core/SkImage.h"
|
2016-04-21 23:05:41 -07:00
|
|
|
#include "third_party/skia/include/core/SkPicture.h"
|
2016-01-12 19:59:18 -08:00
|
|
|
#include "third_party/skia/include/core/SkSurface.h"
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2019-04-17 14:38:45 -07:00
|
|
|
namespace flutter {
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2018-11-12 19:59:29 -08:00
|
|
|
RasterCacheResult::RasterCacheResult() {}
|
|
|
|
|
|
|
|
|
|
RasterCacheResult::RasterCacheResult(const RasterCacheResult& other) = default;
|
|
|
|
|
|
|
|
|
|
RasterCacheResult::~RasterCacheResult() = default;
|
|
|
|
|
|
|
|
|
|
RasterCacheResult::RasterCacheResult(sk_sp<SkImage> image,
|
|
|
|
|
const SkRect& logical_rect)
|
|
|
|
|
: image_(std::move(image)), logical_rect_(logical_rect) {}
|
|
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const {
|
2019-08-14 16:13:49 -07:00
|
|
|
TRACE_EVENT0("flutter", "RasterCacheResult::draw");
|
2018-05-31 16:41:58 -07:00
|
|
|
SkAutoCanvasRestore auto_restore(&canvas, true);
|
2018-05-23 16:53:20 -07:00
|
|
|
SkIRect bounds =
|
|
|
|
|
RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix());
|
2018-07-26 12:49:34 -07:00
|
|
|
FML_DCHECK(bounds.size() == image_->dimensions());
|
2018-09-11 15:29:08 -07:00
|
|
|
canvas.resetMatrix();
|
2018-10-11 15:09:09 -07:00
|
|
|
canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint);
|
2018-05-23 16:53:20 -07:00
|
|
|
}
|
|
|
|
|
|
2019-03-28 14:37:00 -07:00
|
|
|
RasterCache::RasterCache(size_t access_threshold,
|
|
|
|
|
size_t picture_cache_limit_per_frame)
|
|
|
|
|
: access_threshold_(access_threshold),
|
2019-02-08 17:40:38 -08:00
|
|
|
picture_cache_limit_per_frame_(picture_cache_limit_per_frame),
|
|
|
|
|
checkerboard_images_(false),
|
|
|
|
|
weak_factory_(this) {}
|
2017-06-05 15:57:05 -07:00
|
|
|
|
|
|
|
|
RasterCache::~RasterCache() = default;
|
|
|
|
|
|
|
|
|
|
static bool CanRasterizePicture(SkPicture* picture) {
|
|
|
|
|
if (picture == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SkRect cull_rect = picture->cullRect();
|
|
|
|
|
|
|
|
|
|
if (cull_rect.isEmpty()) {
|
|
|
|
|
// No point in ever rasterizing an empty picture.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cull_rect.isFinite()) {
|
|
|
|
|
// Cannot attempt to rasterize into an infinitely large surface.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool IsPictureWorthRasterizing(SkPicture* picture,
|
|
|
|
|
bool will_change,
|
|
|
|
|
bool is_complex) {
|
|
|
|
|
if (will_change) {
|
|
|
|
|
// If the picture is going to change in the future, there is no point in
|
|
|
|
|
// doing to extra work to rasterize.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!CanRasterizePicture(picture)) {
|
|
|
|
|
// No point in deciding whether the picture is worth rasterizing if it
|
|
|
|
|
// cannot be rasterized at all.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_complex) {
|
|
|
|
|
// The caller seems to have extra information about the picture and thinks
|
|
|
|
|
// the picture is always worth rasterizing.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-01-08 01:53:36 -08:00
|
|
|
|
|
|
|
|
// TODO(abarth): We should find a better heuristic here that lets us avoid
|
|
|
|
|
// wasting memory on trivial layers that are easy to re-rasterize every frame.
|
2019-02-19 14:46:42 -08:00
|
|
|
return picture->approximateOpCount() > 5;
|
2016-01-08 01:53:36 -08:00
|
|
|
}
|
|
|
|
|
|
2019-11-22 12:20:02 -08:00
|
|
|
/// @note Procedure doesn't copy all closures.
|
2018-10-11 15:09:09 -07:00
|
|
|
static RasterCacheResult Rasterize(
|
|
|
|
|
GrContext* context,
|
|
|
|
|
const SkMatrix& ctm,
|
|
|
|
|
SkColorSpace* dst_color_space,
|
|
|
|
|
bool checkerboard,
|
|
|
|
|
const SkRect& logical_rect,
|
2019-11-22 12:20:02 -08:00
|
|
|
const std::function<void(SkCanvas*)>& draw_function) {
|
2019-06-12 10:25:49 -07:00
|
|
|
TRACE_EVENT0("flutter", "RasterCachePopulate");
|
2018-05-23 16:53:20 -07:00
|
|
|
SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm);
|
2017-11-08 14:59:54 -08:00
|
|
|
|
2019-01-22 15:34:51 -05:00
|
|
|
const SkImageInfo image_info = SkImageInfo::MakeN32Premul(
|
|
|
|
|
cache_rect.width(), cache_rect.height(), sk_ref_sp(dst_color_space));
|
2017-06-05 15:57:05 -07:00
|
|
|
|
|
|
|
|
sk_sp<SkSurface> surface =
|
|
|
|
|
context
|
|
|
|
|
? SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, image_info)
|
|
|
|
|
: SkSurface::MakeRaster(image_info);
|
|
|
|
|
|
|
|
|
|
if (!surface) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkCanvas* canvas = surface->getCanvas();
|
|
|
|
|
canvas->clear(SK_ColorTRANSPARENT);
|
2018-05-23 16:53:20 -07:00
|
|
|
canvas->translate(-cache_rect.left(), -cache_rect.top());
|
|
|
|
|
canvas->concat(ctm);
|
2018-10-11 15:09:09 -07:00
|
|
|
draw_function(canvas);
|
2017-06-05 15:57:05 -07:00
|
|
|
|
|
|
|
|
if (checkerboard) {
|
|
|
|
|
DrawCheckerboard(canvas, logical_rect);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 16:53:20 -07:00
|
|
|
return {surface->makeImageSnapshot(), logical_rect};
|
2016-01-08 01:53:36 -08:00
|
|
|
}
|
|
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
RasterCacheResult RasterizePicture(SkPicture* picture,
|
|
|
|
|
GrContext* context,
|
|
|
|
|
const SkMatrix& ctm,
|
|
|
|
|
SkColorSpace* dst_color_space,
|
|
|
|
|
bool checkerboard) {
|
|
|
|
|
return Rasterize(context, ctm, dst_color_space, checkerboard,
|
|
|
|
|
picture->cullRect(),
|
|
|
|
|
[=](SkCanvas* canvas) { canvas->drawPicture(picture); });
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-05 15:57:05 -07:00
|
|
|
static inline size_t ClampSize(size_t value, size_t min, size_t max) {
|
|
|
|
|
if (value > max) {
|
|
|
|
|
return max;
|
|
|
|
|
}
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2017-06-05 15:57:05 -07:00
|
|
|
if (value < min) {
|
|
|
|
|
return min;
|
|
|
|
|
}
|
2016-01-08 14:37:15 -08:00
|
|
|
|
2017-06-05 15:57:05 -07:00
|
|
|
return value;
|
|
|
|
|
}
|
2016-01-08 14:37:15 -08:00
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
void RasterCache::Prepare(PrerollContext* context,
|
|
|
|
|
Layer* layer,
|
|
|
|
|
const SkMatrix& ctm) {
|
2019-04-18 14:14:06 -07:00
|
|
|
LayerRasterCacheKey cache_key(layer->unique_id(), ctm);
|
2018-10-11 15:09:09 -07:00
|
|
|
Entry& entry = layer_cache_[cache_key];
|
2019-03-28 14:37:00 -07:00
|
|
|
entry.access_count = ClampSize(entry.access_count + 1, 0, access_threshold_);
|
2018-10-11 15:09:09 -07:00
|
|
|
entry.used_this_frame = true;
|
|
|
|
|
if (!entry.image.is_valid()) {
|
2019-11-08 17:14:50 -08:00
|
|
|
entry.image = Rasterize(
|
|
|
|
|
context->gr_context, ctm, context->dst_color_space,
|
|
|
|
|
checkerboard_images_, layer->paint_bounds(),
|
|
|
|
|
[layer, context](SkCanvas* canvas) {
|
|
|
|
|
SkISize canvas_size = canvas->getBaseLayerSize();
|
|
|
|
|
SkNWayCanvas internal_nodes_canvas(canvas_size.width(),
|
|
|
|
|
canvas_size.height());
|
|
|
|
|
internal_nodes_canvas.addCanvas(canvas);
|
|
|
|
|
Layer::PaintContext paintContext = {
|
|
|
|
|
(SkCanvas*)&internal_nodes_canvas,
|
|
|
|
|
canvas,
|
|
|
|
|
context->gr_context,
|
|
|
|
|
nullptr,
|
|
|
|
|
context->raster_time,
|
|
|
|
|
context->ui_time,
|
|
|
|
|
context->texture_registry,
|
|
|
|
|
context->has_platform_view ? nullptr : context->raster_cache,
|
2019-12-18 16:05:38 -08:00
|
|
|
context->checkerboard_offscreen_layers,
|
|
|
|
|
context->frame_physical_depth,
|
|
|
|
|
context->frame_device_pixel_ratio};
|
2019-11-08 17:14:50 -08:00
|
|
|
if (layer->needs_painting()) {
|
|
|
|
|
layer->Paint(paintContext);
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-10-11 15:09:09 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool RasterCache::Prepare(GrContext* context,
|
|
|
|
|
SkPicture* picture,
|
|
|
|
|
const SkMatrix& transformation_matrix,
|
|
|
|
|
SkColorSpace* dst_color_space,
|
|
|
|
|
bool is_complex,
|
|
|
|
|
bool will_change) {
|
2019-02-08 17:40:38 -08:00
|
|
|
if (picture_cached_this_frame_ >= picture_cache_limit_per_frame_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-06-05 15:57:05 -07:00
|
|
|
if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) {
|
|
|
|
|
// We only deal with pictures that are worthy of rasterization.
|
2018-10-11 15:09:09 -07:00
|
|
|
return false;
|
2017-06-05 15:57:05 -07:00
|
|
|
}
|
2016-01-08 14:37:15 -08:00
|
|
|
|
2017-06-05 15:57:05 -07:00
|
|
|
// Decompose the matrix (once) for all subsequent operations. We want to make
|
|
|
|
|
// sure to avoid volumetric distortions while accounting for scaling.
|
|
|
|
|
const MatrixDecomposition matrix(transformation_matrix);
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2017-06-05 15:57:05 -07:00
|
|
|
if (!matrix.IsValid()) {
|
|
|
|
|
// The matrix was singular. No point in going further.
|
2018-10-11 15:09:09 -07:00
|
|
|
return false;
|
2017-06-05 15:57:05 -07:00
|
|
|
}
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix);
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
Entry& entry = picture_cache_[cache_key];
|
2019-03-28 14:37:00 -07:00
|
|
|
entry.access_count = ClampSize(entry.access_count + 1, 0, access_threshold_);
|
2016-01-08 01:53:36 -08:00
|
|
|
entry.used_this_frame = true;
|
|
|
|
|
|
2019-03-28 14:37:00 -07:00
|
|
|
if (entry.access_count < access_threshold_ || access_threshold_ == 0) {
|
2017-06-05 15:57:05 -07:00
|
|
|
// Frame threshold has not yet been reached.
|
2018-10-11 15:09:09 -07:00
|
|
|
return false;
|
2016-01-08 01:53:36 -08:00
|
|
|
}
|
|
|
|
|
|
2017-06-05 15:57:05 -07:00
|
|
|
if (!entry.image.is_valid()) {
|
2018-05-23 16:53:20 -07:00
|
|
|
entry.image = RasterizePicture(picture, context, transformation_matrix,
|
|
|
|
|
dst_color_space, checkerboard_images_);
|
2019-11-20 16:36:23 -08:00
|
|
|
picture_cached_this_frame_++;
|
2016-01-08 01:53:36 -08:00
|
|
|
}
|
2018-10-11 15:09:09 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
2016-01-08 01:53:36 -08:00
|
|
|
|
2018-10-11 15:09:09 -07:00
|
|
|
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(Layer* layer, const SkMatrix& ctm) const {
|
2019-04-18 14:14:06 -07:00
|
|
|
LayerRasterCacheKey cache_key(layer->unique_id(), ctm);
|
2018-10-11 15:09:09 -07:00
|
|
|
auto it = layer_cache_.find(cache_key);
|
|
|
|
|
return it == layer_cache_.end() ? RasterCacheResult() : it->second.image;
|
2016-01-08 01:53:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RasterCache::SweepAfterFrame() {
|
2018-10-11 15:09:09 -07:00
|
|
|
using PictureCache = PictureRasterCacheKey::Map<Entry>;
|
|
|
|
|
using LayerCache = LayerRasterCacheKey::Map<Entry>;
|
|
|
|
|
SweepOneCacheAfterFrame<PictureCache, PictureCache::iterator>(picture_cache_);
|
|
|
|
|
SweepOneCacheAfterFrame<LayerCache, LayerCache::iterator>(layer_cache_);
|
2019-02-08 17:40:38 -08:00
|
|
|
picture_cached_this_frame_ = 0;
|
2019-03-13 13:53:22 -07:00
|
|
|
TraceStatsToTimeline();
|
2016-01-08 01:53:36 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-01 10:33:23 -08:00
|
|
|
void RasterCache::Clear() {
|
2018-10-11 15:09:09 -07:00
|
|
|
picture_cache_.clear();
|
2018-11-20 16:48:13 -08:00
|
|
|
layer_cache_.clear();
|
2016-02-01 10:33:23 -08:00
|
|
|
}
|
|
|
|
|
|
2019-11-08 17:14:50 -08:00
|
|
|
size_t RasterCache::GetCachedEntriesCount() const {
|
|
|
|
|
return layer_cache_.size() + picture_cache_.size();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 13:59:57 -07:00
|
|
|
void RasterCache::SetCheckboardCacheImages(bool checkerboard) {
|
|
|
|
|
if (checkerboard_images_ == checkerboard) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkerboard_images_ = checkerboard;
|
|
|
|
|
|
|
|
|
|
// Clear all existing entries so previously rasterized items (with or without
|
|
|
|
|
// a checkerboard) will be refreshed in subsequent passes.
|
2016-11-07 15:29:03 -08:00
|
|
|
Clear();
|
2016-11-03 13:59:57 -07:00
|
|
|
}
|
|
|
|
|
|
2019-03-13 13:53:22 -07:00
|
|
|
void RasterCache::TraceStatsToTimeline() const {
|
2019-09-27 11:20:54 -07:00
|
|
|
#if !FLUTTER_RELEASE
|
2019-03-13 13:53:22 -07:00
|
|
|
|
|
|
|
|
size_t layer_cache_count = 0;
|
|
|
|
|
size_t layer_cache_bytes = 0;
|
|
|
|
|
size_t picture_cache_count = 0;
|
|
|
|
|
size_t picture_cache_bytes = 0;
|
|
|
|
|
|
|
|
|
|
for (const auto& item : layer_cache_) {
|
|
|
|
|
const auto dimensions = item.second.image.image_dimensions();
|
|
|
|
|
layer_cache_count++;
|
|
|
|
|
layer_cache_bytes += dimensions.width() * dimensions.height() * 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& item : picture_cache_) {
|
|
|
|
|
const auto dimensions = item.second.image.image_dimensions();
|
|
|
|
|
picture_cache_count++;
|
|
|
|
|
picture_cache_bytes += dimensions.width() * dimensions.height() * 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FML_TRACE_COUNTER("flutter", "RasterCache",
|
|
|
|
|
reinterpret_cast<int64_t>(this), //
|
|
|
|
|
"LayerCount", layer_cache_count, //
|
|
|
|
|
"LayerMBytes", layer_cache_bytes * 1e-6, //
|
|
|
|
|
"PictureCount", picture_cache_count, //
|
|
|
|
|
"PictureMBytes", picture_cache_bytes * 1e-6 //
|
|
|
|
|
);
|
|
|
|
|
|
2019-09-27 11:20:54 -07:00
|
|
|
#endif // !FLUTTER_RELEASE
|
2019-03-13 13:53:22 -07:00
|
|
|
}
|
|
|
|
|
|
2019-04-17 14:38:45 -07:00
|
|
|
} // namespace flutter
|