提交 1c6a531e 编写于 作者: C Chinmay Garde 提交者: GitHub

Rework raster cache to fix numerous issues. (#3717)

* Fix pixel rounding error in the picture layer by first ensuring that
  the texture for the image is at least as big as the next integer size
  along each dimension and using kStrict_SrcRectConstraint while
  drawing the same image. We already select the source subset by
  looking at the cull rect of the picture.
* Decompose the transformation matrix into a series of operations that
  generated the same to calculate the scale at which to rasterize the
  picture. This make the rasterization scale resilient to
  transformations that introduce a perspective component to the
  resultant matrix.
* The scale in the decomposed matrix is now part of the key in the
  cache.
* Raster cache images that could never be rasterized were still taking
  part in the cache. Now, those entries are rejected early on. This
  leads to the sweep after the frame iterating over fewer items.
* Added a unit test target.
上级 c3721a58
......@@ -25,6 +25,7 @@ group("flutter") {
deps += [ "//flutter/shell/platform/darwin:flutter_channels_unittests" ]
}
deps += [
"//flutter/flow:flow_unittests",
"//flutter/fml:fml_unittests",
"//flutter/sky/engine/wtf:wtf_unittests",
"//flutter/synchronization:synchronization_unittests",
......
......@@ -6,6 +6,8 @@ source_set("flow") {
sources = [
"compositor_context.cc",
"compositor_context.h",
"debug_print.cc",
"debug_print.h",
"instrumentation.cc",
"instrumentation.h",
"layers/backdrop_filter_layer.cc",
......@@ -36,11 +38,15 @@ source_set("flow") {
"layers/shader_mask_layer.h",
"layers/transform_layer.cc",
"layers/transform_layer.h",
"matrix_decomposition.cc",
"matrix_decomposition.h",
"paint_utils.cc",
"paint_utils.h",
"process_info.h",
"raster_cache.cc",
"raster_cache.h",
"raster_cache_key.cc",
"raster_cache_key.h",
"scene_update_context.cc",
"scene_update_context.h",
]
......@@ -65,3 +71,19 @@ source_set("flow") {
]
}
}
executable("flow_unittests") {
testonly = true
sources = [
"matrix_decomposition_unittests.cc",
"raster_cache_unittests.cc",
]
deps = [
":flow",
"//dart/runtime:libdart_jit", # for tracing
"//flutter/testing",
"//third_party/skia",
]
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/debug_print.h"
#include <ostream>
#include "third_party/skia/include/core/SkString.h"
std::ostream& operator<<(std::ostream& os, const flow::MatrixDecomposition& m) {
if (!m.IsValid()) {
os << "Invalid Matrix!" << std::endl;
return os;
}
os << "Translation (x, y, z): " << m.translation() << std::endl;
os << "Scale (z, y, z): " << m.scale() << std::endl;
os << "Shear (zy, yz, zx): " << m.shear() << std::endl;
os << "Perspective (x, y, z, w): " << m.perspective() << std::endl;
os << "Rotation (x, y, z, w): " << m.rotation() << std::endl;
return os;
}
std::ostream& operator<<(std::ostream& os, const SkMatrix& m) {
SkString string;
m.toString(&string);
os << string.c_str();
return os;
}
std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) {
os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", "
<< m.get(0, 3) << std::endl;
os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", "
<< m.get(1, 3) << std::endl;
os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", "
<< m.get(2, 3) << std::endl;
os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", "
<< m.get(3, 3);
return os;
}
std::ostream& operator<<(std::ostream& os, const SkVector3& v) {
os << v.x() << ", " << v.y() << ", " << v.z();
return os;
}
std::ostream& operator<<(std::ostream& os, const SkVector4& v) {
os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", "
<< v.fData[3];
return os;
}
std::ostream& operator<<(std::ostream& os, const flow::RasterCacheKey& k) {
os << "Picture: " << k.picture_id() << " Scale: " << k.scale_key().width()
<< ", " << k.scale_key().height();
return os;
}
// Copyright 2017 The Chromium 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_DEBUG_PRINT_H_
#define FLUTTER_FLOW_DEBUG_PRINT_H_
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/flow/raster_cache_key.h"
#include "lib/ftl/macros.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkMatrix44.h"
#include "third_party/skia/include/core/SkPoint3.h"
#define DEF_PRINTER(x) std::ostream& operator<<(std::ostream&, const x&);
DEF_PRINTER(flow::RasterCacheKey);
DEF_PRINTER(flow::MatrixDecomposition);
DEF_PRINTER(SkMatrix);
DEF_PRINTER(SkMatrix44);
DEF_PRINTER(SkVector3);
DEF_PRINTER(SkVector4);
#endif // FLUTTER_FLOW_DEBUG_PRINT_H_
......@@ -5,7 +5,6 @@
#include "flutter/flow/layers/picture_layer.h"
#include "flutter/common/threads.h"
#include "flutter/flow/raster_cache.h"
#include "lib/ftl/logging.h"
namespace flow {
......@@ -23,8 +22,8 @@ PictureLayer::~PictureLayer() {
void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
if (auto cache = context->raster_cache) {
image_ = cache->GetPrerolledImage(context->gr_context, picture_.get(),
matrix, is_complex_, will_change_);
raster_cache_result_ = cache->GetPrerolledImage(
context->gr_context, picture_.get(), matrix, is_complex_, will_change_);
}
SkRect bounds = picture_->cullRect().makeOffset(offset_.x(), offset_.y());
......@@ -35,15 +34,19 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
void PictureLayer::Paint(PaintContext& context) {
FTL_DCHECK(picture_);
TRACE_EVENT1("flutter", "PictureLayer::Paint", "image",
image_ ? "prerolled" : "normal");
TRACE_EVENT0("flutter", "PictureLayer::Paint");
SkAutoCanvasRestore save(&context.canvas, true);
context.canvas.translate(offset_.x(), offset_.y());
if (image_) {
context.canvas.drawImageRect(image_.get(), picture_->cullRect(), nullptr,
SkCanvas::kFast_SrcRectConstraint);
if (raster_cache_result_.is_valid()) {
context.canvas.drawImageRect(
raster_cache_result_.image(), // image
raster_cache_result_.source_rect(), // source
raster_cache_result_.destination_rect(), // destination
nullptr, // paint
SkCanvas::kStrict_SrcRectConstraint // source constraint
);
} else {
context.canvas.drawPicture(picture_.get());
}
......
......@@ -6,6 +6,7 @@
#define FLUTTER_FLOW_LAYERS_PICTURE_LAYER_H_
#include "flutter/flow/layers/layer.h"
#include "flutter/flow/raster_cache.h"
namespace flow {
......@@ -30,9 +31,7 @@ class PictureLayer : public Layer {
sk_sp<SkPicture> picture_;
bool is_complex_ = false;
bool will_change_ = false;
// If we rasterized the picture separately, image_ holds the pixels.
sk_sp<SkImage> image_;
RasterCacheResult raster_cache_result_;
FTL_DISALLOW_COPY_AND_ASSIGN(PictureLayer);
};
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/matrix_decomposition.h"
namespace flow {
static inline SkVector3 SkVector3Combine(const SkVector3& a,
float a_scale,
const SkVector3& b,
float b_scale) {
return {
a_scale * a.fX + b_scale * b.fX, //
a_scale * a.fY + b_scale * b.fY, //
a_scale * a.fZ + b_scale * b.fZ, //
};
}
static inline SkVector3 SkVector3Cross(const SkVector3& a, const SkVector3& b) {
return {
(a.fY * b.fZ) - (a.fZ * b.fY), //
(a.fZ * b.fX) - (a.fX * b.fZ), //
(a.fX * b.fY) - (a.fY * b.fX) //
};
}
MatrixDecomposition::MatrixDecomposition(const SkMatrix& matrix)
: MatrixDecomposition(SkMatrix44{matrix}) {}
MatrixDecomposition::MatrixDecomposition(SkMatrix44 matrix) : valid_(false) {
if (matrix.get(3, 3) == 0) {
return;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
matrix.set(j, i, matrix.get(j, i) / matrix.get(3, 3));
}
}
SkMatrix44 perpective_matrix = matrix;
for (int i = 0; i < 3; i++) {
perpective_matrix.set(3, i, 0.0);
}
perpective_matrix.set(3, 3, 1.0);
if (perpective_matrix.determinant() == 0.0) {
return;
}
if (matrix.get(3, 0) != 0.0 || matrix.get(3, 1) != 0.0 ||
matrix.get(3, 2) != 0.0) {
const SkVector4 right_hand_side(matrix.get(3, 0), matrix.get(3, 1),
matrix.get(3, 2), matrix.get(3, 3));
SkMatrix44 inverted_transposed(
SkMatrix44::Uninitialized_Constructor::kUninitialized_Constructor);
if (!perpective_matrix.invert(&inverted_transposed)) {
return;
}
inverted_transposed.transpose();
perspective_ = inverted_transposed * right_hand_side;
matrix.set(3, 0, 0);
matrix.set(3, 1, 0);
matrix.set(3, 2, 0);
matrix.set(3, 3, 1);
}
translation_ = {matrix.get(0, 3), matrix.get(1, 3), matrix.get(2, 3)};
matrix.set(0, 3, 0.0);
matrix.set(1, 3, 0.0);
matrix.set(2, 3, 0.0);
SkVector3 row[3];
for (int i = 0; i < 3; i++) {
row[i].set(matrix.get(0, i), matrix.get(1, i), matrix.get(2, i));
}
scale_.fX = row[0].length();
row[0].normalize();
shear_.fX = row[0].dot(row[1]);
row[1] = SkVector3Combine(row[1], 1.0, row[0], -shear_.fX);
scale_.fY = row[1].length();
row[1].normalize();
shear_.fX /= scale_.fY;
shear_.fY = row[0].dot(row[2]);
row[2] = SkVector3Combine(row[2], 1.0, row[0], -shear_.fY);
shear_.fZ = row[1].dot(row[2]);
row[2] = SkVector3Combine(row[2], 1.0, row[1], -shear_.fZ);
scale_.fZ = row[2].length();
row[2].normalize();
shear_.fY /= scale_.fZ;
shear_.fZ /= scale_.fZ;
if (row[0].dot(SkVector3Cross(row[1], row[2])) < 0) {
scale_.fX *= -1;
scale_.fY *= -1;
scale_.fZ *= -1;
for (int i = 0; i < 3; i++) {
row[i].fX *= -1;
row[i].fY *= -1;
row[i].fZ *= -1;
}
}
rotation_.set(0.5 * sqrt(fmax(1.0 + row[0].fX - row[1].fY - row[2].fZ, 0.0)),
0.5 * sqrt(fmax(1.0 - row[0].fX + row[1].fY - row[2].fZ, 0.0)),
0.5 * sqrt(fmax(1.0 - row[0].fX - row[1].fY + row[2].fZ, 0.0)),
0.5 * sqrt(fmax(1.0 + row[0].fX + row[1].fY + row[2].fZ, 0.0)));
if (row[2].fY > row[1].fZ) {
rotation_.fData[0] = -rotation_.fData[0];
}
if (row[0].fZ > row[2].fX) {
rotation_.fData[1] = -rotation_.fData[1];
}
if (row[1].fX > row[0].fY) {
rotation_.fData[2] = -rotation_.fData[2];
}
valid_ = true;
}
MatrixDecomposition::~MatrixDecomposition() = default;
bool MatrixDecomposition::IsValid() const {
return valid_;
}
} // namespace flow
// Copyright 2017 The Chromium 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_MATRIX_DECOMPOSITION_H_
#define FLUTTER_FLOW_MATRIX_DECOMPOSITION_H_
#include "lib/ftl/macros.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkMatrix44.h"
#include "third_party/skia/include/core/SkPoint3.h"
namespace flow {
/// Decomposes a given non-degenerate transformation matrix into a sequence of
/// operations that produced it. The validity of the decomposition must always
/// be checked before attempting to access any of the decomposed elements.
class MatrixDecomposition {
public:
MatrixDecomposition(const SkMatrix& matrix);
MatrixDecomposition(SkMatrix44 matrix);
~MatrixDecomposition();
bool IsValid() const;
const SkVector3& translation() const { return translation_; }
const SkVector3& scale() const { return scale_; }
const SkVector3& shear() const { return shear_; }
const SkVector4& perspective() const { return perspective_; }
const SkVector4& rotation() const { return rotation_; }
private:
bool valid_;
SkVector3 translation_;
SkVector3 scale_;
SkVector3 shear_;
SkVector4 perspective_;
SkVector4 rotation_;
FTL_DISALLOW_COPY_AND_ASSIGN(MatrixDecomposition);
};
} // namespace flow
#endif // FLUTTER_FLOW_MATRIX_DECOMPOSITION_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/matrix_decomposition.h"
#include "third_party/gtest/include/gtest/gtest.h"
TEST(MatrixDecomposition, Rotation) {
SkMatrix44 matrix = SkMatrix44::I();
const auto angle = M_PI_4;
matrix.setRotateAbout(0.0, 0.0, 1.0, angle);
flow::MatrixDecomposition decomposition(matrix);
ASSERT_TRUE(decomposition.IsValid());
const auto sine = sin(angle * 0.5);
ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[0]);
ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[1]);
ASSERT_FLOAT_EQ(sine, decomposition.rotation().fData[2]);
ASSERT_FLOAT_EQ(cos(angle * 0.5), decomposition.rotation().fData[3]);
}
TEST(MatrixDecomposition, Scale) {
SkMatrix44 matrix = SkMatrix44::I();
const auto scale = 5.0;
matrix.setScale(scale + 0, scale + 1, scale + 2);
flow::MatrixDecomposition decomposition(matrix);
ASSERT_TRUE(decomposition.IsValid());
ASSERT_FLOAT_EQ(scale + 0, decomposition.scale().fX);
ASSERT_FLOAT_EQ(scale + 1, decomposition.scale().fY);
ASSERT_FLOAT_EQ(scale + 2, decomposition.scale().fZ);
}
TEST(MatrixDecomposition, Translate) {
SkMatrix44 matrix = SkMatrix44::I();
const auto translate = 125.0;
matrix.setTranslate(translate + 0, translate + 1, translate + 2);
flow::MatrixDecomposition decomposition(matrix);
ASSERT_TRUE(decomposition.IsValid());
ASSERT_FLOAT_EQ(translate + 0, decomposition.translation().fX);
ASSERT_FLOAT_EQ(translate + 1, decomposition.translation().fY);
ASSERT_FLOAT_EQ(translate + 2, decomposition.translation().fZ);
}
TEST(MatrixDecomposition, Combination) {
SkMatrix44 matrix = SkMatrix44::I();
const auto rotation = M_PI_4;
const auto scale = 5;
const auto translate = 125.0;
SkMatrix44 m1 = SkMatrix44::I();
m1.setRotateAbout(0, 0, 1, rotation);
SkMatrix44 m2 = SkMatrix44::I();
m2.setScale(scale);
SkMatrix44 m3 = SkMatrix44::I();
m3.setTranslate(translate, translate, translate);
SkMatrix44 combined = m3 * m2 * m1;
flow::MatrixDecomposition decomposition(combined);
ASSERT_TRUE(decomposition.IsValid());
ASSERT_FLOAT_EQ(translate, decomposition.translation().fX);
ASSERT_FLOAT_EQ(translate, decomposition.translation().fY);
ASSERT_FLOAT_EQ(translate, decomposition.translation().fZ);
ASSERT_FLOAT_EQ(scale, decomposition.scale().fX);
ASSERT_FLOAT_EQ(scale, decomposition.scale().fY);
ASSERT_FLOAT_EQ(scale, decomposition.scale().fZ);
const auto sine = sin(rotation * 0.5);
ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[0]);
ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[1]);
ASSERT_FLOAT_EQ(sine, decomposition.rotation().fData[2]);
ASSERT_FLOAT_EQ(cos(rotation * 0.5), decomposition.rotation().fData[3]);
}
......@@ -17,96 +17,172 @@
namespace flow {
static const int kRasterThreshold = 3;
RasterCache::RasterCache(size_t threshold)
: threshold_(threshold), checkerboard_images_(false), weak_factory_(this) {}
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;
}
static bool isWorthRasterizing(SkPicture* picture) {
// 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.
return picture->approximateOpCount() > 10;
}
RasterCache::RasterCache() : checkerboard_images_(false), weak_factory_(this) {}
RasterCacheResult RasterizePicture(SkPicture* picture,
GrContext* context,
const MatrixDecomposition& matrix,
bool checkerboard) {
TRACE_EVENT0("flutter", "RasterCachePopulate");
const SkVector3& scale = matrix.scale();
SkRect logical_rect = picture->cullRect();
const SkImageInfo image_info = SkImageInfo::MakeN32Premul(
std::ceil(logical_rect.width() * std::abs(scale.x())), // physical width
std::ceil(logical_rect.height() *
std::abs(scale.y())), // physical height
nullptr // colorspace
);
sk_sp<SkSurface> surface =
context
? SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, image_info)
: SkSurface::MakeRaster(image_info);
if (!surface) {
FTL_DCHECK(false);
return {};
}
SkCanvas* canvas = surface->getCanvas();
RasterCache::~RasterCache() {}
canvas->clear(SK_ColorTRANSPARENT);
canvas->scale(std::abs(scale.x()), std::abs(scale.y()));
canvas->translate(-logical_rect.left(), -logical_rect.top());
canvas->drawPicture(picture);
if (checkerboard) {
DrawCheckerboard(canvas, logical_rect);
}
RasterCache::Entry::Entry() {
physical_size.setEmpty();
return {
surface->makeImageSnapshot(), // image
SkRect::MakeWH(
logical_rect.width() * std::abs(scale.x()),
logical_rect.height() * std::abs(scale.y())), // source rect
logical_rect // destination rect
};
}
RasterCache::Entry::~Entry() {}
static inline size_t ClampSize(size_t value, size_t min, size_t max) {
if (value > max) {
return max;
}
sk_sp<SkImage> RasterCache::GetPrerolledImage(GrContext* context,
SkPicture* picture,
const SkMatrix& ctm,
bool is_complex,
bool will_change) {
SkScalar scaleX = ctm.getScaleX();
SkScalar scaleY = ctm.getScaleY();
if (value < min) {
return min;
}
SkRect rect = picture->cullRect();
return value;
}
SkISize physical_size =
SkISize::Make(rect.width() * scaleX, rect.height() * scaleY);
RasterCacheResult RasterCache::GetPrerolledImage(
GrContext* context,
SkPicture* picture,
const SkMatrix& transformation_matrix,
bool is_complex,
bool will_change) {
if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) {
// We only deal with pictures that are worthy of rasterization.
return {};
}
if (physical_size.isEmpty())
return nullptr;
// 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);
Entry& entry = cache_[picture->uniqueID()];
if (!matrix.IsValid()) {
// The matrix was singular. No point in going further.
return {};
}
const bool size_matched = entry.physical_size == physical_size;
RasterCacheKey cache_key(*picture, matrix);
Entry& entry = cache_[cache_key];
entry.access_count = ClampSize(entry.access_count + 1, 0, threshold_);
entry.used_this_frame = true;
entry.physical_size = physical_size;
if (!size_matched) {
entry.access_count = 1;
entry.image = nullptr;
return nullptr;
}
entry.access_count++;
if (entry.access_count >= kRasterThreshold) {
// Saturate at the threshhold.
entry.access_count = kRasterThreshold;
if (!entry.image && !will_change &&
(is_complex || isWorthRasterizing(picture))) {
TRACE_EVENT2("flutter", "Rasterize picture layer", "width",
std::to_string(physical_size.width()).c_str(), "height",
std::to_string(physical_size.height()).c_str());
SkImageInfo info = SkImageInfo::MakeN32Premul(physical_size);
sk_sp<SkSurface> surface =
SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
if (surface) {
SkCanvas* canvas = surface->getCanvas();
canvas->clear(SK_ColorTRANSPARENT);
canvas->scale(scaleX, scaleY);
canvas->translate(-rect.left(), -rect.top());
canvas->drawPicture(picture);
if (checkerboard_images_) {
DrawCheckerboard(canvas, rect);
}
entry.image = surface->makeImageSnapshot();
}
}
if (entry.access_count < threshold_ || threshold_ == 0) {
// Frame threshold has not yet been reached.
return {};
}
if (!entry.image.is_valid()) {
entry.image =
RasterizePicture(picture, context, matrix, checkerboard_images_);
}
// We are not considering unrasterizable images. So if we don't have an image
// by now, we know that rasterization itself failed.
FTL_DCHECK(entry.image.is_valid());
return entry.image;
}
void RasterCache::SweepAfterFrame() {
std::vector<Cache::iterator> dead;
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)
if (!entry.used_this_frame) {
dead.push_back(it);
}
entry.used_this_frame = false;
}
for (auto it : dead)
for (auto it : dead) {
cache_.erase(it);
}
}
void RasterCache::Clear() {
......
......@@ -9,6 +9,7 @@
#include <unordered_map>
#include "flutter/flow/instrumentation.h"
#include "flutter/flow/raster_cache_key.h"
#include "lib/ftl/macros.h"
#include "lib/ftl/memory/weak_ptr.h"
#include "third_party/skia/include/core/SkImage.h"
......@@ -16,16 +17,44 @@
namespace flow {
class RasterCacheResult {
public:
RasterCacheResult()
: source_rect_(SkRect::MakeEmpty()),
destination_rect_(SkRect::MakeEmpty()) {}
RasterCacheResult(sk_sp<SkImage> image, SkRect source, SkRect destination)
: image_(std::move(image)),
source_rect_(source),
destination_rect_(destination) {}
operator bool() const { return static_cast<bool>(image_); }
bool is_valid() const { return static_cast<bool>(image_); };
sk_sp<SkImage> image() const { return image_; }
const SkRect& source_rect() const { return source_rect_; }
const SkRect& destination_rect() const { return destination_rect_; }
private:
sk_sp<SkImage> image_;
SkRect source_rect_;
SkRect destination_rect_;
};
class RasterCache {
public:
RasterCache();
explicit RasterCache(size_t threshold = 3);
~RasterCache();
sk_sp<SkImage> GetPrerolledImage(GrContext* context,
SkPicture* picture,
const SkMatrix& ctm,
bool is_complex,
bool will_change);
RasterCacheResult GetPrerolledImage(GrContext* context,
SkPicture* picture,
const SkMatrix& transformation_matrix,
bool is_complex,
bool will_change);
void SweepAfterFrame();
void Clear();
......@@ -34,18 +63,13 @@ class RasterCache {
private:
struct Entry {
Entry();
~Entry();
bool used_this_frame = false;
int access_count = 0;
SkISize physical_size;
sk_sp<SkImage> image;
size_t access_count = 0;
RasterCacheResult image;
};
using Cache = std::unordered_map<uint32_t, Entry>;
Cache cache_;
const size_t threshold_;
RasterCacheKey::Map<Entry> cache_;
bool checkerboard_images_;
ftl::WeakPtrFactory<RasterCache> weak_factory_;
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/raster_cache_key.h"
namespace flow {
//
} // namespace flow
// Copyright 2017 The Chromium 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_KEY_H_
#define FLUTTER_FLOW_RASTER_CACHE_KEY_H_
#include <unordered_map>
#include "flutter/flow/matrix_decomposition.h"
#include "lib/ftl/macros.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkPicture.h"
namespace flow {
class RasterCacheKey {
public:
RasterCacheKey(const SkPicture& picture, const MatrixDecomposition& matrix)
: picture_id_(picture.uniqueID()),
scale_key_(SkISize::Make(matrix.scale().x() * 1e3,
matrix.scale().y() * 1e3)) {}
uint32_t picture_id() const { return picture_id_; }
const SkISize& scale_key() const { return scale_key_; }
struct Hash {
std::size_t operator()(RasterCacheKey const& key) const {
return key.picture_id_;
}
};
struct Equal {
constexpr bool operator()(const RasterCacheKey& lhs,
const RasterCacheKey& rhs) const {
return lhs.picture_id_ == rhs.picture_id_ &&
lhs.scale_key_ == rhs.scale_key_;
}
};
template <class Value>
using Map = std::unordered_map<RasterCacheKey, Value, Hash, Equal>;
private:
uint32_t picture_id_;
SkISize scale_key_;
};
} // namespace flow
#endif // FLUTTER_FLOW_RASTER_CACHE_KEY_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/raster_cache.h"
#include "third_party/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
sk_sp<SkPicture> GetSamplePicture() {
SkPictureRecorder recorder;
recorder.beginRecording(SkRect::MakeWH(150, 100));
SkPaint paint;
paint.setColor(SK_ColorRED);
recorder.getRecordingCanvas()->drawRect(SkRect::MakeXYWH(10, 10, 80, 80),
paint);
return recorder.finishRecordingAsPicture();
}
TEST(RasterCache, SimpleInitialization) {
flow::RasterCache cache;
ASSERT_TRUE(true);
}
TEST(RasterCache, ThresholdIsRespected) {
size_t threshold = 3;
flow::RasterCache cache(threshold);
SkMatrix matrix = SkMatrix::I();
auto picture = GetSamplePicture();
sk_sp<SkImage> image;
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 1
cache.SweepAfterFrame();
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 2
cache.SweepAfterFrame();
ASSERT_TRUE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 3
cache.SweepAfterFrame();
}
TEST(RasterCache, ThresholdIsRespectedWhenZero) {
size_t threshold = 0;
flow::RasterCache cache(threshold);
SkMatrix matrix = SkMatrix::I();
auto picture = GetSamplePicture();
sk_sp<SkImage> image;
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 1
cache.SweepAfterFrame();
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 2
cache.SweepAfterFrame();
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 3
cache.SweepAfterFrame();
}
TEST(RasterCache, SweepsRemoveUnusedFrames) {
size_t threshold = 3;
flow::RasterCache cache(threshold);
SkMatrix matrix = SkMatrix::I();
auto picture = GetSamplePicture();
sk_sp<SkImage> image;
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 1
cache.SweepAfterFrame();
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 2
cache.SweepAfterFrame();
ASSERT_TRUE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 3
cache.SweepAfterFrame();
ASSERT_TRUE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 4
cache.SweepAfterFrame();
cache.SweepAfterFrame(); // Extra frame without a preroll image access.
ASSERT_FALSE(
cache.GetPrerolledImage(NULL, picture.get(), matrix, true, false)); // 5
}
......@@ -1365,10 +1365,18 @@ FILE: ../../../flutter/content_handler/vulkan_native_rasterizer.cc
FILE: ../../../flutter/content_handler/vulkan_native_rasterizer.h
FILE: ../../../flutter/content_handler/vulkan_rasterizer.cc
FILE: ../../../flutter/content_handler/vulkan_rasterizer.h
FILE: ../../../flutter/flow/debug_print.cc
FILE: ../../../flutter/flow/debug_print.h
FILE: ../../../flutter/flow/layers/physical_model_layer.cc
FILE: ../../../flutter/flow/layers/physical_model_layer.h
FILE: ../../../flutter/flow/matrix_decomposition.cc
FILE: ../../../flutter/flow/matrix_decomposition.h
FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc
FILE: ../../../flutter/flow/paint_utils.cc
FILE: ../../../flutter/flow/paint_utils.h
FILE: ../../../flutter/flow/raster_cache_key.cc
FILE: ../../../flutter/flow/raster_cache_key.h
FILE: ../../../flutter/flow/raster_cache_unittests.cc
FILE: ../../../flutter/fml/icu_util.cc
FILE: ../../../flutter/fml/icu_util.h
FILE: ../../../flutter/fml/mapping.cc
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册