diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 37977c60e9b52..1870ef8896eba 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -110,9 +110,6 @@ FILE: ../../../flutter/flow/layers/texture_layer_unittests.cc FILE: ../../../flutter/flow/layers/transform_layer.cc FILE: ../../../flutter/flow/layers/transform_layer.h FILE: ../../../flutter/flow/layers/transform_layer_unittests.cc -FILE: ../../../flutter/flow/matrix_decomposition.cc -FILE: ../../../flutter/flow/matrix_decomposition.h -FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc FILE: ../../../flutter/flow/mutators_stack_unittests.cc FILE: ../../../flutter/flow/paint_region.cc FILE: ../../../flutter/flow/paint_region.h diff --git a/flow/BUILD.gn b/flow/BUILD.gn index fe9bb90c2e839..642541ee8dfc0 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -60,8 +60,6 @@ source_set("flow") { "layers/texture_layer.h", "layers/transform_layer.cc", "layers/transform_layer.h", - "matrix_decomposition.cc", - "matrix_decomposition.h", "paint_region.cc", "paint_region.h", "paint_utils.cc", @@ -160,7 +158,6 @@ if (enable_unittests) { "layers/shader_mask_layer_unittests.cc", "layers/texture_layer_unittests.cc", "layers/transform_layer_unittests.cc", - "matrix_decomposition_unittests.cc", "mutators_stack_unittests.cc", "raster_cache_unittests.cc", "rtree_unittests.cc", diff --git a/flow/matrix_decomposition.cc b/flow/matrix_decomposition.cc deleted file mode 100644 index 2207fa35fb878..0000000000000 --- a/flow/matrix_decomposition.cc +++ /dev/null @@ -1,128 +0,0 @@ -// 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. - -#include "flutter/flow/matrix_decomposition.h" - -namespace flutter { - -static inline SkV3 SkV3Combine(const SkV3& a, - float a_scale, - const SkV3& b, - float b_scale) { - return (a * a_scale) + (b * b_scale); -} - -MatrixDecomposition::MatrixDecomposition(const SkMatrix& matrix) - : MatrixDecomposition(SkM44{matrix}) {} - -// Use custom normalize to avoid skia precision loss/normalize() privatization. -static inline void SkV3Normalize(SkV3* v) { - double mag = sqrt(v->x * v->x + v->y * v->y + v->z * v->z); - double scale = 1.0 / mag; - v->x *= scale; - v->y *= scale; - v->z *= scale; -} - -MatrixDecomposition::MatrixDecomposition(SkM44 matrix) : valid_(false) { - if (matrix.rc(3, 3) == 0) { - return; - } - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - matrix.setRC(j, i, matrix.rc(j, i) / matrix.rc(3, 3)); - } - } - - SkM44 perpective_matrix = matrix; - for (int i = 0; i < 3; i++) { - perpective_matrix.setRC(3, i, 0.0); - } - - perpective_matrix.setRC(3, 3, 1.0); - - SkM44 inverted(SkM44::Uninitialized_Constructor::kUninitialized_Constructor); - if (!perpective_matrix.invert(&inverted)) { - return; - } - - if (matrix.rc(3, 0) != 0.0 || matrix.rc(3, 1) != 0.0 || - matrix.rc(3, 2) != 0.0) { - const SkV4 right_hand_side = matrix.row(3); - - perspective_ = inverted.transpose() * right_hand_side; - - matrix.setRow(3, {0, 0, 0, 1}); - } - - translation_ = {matrix.rc(0, 3), matrix.rc(1, 3), matrix.rc(2, 3)}; - - matrix.setRC(0, 3, 0.0); - matrix.setRC(1, 3, 0.0); - matrix.setRC(2, 3, 0.0); - - SkV3 row[3]; - for (int i = 0; i < 3; i++) { - row[i] = {matrix.rc(0, i), matrix.rc(1, i), matrix.rc(2, i)}; - } - - scale_.x = row[0].length(); - - SkV3Normalize(&row[0]); - - shear_.x = row[0].dot(row[1]); - row[1] = SkV3Combine(row[1], 1.0, row[0], -shear_.x); - - scale_.y = row[1].length(); - - SkV3Normalize(&row[1]); - - shear_.x /= scale_.y; - - shear_.y = row[0].dot(row[2]); - row[2] = SkV3Combine(row[2], 1.0, row[0], -shear_.y); - shear_.z = row[1].dot(row[2]); - row[2] = SkV3Combine(row[2], 1.0, row[1], -shear_.z); - - scale_.z = row[2].length(); - - SkV3Normalize(&row[2]); - - shear_.y /= scale_.z; - shear_.z /= scale_.z; - - if (row[0].dot(row[1].cross(row[2])) < 0) { - scale_ *= -1; - - for (int i = 0; i < 3; i++) { - row[i] *= -1; - } - } - - rotation_.x = 0.5 * sqrt(fmax(1.0 + row[0].x - row[1].y - row[2].z, 0.0)); - rotation_.y = 0.5 * sqrt(fmax(1.0 - row[0].x + row[1].y - row[2].z, 0.0)); - rotation_.z = 0.5 * sqrt(fmax(1.0 - row[0].x - row[1].y + row[2].z, 0.0)); - rotation_.w = 0.5 * sqrt(fmax(1.0 + row[0].x + row[1].y + row[2].z, 0.0)); - - if (row[2].y > row[1].z) { - rotation_.x = -rotation_.x; - } - if (row[0].z > row[2].x) { - rotation_.y = -rotation_.y; - } - if (row[1].x > row[0].y) { - rotation_.z = -rotation_.z; - } - - valid_ = true; -} - -MatrixDecomposition::~MatrixDecomposition() = default; - -bool MatrixDecomposition::IsValid() const { - return valid_; -} - -} // namespace flutter diff --git a/flow/matrix_decomposition.h b/flow/matrix_decomposition.h deleted file mode 100644 index ea0af37ae9b50..0000000000000 --- a/flow/matrix_decomposition.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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_MATRIX_DECOMPOSITION_H_ -#define FLUTTER_FLOW_MATRIX_DECOMPOSITION_H_ - -#include "flutter/fml/macros.h" -#include "third_party/skia/include/core/SkM44.h" -#include "third_party/skia/include/core/SkMatrix.h" - -namespace flutter { - -/// 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(SkM44 matrix); - - ~MatrixDecomposition(); - - bool IsValid() const; - - const SkV3& translation() const { return translation_; } - - const SkV3& scale() const { return scale_; } - - const SkV3& shear() const { return shear_; } - - const SkV4& perspective() const { return perspective_; } - - const SkV4& rotation() const { return rotation_; } - - private: - bool valid_; - SkV3 translation_; - SkV3 scale_; - SkV3 shear_; - SkV4 perspective_; - SkV4 rotation_; - - FML_DISALLOW_COPY_AND_ASSIGN(MatrixDecomposition); -}; - -} // namespace flutter - -#endif // FLUTTER_FLOW_MATRIX_DECOMPOSITION_H_ diff --git a/flow/matrix_decomposition_unittests.cc b/flow/matrix_decomposition_unittests.cc deleted file mode 100644 index bf23937415fb4..0000000000000 --- a/flow/matrix_decomposition_unittests.cc +++ /dev/null @@ -1,159 +0,0 @@ -// 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. - -#include "flutter/flow/matrix_decomposition.h" - -#include - -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -TEST(MatrixDecomposition, Rotation) { - SkM44 matrix; - - const auto angle = M_PI_4; - matrix.setRotate({0.0, 0.0, 1.0}, angle); - - flutter::MatrixDecomposition decomposition(matrix); - ASSERT_TRUE(decomposition.IsValid()); - - const auto sine = sin(angle * 0.5); - - ASSERT_FLOAT_EQ(0, decomposition.rotation().x); - ASSERT_FLOAT_EQ(0, decomposition.rotation().y); - ASSERT_FLOAT_EQ(sine, decomposition.rotation().z); - ASSERT_FLOAT_EQ(cos(angle * 0.5), decomposition.rotation().w); -} - -TEST(MatrixDecomposition, Scale) { - SkM44 matrix; - - const auto scale = 5.0; - matrix.setScale(scale + 0, scale + 1, scale + 2); - - flutter::MatrixDecomposition decomposition(matrix); - ASSERT_TRUE(decomposition.IsValid()); - - ASSERT_FLOAT_EQ(scale + 0, decomposition.scale().x); - ASSERT_FLOAT_EQ(scale + 1, decomposition.scale().y); - ASSERT_FLOAT_EQ(scale + 2, decomposition.scale().z); -} - -TEST(MatrixDecomposition, Translate) { - SkM44 matrix; - - const auto translate = 125.0; - matrix.setTranslate(translate + 0, translate + 1, translate + 2); - - flutter::MatrixDecomposition decomposition(matrix); - ASSERT_TRUE(decomposition.IsValid()); - - ASSERT_FLOAT_EQ(translate + 0, decomposition.translation().x); - ASSERT_FLOAT_EQ(translate + 1, decomposition.translation().y); - ASSERT_FLOAT_EQ(translate + 2, decomposition.translation().z); -} - -TEST(MatrixDecomposition, Combination) { - const auto rotation = M_PI_4; - const auto scale = 5; - const auto translate = 125.0; - - SkM44 m1; - m1.setRotate({0, 0, 1}, rotation); - - SkM44 m2; - m2.setScale(scale, scale, scale); - - SkM44 m3; - m3.setTranslate(translate, translate, translate); - - SkM44 combined = m3 * m2 * m1; - - flutter::MatrixDecomposition decomposition(combined); - ASSERT_TRUE(decomposition.IsValid()); - - ASSERT_FLOAT_EQ(translate, decomposition.translation().x); - ASSERT_FLOAT_EQ(translate, decomposition.translation().y); - ASSERT_FLOAT_EQ(translate, decomposition.translation().z); - - ASSERT_FLOAT_EQ(scale, decomposition.scale().x); - ASSERT_FLOAT_EQ(scale, decomposition.scale().y); - ASSERT_FLOAT_EQ(scale, decomposition.scale().z); - - const auto sine = sin(rotation * 0.5); - - ASSERT_FLOAT_EQ(0, decomposition.rotation().x); - ASSERT_FLOAT_EQ(0, decomposition.rotation().y); - ASSERT_FLOAT_EQ(sine, decomposition.rotation().z); - ASSERT_FLOAT_EQ(cos(rotation * 0.5), decomposition.rotation().w); -} - -TEST(MatrixDecomposition, ScaleFloatError) { - constexpr float scale_increment = 0.00001f; - float scale = 0.0001f; - while (scale < 2.0f) { - SkM44 matrix; - matrix.setScale(scale, scale, 1.0f); - - flutter::MatrixDecomposition decomposition3(matrix); - ASSERT_TRUE(decomposition3.IsValid()); - - ASSERT_FLOAT_EQ(scale, decomposition3.scale().x); - ASSERT_FLOAT_EQ(scale, decomposition3.scale().y); - ASSERT_FLOAT_EQ(1.f, decomposition3.scale().z); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().x); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().y); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().z); - scale += scale_increment; - } - - SkM44 matrix; - const auto scale1 = 1.7734375f; - matrix.setScale(scale1, scale1, 1.f); - - // Bug upper bound (empirical) - const auto scale2 = 1.773437559603f; - SkM44 matrix2; - matrix2.setScale(scale2, scale2, 1.f); - - // Bug lower bound (empirical) - const auto scale3 = 1.7734374403954f; - SkM44 matrix3; - matrix3.setScale(scale3, scale3, 1.f); - - flutter::MatrixDecomposition decomposition(matrix); - ASSERT_TRUE(decomposition.IsValid()); - - flutter::MatrixDecomposition decomposition2(matrix2); - ASSERT_TRUE(decomposition2.IsValid()); - - flutter::MatrixDecomposition decomposition3(matrix3); - ASSERT_TRUE(decomposition3.IsValid()); - - ASSERT_FLOAT_EQ(scale1, decomposition.scale().x); - ASSERT_FLOAT_EQ(scale1, decomposition.scale().y); - ASSERT_FLOAT_EQ(1.f, decomposition.scale().z); - ASSERT_FLOAT_EQ(0, decomposition.rotation().x); - ASSERT_FLOAT_EQ(0, decomposition.rotation().y); - ASSERT_FLOAT_EQ(0, decomposition.rotation().z); - - ASSERT_FLOAT_EQ(scale2, decomposition2.scale().x); - ASSERT_FLOAT_EQ(scale2, decomposition2.scale().y); - ASSERT_FLOAT_EQ(1.f, decomposition2.scale().z); - ASSERT_FLOAT_EQ(0, decomposition2.rotation().x); - ASSERT_FLOAT_EQ(0, decomposition2.rotation().y); - ASSERT_FLOAT_EQ(0, decomposition2.rotation().z); - - ASSERT_FLOAT_EQ(scale3, decomposition3.scale().x); - ASSERT_FLOAT_EQ(scale3, decomposition3.scale().y); - ASSERT_FLOAT_EQ(1.f, decomposition3.scale().z); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().x); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().y); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().z); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 5707afd7ff3da..ce39e20ea33b2 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -234,11 +234,7 @@ bool RasterCache::Prepare(PrerollContext* context, SkMatrix transformation_matrix = untranslated_matrix; transformation_matrix.preTranslate(offset.x(), offset.y()); - // 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); - - if (!matrix.IsValid()) { + if (!transformation_matrix.invert(nullptr)) { // The matrix was singular. No point in going further. return false; } @@ -285,11 +281,7 @@ bool RasterCache::Prepare(PrerollContext* context, SkMatrix transformation_matrix = untranslated_matrix; transformation_matrix.preTranslate(offset.x(), offset.y()); - // 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); - - if (!matrix.IsValid()) { + if (!transformation_matrix.invert(nullptr)) { // The matrix was singular. No point in going further. return false; } diff --git a/flow/raster_cache_key.h b/flow/raster_cache_key.h index 21d6bfe3dfdcb..2b71ca748545b 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -7,8 +7,8 @@ #include -#include "flutter/flow/matrix_decomposition.h" #include "flutter/fml/logging.h" +#include "third_party/skia/include/core/SkMatrix.h" namespace flutter { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 79ea8a06ae69a..e7c48c2730611 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -473,6 +473,74 @@ TEST(RasterCache, NestedOpCountMetricUsedForDisplayList) { ASSERT_TRUE(cache.Draw(*display_list, dummy_canvas)); } +TEST(RasterCache, SkPictureWithSingularMatrixIsNotCached) { + size_t threshold = 2; + flutter::RasterCache cache(threshold); + + SkMatrix matrices[] = { + SkMatrix::Scale(0, 1), + SkMatrix::Scale(1, 0), + SkMatrix::Skew(1, 1), + }; + int matrixCount = sizeof(matrices) / sizeof(matrices[0]); + + auto picture = GetSamplePicture(); + + SkCanvas dummy_canvas; + + PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + + for (int i = 0; i < 10; i++) { + cache.PrepareNewFrame(); + + for (int j = 0; j < matrixCount; j++) { + ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, + picture.get(), true, false, matrices[j])); + } + + for (int j = 0; j < matrixCount; j++) { + dummy_canvas.setMatrix(matrices[j]); + ASSERT_FALSE(cache.Draw(*picture, dummy_canvas)); + } + + cache.CleanupAfterFrame(); + } +} + +TEST(RasterCache, DisplayListWithSingularMatrixIsNotCached) { + size_t threshold = 2; + flutter::RasterCache cache(threshold); + + SkMatrix matrices[] = { + SkMatrix::Scale(0, 1), + SkMatrix::Scale(1, 0), + SkMatrix::Skew(1, 1), + }; + int matrixCount = sizeof(matrices) / sizeof(matrices[0]); + + auto display_list = GetSampleDisplayList(); + + SkCanvas dummy_canvas; + + PrerollContextHolder preroll_context_holder = GetSamplePrerollContextHolder(); + + for (int i = 0; i < 10; i++) { + cache.PrepareNewFrame(); + + for (int j = 0; j < matrixCount; j++) { + ASSERT_FALSE(cache.Prepare(&preroll_context_holder.preroll_context, + display_list.get(), true, false, matrices[j])); + } + + for (int j = 0; j < matrixCount; j++) { + dummy_canvas.setMatrix(matrices[j]); + ASSERT_FALSE(cache.Draw(*display_list, dummy_canvas)); + } + + cache.CleanupAfterFrame(); + } +} + } // namespace testing } // namespace flutter