mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This is part of a larger effort to expose the difference between GrDirectContext, which runs on the GPU thread and can directly perform operations like uploading textures, and GrRecordingContext, which can only queue up work to be delivered to the GrDirectContext later.
212 lines
6.6 KiB
C++
212 lines
6.6 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.
|
|
|
|
#ifndef FLUTTER_FLOW_RASTER_CACHE_H_
|
|
#define FLUTTER_FLOW_RASTER_CACHE_H_
|
|
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
|
|
#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<SkImage> image, const SkRect& logical_rect);
|
|
|
|
virtual ~RasterCacheResult() = default;
|
|
|
|
virtual void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) 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<SkImage> 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 GrContext 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<RasterCacheResult> 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<RasterCacheResult> 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<RasterCacheResult> image;
|
|
};
|
|
|
|
template <class Cache>
|
|
static void SweepOneCacheAfterFrame(Cache& cache) {
|
|
std::vector<typename Cache::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 access_threshold_;
|
|
const size_t picture_cache_limit_per_frame_;
|
|
size_t picture_cached_this_frame_ = 0;
|
|
mutable PictureRasterCacheKey::Map<Entry> picture_cache_;
|
|
mutable LayerRasterCacheKey::Map<Entry> layer_cache_;
|
|
bool checkerboard_images_;
|
|
|
|
void TraceStatsToTimeline() const;
|
|
|
|
FML_DISALLOW_COPY_AND_ASSIGN(RasterCache);
|
|
};
|
|
|
|
} // namespace flutter
|
|
|
|
#endif // FLUTTER_FLOW_RASTER_CACHE_H_
|