-
Notifications
You must be signed in to change notification settings - Fork 6k
optimize out unnecessary save restore pairs #34967
optimize out unnecessary save restore pairs #34967
Conversation
|
I would write test cases along the lines of: |
|
I deleted some earlier comments about how the nested counts would add up and result in multiple extra save records in the stream, but that was before I noticed that the deferred save count was being kept in the layer info. Still thinking through potential issues this solution might cause, but wanted to indicate why those comments were missing now. |
|
The failure in the smoke tests is likely due to the new deferred save counts not returning an accurate value. The framework code that calls a CustomPaint's paint method gets a save count before and after the method and expects them to match. |
|
getSaveCount() returns the number of records in the stack, but deferred saves are not accounted in that number. This will also affect uses of restoreToCount() which will only concern itself that it restores the right number of layer infos rather than undoing the real+deferred saves since the previous state. This might have caused the errors seen in the framework smoke tests. Fixing the value of getSaveCount() to account for deferred saves should also make sure that restoreToCount() functions correctly. |
|
I wrote the following 2 test cases on top of a local copy of the fix just to verify to myself that these cases work right (and they did). I leave them here in case you want to add them to the list of tests... Test case 1Test case 2 |
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.
There is a known bug discovered by the PR checks where the value we return for getSaveCount() is wrong - I've added some ideas about that in the PR comments.
I believe the changes to dl_utils.h/cc are redundant and should be removed, but let me know if I missed something.
Other than that, just a naming nit and a potential inlining suggestion.
display_list/display_list_builder.h
Outdated
|
|
||
| sk_sp<DisplayList> Build(); | ||
|
|
||
| unsigned save_count() const { return save_count_; } |
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 appears to be a new statistic for the total number of save calls added to the stream. In order to distinguish itself from getSaveCount() it should probably be called either total_save_count_ or save_records_count_?
If this is just for testing, then I think some good tests using the verbose DL comparison testing facility would be more targeted, but I do acknowledge that this adds a quick extra check to all of our existing unit tests. Were any of those additional checks of the "save_count()" informative in terms of noticing optimized save/restores?
display_list/display_list_utils.cc
Outdated
| } | ||
| } | ||
|
|
||
| void DisplayListBoundsCalculator::save() { |
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.
deferred save elimination was already done during construction. Why is it being done here as well? The stream should already be reduced so all of this code in the calculator should be completely redundant. Does it actually encounter any missed deferred save/restores?
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 still seems unnecessary and yet it is still here. Why are you checking the save/restores in the Calculator? That operation was already done in the Builder.
|
I would like to get @ColdPaleLight to see how this will interact with the PR to collapse the bounds calculations into the constructor. Also, a perhaps less "disruptive" change would be to switch to always having a layer_stack entry for every save/restore pair regardless of whether it is deferred or not and then just having a boolean in the layer info indicating whether it is deferred. That would mean that the size of the layer stack would remain a good answer for getSaveCount and it could potentially mean that it will fit in better with #34365 Pseudo-code for boolean always-layer-info solution |
I think PR #34365 can be easily adapted whether it is the current implementation by @JsouLiang or the solution mentioned by @flar . In fact, when @JsouLiang wrote this solution, we discussed how to implement it offline. The current implementation is from SkCanvas, https://github.com/google/skia/blob/b676a02b01727ba6453ae40c7e9ac5201785aba4/src/core/SkCanvas.cpp#L611 However, it should be noted here that since the additional save/restore is not pushed to the stream, this may cause the count of ops may not be equal between Displaylist and SkPicture when draw the same content. Because Skia records all Ops when using SkPictureRecorder. Skia does not optimize unnecessary save/restore when recording Ops, but when actually drawing. And here we have a test that checks if the op counts of Displaylist and SkPicture are the same.
|
Where do you see Skia recording elided save records? I found the opposite which is why I had to write all of the save tests in display_list_unittests.cc to ensure that the optimization would not apply. Without that code I was getting op count mismatches. Now that DisplayList elides them as well, some of these workarounds may no longer be needed. c.f. engine/display_list/display_list_unittests.cc Line 461 in 862773f
|
|
Note that SkRecorder records a save record on all calls to c.f. https://skia.googlesource.com/skia/+/refs/heads/main/src/core/SkRecorder.cpp#326 |
Yes, the |
Ahh, yes, you are right. The background is that I am trying to solve the issue flutter/flutter#107580 . I've tried in |
display_list/display_list_utils.cc
Outdated
| } | ||
| } | ||
|
|
||
| void DisplayListBoundsCalculator::save() { |
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 still seems unnecessary and yet it is still here. Why are you checking the save/restores in the Calculator? That operation was already done in the Builder.
Where do you get the op count mismatches? In the unit tests? If the unit tests are pushing NOP operations then the unit tests should be fixed. If they are not pushing NOP operations then there was something wrong with your logic that avoided pushing them. |
@flar Yes,in the unit tests. I didn't push NOP at first, so I encountered a count mismatches error. pushing NOP can fix them. |
Which tests? As far as I know all of our tests are testing non-NOP rendering. If the tests encountered cases where you failed to push an op then either the test is wrong or your code was wrong. |
Ignore previous comments, I discovered a reason why those unit tests had an omitted op. We need to find a better solution that doesn't trigger that condition. I'll follow up with a comment in #35024. |
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.
Almost there. Apologies for not double-checking the coverage for deferred save calls.
I suggested a new test for an odd "late save" case that we should check. Also, should we have a quick test for each of the methods that does a deferred check to verify that it does its check? Something small like:
{
save
<deferred-trigger-call>
drawRect
restore
...
EQ_Verbose(...)
}
// Repeat that for each of:
// - translate
// - scale
// - skew
// - transform2D
// - transformPersp
// - resetTransform
// - clipRect
// - RRect
// - Path
To be even more complete in testing we might want to check things like NOP-transforms don't realize the deferred save, and that methods that forward their operation to a simpler method such as transformPerspective(2D affine matrix) and clipPath(rectangle/oval/rrect path) also trigger a deferred save.
a8f004a to
6fa6ce0
Compare
6fa6ce0 to
d280061
Compare
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 is looking ready to go. Please fix the TranslateTriggersDeferredSave test that seems to have strayed from the pattern of the other Triggers tests.
|
3531af1 to
085100e
Compare
* drafting the solution to optimize out unnecessary save restore pairs * remove unnecessary save/restore pairs * delete the calculator change; * fix some logic; Add some testcases * Add test for set DlPaint * update test cases * Prune TranslateTriggersDeferredSave unittest
Add a mechanism for DisplayListBuilder to optimize out unnecessary save/restore pairs.