Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 019f9e3

Browse files
author
Jonah Williams
authored
[Impeller] fix drawPoints scaling factors. (#54368)
Fixes flutter/flutter#152780 Fixes flutter/flutter#152794 Problems: * If point scale is < 1, we discarded it too early by computing circle divisions with the rounded scale. Since we multiply the scale by the transform _after_ rounding, we may end up generating too many points in some circumstances. * Tessellator used max basis XYZ instead of max basis XY. The latter will never allow scaling factors less than 1 as Flutter canvas scale does not impact Z, only transform layers do. * Computation of max basis required squaring the scaling factor, which would cause us to hit float::inf too early. For translate scale just take the max of m[0] and m[5]. * Draw points minimum circumference is 1, so the min radius should be 0.5
1 parent 6cf8394 commit 019f9e3

File tree

6 files changed

+84
-14
lines changed

6 files changed

+84
-14
lines changed

impeller/display_list/aiks_dl_unittests.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,5 +800,46 @@ TEST_P(AiksTest, MatrixSaveLayerFilter) {
800800
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
801801
}
802802

803+
// Regression test for flutter/flutter#152780
804+
TEST_P(AiksTest, CanDrawScaledPointsSmallScaleLargeRadius) {
805+
std::vector<SkPoint> point = {
806+
{0, 0}, //
807+
};
808+
809+
DlPaint paint;
810+
paint.setStrokeCap(DlStrokeCap::kRound);
811+
paint.setColor(DlColor::kRed());
812+
paint.setStrokeWidth(100 * 1000000);
813+
814+
DisplayListBuilder builder(GetCullRect(GetWindowSize()));
815+
builder.Translate(200, 200);
816+
builder.Scale(0.000001, 0.000001);
817+
818+
builder.DrawPoints(DlCanvas::PointMode::kPoints, point.size(), point.data(),
819+
paint);
820+
821+
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
822+
}
823+
824+
// Regression test for flutter/flutter#152780
825+
TEST_P(AiksTest, CanDrawScaledPointsLargeScaleSmallRadius) {
826+
std::vector<SkPoint> point = {
827+
{0, 0}, //
828+
};
829+
830+
DlPaint paint;
831+
paint.setStrokeCap(DlStrokeCap::kRound);
832+
paint.setColor(DlColor::kRed());
833+
paint.setStrokeWidth(100 * 0.000001);
834+
835+
DisplayListBuilder builder(GetCullRect(GetWindowSize()));
836+
builder.Translate(200, 200);
837+
builder.Scale(1000000, 1000000);
838+
839+
builder.DrawPoints(DlCanvas::PointMode::kPoints, point.size(), point.data(),
840+
paint);
841+
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
842+
}
843+
803844
} // namespace testing
804845
} // namespace impeller

impeller/entity/geometry/point_field_geometry.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#include "impeller/entity/geometry/point_field_geometry.h"
66

7-
#include "impeller/core/vertex_buffer.h"
7+
#include "impeller/core/formats.h"
88
#include "impeller/entity/geometry/geometry.h"
99
#include "impeller/renderer/command_buffer.h"
1010

@@ -22,18 +22,17 @@ GeometryResult PointFieldGeometry::GetPositionBuffer(
2222
if (radius_ < 0.0) {
2323
return {};
2424
}
25-
Matrix transform = entity.GetTransform();
26-
Scalar determinant = transform.GetDeterminant();
27-
if (determinant == 0) {
25+
const Matrix& transform = entity.GetTransform();
26+
27+
Scalar max_basis = transform.GetMaxBasisLengthXY();
28+
if (max_basis == 0) {
2829
return {};
2930
}
30-
31-
Scalar min_size = 1.0f / sqrt(std::abs(determinant));
31+
Scalar min_size = 0.5f / max_basis;
3232
Scalar radius = std::max(radius_, min_size);
3333

3434
HostBuffer& host_buffer = renderer.GetTransientsBuffer();
3535
VertexBufferBuilder<SolidFillVertexShader::PerVertexData> vtx_builder;
36-
3736
if (round_) {
3837
// Get triangulation relative to {0, 0} so we can translate it to each
3938
// point in turn.

impeller/geometry/matrix.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ struct Matrix {
298298
Scalar GetMaxBasisLength() const;
299299

300300
constexpr Scalar GetMaxBasisLengthXY() const {
301+
// The full basis computation requires computing the squared scaling factor
302+
// for translate/scale only matrices. This substantially limits the range of
303+
// precision for small and large scales. Instead, check for the common cases
304+
// and directly return the max scaling factor.
305+
if (e[0][1] == 0 && e[1][0] == 0) {
306+
return std::max(e[0][0], e[1][1]);
307+
}
301308
return std::sqrt(std::max(e[0][0] * e[0][0] + e[0][1] * e[0][1],
302309
e[1][0] * e[1][0] + e[1][1] * e[1][1]));
303310
}

impeller/geometry/matrix_unittests.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "flutter/impeller/geometry/matrix.h"
88

99
#include "flutter/impeller/geometry/geometry_asserts.h"
10+
#include "impeller/geometry/constants.h"
1011

1112
namespace impeller {
1213
namespace testing {
@@ -160,5 +161,21 @@ TEST(MatrixTest, TransformHomogenous) {
160161
Vector3(32.0f, 33.0f, 41.0f));
161162
}
162163

164+
// Verifies a translate scale matrix doesn't need to compute sqrt(pow(scale, 2))
165+
TEST(MatrixTest, GetMaxBasisXYWithLargeAndSmallScalingFactor) {
166+
Matrix m = Matrix::MakeScale({2.625e+20, 2.625e+20, 1});
167+
EXPECT_NEAR(m.GetMaxBasisLengthXY(), 2.625e+20, 1e+20);
168+
169+
m = Matrix::MakeScale({2.625e-20, 2.625e-20, 1});
170+
EXPECT_NEAR(m.GetMaxBasisLengthXY(), 2.625e-20, 1e-20);
171+
}
172+
173+
TEST(MatrixTest, GetMaxBasisXYWithLargeAndSmallScalingFactorNonScaleTranslate) {
174+
Matrix m = Matrix::MakeScale({2.625e+20, 2.625e+20, 1});
175+
m.e[0][1] = 2;
176+
177+
EXPECT_TRUE(std::isinf(m.GetMaxBasisLengthXY()));
178+
}
179+
163180
} // namespace testing
164181
} // namespace impeller

impeller/tessellator/tessellator.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,8 @@ EllipticalVertexGenerator Tessellator::FilledCircle(
238238
const Matrix& view_transform,
239239
const Point& center,
240240
Scalar radius) {
241-
auto divisions =
242-
ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
241+
size_t divisions =
242+
ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
243243
return EllipticalVertexGenerator(Tessellator::GenerateFilledCircle,
244244
GetTrigsForDivisions(divisions),
245245
PrimitiveType::kTriangleStrip, 4,
@@ -257,7 +257,7 @@ EllipticalVertexGenerator Tessellator::StrokedCircle(
257257
Scalar half_width) {
258258
if (half_width > 0) {
259259
auto divisions = ComputeQuadrantDivisions(
260-
view_transform.GetMaxBasisLength() * radius + half_width);
260+
view_transform.GetMaxBasisLengthXY() * radius + half_width);
261261
return EllipticalVertexGenerator(Tessellator::GenerateStrokedCircle,
262262
GetTrigsForDivisions(divisions),
263263
PrimitiveType::kTriangleStrip, 8,
@@ -280,7 +280,7 @@ EllipticalVertexGenerator Tessellator::RoundCapLine(
280280
auto length = along.GetLength();
281281
if (length > kEhCloseEnough) {
282282
auto divisions =
283-
ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
283+
ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
284284
return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
285285
GetTrigsForDivisions(divisions),
286286
PrimitiveType::kTriangleStrip, 4,
@@ -302,8 +302,8 @@ EllipticalVertexGenerator Tessellator::FilledEllipse(
302302
bounds.GetWidth() * 0.5f);
303303
}
304304
auto max_radius = bounds.GetSize().MaxDimension();
305-
auto divisions =
306-
ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * max_radius);
305+
auto divisions = ComputeQuadrantDivisions(
306+
view_transform.GetMaxBasisLengthXY() * max_radius);
307307
auto center = bounds.GetCenter();
308308
return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
309309
GetTrigsForDivisions(divisions),
@@ -323,7 +323,7 @@ EllipticalVertexGenerator Tessellator::FilledRoundRect(
323323
radii.height * 2 < bounds.GetHeight()) {
324324
auto max_radius = radii.MaxDimension();
325325
auto divisions = ComputeQuadrantDivisions(
326-
view_transform.GetMaxBasisLength() * max_radius);
326+
view_transform.GetMaxBasisLengthXY() * max_radius);
327327
auto upper_left = bounds.GetLeftTop() + radii;
328328
auto lower_right = bounds.GetRightBottom() - radii;
329329
return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,

testing/impeller_golden_tests_output.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ impeller_Play_AiksTest_CanDrawPointsWithTextureMap_Vulkan.png
218218
impeller_Play_AiksTest_CanDrawPoints_Metal.png
219219
impeller_Play_AiksTest_CanDrawPoints_OpenGLES.png
220220
impeller_Play_AiksTest_CanDrawPoints_Vulkan.png
221+
impeller_Play_AiksTest_CanDrawScaledPointsLargeScaleSmallRadius_Metal.png
222+
impeller_Play_AiksTest_CanDrawScaledPointsLargeScaleSmallRadius_OpenGLES.png
223+
impeller_Play_AiksTest_CanDrawScaledPointsLargeScaleSmallRadius_Vulkan.png
224+
impeller_Play_AiksTest_CanDrawScaledPointsSmallScaleLargeRadius_Metal.png
225+
impeller_Play_AiksTest_CanDrawScaledPointsSmallScaleLargeRadius_OpenGLES.png
226+
impeller_Play_AiksTest_CanDrawScaledPointsSmallScaleLargeRadius_Vulkan.png
221227
impeller_Play_AiksTest_CanEmptyPictureConvertToImage_Metal.png
222228
impeller_Play_AiksTest_CanEmptyPictureConvertToImage_OpenGLES.png
223229
impeller_Play_AiksTest_CanEmptyPictureConvertToImage_Vulkan.png

0 commit comments

Comments
 (0)