-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Implemented layout and render time graph overlays #10141
Conversation
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
Outdated
Show resolved
Hide resolved
_fpsCounter.RenderFps(targetContext, FormattableString.Invariant($"M:{managedMem} / N:{nativeMem} R:{RenderedVisuals:0000}")); | ||
if (captureTiming) | ||
{ | ||
var elapsed = StopwatchHelper.GetElapsedTime(startingTimestamp); |
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.
Note that at this point the frame rendering isn't actually completed, the commands are still in-flight on the GPU.
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.
Yes, that's more "CPU-side render time" currently. Is there currently any easy way to know when the GPU rendering is completely done? (without waiting for the next buffer swap since there's no point in measuring the wait for a vblank).
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.
With Vulkan we could asynchronously wait for a fence.
With ANGLE / OpenGL we can probably put glFinish
at the end of the frame if diagnostics output is enabled, but that's less than ideal since it would force other windows' rendering to be stalled.
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.
Thank you, I had a look at Vulkan fences. The same in OpenGL seems to be sync objects. There's glFenceSync
available with GL ≥ 3.2 and eglClientWaitSync
in EGL ≥ 1.5. I'll give them a try, that's uncharted territory to me ;)
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.
In general just calling glFinish
should be enough since it will wait for pending drawing to complete.
But has to be enabled by a separate flag since it will affect FPS for the rest of the app.
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.
We can ignore it for now since there are some planned changes for the rendering code.
} | ||
} | ||
|
||
partial void OnLastLayoutPassTimingChanged() |
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.
What happens if there was no layout pass for the frame? e. g. only RenderTransform
got changed or the frame render was triggered by a render-thread animation?
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.
Nothing happens in this case. The LayoutManager
isn't triggered ⇒ LastLayoutPassTiming
doesn't change ⇒ no value is added to the graph ⇒ the layout graph doesn't change.
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.
So the layout graph won't show that there was zero layout time for the frame?
Also, technically more than one composition batch could have been applied before a frame rendering, how is that handled?
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.
The two graphs are completely independent at the moment: the layout graph only advances when there's a layout pass, same thing for the render graph when there's a render pass.
Multiple composition batches before a render should be fine: each time the batch is deserialized with a new LastLayoutPassTiming
, that new value is added to the layout graph in OnLastLayoutPassTimingChanged
. Later the renderer can use all the stored values to draw the layout graph with several new points in it.
In the future I'd like to add a real "total frame" graph with layout+render, including GPU if possible. In this case, there will be a need to correlate N layouts passes with a single rendered frame, with potentially large delays between the two passes.
My main motivation here was the layout graph. Since the rendering doesn't include the GPU time and the two graphs don't advance at the same time, I can remove the render graph if that makes more sense. (I personally still find the render graph useful for scenarios with custom drawing: have a look at RenderDemo's WriteableBitmap with the graph on).
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.
Ah, OK, so graph correlation wasn't a goal yet.
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.
Sorry I should have made this clear from the start.
With the two threads being independent, having two independent graphs made sense to me (especially in render-only animation demos), but I acknowledge it can also be confusing.
You can test this PR using the following package version. |
@MrJul thanks for bringing this functionality! Just a small consideration. For screen DPIs > 100%, the graphics are not scaled (and layout) correctly. For example, this is how it looks in my 125% DPI screen: In 250% the graph is not even visible. Hope it's easy to fix ;-) |
You can test this PR using the following package version. |
Very as I used the wrong variable :( I just pushed a fix! |
Yep, working as expected now! |
You can test this PR using the following package version. |
What does the pull request do?
This PR adds a layout and a render time graph, drawn directly by the renderer for diagnostic purposes. The display of these graphs can be toggled using the dev tools.
With the graphs, it's much easier to see at a glance if there are performance problems in apps.
Screenshot:
Breaking changes
IRenderer
has aDiagnostics
property instead ofDrawFps
,DrawDirtyRects
, etc. Additional diagnostics and overlays can be added to the diagnostic class later without breaking theIRenderer
's implementors each time.