Skip to content

Commit

Permalink
Rework raster cache to fix numerous issues. (flutter#3717)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
chinmaygarde authored Jun 5, 2017
1 parent c3721a5 commit 1c6a531
Show file tree
Hide file tree
Showing 15 changed files with 735 additions and 84 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
22 changes: 22 additions & 0 deletions flow/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
]
Expand All @@ -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",
]
}
60 changes: 60 additions & 0 deletions flow/debug_print.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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;
}
24 changes: 24 additions & 0 deletions flow/debug_print.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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_
19 changes: 11 additions & 8 deletions flow/layers/picture_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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());
Expand All @@ -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());
}
Expand Down
5 changes: 2 additions & 3 deletions flow/layers/picture_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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);
};
Expand Down
141 changes: 141 additions & 0 deletions flow/matrix_decomposition.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// 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
51 changes: 51 additions & 0 deletions flow/matrix_decomposition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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_
Loading

0 comments on commit 1c6a531

Please sign in to comment.