gfxreconstruct
gfxreconstruct copied to clipboard
[replay] Trimmed trace replay is stuck at the start due to WaitForPresentKHR wait for non-existent presentId
Describe the replay bug:
When I replay trimmed capture of a game it gets stuck on the first WaitForPresentKHR because there were no vkQueuePresentKHR before it, so it wait for non-existent presentId indefinitely.
E.g. first WaitForPresentKHR
{
"index": 26809,
"function": {
"name": "vkWaitForPresentKHR",
"thread": 11,
"return": "VK_SUCCESS",
"args": {
"device": 7,
"swapchain": 493,
"presentId": 571,
"timeout": 18446744073709551615
}
}
},
And after it the first vkQueuePresentKHR
{
"index": 29181,
"function": {
"name": "vkQueuePresentKHR",
"thread": 8,
"sub_index": 5,
"return": "VK_SUCCESS",
"args": {
"queue": 8,
"pPresentInfo": {
"sType": "VK_STRUCTURE_TYPE_PRESENT_INFO_KHR",
"waitSemaphoreCount": 1,
"pWaitSemaphores": [
505
],
"swapchainCount": 1,
"pSwapchains": [
493
],
"pImageIndices": [
1
],
"pResults": null,
"pNext": {
"sType": "VK_STRUCTURE_TYPE_PRESENT_ID_KHR",
"swapchainCount": 1,
"pPresentIds": 572,
"pNext": null
}
}
}
}
},
Verify before submission:
- Was trimming enabled? Yes
- Was replayer renamed if necessary? No
- Was --sync used if title is known to need forced synchronization? -
Build Environment:
To Reproduce
I could attach a trace but it is captured on Adreno 750.
Screenshots:
System environment:
- Adreno 750
Title configuration:
- Title name:
Donut County - Title version
- Steam ID if applicable
702670 - Is DXR enabled? No
Additional information (optional):
In an application, the above sequence would not hang because the vkQueuePresent is executed from a different thread than the vkWaitForPresent. gfxreconstruct currently doesn't handle multithreaded capture/replay correctly.
We probably don't write the previous Present because trimming is enabled and that Present wasn't inside the current trimmed frame range, and therefore WaitForPresent will never return. The solution may be tracking the previous PresentIds, making sure capture waits for them at WriteState, then marking them in the capture file as completed so that replay of the WaitForPresent can be skipped.
@werman are you familiar with the codebase? Is WaitForPresent happening here because the game/engine does something with the swapchain image, or does it require very specific timing?
In an application, the above sequence would not hang because the vkQueuePresent is executed from a different thread than the vkWaitForPresent. gfxreconstruct currently doesn't handle multithreaded capture/replay correctly.
It's an excellent point that we don't replay multithreaded, but I don't think is what's specifically causing this issue.
In the quoted convert output, the very first WaitPresent is waiting on PresentID 571, but the next Present specifies 572, so that first WaitPresent wouldn't be waiting on that Present.
I recorded game from the start and filtered only present and wait vk calls: present_wait.zip
{"index":3181,"function":{"name":"vkQueuePresentKHR","thread":8,"sub_index":8,"return":"VK_SUCCESS","args":{"queue":8,"pPresentInfo":{"sType":"VK_STRUCTURE_TYPE_PRESENT_INFO_KHR","waitSemaphoreCount":1,"pWaitSemaphores":[424],"swapchainCount":1,"pSwapchains":[414],"pImageIndices":[0],"pResults":null,"pNext":{"sType":"VK_STRUCTURE_TYPE_PRESENT_ID_KHR","swapchainCount":1,"pPresentIds":17,"pNext":null}}}}}
{"index":3184,"function":{"name":"vkWaitForPresentKHR","thread":11,"return":"VK_SUCCESS","args":{"device":7,"swapchain":414,"presentId":17,"timeout":18446744073709551615}}}
{"index":3244,"function":{"name":"vkQueuePresentKHR","thread":8,"sub_index":11,"return":"VK_SUBOPTIMAL_KHR","args":{"queue":8,"pPresentInfo":{"sType":"VK_STRUCTURE_TYPE_PRESENT_INFO_KHR","waitSemaphoreCount":1,"pWaitSemaphores":[426],"swapchainCount":1,"pSwapchains":[414],"pImageIndices":[1],"pResults":null,"pNext":{"sType":"VK_STRUCTURE_TYPE_PRESENT_ID_KHR","swapchainCount":1,"pPresentIds":18,"pNext":null}}}}}
{"index":3252,"function":{"name":"vkWaitForPresentKHR","thread":11,"return":"VK_SUCCESS","args":{"device":7,"swapchain":414,"presentId":18,"timeout":18446744073709551615}}}
{"index":3411,"function":{"name":"vkQueuePresentKHR","thread":8,"sub_index":14,"return":"VK_SUCCESS","args":{"queue":8,"pPresentInfo":{"sType":"VK_STRUCTURE_TYPE_PRESENT_INFO_KHR","waitSemaphoreCount":1,"pWaitSemaphores":[503],"swapchainCount":1,"pSwapchains":[493],"pImageIndices":[0],"pResults":null,"pNext":{"sType":"VK_STRUCTURE_TYPE_PRESENT_ID_KHR","swapchainCount":1,"pPresentIds":19,"pNext":null}}}}}
Presents and waits are from different threads. Though in non-trimmed capture there are not issues with replay.
I should have mentioned that it's a D3D11 game running though DXVK. The issue would happen for all games running through DXVK, since DXVK does vkWaitForPresentKHR on a separate thread: https://github.com/doitsujin/dxvk/blob/eb806952d8d0da94e3c0d449b643f80ee2005030/src/dxvk/dxvk_presenter.cpp#L683
However, reading the DXVK code, the wait happens strictly after the present.
I got the same issue with Resident Evil 2, Steam ID 883710, when I trimmed a capture I had made, where it would get stuck on vkWaitForPresentKHR().
This was also on an Adreno A750 with Turnip.
When I overwrote changed the timeout for some vkWaitForPresentKHR() in the driver, the trimmed capture worked fine.
We keep running into this issue, would be great if it could be properly fixed.
Replaying WaitForPresentKHR isn't super meaningful though due to the out of order nature of it.
@werman could you please help me to test this PR https://github.com/LunarG/gfxreconstruct/pull/2088? I don't have a device that support VK_KHR_present_id and VK_KHR_present_wait. Thank you.
Edit: Test sample: https://github.com/locke-lunarg/Vulkan-Tools/tree/present_id It requires Linux Mesa driver. My device is Ubuntu 24.04.2, Mesa 25.0.2, AMD RX 7900XT.