So I was following this tutorial to creating a SDL window with Vulkan. However when enabling window resize: SDL_SetWindowResizable(mWindow, SDL_TRUE); however it crashes before getting back to the event handling loop to address adjusting the swap chain. What am I missing?
// Provided by VK_KHR_swapchain
VK_ERROR_OUT_OF_DATE_KHR = -1000001004,
`VK_ERROR_OUT_OF_DATE_KHR`: The swap chain has become incompatible with the surface and can no longer be used for rendering. Usually happens after a window resize.
I don’t know enough about SDL’s vulkan backend to know if this is a genuine bug, but it could be that it’s not handling this soft-error condition correctly.
I haven’t looked it up, but it’s possible that the callbacks from SDL_AddEventWatch() are only called once SDL_PumpEvents() runs (because that’s when SDL_Events are added to the internal queue).
No idea if your problem is solvable (or if resizable SDL windows with Vulkan just don’t work); but someone should know? You can’t be the first running into this problem…
Maybe @icculus (this problem also seems relevant to the new 3D API that wraps Vulkan)?
VK_ERROR_OUT_OF_DATE_KHR is a perfectly normal thing to happen, and is expected behavior when the window resizes. When it does, you should recreate your VkSwapchainKHR. It doesn’t matter what SDL events are sent, it only matters when you get this error, and that you handle it properly.
If you are running a normal game loop style thing that reads SDL events first, then draws, then you will definitely know the current size of the resized window and be able to act upon it appropriately when you get an VK_ERROR_OUT_OF_DATE_KHR.
I do not know where this is crashing (or even which code in the tutorial you are using), so I can’t say why it crashes, but I would definitely get the callstack before going any further, because that’ll tell you more than we can from where we are sitting.
Yeah, you need to recreate the swapchain if the window is resized.
Set a flag when a resize event is detected, then before any rendering check the flag and recreate the swapchain. As @icculus pointed out, you should also set your “recreate swapchain” flag whenever VK_ERROR_OUT_OF_DATE_KHR occurs. Which in this case coincides with the window being resized, but may happen at other times.
Is there a reason you’re using SDL_SetWindowResizable() instead of just passing SDL_WINDOW_RESIZABLE as one of the flags when creating the window? It may very well be causing the swapchain to be invalidated when you do this, even if you don’t actually resize the window.
The X11 target calls XResizeWindow() during your SDL_SetWindowResizable() call, for various reasons, which at least makes it possible it’ll invalidate your swapchain.
It crashes at resize. If I don’t reize, it continues with the render loop just fine. The issue is I was try to rebuild the swap chain at the point of the resize event, but it crashes before either event handler is reached.
@icculus From what I understand, you’re saying I shouldn’t rely on the event handler, but check for size difference at the point of pushing the next frame to the swap chain? As for the code, here’s the github source of that tutorial: vk_engine.cpp . The swap chain code is at the bottom of ::draw().
@icculus Sorry if I’m beating a dead horse here, but something doesn’t add up with what I’m seeing. VK_CHECK() never printed out any messages before crashing. I just rebuilt with it deliberately set to hang when that case is reached, but it just goes on without issue.
everything is just fine
451 - /home/adminz/projects/vulcanPrac/vulkanTest/src/vkEngine.cpp
453 - /home/adminz/projects/vulcanPrac/vulkanTest/src/vkEngine.cpp
455 - /home/adminz/projects/vulcanPrac/vulkanTest/src/vkEngine.cpp
everything is just fine
451 - /home/adminz/projects/vulcanPrac/vulkanTest/src/vkEngine.cpp
453 - /home/adminz/projects/vulcanPrac/vulkanTest/src/vkEngine.cpp
Detected Vulkan error: -1000001004
454
Aborted (core dumped)
Edit:
Slight disclaimer, technically there is one VK_CHECK() that outputs text but that’s 454: VK_CHECK(vkQueuePresentKHR(mGraphicsQueue, &presentInfo)) and by then we’re already off the rails.
Same behavior either way, changed it to just the flag. In either case I am not getting VK_ERROR_OUT_OF_DATE_KHR; vkAcquireNextImageKHR() is still returning VK_SUCCESS when the window resizes.
You shouldn’t be calling SDL_SetWindowResizable() during draw. If you are, that’s why vkAcquireNextImageKHR() isn’t returning an error: everything’s fine at that point, and then you call SDL_SetWindowResizable() which invalidates the backing surface, and the swapchain is invalid by the time you try to present the frame, at which point it blows up.
If you call SDL_SetWindowResizable() you also need to set your flag that the swapchain should be rebuilt.
Right now this is what I have SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN|SDL_WINDOW_RESIZABLE); and I’m still stuck with the above behavior.
EDIT:
Also made sure those function calls are removed.
EDIT #2:
Just in case this is relevant; sdl version according to synaptic package manager 2.0.8
Honestly, I’m not sure then. It’s been a while since I’ve used Vulkan.
Reading back up on it, there are two things I’d forgotten about:
Not all Vulkan drivers on all platforms will let you know when the swapchain needs to be recreated by returning VK_ERROR_OUT_OF_DATE_KHR, so you need to make sure to keep track of when the window is resized
It’s perfectly valid for vkQueuePresentKHR() (not just vkAcquireNextImageKHR()) to return VK_ERROR_OUT_OF_DATE_KHR, and it also just means you need to recreate the swapchain
Do something like (C++ pseudocode):
// *** in event handling ***
case SDL_WINDOW_RESIZED:
windowSize = getWindowSize();
needSwapchainRebuilt = true;
break;
// *** in draw function, before trying to acquire the next swapchain image ***
vkWaitForFences(blah blah blah)
// DO NOT DO vkResetFences() HERE!
if(needSwapchainRebuilt) {
needSwapchainRebuilt = false;
rebuildSwapchain();
}
VkResult result = vkAcquireNextImageKHR(etc etc etc);
if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
rebuildSwapchain();
// skip this frame
return;
} else if(result != VK_SUCCESS) {
// handle error
}
// *Now* we can call this without possibly introducing a deadlock
vkResetFences(whatever);
// draw some stuff
// *** at the bottom of draw ***
VkResult presentResult = vkQueuePresentKHR(etc etc etc);
if(presentResult == VK_ERROR_OUT_OF_DATE_KHR || presentResult == VK_SUBOPTIMAL_KHR) {
rebuildSwapchain();
// the frame gets thrown out instead of drawn, but that's OK
} else if(presentResult != VK_SUCCESS) {
// handle error
}
Be aware that vkAcquireNextImageKHR() and vkQueuePresentKHR() can also return VK_SUBOPTIMAL_KHR, which isn’t an error but rather a warning that the pixel format or whatever of the swapchain has become sub-optimal and you should rebuilt it.
Finally, your program isn’t really crashing. The VK_CHECK() macro is just calling abort() if it sees any result other than VK_SUCCESS
edit: updated code sample to avoid deadlock if the swapchain needs to be recreated
I kind of wonder if it’s vendor/version related, going to see what happens with my laptop…
UPDATE:
This is becoming comical; so I still don’t get VK_ERROR_OUT_OF_DATE_KHR, but now it just keeps on chugging along with no crash. Instead the rendered image keeps its original geometry while showing blank when expanding and clipping in when shrinking. So where my desktop has a GeForce GTX 1070 Ti, the laptop is using integrated graphics (FYI it’s the Framework laptop)