Vulkan Swapchain FIFO stutter due to incorrect image rotation

Hi!

Posting here as i’m investigating a swapchain synchronisation issue and hoping that anyone can try to reproduce this with the sample app/has an idea as to what is going on.

Description:

After swapchain recreation, vkAcquireNextImageKHR sometimes/randomly retrieves the same image index back to back (even in the case of tripple buffering) causing the visual output to look extremely choppy. This can be (sometimes) fixed by destroying and recreating the swapchain again.

I can reproduce this with this repository ( GitHub - SaschaWillems/Vulkan: C++ examples for the Vulkan graphics API ) which is a popular vulkan tutorial / reference implementation.

I’m on Windows 11 with a Nvidia Geforce RTX 5070 TI on the latest driver version 591.59 using a dual monitor setup.


Build instructions for the sample: [ Repository: GitHub - SaschaWillems/Vulkan: C++ examples for the Vulkan graphics API ]

git clone --recursive https://github.com/SaschaWillems/Vulkan.git
cd Vulkan
cmake -G "Visual Studio 17 2022" -A x64

Open vulkanExamples.sln, build everything and set the startup project to “deferred”.

Note that you have to enable vsync by modifying the “Settings” struct in vulkanexamplebase.h:

sruct Settings {
...
    bool vsync = true;  //<<<<<<<<< SET THIS TO TRUE!
...
} settings;

Then you can start the sample.

I have recorded a video on my screen via screencapture to show the stuttering behavior (Moving the camera with the mouse to see how smooth it is):

Stutter starts at around 0:18 seconds. I force a swapchain refresh/recreation by moving the window around. Note that it often looks fine but sometimes after a forced swapchain refresh the visual output is not smooth anymore.

Note that the stuttering can even happen at the first startup (so after the first swapchain initialisation, without previously deleting an existing swapchain) making me suspect that this is a driver issue.

For debugging purposes I added an output command to print the previous and currently acquired swapchain index to the debug output window (which can be also seen in the video above on the right side of the screen.)

//added in prepareFrame() of vulkanExamplesBase.cpp around the acquireNextImage() call:

auto previous = currentImageIndex;

	VkResult result = swapChain.acquireNextImage(presentCompleteSemaphores[currentBuffer], currentImageIndex);

	std::string out = "SC: " + std::to_string(previous) + " > " + std::to_string(currentImageIndex)+"\n";
	OutputDebugString(out.c_str());

Note in the video that once the stutter happens, the same image index will be acquired from the swapchain back-to back multiple times.

So instead of having an image index rotation like 0 > 1, 0 >1, 0 > 1, etc…

we get 1 > 1, 0 > 0, 1 > 1, etc…


As far as i can tell the sample does everything correct according to spec? It uses two semaphores (1 for frame acqusition and 1 for signaling) as well as a fence to wait until the command buffer was submitted on the CPU side before calling vkAcquireNextImageKHR.

Anyone an idea as to what the cause of this could be? Could this be a driver issue?

I discovered the same behavior with a custom engine. I get some blurry,jaggy motion just sporadically and when i do i get also weird swapchain behavior from acquireNextImageKHR.


    vkCheck(mImpl->LogicalDevice.acquireNextImageKHR(mImpl->Swapchain,100000000, frameState->AcquireSemaphore, nullptr, &mImpl->CurrentSwapchainImageIndex), “acquireNextImageKHR”);

LogDebug(“Acquired swapchain index %u\n”, mImpl->CurrentSwapchainImageIndex);

Here an example using 2 in-flight frames

1771946489 [Debug] [Runtime] Acquired swapchain index 0
1771946489 [Debug] [Runtime] Acquired swapchain index 0
1771946489 [Debug] [Runtime] Acquired swapchain index 1
1771946489 [Debug] [Runtime] Acquired swapchain index 1
1771946489 [Debug] [Runtime] Acquired swapchain index 0
1771946489 [Debug] [Runtime] Acquired swapchain index 0
1771946489 [Debug] [Runtime] Acquired swapchain index 1
1771946489 [Debug] [Runtime] Acquired swapchain index 1

and so on.

This is the log when everything is smooth:

1771946590 [Debug] [Runtime] Acquired swapchain index 1
1771946590 [Debug] [Runtime] Acquired swapchain index 0
1771946590 [Debug] [Runtime] Acquired swapchain index 1
1771946590 [Debug] [Runtime] Acquired swapchain index 0
1771946590 [Debug] [Runtime] Acquired swapchain index 1

Swapchain is always acquired from the same (render) thread.

Comes and goes with input events in SDL3 or window movement. It’s not tied to swapchain recreation on my side.

I use the acquired swapchain index to index into swapchain images.
Use an in-flight index to index into the rest of the state (depth images, command buffers) etc.

I’m just not 100% if i’m not doing anything incorrect because all the games i’m playing using Vulkan work fine.

But they also seem to capture focus differently, not allowing windows key to open start menu for example in Doom The Dark Ages even in Windowed mode. When i use SDL3 in Windowed pressing windows key opens startmenu, nothing i can do about that. This also often triggers incorrect swapchain results.

The problem is that it is not deterministic on my part. Sometimes it also happens, as Dev_Lewa statet, from the beginning.

Ok there is definitly something wrong either on Windows or SDL parts when this happens. Or at least something wrong too.

When this occurs, also SDL3s RelativeMouseMode is not working correctly anymore, even when SDL_SetWindowKeyboardGrab and SDL_SetWindowRelativeMouseMode are both enabled and worked fine when the swapchain index rotation is normal.

When the error occurs, also SDL events are not commited every frame, but skip some (like MouseMotionEvent) here.

When everything is fine and swapchain index is rotation:

1771948761 [Debug] [Runtime] Acquired swapchain index 1
1771948761 [Debug] [Runtime] Mouse delta: -4.000000 -3.000000
1771948761 [Debug] [Runtime] Acquired swapchain index 0
1771948761 [Debug] [Runtime] Mouse delta: -4.000000 -2.000000
1771948761 [Debug] [Runtime] Acquired swapchain index 1
1771948761 [Debug] [Runtime] Mouse delta: -4.000000 -3.000000
1771948761 [Debug] [Runtime] Acquired swapchain index 0
1771948761 [Debug] [Runtime] Mouse delta: -10.000000 -3.000000
1771948761 [Debug] [Runtime] Acquired swapchain index 1
1771948761 [Debug] [Runtime] Mouse delta: -9.000000 -4.000000
1771948761 [Debug] [Runtime] Acquired swapchain index 0
1771948761 [Debug] [Runtime] Mouse delta: -16.000000 -6.000000

When broken:

[Runtime] Mouse delta: -62.000000 7.000000
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Mouse delta: -27.000000 1.000000
1771948764 [Debug] [Runtime] Acquired swapchain index 1
1771948764 [Debug] [Runtime] Acquired swapchain index 1
1771948764 [Debug] [Runtime] Mouse delta: -5.000000 0.000000
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Acquired swapchain index 1
1771948764 [Debug] [Runtime] Acquired swapchain index 1
1771948764 [Debug] [Runtime] Mouse delta: 0.000000 -1.000000
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Mouse delta: 1.000000 2.000000
1771948764 [Debug] [Runtime] Acquired swapchain index 1
1771948764 [Debug] [Runtime] Acquired swapchain index 1
1771948764 [Debug] [Runtime] Mouse delta: 8.000000 3.000000
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Acquired swapchain index 0
1771948764 [Debug] [Runtime] Mouse delta: 11.000000 3.000000

Mouse Motion is not emitted correctly anymore. Sometimes 4 swapchain acquisitions are between this and the next sdl event emit. But all the time there are at least 2 acquisitions in between.

I disabled G-Sync in Nvidia APP and now my FIFO works.

If i have G-Sync enabled, i must use Mailbox which acts like FIFO: 4% GPU usage at 99fps. The same without Gsync and FIFO.

GSYNC disabled and Mailbox unlocks FPS.

What weird behavior.

Edit:

Its not the normal gsync setting in System→Gsync and Surround but the one below in System→ Display Properties. This is where i must disable GSync otherwise i get broken swapchain indicies most of the time.

Edit 2:

Using FIFO with GSYNC works if i fps limit the game loop at refresh rate. Using SDL_Delay to sleep the remaining expected frame time. Then its perfectly fine.

I can’t help you with your situation, but I have also noticed it. Running Red Dead Redemption 2 with Vulkan + triple buffering + vsync + nvidia DXGI swapchain enabled, causes horrible frame pacing. This is on Windows 11, RTX 5060 Ti 16GB, driver 591.74

To fix it instantly just set OpenGL/Vulkan present method in Control Panel to native. This always give the correct order. If auto (on windows DXGI) is used, the order breaks.

But as one can only assume the user has the default auto set, the solution is not good.

In my engine i solved it by fixing one index bug (used swapchain image index, should’ve used inflight frame index for render targets) and implemented timeline semaphores and VK_KHR_present_wait.

Especially the latter seems to be reliably independent of the swapchain image index order.

I also moved swapchain image acquire as late as possible into the render frame. As i render to render images dependent on the inflight image index, i can move the swapchain acquire just before blitting the render image onto the swapchain and presenting the swapchain.

This also gave way more stability.

Hi @Dev_Lewa,

Welcome to the NVIDIA developer forums, and thank you for reporting this issue. I have unfortunately not been able to reproduce the problem you describe yet. I cannot rule out a driver issue just yet, so I have a few questions to maybe narrow things down a bit:

  • Are your displays driven by an integrated GPU?
  • What kind of monitors are you using (Refresh rate, VRR, etc.)?
  • Do you have any other settings from the NVIDIA Control Panel / NVIDIA App explicitly set, such as a “Max Framerate”, “Background Application Max Frame Rate”, etc.?
  • Do you have windowed G-Sync enabled in the NVIDIA Control Panel?

I would also like to clarify a couple points that @rma123 mentioned. You are right that when setting “OpenGL/Vulkan present method” to “auto”, the driver may use DXGI to present. When doing this, the driver may copy swapchain images to an internal buffer when the application calls vkQueuePresentKHR. Once that copy is done, the Vulkan image that was presented can be re-used. If this happens fast enough, the application may receive the same swapchain image index from vkAcquireNextImageKHR multiple times in a row. There are some advantages I won’t get into for doing this in our implementation, but this is why Vulkan returns an explicit image index in the first place; there is no implicit cycling order that the implementation has to follow. I understand that in the cases where you are experiencing stutter you noticed this happens a lot, but my point is it’s possible that the implementation is able to re-use images so quickly precisely because the stutter gives it enough time to complete everything before the next vkAcquireNextImageKHR. In other words, the lower FPS could be the cause of this uncommon cycling order, and not its consequence.

As far as mailbox and G-Sync goes, I am not sure I understood what @rma123 was saying. The Vulkan specifications only sparsely expose variable refresh rate capabilities. On Windows, turning off V-Sync (i.e. VK_PRESENT_MODE_IMMEDIATE_KHR) should give you variable refresh rate, just like it does on DXGI. Mailbox is still a V-Sync’ed present mode, except some internal synchronization allows the implementation to return images to the application earlier than the next vblank. In theory it should give you infinite framerate; in practice, that’s not really how Windows works and the behavior is a bit closer to VK_PRESENT_MODE_FIFO_LATEST_READY_KHR. It’s definitely hard to get frame pacing right with Mailbox though, regardless of how well the implementation supports it.

Thanks,
Lionel

To fix it instantly just set OpenGL/Vulkan present method in Control Panel to native. This always give the correct order. If auto (on windows DXGI) is used, the order breaks

Yea, the workaround is switching to native. If DXGI swapchain needs to be kept enabled for whatever reason, another workaround (at least for RDR2) is switching to double-buffering in the game’s settings, which also seems to alleviate it.

A small update: This seems to be a multi faceted issue.

For one, i found an nvidia vulkan sample that uses timeline semaphores instead of fences for the GPU-CPU sync: GitHub - nvpro-samples/vk_minimal_latest: A minimal, self-contained example demonstrating best practices for Vulkan development in a single file—no frameworks required. · GitHub

Implementing that swapchain sync method seemed to improve things on my end. (Timeline semaphores are more flexible and make fences basically redundant as far as i can tell as you can also wait for their signal on the CPU side.)

The other issue is that this only really works with the native vulkan swapchain and not with the DXGI layering of the nvidia driver.

I created a seperate topic mentioning this: ** VSync behaviour with DXGI Swapchain **

From what i was able to tell with regards to DXGI layering is that FIFO runs unlocked (and the DXGI layer uses the last submitted frame on the screen) which causes stuttering due to framepacing misalignment. (As the DXGI layer and the vulkan submissions run independently / don’t wait on each other.)

Currently looking into that as even vulkan extensions like VK_KHR_present_wait which are supposed to allow you to wait for the screen display don’t seem to be nearly as accurate as the native swapchain where vkQueuePresent blocks/waits for you automatically.

Don’t have a solution for that yet.

Bumping this thread to mention that one of the games we recently released on Steam also displays this specific issue. That is the Vulkan build of Darksiders Warmastered Edition (which is now the default build you get when not selecting any beta branch).

When enabling V-Sync in the game, every now and then it ends up in this state where the frame pacing becomes very uneven. Instead of the frame timings going something like “16, 16, 16, 17, 16, 16, […]”, they go more like “4, 28, 4, 28, 4, 28, 4, 28, […]”, leading to any motion appearing super unsmooth. This was tested on a PC with an RTX 4070 TI and another one with an RTX 3060 (the latter one running the newest game-ready driver, 610.62). From what we could observe, the same game running on an AMD GPU did not display this specific issue.

As suggested above, switching the Vulkan present method in the NVIDIA Control Panel to “native” fixes this particular issue, but since this requires the end user to take a specific action, it would still be great to get a fix directly in the driver (or if you think it’s not a driver issue, a word on what the recommended code fix should be).