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

Conversation

@ColdPaleLight
Copy link
Member

@ColdPaleLight ColdPaleLight commented Jul 14, 2022

fix flutter/flutter#107622

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides].
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the [CLA].
  • All existing and new tests are passing.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat (don't just cc him here, he won't see it! He's on Discord!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@ColdPaleLight
Copy link
Member Author

@chinmaygarde
Copy link
Member

cc @flar

Copy link
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best fix - find where we have the round-off error and fix the math there.

Acceptable fix - relaxing the test by a very tiny amount, much less than half a pixel and switch to a test that doesn't allow the cache image to be smaller than the device bounds.

FML_DCHECK(std::abs(bounds.width() - image_->dimensions().width()) <= 1 &&
std::abs(bounds.height() - image_->dimensions().height()) <= 1);
// clang-format off
FML_DCHECK(std::round(std::abs(bounds.width() - image_->dimensions().width())) <=1 &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::round is not the answer. If this delta is >1 then we have a problem somewhere. This DCHECK is there for a reason.

Now, round-off error in the range of .00012 likely doesn't represent a big problem and we could probably just accept it, though it would be interesting to see why our math allowed that round-off error to affect the calculations here in the first place.

If we are going to fix this bug by relaxing the assertion, then we should use 1+epsilon rather than round which will allow up to 1+0.5. One concept for an acceptable epsilon is whether or not it would affect the next pixel which would require a coverage of at least 1/256 (or 1/512 representing half of an AA sample) since that would be the smallest coverage that might cause that pixel to rise from transparency to a tiny nearly invisible pixel.

But, I'd rather see an analysis of where we incurred the round-off.

Also, I'm not sure about the abs() function. The image dimensions should always be larger than the device bounds, I'd rather see the subtraction reversed and >=0 <=1 be used as the test.

@ColdPaleLight
Copy link
Member Author

1. The height of the image is almost one pixel larger than bounds at the beginning

In Rasterize, the height of dest_rect (bounds) is 1890, but its hex value is 0x1.d88002p+10, so the height of the image calculated by SkScalarCeilToInt(dest_rect.height()) is 1891

2. Round-off error caused by different dy in matrix

The cause for the issue is that there is a slight difference in the height of bounds calculated by the method RasterCacheUtil::GetDeviceBounds in the RasterCache::Rasterize and RasterCacheResult::draw.

And bounds is calculated by logical_rect and matrix, their logical_rect is the same.
Here is its decimal value and hex value via std::hexfloat
The following is the logical_rect:

name value hex
left -8 -0x1p+3
top 8 0x1p+3
right 419.429 0x1.a36db6p+8
bottom 548 0x1.12p+9
width 427.429 0x1.ab6db6p+8
height 540 0x1.0ep+9

But m[5] (dy) in their matrix is ​​different, and it's what caused the round-off error.

RasterCache::Rasterize

The following is the matrix used in RasterCache::Rasterize

name value hex
m0 3.5 0x1.cp+1
m1 0 0x0p+0
m2 0 0x0p+0
m3 0 0x0p+0
m4 3.5 0x1.cp+1
m5 1415.24 0x1.61cfaep+10
m6 0 0x0p+0
m7 0 0x0p+0
m8 1 0x1p+0

The bounds calculated by logical_rect and matrix in RasterCache::Rasterize:

name value hex
left -28 -0x1.cp+4
top 1443.24 0x1.68cfaep+10
right 1468 0x1.6fp+10
bottom 3333.25 0x1.a0a7d8p+11
width 1496 0x1.76p+10
height 1890 0x1.d88002p+10

image height is: 1891

The result of subtracting the image height from the bounds height is:

-0.999878 ( -0x1.fffp-1 )

RasterCacheResult::draw

The following is the matrix used in RasterCacheResult::draw

name value hex
m0 3.5 0x1.cp+1
m1 0 0x0p+0
m2 0 0x0p+0
m3 0 0x0p+0
m4 3.5 0x1.cp+1
m5 675.173 0x1.519634p+9
m6 0 0x0p+0
m7 0 0x0p+0
m8 1 0x1p+0

The bounds calculated by logical_rect and matrix in RasterCacheResult::draw:

name value hex
left -28 -0x1.cp+4
top 703.173 0x1.68cfaep+10
right 1468 0x1.6fp+10
bottom 2593.17 0x1.44258cp+11
width 1496 0x1.76p+10
height 1890 0x1.d87ffep+10

image height is: 1891

The result of subtracting the image height from the bounds height is:

-1.00012 ( -0x1.0008p+0 )

@ColdPaleLight
Copy link
Member Author

@flar Although the cause has been found, I still don't know how to fix the math. So I set epsilon to 1/512 first. One thing to note here is that the test below also needs to use epsilon instead of 0. Otherwise it will crash too.

image_->dimensions().width() - bounds.width() > -epsilon

Please let me know if you have any thoughts :)

@ColdPaleLight ColdPaleLight requested a review from flar July 15, 2022 09:11
@flar
Copy link
Contributor

flar commented Jul 15, 2022

@flar Although the cause has been found, I still don't know how to fix the math. So I set epsilon to 1/512 first. One thing to note here is that the test below also needs to use epsilon instead of 0. Otherwise it will crash too.

image_->dimensions().width() - bounds.width() > -epsilon

Please let me know if you have any thoughts :)

Yes, I see now that the issue is related to changing floating point resolution as you translate an object. At one offset two values might differ by just smaller than an integer and at another offset they will be greater, depending on how many bits of mantissa were lost.

The only way I could see to prevent the problem would be to use a "coverage aware ceil" operation on the bounds. Basically, avoid padding the bounds if they are just slightly larger than a pixel. So, something like "ceil(width - epsilon)". No use allocating a pixel for something that will not see any non-transparent pixels in practice.

Copy link
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looks good.

We could also look to solve this by avoiding padding the size of the cache image when the actual floating point size is not enough larger than an integer to actually capture any rendering past the pixel boundary, but I think we might still need an epsilon test like this here for when the drawn bounds end up with a slight overflow but the cached bounds did not.

// introduce epsilon to solve the round-off error. The value of epsilon is
// 1/512, which represents half of an AA sample.
//
// see https://github.com/flutter/flutter/issues/107622
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please inline all the useful information rather than linking to the bug database. We don't know if the bug database will outlive the source code.

@Hixie
Copy link
Contributor

Hixie commented Jul 16, 2022

test-exempt: asserts are tests.

@ColdPaleLight ColdPaleLight added autosubmit Merge PR when tree becomes green via auto submit App and removed needs tests labels Jul 18, 2022
@ColdPaleLight ColdPaleLight changed the title Tweak the condition of 'FML_DCHECK' to solve the issue caused by floating point error Tweak the condition of 'FML_DCHECK' to solve the issue caused by round-off error Jul 18, 2022
@ColdPaleLight ColdPaleLight added autosubmit Merge PR when tree becomes green via auto submit App and removed autosubmit Merge PR when tree becomes green via auto submit App labels Jul 18, 2022
@auto-submit auto-submit bot merged commit 7186455 into flutter:main Jul 18, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jul 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"complex_layout" project will crash when scrolling if you use debug local engine

4 participants