// 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. #ifndef FLUTTER_FLOW_RASTER_CACHE_H_ #define FLUTTER_FLOW_RASTER_CACHE_H_ #include #include #include "flutter/flow/raster_cache_key.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSize.h" namespace flutter { class RasterCacheResult { public: RasterCacheResult(sk_sp image, const SkRect& logical_rect); virtual ~RasterCacheResult() = default; virtual void draw(SkCanvas& canvas, const SkPaint* paint) const; virtual SkISize image_dimensions() const { return image_ ? image_->dimensions() : SkISize::Make(0, 0); }; virtual int64_t image_bytes() const { return image_ ? image_->dimensions().area() * image_->imageInfo().bytesPerPixel() : 0; }; private: sk_sp image_; SkRect logical_rect_; }; struct PrerollContext; class RasterCache { public: // The default max number of picture raster caches to be generated per frame. // Generating too many caches in one frame may cause jank on that frame. This // limit allows us to throttle the cache and distribute the work across // multiple frames. static constexpr int kDefaultPictureCacheLimitPerFrame = 3; explicit RasterCache( size_t access_threshold = 3, size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); virtual ~RasterCache() = default; /** * @brief Rasterize a picture object and produce a RasterCacheResult * to be stored in the cache. * * @param picture the SkPicture object to be cached. * @param context the GrDirectContext used for rendering. * @param ctm the transformation matrix used for rendering. * @param dst_color_space the destination color space that the cached * rendering will be drawn into * @param checkerboard a flag indicating whether or not a checkerboard * pattern should be rendered into the cached image for debug * analysis * @return a RasterCacheResult that can draw the rendered picture into * the destination using a simple image blit */ virtual std::unique_ptr RasterizePicture( SkPicture* picture, GrDirectContext* context, const SkMatrix& ctm, SkColorSpace* dst_color_space, bool checkerboard) const; /** * @brief Rasterize an engine Layer and produce a RasterCacheResult * to be stored in the cache. * * @param context the PrerollContext containing important information * needed for rendering a layer. * @param layer the Layer object to be cached. * @param ctm the transformation matrix used for rendering. * @param checkerboard a flag indicating whether or not a checkerboard * pattern should be rendered into the cached image for debug * analysis * @return a RasterCacheResult that can draw the rendered layer into * the destination using a simple image blit */ virtual std::unique_ptr RasterizeLayer( PrerollContext* context, Layer* layer, const SkMatrix& ctm, bool checkerboard) const; static SkIRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) { SkRect device_rect; ctm.mapRect(&device_rect, rect); SkIRect bounds; device_rect.roundOut(&bounds); return bounds; } /** * @brief Snap the translation components of the matrix to integers. * * The snapping will only happen if the matrix only has scale and translation * transformations. * * @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; } // 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 // 4. There are too many pictures to be cached in the current frame. // (See also kDefaultPictureCacheLimitPerFrame.) bool Prepare(GrDirectContext* context, SkPicture* picture, const SkMatrix& transformation_matrix, SkColorSpace* dst_color_space, bool is_complex, bool will_change); void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); // Find the raster cache for the picture and draw it to the canvas. // // Return true if it's found and drawn. bool Draw(const SkPicture& picture, SkCanvas& canvas) const; // Find the raster cache for the layer and draw it to the canvas. // // Addional paint can be given to change how the raster cache is drawn (e.g., // draw the raster cache with some opacity). // // Return true if the layer raster cache is found and drawn. bool Draw(const Layer* layer, SkCanvas& canvas, SkPaint* paint = nullptr) const; void SweepAfterFrame(); void Clear(); void SetCheckboardCacheImages(bool checkerboard); size_t GetCachedEntriesCount() const; size_t GetLayerCachedEntriesCount() const; size_t GetPictureCachedEntriesCount() const; private: struct Entry { bool used_this_frame = false; size_t access_count = 0; std::unique_ptr image; }; template static void SweepOneCacheAfterFrame(Cache& cache) { std::vector 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 access_threshold_; const size_t picture_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; mutable PictureRasterCacheKey::Map picture_cache_; mutable LayerRasterCacheKey::Map layer_cache_; bool checkerboard_images_; void TraceStatsToTimeline() const; FML_DISALLOW_COPY_AND_ASSIGN(RasterCache); }; } // namespace flutter #endif // FLUTTER_FLOW_RASTER_CACHE_H_