dolphin
dolphin copied to clipboard
Implement VK_KHR_present_wait to instrument actual present timings
Very few GPUs currently support the VK_KHR_present_wait extension.
But it has better support on desktop GPUs than VK_GOOGLE_display_timing, and we can abuse it to get presentation timings, which we can use for pretty graphs of when the user actually sees the a new frame.
Why present timings?
Because the frame timings don't actually represent what the user experiences. For example, here is a 30 FPS game running vsyned on my 100hz monitor. Because 30 doesn't divide into 100, the frame pacing is bad.
For another example, this sometimes happens to a 59.97 fps game when vsynced to my monitor at 60hz. It only happens if dolphin manages to run without a single stutter for several min. I'm guessing my monitor is actually slightly lower than 59.97hz and eventually dolphin runs ahead and is forced to wait for vsync.
Supported GPUs/Drivers
Currently only Nvidia on both Linux and Windows. But the next release of Mesa will add present_wait for AMD and Intel GPUs. But only if you use X11 and set a flag to force it on
Limitations:
Currently doesn't support wayland. And X11 will only show true present timings when running in full screen. Otherwise you get the timing that the compositor accepted the frame. Not the true present timing. This is could probably be considered to be a bug in Xorg.
But only if you use X11 and set a flag to force it on
Slight correction: present_wait will be exposed by default on x11 by the next mesa release if the application doesn't enable any extension that can't support it. Which means in practice that it'll be available if VK_KHR_wayland_surface isn't enabled.
Not the behaviour I experienced. When I built the current mesa trunk from source, the extension wasn't listed until I created the following /etc/drirc
:
<driconf>
<device>
<application name="all">
<option name="vk_khr_present_wait" value="true" />
</application>
</device>
</driconf>
If you used the linux version of vulkaninfo
then this is expected, because it enables VK_KHR_wayland_surface and thus won't list present_wait by default. If you e.g. run the vulkaninfo.exe
from the windows vulkan sdk on a recent version of wine, it will, however, list it by default, because wine only supports x11 at the moment and doesn't enable VK_KHR_wayland_surface.
As a side note, you don't need to edit drirc to force it on, vk_khr_present_wait=true
also works as an environment variable, like all drirc options.
If you used the linux version of
vulkaninfo
then this is expected
Oh... Right... Good point.
Metal has an API much closer to VK_GOOGLE_display_timing
(where you can ask for the actual time of the present, instead of just getting a callback some time after it happened)
Would it make sense to try to make the VideoCommon API support that here (being given timestamps instead of calculating them as "the time you called this function") or should I leave that for when I add a Metal implementation for this?
D3D12 also has something very similar to VK_KHR_present_wait as far as I know. So it would make sense to design something in VideoCommon.
This adds to VideoCommon. Just a question of what API we want there.
Metal and VK_GOOGLE_display_timing
can provide their own timestamp for the best accuracy
VK_KHR_present_wait
can't, and needs to rely on a CPU timestamp
The current API is just a function you call that records the current CPU timestamp as the time of the present.
This adds to VideoCommon
Well. It's dubious if performance metrics should be considered to be part of video common or not. I want to move everything ImGUI out of VideoCommon into a DolphinImGui subproject that sits along side DolphinQt and DolphinNogui. The drawing parts of PerformanceMetrics
would obviously move, and maybe the collection parts too?
But we do want a VideoCommon API eventually (potentially not part of this PR).
The wait on present behaviour is somewhat useful for the async present PR I'm about to work on. As part of my almost finished refactor that Kills Renderer, I'm thinking about adding a "VideoEvents" class that contains various frame life-cycle events to replace the current messy code in RenderBase::Swap
.
Most importantly, a clean FrameEnd
event that various things can hook into. But it would also be useful to have BeforePresent and AfterPresent events. That AfterPresent event could be defined to happen after present wait (if the backend/driver have support for it, otherwise you might just get instant or post-vsync timings).
It would also be nice to have a VideoCommon API that exposes historical present timings. When the driver/backend doesn't support VK_GOOGLE_display_timing or VK_EXT_present_timing, it can fall back to CPU time of present wait (or worse).
This adds to VideoCommon. Just a question of what API we want there.
@TellowKrinkle My intended API ended up in the massive KillRenderer PR here
Which probably means this should wait for #11522 (and also #11539) before merging.
I'd recommend using #11532 to record timings with something like m_present_counter.Count(present_times, true);
, which should give you a really good graph.
If you begin recording something like frame latency, it would be like m_frame_latency.Count(latency, false);
There is more information about what the boolean does in the PR, but it should give you a nice way to get some fast graphs. I spent a lot of time optimizing the PerformanceTracker
class so there shouldn't be too much overhead to adding more counters.
I'm going to mark this as draft until I finish my current work refactoring the whole vulkan backend.