-
Notifications
You must be signed in to change notification settings - Fork 6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Impeller] fix drawPoints scaling factors. #54368
Changes from 3 commits
b897f3f
7e6fdbf
ec28758
badfb0c
c174ab2
1ce6a7b
f372355
f6ca941
fcce4aa
16ad4f5
ce54247
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
|
||
#include "impeller/entity/geometry/point_field_geometry.h" | ||
|
||
#include "impeller/core/vertex_buffer.h" | ||
#include "impeller/core/formats.h" | ||
#include "impeller/entity/geometry/geometry.h" | ||
#include "impeller/renderer/command_buffer.h" | ||
|
||
|
@@ -28,17 +28,17 @@ GeometryResult PointFieldGeometry::GetPositionBuffer( | |
return {}; | ||
} | ||
|
||
Scalar min_size = 1.0f / sqrt(std::abs(determinant)); | ||
Scalar min_size = 0.5f / sqrt(std::abs(determinant)); | ||
Scalar radius = std::max(radius_, min_size); | ||
|
||
HostBuffer& host_buffer = renderer.GetTransientsBuffer(); | ||
VertexBufferBuilder<SolidFillVertexShader::PerVertexData> vtx_builder; | ||
|
||
if (round_) { | ||
// Get triangulation relative to {0, 0} so we can translate it to each | ||
// point in turn. | ||
// point in turn. Note: we intentionally used the original radius here | ||
// to capture any scaling < 1.0. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the minimum size of stroked primitives is (somewhat arbitrarily chosen as) 1.0 device pixels which means you are allowing the transform to scale the size of the dots to (near) 0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will change this so we enforce the min size in the tessellator logic. Right now we're rounding too early There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The rounding was supposed to be reversible, but I think the issue here is that the rounding is 1-dimensional and the use of the rounded radius was 2-dimensional? Also, tessellator is also used for filled shapes and those are allowed to scale to 0, so it minimally needs a flag indicating if the operation is a stroke or a fill in order to know when to apply the rounding. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realized that since the rounding code in the points code is using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm good point. maybe then Tessellator::FilledCircle should have separate radius and pixel_radius arguments and we can let the caller manage.
Ahh good catch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the new radius works, then we should just use it in the call get get the generator below. |
||
auto generator = | ||
renderer.GetTessellator()->FilledCircle(transform, {}, radius); | ||
renderer.GetTessellator()->FilledCircle(transform, {}, radius_); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have a test that asserts this. Probably a golden test that is similar to the reproduction code on the linked issue is easiest. |
||
FML_DCHECK(generator.GetTriangleType() == PrimitiveType::kTriangleStrip); | ||
std::vector<Point> circle_vertices; | ||
circle_vertices.reserve(generator.GetVertexCount()); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -298,6 +298,13 @@ struct Matrix { | |
Scalar GetMaxBasisLength() const; | ||
|
||
constexpr Scalar GetMaxBasisLengthXY() const { | ||
// The full basis computation requires computing the squared scaling factor | ||
// for translate/scale only matrices. This substantially limits the range of | ||
// precision for small and large scales. Instead, check for the common cases | ||
// and directly return the max scaling factor. | ||
if (IsTranslationScaleOnly()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically, we only care if [0,1] and [1,0] are 0s. The rest of the entries don't matter since we're not planning on consulting them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need to do this still. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another thing to consider, probably in a follow-on PR, there are only a couple of other uses of the non XY version of this method and they should probably all be converted to the XY version and then the non-XY method removed so it doesn't cause more problems. All of our rendering ops are 2D geometry so I don't think there is any code in the system that would want to use the regular version of this method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. YEah that sounds like a good idea. I can take a pass after this change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Done with this one though) |
||
return std::max(m[0], m[5]); | ||
} | ||
return std::sqrt(std::max(e[0][0] * e[0][0] + e[0][1] * e[0][1], | ||
e[1][0] * e[1][0] + e[1][1] * e[1][1])); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
#include "flutter/impeller/geometry/matrix.h" | ||
|
||
#include "flutter/impeller/geometry/geometry_asserts.h" | ||
#include "impeller/geometry/constants.h" | ||
|
||
namespace impeller { | ||
namespace testing { | ||
|
@@ -160,5 +161,20 @@ TEST(MatrixTest, TransformHomogenous) { | |
Vector3(32.0f, 33.0f, 41.0f)); | ||
} | ||
|
||
// Verifies a translate scale matrix doesn't need to compute sqrt(pow(scale, 2)) | ||
TEST(MatrixTest, GetMaxBasisXYWithLargeAndSmallScalingFactor) { | ||
Matrix m = Matrix::MakeScale({2.625e+20, 2.625e+20, 1}); | ||
EXPECT_NEAR(m.GetMaxBasisLengthXY(), 2.625e+20, 1e+20); | ||
|
||
m = Matrix::MakeScale({2.625e-20, 2.625e-20, 1}); | ||
EXPECT_NEAR(m.GetMaxBasisLengthXY(), 2.625e-20, 1e-20); | ||
} | ||
|
||
TEST(MatrixTest, GetMaxBasisXYWithLargeAndSmallScalingFactorNonScaleTranslate) { | ||
Matrix m = Matrix::MakeScale({2.625e+20, 2.625e+20, 1}) * | ||
Matrix::MakeRotationX(Radians(kPi / 2)); | ||
EXPECT_TRUE(std::isinf(m.GetMaxBasisLengthXY())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is failing, perhaps that isn't enough to create infinities on some platforms? Could this be a case of a platform treating results that are too big for single precision as large, but not infinite values? I looked for answers and discovered that a compliant implementation is allowed to round to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, I think its just not tripping on the scale/translate check anymore. I should just set one of matrix entires that will trigger it directly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the math done in doubles and so the sqrt reduces it back into 32-bit float before the return? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, so the matrix is something like:
am I making the right check here?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You rotated around the X axis which moved it into the 3rd dimension. We're seeing it end-on, so the Y scale went to zero as everything is in the Y=0 plane. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another way to look at this is that if the optimization doesn't trigger then only these 4 entries will be used to compute the value. So, if 2 of them are 0 (2 that happen to be 0 a lot), then you can simplify the calculation. One question is why you are mixing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No Its not an efficiency thing, I was just being a bit lazy. So otherwise this code is correct then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "optimization" doesn't necessarily mean an efficiency thing. You did it to do less work because more work was causing an overflow. It's still a more direct path to the answer. The new test code is one way to force the old computation to occur. Yes. |
||
} | ||
|
||
} // namespace testing | ||
} // namespace impeller |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include <cmath> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stray include, did you forget to include some tessellator tests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I had some but I removed them. The only way to test that we're using masBasisXY instead of XYZ is to assert the subdivision count - but these are supposed to be arbitrary so it doesnt seem like a good route. Will stick with goldens for now. |
||
#include "flutter/testing/testing.h" | ||
#include "gtest/gtest.h" | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having a brain fart, should this actually be 0.5 / transform.GetMaxBasisLengthXY(); ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. Determinant of 2D matrices is the area of the (transformed) unit square which is pretty much the same thing, but it combines them geometrically so 2x in one direction balances 0.5 in the other. But matrix is a 3D thing so it becomes volume of the unit cube which might not be what we want. Or maybe it's OK since the volume of the cube is the area of the square times the height which is 1.0 - but still it will combine scales between the directions which is not what we want.
So, I think MaxBasisXY is a better value to use.
Really in all of these cases we should be doing separated 2D measurements rather than using the "max" or determinant. We should independently clamp each dimension separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if they've imposed a Z scale then determinant would be not at all what we want. Another reason to go with MaxBasisXY
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done