-
Notifications
You must be signed in to change notification settings - Fork 6k
Tweak the condition of 'FML_DCHECK' to solve the issue caused by round-off error #34660
Tweak the condition of 'FML_DCHECK' to solve the issue caused by round-off error #34660
Conversation
|
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. |
|
Add "clang-format off" comment cause the code after formatting looks weird |
|
cc @flar |
flar
left a comment
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.
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.
flow/raster_cache.cc
Outdated
| 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 && |
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.
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.
1. The height of the image is almost one pixel larger than bounds at the beginningIn 2. Round-off error caused by different dy in matrixThe cause for the issue is that there is a slight difference in the And
But
|
| 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 )
|
@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. 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. |
flar
left a comment
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.
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.
flow/raster_cache.cc
Outdated
| // 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 |
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.
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.
|
test-exempt: asserts are tests. |
… by round-off error (flutter/engine#34660)
fix flutter/flutter#107622
Pre-launch Checklist
writing and running engine tests.
///).