Canvas drawing outside requestAnimationFrame callbacks leads to overproduction (needs more backpressure) - paints become slower and slower
Categories
(Core :: Graphics: Canvas2D, defect)
Tracking
()
Performance Impact | low |
People
(Reporter: mstange, Unassigned)
References
(Blocks 1 open bug)
Details
(Keywords: perf:responsiveness)
Attachments
(1 file, 1 obsolete file)
1.32 KB,
text/html
|
Details |
I originally noticed this while interacting with the profiler flame graph, for example when scrolling up in this profile: https://share.firefox.dev/3qi6hwO
Steps to reproduce:
- Load the attached testcase.
- Scroll over the canvas, up and down, repeatedly until drawing gets really delayed.
Expected results:
Drawing latency should not increase. It should be equally slow at the start and at the end of the scroll gesture.
Actual results:
Drawing latency gets worse over the course of the scroll gesture.
Here's a profile which I captured when the problem was happening in the profiler: https://share.firefox.dev/3HpTX3v
You can see that the CanvasRenderer thread has fallen way behind. But the main thread keeps adding more canvas work during the wheel event handler.
The current backpressure mechanism only delays refresh ticks. But wheel events are fired outside of refresh ticks. There needs to be another backpressure mechanism (e.g. during the canvas methods themselves) for non-refreshtick work.
(Bonus badness visible in the profile: There's sync IPC during GC in ~DrawTargetWebGL
, visible in two of the background processes.)
Reporter | ||
Comment 1•2 years ago
|
||
Reporter | ||
Comment 2•2 years ago
|
||
![]() |
||
Updated•2 years ago
|
Reporter | ||
Updated•2 years ago
|
Comment 3•2 years ago
|
||
Ah, this is an interesting case. There is nothing else in the main thread of the content process, so even very low priority input events can be processed, as one would expect.
We could suspend InputTaskManager temporarily when we know there will be a tick a bit later. But do we know in this case?
Hmm, I wonder what happens if we suspended input event handling until the next DidComposite or something like that.
But I can't reproduce this on Linux, but on Windows I can see max 2 wheel event per animation frame.
Reporter | ||
Comment 4•1 year ago
|
||
This also affects canvas animations running inside setTimeout callbacks, see bug 1879824 for an example.
Reporter | ||
Updated•1 year ago
|
Reporter | ||
Comment 5•1 year ago
|
||
Bob, do we still have any limit on the number of bytes we have in-flight for canvas drawing? I wonder if we can just pick some large but not infinite size and be in a better place.
Comment 6•1 year ago
|
||
(In reply to Markus Stange [:mstange] from comment #5)
Bob, do we still have any limit on the number of bytes we have in-flight for canvas drawing? I wonder if we can just pick some large but not infinite size and be in a better place.
Yes we added a maximum number of buffers in bug 1872705.
Currently 256 x 32K. (8MB)
Reporter | ||
Comment 7•1 year ago
•
|
||
Ah, nice, that probably explains why the testcase I attached to this bug runs much better today than when I filed this bug.
Then I wonder how the overproduction problem shown in the profiles from bug 1879824 comment 7 can be addressed. Do we have to dynamically adapt the maximum size based on the observed consumption rate?
Comment 8•1 year ago
•
|
||
(In reply to Markus Stange [:mstange] from comment #7)
Ah, nice, that probably explains why the testcase I attached to this bug runs much better today than when I filed this bug.
Then I wonder how the overproduction problem shown in the profiles from bug 1879824 comment 7 can be addressed. Do we have to dynamically adapt the maximum size based on the observed consumption rate?
Possibly, if I drop the max buffers to 64 or even 32 for bug 1879824, the animation appears to run more smoothly.
It might be good if we had a more intelligent way of throttling the JS in these circumstances, instead of stopping the main thread in its tracks.
I can't work out why it is so slow.
The CPU is doing a fair amount of work, but doesn't appear to be maxing out on one thread.
The GPU also doesn't appear that taxed, with a small amount on one of the smaller engines in process explorer.
It mainly seems to be sitting in the function FillRoundedRectangleTessellator::SendGeometry and below, most of that in RoundedRectangleTessellationSink::AddBeziers and below.
Update: to future readers, looks like the reasons are set out in bug 1879824 comment 11 and bugs that it links to.
Description
•