Procházet zdrojové kódy

GPU: Add swapchain dimension out params (#11003)

Evan Hemsley před 6 měsíci
rodič
revize
afdf325fb4

+ 13 - 9
include/SDL3/SDL_gpu.h

@@ -3316,7 +3316,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseWindowFromGPUDevice(
  * \param window an SDL_Window that has been claimed.
  * \param swapchain_composition the desired composition of the swapchain.
  * \param present_mode the desired present mode for the swapchain.
- * \returns true if successful, false on error.
+ * \returns true if successful, false on error; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3350,20 +3350,22 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
  * When a swapchain texture is acquired on a command buffer, it will
  * automatically be submitted for presentation when the command buffer is
  * submitted. The swapchain texture should only be referenced by the command
- * buffer used to acquire it. The swapchain texture handle can be NULL under
+ * buffer used to acquire it. The swapchain texture handle can be filled in with NULL under
  * certain conditions. This is not necessarily an error. If this function
- * returns false then there is an error. The swapchain texture is managed by
- * the implementation and must not be freed by the user. The texture
- * dimensions will be the width and height of the claimed window. You can
- * obtain these dimensions by calling SDL_GetWindowSizeInPixels. You MUST NOT
+ * returns false then there is an error.
+ *
+ * The swapchain texture is managed by
+ * the implementation and must not be freed by the user. You MUST NOT
  * call this function from any thread other than the one that created the
  * window.
  *
  * \param command_buffer a command buffer.
  * \param window a window that has been claimed.
- * \param swapchainTexture a pointer filled in with a swapchain texture
+ * \param swapchain_texture a pointer filled in with a swapchain texture
  *                         handle.
- * \returns true on success, false on error.
+ * \param swapchain_texture_width a pointer filled in with the swapchain texture width, may be NULL.
+ * \param swapchain_texture_height a pointer filled in with the swapchain texture height, may be NULL.
+ * \returns true on success, false on error; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3375,7 +3377,9 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
 extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture(
     SDL_GPUCommandBuffer *command_buffer,
     SDL_Window *window,
-    SDL_GPUTexture **swapchainTexture);
+    SDL_GPUTexture **swapchain_texture,
+    Uint32 *swapchain_texture_width,
+    Uint32 *swapchain_texture_height);
 
 /**
  * Submits a command buffer so its commands can be processed on the GPU.

+ 1 - 1
src/dynapi/SDL_dynapi_procs.h

@@ -50,7 +50,7 @@ SDL_DYNAPI_PROC(int,SDL_swprintf,(SDL_OUT_Z_CAP(b) wchar_t *a, size_t b, SDL_PRI
 // New API symbols are added at the end
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_GPUCommandBuffer*,SDL_AcquireGPUCommandBuffer,(SDL_GPUDevice *a),(a),return)
-SDL_DYNAPI_PROC(bool,SDL_AcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a, SDL_Window *b, SDL_GPUTexture **c),(a,b,c),return)
+SDL_DYNAPI_PROC(bool,SDL_AcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a, SDL_Window *b, SDL_GPUTexture **c, Uint32 *d, Uint32 *e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(int,SDL_AddAtomicInt,(SDL_AtomicInt *a, int b),(a,b),return)
 SDL_DYNAPI_PROC(bool,SDL_AddEventWatch,(SDL_EventFilter a, void *b),(a,b),return)
 SDL_DYNAPI_PROC(int,SDL_AddGamepadMapping,(const char *a),(a),return)

+ 8 - 4
src/gpu/SDL_gpu.c

@@ -2613,7 +2613,9 @@ SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(
 bool SDL_AcquireGPUSwapchainTexture(
     SDL_GPUCommandBuffer *command_buffer,
     SDL_Window *window,
-    SDL_GPUTexture **swapchainTexture)
+    SDL_GPUTexture **swapchain_texture,
+    Uint32 *swapchain_texture_width,
+    Uint32 *swapchain_texture_height)
 {
     if (command_buffer == NULL) {
         SDL_InvalidParamError("command_buffer");
@@ -2623,8 +2625,8 @@ bool SDL_AcquireGPUSwapchainTexture(
         SDL_InvalidParamError("window");
         return false;
     }
-    if (swapchainTexture == NULL) {
-        SDL_InvalidParamError("swapchainTexture");
+    if (swapchain_texture == NULL) {
+        SDL_InvalidParamError("swapchain_texture");
         return false;
     }
 
@@ -2636,7 +2638,9 @@ bool SDL_AcquireGPUSwapchainTexture(
     return COMMAND_BUFFER_DEVICE->AcquireSwapchainTexture(
         command_buffer,
         window,
-        swapchainTexture);
+        swapchain_texture,
+        swapchain_texture_width,
+        swapchain_texture_height);
 }
 
 bool SDL_SubmitGPUCommandBuffer(

+ 3 - 1
src/gpu/SDL_sysgpu.h

@@ -651,7 +651,9 @@ struct SDL_GPUDevice
     bool (*AcquireSwapchainTexture)(
         SDL_GPUCommandBuffer *commandBuffer,
         SDL_Window *window,
-        SDL_GPUTexture **swapchainTexture);
+        SDL_GPUTexture **swapchainTexture,
+        Uint32 *swapchainTextureWidth,
+        Uint32 *swapchainTextureHeight);
 
     bool (*Submit)(
         SDL_GPUCommandBuffer *commandBuffer);

+ 22 - 1
src/gpu/d3d11/SDL_gpu_d3d11.c

@@ -480,6 +480,8 @@ typedef struct D3D11WindowData
     SDL_GPUSwapchainComposition swapchainComposition;
     DXGI_FORMAT swapchainFormat;
     DXGI_COLOR_SPACE_TYPE swapchainColorSpace;
+    Uint32 width;
+    Uint32 height;
     SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
     Uint32 frameCounter;
     bool needsSwapchainRecreate;
@@ -5250,6 +5252,8 @@ static bool D3D11_INTERNAL_CreateSwapchain(
     windowData->texture.container = &windowData->textureContainer;
     windowData->texture.containerIndex = 0;
 
+    windowData->width = w;
+    windowData->height = h;
     return true;
 }
 
@@ -5288,6 +5292,8 @@ static bool D3D11_INTERNAL_ResizeSwapchain(
 
     windowData->textureContainer.header.info.width = w;
     windowData->textureContainer.header.info.height = h;
+    windowData->width = w;
+    windowData->height = h;
     windowData->needsSwapchainRecreate = !result;
     return result;
 }
@@ -5469,7 +5475,9 @@ static void D3D11_ReleaseWindow(
 static bool D3D11_AcquireSwapchainTexture(
     SDL_GPUCommandBuffer *commandBuffer,
     SDL_Window *window,
-    SDL_GPUTexture **swapchainTexture)
+    SDL_GPUTexture **swapchainTexture,
+    Uint32 *swapchainTextureWidth,
+    Uint32 *swapchainTextureHeight)
 {
     D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
     D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer;
@@ -5477,6 +5485,12 @@ static bool D3D11_AcquireSwapchainTexture(
     HRESULT res;
 
     *swapchainTexture = NULL;
+    if (swapchainTextureWidth) {
+        *swapchainTextureWidth = 0;
+    }
+    if (swapchainTextureHeight) {
+        *swapchainTextureHeight = 0;
+    }
 
     windowData = D3D11_INTERNAL_FetchWindowData(window);
     if (windowData == NULL) {
@@ -5489,6 +5503,13 @@ static bool D3D11_AcquireSwapchainTexture(
         }
     }
 
+    if (swapchainTextureWidth) {
+        *swapchainTextureWidth = windowData->width;
+    }
+    if (swapchainTextureHeight) {
+        *swapchainTextureHeight = windowData->height;
+    }
+
     if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
         if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) {
             // In VSYNC mode, block until the least recent presented frame is done

+ 25 - 1
src/gpu/d3d12/SDL_gpu_d3d12.c

@@ -554,6 +554,8 @@ typedef struct D3D12WindowData
 
     D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT];
     SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
+    Uint32 width;
+    Uint32 height;
     bool needsSwapchainRecreate;
 } D3D12WindowData;
 
@@ -6331,6 +6333,8 @@ static bool D3D12_INTERNAL_ResizeSwapchain(
         }
     }
 
+    windowData->width = w;
+    windowData->height = h;
     windowData->needsSwapchainRecreate = false;
     return true;
 }
@@ -6382,6 +6386,9 @@ static bool D3D12_INTERNAL_CreateSwapchain(
 
     swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
 
+    int w, h;
+    SDL_GetWindowSizeInPixels(windowData->window, &w, &h);
+
     // Initialize the swapchain buffer descriptor
     swapchainDesc.Width = 0;
     swapchainDesc.Height = 0;
@@ -6477,6 +6484,8 @@ static bool D3D12_INTERNAL_CreateSwapchain(
     windowData->swapchainComposition = swapchainComposition;
     windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition];
     windowData->frameCounter = 0;
+    windowData->width = w;
+    windowData->height = h;
 
     // Precache blit pipelines for the swapchain format
     for (Uint32 i = 0; i < 5; i += 1) {
@@ -6907,7 +6916,9 @@ static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer(
 static bool D3D12_AcquireSwapchainTexture(
     SDL_GPUCommandBuffer *commandBuffer,
     SDL_Window *window,
-    SDL_GPUTexture **swapchainTexture)
+    SDL_GPUTexture **swapchainTexture,
+    Uint32 *swapchainTextureWidth,
+    Uint32 *swapchainTextureHeight)
 {
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
     D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
@@ -6916,6 +6927,12 @@ static bool D3D12_AcquireSwapchainTexture(
     HRESULT res;
 
     *swapchainTexture = NULL;
+    if (swapchainTextureWidth) {
+        *swapchainTextureWidth = 0;
+    }
+    if (swapchainTextureHeight) {
+        *swapchainTextureHeight = 0;
+    }
 
     windowData = D3D12_INTERNAL_FetchWindowData(window);
     if (windowData == NULL) {
@@ -6928,6 +6945,13 @@ static bool D3D12_AcquireSwapchainTexture(
         }
     }
 
+    if (swapchainTextureWidth) {
+        *swapchainTextureWidth = windowData->width;
+    }
+    if (swapchainTextureHeight) {
+        *swapchainTextureHeight = windowData->height;
+    }
+
     if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
         if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) {
             // In VSYNC mode, block until the least recent presented frame is done

+ 15 - 1
src/gpu/metal/SDL_gpu_metal.m

@@ -3590,7 +3590,9 @@ static void METAL_ReleaseWindow(
 static bool METAL_AcquireSwapchainTexture(
     SDL_GPUCommandBuffer *commandBuffer,
     SDL_Window *window,
-    SDL_GPUTexture **texture)
+    SDL_GPUTexture **texture,
+    Uint32 *swapchainTextureWidth,
+    Uint32 *swapchainTextureHeight)
 {
     @autoreleasepool {
         MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
@@ -3599,6 +3601,12 @@ static bool METAL_AcquireSwapchainTexture(
         CGSize drawableSize;
 
         *texture = NULL;
+        if (swapchainTextureWidth) {
+            *swapchainTextureWidth = 0;
+        }
+        if (swapchainTextureHeight) {
+            *swapchainTextureHeight = 0;
+        }
 
         windowData = METAL_INTERNAL_FetchWindowData(window);
         if (windowData == NULL) {
@@ -3613,6 +3621,12 @@ static bool METAL_AcquireSwapchainTexture(
         drawableSize = windowData->layer.drawableSize;
         windowData->textureContainer.header.info.width = (Uint32)drawableSize.width;
         windowData->textureContainer.header.info.height = (Uint32)drawableSize.height;
+        if (swapchainTextureWidth) {
+            *swapchainTextureWidth = (Uint32)drawableSize.width;
+        }
+        if (swapchainTextureHeight) {
+            *swapchainTextureHeight = (Uint32)drawableSize.height;
+        }
 
         // Set up presentation
         if (metalCommandBuffer->windowDataCount == metalCommandBuffer->windowDataCapacity) {

+ 244 - 248
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -665,8 +665,13 @@ typedef struct VulkanFramebuffer
     SDL_AtomicInt referenceCount;
 } VulkanFramebuffer;
 
-typedef struct VulkanSwapchainData
+typedef struct WindowData
 {
+    SDL_Window *window;
+    SDL_GPUSwapchainComposition swapchainComposition;
+    SDL_GPUPresentMode presentMode;
+    bool needsSwapchainRecreate;
+
     // Window surface
     VkSurfaceKHR surface;
 
@@ -675,12 +680,13 @@ typedef struct VulkanSwapchainData
     VkFormat format;
     VkColorSpaceKHR colorSpace;
     VkComponentMapping swapchainSwizzle;
-    VkPresentModeKHR presentMode;
     bool usingFallbackFormat;
 
     // Swapchain images
     VulkanTextureContainer *textureContainers; // use containers so that swapchain textures can use the same API as other textures
     Uint32 imageCount;
+    Uint32 width;
+    Uint32 height;
 
     // Synchronization primitives
     VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT];
@@ -688,15 +694,6 @@ typedef struct VulkanSwapchainData
     SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
 
     Uint32 frameCounter;
-} VulkanSwapchainData;
-
-typedef struct WindowData
-{
-    SDL_Window *window;
-    SDL_GPUSwapchainComposition swapchainComposition;
-    SDL_GPUPresentMode presentMode;
-    VulkanSwapchainData *swapchainData;
-    bool needsSwapchainRecreate;
 } WindowData;
 
 typedef struct SwapchainSupportDetails
@@ -1863,7 +1860,7 @@ static Uint8 VULKAN_INTERNAL_BindImageMemory(
 
     SDL_UnlockMutex(usedRegion->allocation->memoryLock);
 
-    CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindBufferMemory, 0)
+    CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindImageMemory, 0)
 
     return 1;
 }
@@ -3110,57 +3107,61 @@ static void VULKAN_INTERNAL_DestroySwapchain(
     WindowData *windowData)
 {
     Uint32 i;
-    VulkanSwapchainData *swapchainData;
 
     if (windowData == NULL) {
         return;
     }
 
-    swapchainData = windowData->swapchainData;
-
-    if (swapchainData == NULL) {
-        return;
-    }
-
-    for (i = 0; i < swapchainData->imageCount; i += 1) {
+    for (i = 0; i < windowData->imageCount; i += 1) {
         VULKAN_INTERNAL_RemoveFramebuffersContainingView(
             renderer,
-            swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]);
+            windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]);
         renderer->vkDestroyImageView(
             renderer->logicalDevice,
-            swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0],
+            windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0],
             NULL);
-        SDL_free(swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews);
-        SDL_free(swapchainData->textureContainers[i].activeTexture->subresources);
-        SDL_free(swapchainData->textureContainers[i].activeTexture);
+        SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews);
+        SDL_free(windowData->textureContainers[i].activeTexture->subresources);
+        SDL_free(windowData->textureContainers[i].activeTexture);
     }
+    windowData->imageCount = 0;
 
-    SDL_free(swapchainData->textureContainers);
-
-    renderer->vkDestroySwapchainKHR(
-        renderer->logicalDevice,
-        swapchainData->swapchain,
-        NULL);
-
-    renderer->vkDestroySurfaceKHR(
-        renderer->instance,
-        swapchainData->surface,
-        NULL);
+    SDL_free(windowData->textureContainers);
+    windowData->textureContainers = NULL;
 
-    for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
-        renderer->vkDestroySemaphore(
+    if (windowData->swapchain) {
+        renderer->vkDestroySwapchainKHR(
             renderer->logicalDevice,
-            swapchainData->imageAvailableSemaphore[i],
+            windowData->swapchain,
             NULL);
+        windowData->swapchain = VK_NULL_HANDLE;
+    }
 
-        renderer->vkDestroySemaphore(
-            renderer->logicalDevice,
-            swapchainData->renderFinishedSemaphore[i],
+    if (windowData->surface) {
+        renderer->vkDestroySurfaceKHR(
+            renderer->instance,
+            windowData->surface,
             NULL);
+        windowData->surface = VK_NULL_HANDLE;
     }
 
-    windowData->swapchainData = NULL;
-    SDL_free(swapchainData);
+    for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
+        if (windowData->imageAvailableSemaphore[i]) {
+            renderer->vkDestroySemaphore(
+                renderer->logicalDevice,
+                windowData->imageAvailableSemaphore[i],
+                NULL);
+            windowData->imageAvailableSemaphore[i] = VK_NULL_HANDLE;
+        }
+
+        if (windowData->renderFinishedSemaphore[i]) {
+            renderer->vkDestroySemaphore(
+                renderer->logicalDevice,
+                windowData->renderFinishedSemaphore[i],
+                NULL);
+            windowData->renderFinishedSemaphore[i] = VK_NULL_HANDLE;
+        }
+    }
 }
 
 static void VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(
@@ -4360,12 +4361,21 @@ static bool VULKAN_INTERNAL_VerifySwapPresentMode(
     return false;
 }
 
-static bool VULKAN_INTERNAL_CreateSwapchain(
+/* It would be nice if VULKAN_INTERNAL_CreateSwapchain could return a bool.
+ * Unfortunately, some Win32 NVIDIA drivers are stupid
+ * and will return surface extents of (0, 0)
+ * in certain edge cases, and the swapchain extents are not allowed to be 0.
+ * In this case, the client probably still wants to claim the window
+ * or recreate the swapchain, so we should return 2 to indicate retry.
+ * -cosmonaut
+ */
+#define VULKAN_INTERNAL_TRY_AGAIN 2
+
+static Uint32 VULKAN_INTERNAL_CreateSwapchain(
     VulkanRenderer *renderer,
     WindowData *windowData)
 {
     VkResult vulkanResult;
-    VulkanSwapchainData *swapchainData;
     VkSwapchainCreateInfoKHR swapchainCreateInfo;
     VkImage *swapchainImages;
     VkSemaphoreCreateInfo semaphoreCreateInfo;
@@ -4377,18 +4387,15 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
 
     SDL_assert(_this && _this->Vulkan_CreateSurface);
 
-    swapchainData = SDL_malloc(sizeof(VulkanSwapchainData));
-    swapchainData->frameCounter = 0;
+    windowData->frameCounter = 0;
 
     // Each swapchain must have its own surface.
-
     if (!_this->Vulkan_CreateSurface(
             _this,
             windowData->window,
             renderer->instance,
             NULL, // FIXME: VAllocationCallbacks
-            &swapchainData->surface)) {
-        SDL_free(swapchainData);
+            &windowData->surface)) {
         SDL_LogError(
             SDL_LOG_CATEGORY_GPU,
             "Vulkan_CreateSurface failed: %s",
@@ -4399,11 +4406,11 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
     if (!VULKAN_INTERNAL_QuerySwapchainSupport(
             renderer,
             renderer->physicalDevice,
-            swapchainData->surface,
+            windowData->surface,
             &swapchainSupportDetails)) {
         renderer->vkDestroySurfaceKHR(
             renderer->instance,
-            swapchainData->surface,
+            windowData->surface,
             NULL);
         if (swapchainSupportDetails.formatsLength > 0) {
             SDL_free(swapchainSupportDetails.formats);
@@ -4411,61 +4418,41 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
         if (swapchainSupportDetails.presentModesLength > 0) {
             SDL_free(swapchainSupportDetails.presentModes);
         }
-        SDL_free(swapchainData);
-        return false;
-    }
-
-    if (swapchainSupportDetails.capabilities.currentExtent.width == 0 ||
-        swapchainSupportDetails.capabilities.currentExtent.height == 0) {
-        // Not an error, just minimize behavior!
-        renderer->vkDestroySurfaceKHR(
-            renderer->instance,
-            swapchainData->surface,
-            NULL);
-        if (swapchainSupportDetails.formatsLength > 0) {
-            SDL_free(swapchainSupportDetails.formats);
-        }
-        if (swapchainSupportDetails.presentModesLength > 0) {
-            SDL_free(swapchainSupportDetails.presentModes);
-        }
-        SDL_free(swapchainData);
         return false;
     }
 
     // Verify that we can use the requested composition and present mode
-
-    swapchainData->format = SwapchainCompositionToFormat[windowData->swapchainComposition];
-    swapchainData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition];
-    swapchainData->swapchainSwizzle = SwapchainCompositionSwizzle[windowData->swapchainComposition];
-    swapchainData->usingFallbackFormat = false;
+    windowData->format = SwapchainCompositionToFormat[windowData->swapchainComposition];
+    windowData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition];
+    windowData->swapchainSwizzle = SwapchainCompositionSwizzle[windowData->swapchainComposition];
+    windowData->usingFallbackFormat = false;
 
     hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
-        swapchainData->format,
-        swapchainData->colorSpace,
+        windowData->format,
+        windowData->colorSpace,
         swapchainSupportDetails.formats,
         swapchainSupportDetails.formatsLength);
 
     if (!hasValidSwapchainComposition) {
         // Let's try again with the fallback format...
-        swapchainData->format = SwapchainCompositionToFallbackFormat[windowData->swapchainComposition];
-        swapchainData->usingFallbackFormat = true;
+        windowData->format = SwapchainCompositionToFallbackFormat[windowData->swapchainComposition];
+        windowData->usingFallbackFormat = true;
         hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
-            swapchainData->format,
-            swapchainData->colorSpace,
+            windowData->format,
+            windowData->colorSpace,
             swapchainSupportDetails.formats,
             swapchainSupportDetails.formatsLength);
     }
 
-    swapchainData->presentMode = SDLToVK_PresentMode[windowData->presentMode];
     hasValidPresentMode = VULKAN_INTERNAL_VerifySwapPresentMode(
-        swapchainData->presentMode,
+        SDLToVK_PresentMode[windowData->presentMode],
         swapchainSupportDetails.presentModes,
         swapchainSupportDetails.presentModesLength);
 
     if (!hasValidSwapchainComposition || !hasValidPresentMode) {
         renderer->vkDestroySurfaceKHR(
             renderer->instance,
-            swapchainData->surface,
+            windowData->surface,
             NULL);
 
         if (swapchainSupportDetails.formatsLength > 0) {
@@ -4476,8 +4463,6 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
             SDL_free(swapchainSupportDetails.presentModes);
         }
 
-        SDL_free(swapchainData);
-
         if (!hasValidSwapchainComposition) {
             SET_STRING_ERROR_AND_RETURN("Device does not support requested swapchain composition!", false);
         }
@@ -4487,6 +4472,22 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
         return false;
     }
 
+    // NVIDIA + Win32 can return 0 extent when the window is minimized. Try again!
+    if (swapchainSupportDetails.capabilities.currentExtent.width == 0 ||
+        swapchainSupportDetails.capabilities.currentExtent.height == 0) {
+        renderer->vkDestroySurfaceKHR(
+            renderer->instance,
+            windowData->surface,
+            NULL);
+        if (swapchainSupportDetails.formatsLength > 0) {
+            SDL_free(swapchainSupportDetails.formats);
+        }
+        if (swapchainSupportDetails.presentModesLength > 0) {
+            SDL_free(swapchainSupportDetails.presentModes);
+        }
+        return VULKAN_INTERNAL_TRY_AGAIN;
+    }
+
     // Sync now to be sure that our swapchain size is correct
     SDL_SyncWindow(windowData->window);
     SDL_GetWindowSizeInPixels(
@@ -4494,34 +4495,36 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
         &drawableWidth,
         &drawableHeight);
 
-    swapchainData->imageCount = MAX_FRAMES_IN_FLIGHT;
+    windowData->imageCount = MAX_FRAMES_IN_FLIGHT;
+    windowData->width = drawableWidth;
+    windowData->height = drawableHeight;
 
     if (swapchainSupportDetails.capabilities.maxImageCount > 0 &&
-        swapchainData->imageCount > swapchainSupportDetails.capabilities.maxImageCount) {
-        swapchainData->imageCount = swapchainSupportDetails.capabilities.maxImageCount;
+        windowData->imageCount > swapchainSupportDetails.capabilities.maxImageCount) {
+        windowData->imageCount = swapchainSupportDetails.capabilities.maxImageCount;
     }
 
-    if (swapchainData->imageCount < swapchainSupportDetails.capabilities.minImageCount) {
-        swapchainData->imageCount = swapchainSupportDetails.capabilities.minImageCount;
+    if (windowData->imageCount < swapchainSupportDetails.capabilities.minImageCount) {
+        windowData->imageCount = swapchainSupportDetails.capabilities.minImageCount;
     }
 
-    if (swapchainData->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
+    if (windowData->presentMode == SDL_GPU_PRESENTMODE_MAILBOX) {
         /* Required for proper triple-buffering.
          * Note that this is below the above maxImageCount check!
          * If the driver advertises MAILBOX but does not support 3 swap
          * images, it's not real mailbox support, so let it fail hard.
          * -flibit
          */
-        swapchainData->imageCount = SDL_max(swapchainData->imageCount, 3);
+        windowData->imageCount = SDL_max(windowData->imageCount, 3);
     }
 
     swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
     swapchainCreateInfo.pNext = NULL;
     swapchainCreateInfo.flags = 0;
-    swapchainCreateInfo.surface = swapchainData->surface;
-    swapchainCreateInfo.minImageCount = swapchainData->imageCount;
-    swapchainCreateInfo.imageFormat = swapchainData->format;
-    swapchainCreateInfo.imageColorSpace = swapchainData->colorSpace;
+    swapchainCreateInfo.surface = windowData->surface;
+    swapchainCreateInfo.minImageCount = windowData->imageCount;
+    swapchainCreateInfo.imageFormat = windowData->format;
+    swapchainCreateInfo.imageColorSpace = windowData->colorSpace;
     swapchainCreateInfo.imageExtent.width = drawableWidth;
     swapchainCreateInfo.imageExtent.height = drawableHeight;
     swapchainCreateInfo.imageArrayLayers = 1;
@@ -4533,7 +4536,7 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
     swapchainCreateInfo.pQueueFamilyIndices = NULL;
     swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform;
     swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-    swapchainCreateInfo.presentMode = swapchainData->presentMode;
+    swapchainCreateInfo.presentMode = SDLToVK_PresentMode[windowData->presentMode];
     swapchainCreateInfo.clipped = VK_TRUE;
     swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
 
@@ -4541,7 +4544,7 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
         renderer->logicalDevice,
         &swapchainCreateInfo,
         NULL,
-        &swapchainData->swapchain);
+        &windowData->swapchain);
 
     if (swapchainSupportDetails.formatsLength > 0) {
         SDL_free(swapchainSupportDetails.formats);
@@ -4553,87 +4556,83 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
     if (vulkanResult != VK_SUCCESS) {
         renderer->vkDestroySurfaceKHR(
             renderer->instance,
-            swapchainData->surface,
+            windowData->surface,
             NULL);
-        SDL_free(swapchainData);
         CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSwapchainKHR, false)
     }
 
-    renderer->vkGetSwapchainImagesKHR(
+    vulkanResult = renderer->vkGetSwapchainImagesKHR(
         renderer->logicalDevice,
-        swapchainData->swapchain,
-        &swapchainData->imageCount,
+        windowData->swapchain,
+        &windowData->imageCount,
         NULL);
     CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false)
 
-    swapchainData->textureContainers = SDL_malloc(
-        sizeof(VulkanTextureContainer) * swapchainData->imageCount);
+    windowData->textureContainers = SDL_malloc(
+        sizeof(VulkanTextureContainer) * windowData->imageCount);
 
-    if (!swapchainData->textureContainers) {
+    if (!windowData->textureContainers) {
         renderer->vkDestroySurfaceKHR(
             renderer->instance,
-            swapchainData->surface,
+            windowData->surface,
             NULL);
-        SDL_free(swapchainData);
         return false;
     }
 
-    swapchainImages = SDL_stack_alloc(VkImage, swapchainData->imageCount);
+    swapchainImages = SDL_stack_alloc(VkImage, windowData->imageCount);
 
     vulkanResult = renderer->vkGetSwapchainImagesKHR(
         renderer->logicalDevice,
-        swapchainData->swapchain,
-        &swapchainData->imageCount,
+        windowData->swapchain,
+        &windowData->imageCount,
         swapchainImages);
     CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false)
 
-    for (i = 0; i < swapchainData->imageCount; i += 1) {
+    for (i = 0; i < windowData->imageCount; i += 1) {
 
         // Initialize dummy container
-        SDL_zero(swapchainData->textureContainers[i]);
-        swapchainData->textureContainers[i].canBeCycled = false;
-        swapchainData->textureContainers[i].header.info.width = drawableWidth;
-        swapchainData->textureContainers[i].header.info.height = drawableHeight;
-        swapchainData->textureContainers[i].header.info.layer_count_or_depth = 1;
-        swapchainData->textureContainers[i].header.info.format = SwapchainCompositionToSDLFormat(
+        SDL_zero(windowData->textureContainers[i]);
+        windowData->textureContainers[i].canBeCycled = false;
+        windowData->textureContainers[i].header.info.width = drawableWidth;
+        windowData->textureContainers[i].header.info.height = drawableHeight;
+        windowData->textureContainers[i].header.info.layer_count_or_depth = 1;
+        windowData->textureContainers[i].header.info.format = SwapchainCompositionToSDLFormat(
             windowData->swapchainComposition,
-            swapchainData->usingFallbackFormat);
-        swapchainData->textureContainers[i].header.info.type = SDL_GPU_TEXTURETYPE_2D;
-        swapchainData->textureContainers[i].header.info.num_levels = 1;
-        swapchainData->textureContainers[i].header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
-        swapchainData->textureContainers[i].header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
+            windowData->usingFallbackFormat);
+        windowData->textureContainers[i].header.info.type = SDL_GPU_TEXTURETYPE_2D;
+        windowData->textureContainers[i].header.info.num_levels = 1;
+        windowData->textureContainers[i].header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
+        windowData->textureContainers[i].header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
 
-        swapchainData->textureContainers[i].activeTexture = SDL_malloc(sizeof(VulkanTexture));
-        swapchainData->textureContainers[i].activeTexture->image = swapchainImages[i];
+        windowData->textureContainers[i].activeTexture = SDL_malloc(sizeof(VulkanTexture));
+        windowData->textureContainers[i].activeTexture->image = swapchainImages[i];
 
         // Swapchain memory is managed by the driver
-        swapchainData->textureContainers[i].activeTexture->usedRegion = NULL;
+        windowData->textureContainers[i].activeTexture->usedRegion = NULL;
 
-        swapchainData->textureContainers[i].activeTexture->swizzle = swapchainData->swapchainSwizzle;
-        swapchainData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
-        swapchainData->textureContainers[i].activeTexture->depth = 1;
-        swapchainData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
-        swapchainData->textureContainers[i].activeTexture->container = &swapchainData->textureContainers[i];
-        SDL_SetAtomicInt(&swapchainData->textureContainers[i].activeTexture->referenceCount, 0);
+        windowData->textureContainers[i].activeTexture->swizzle = windowData->swapchainSwizzle;
+        windowData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
+        windowData->textureContainers[i].activeTexture->depth = 1;
+        windowData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
+        windowData->textureContainers[i].activeTexture->container = &windowData->textureContainers[i];
+        SDL_SetAtomicInt(&windowData->textureContainers[i].activeTexture->referenceCount, 0);
 
         // Create slice
-        swapchainData->textureContainers[i].activeTexture->subresourceCount = 1;
-        swapchainData->textureContainers[i].activeTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
-        swapchainData->textureContainers[i].activeTexture->subresources[0].parent = swapchainData->textureContainers[i].activeTexture;
-        swapchainData->textureContainers[i].activeTexture->subresources[0].layer = 0;
-        swapchainData->textureContainers[i].activeTexture->subresources[0].level = 0;
-        swapchainData->textureContainers[i].activeTexture->subresources[0].transitioned = true;
-        swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
-        // TODO: ERROR CHECK
+        windowData->textureContainers[i].activeTexture->subresourceCount = 1;
+        windowData->textureContainers[i].activeTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
+        windowData->textureContainers[i].activeTexture->subresources[0].parent = windowData->textureContainers[i].activeTexture;
+        windowData->textureContainers[i].activeTexture->subresources[0].layer = 0;
+        windowData->textureContainers[i].activeTexture->subresources[0].level = 0;
+        windowData->textureContainers[i].activeTexture->subresources[0].transitioned = true;
+        windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
         if (!VULKAN_INTERNAL_CreateRenderTargetView(
             renderer,
-            swapchainData->textureContainers[i].activeTexture,
+            windowData->textureContainers[i].activeTexture,
             0,
             0,
-            swapchainData->format,
-            swapchainData->swapchainSwizzle,
-            &swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0])) {
-            SDL_free(swapchainData);
+            windowData->format,
+            windowData->swapchainSwizzle,
+            &windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0])) {
             return false;
         }
     }
@@ -4649,10 +4648,9 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
             renderer->logicalDevice,
             &semaphoreCreateInfo,
             NULL,
-            &swapchainData->imageAvailableSemaphore[i]);
+            &windowData->imageAvailableSemaphore[i]);
 
         if (vulkanResult != VK_SUCCESS) {
-            SDL_free(swapchainData);
             CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false)
         }
 
@@ -4660,19 +4658,16 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
             renderer->logicalDevice,
             &semaphoreCreateInfo,
             NULL,
-            &swapchainData->renderFinishedSemaphore[i]);
+            &windowData->renderFinishedSemaphore[i]);
 
         if (vulkanResult != VK_SUCCESS) {
-            SDL_free(swapchainData);
             CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false)
         }
 
-        swapchainData->inFlightFences[i] = NULL;
+        windowData->inFlightFences[i] = NULL;
     }
 
-    windowData->swapchainData = swapchainData;
     windowData->needsSwapchainRecreate = false;
-
     return true;
 }
 
@@ -9348,11 +9343,11 @@ static bool VULKAN_SupportsSwapchainComposition(
     SwapchainSupportDetails supportDetails;
     bool result = false;
 
-    if (windowData == NULL || windowData->swapchainData == NULL) {
+    if (windowData == NULL) {
         SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false)
     }
 
-    surface = windowData->swapchainData->surface;
+    surface = windowData->surface;
 
     if (VULKAN_INTERNAL_QuerySwapchainSupport(
             renderer,
@@ -9393,11 +9388,11 @@ static bool VULKAN_SupportsPresentMode(
     SwapchainSupportDetails supportDetails;
     bool result = false;
 
-    if (windowData == NULL || windowData->swapchainData == NULL) {
+    if (windowData == NULL) {
         SET_STRING_ERROR_AND_RETURN("Must claim window before querying present mode support!", false)
     }
 
-    surface = windowData->swapchainData->surface;
+    surface = windowData->surface;
 
     if (VULKAN_INTERNAL_QuerySwapchainSupport(
             renderer,
@@ -9425,12 +9420,13 @@ static bool VULKAN_ClaimWindow(
     WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
 
     if (windowData == NULL) {
-        windowData = SDL_malloc(sizeof(WindowData));
+        windowData = SDL_calloc(1, sizeof(WindowData));
         windowData->window = window;
         windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC;
         windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
 
-        if (VULKAN_INTERNAL_CreateSwapchain(renderer, windowData)) {
+        Uint32 createSwapchainResult = VULKAN_INTERNAL_CreateSwapchain(renderer, windowData);
+        if (createSwapchainResult == 1) {
             SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
 
             SDL_LockMutex(renderer->windowLock);
@@ -9447,14 +9443,17 @@ static bool VULKAN_ClaimWindow(
 
             SDL_AddEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
 
-            return 1;
+            return true;
+        } else if (createSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
+            windowData->needsSwapchainRecreate = true;
+            return true;
         } else {
             SDL_free(windowData);
-            SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window!", 0);
+            return false;
         }
     } else {
         SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!");
-        return 0;
+        return false;
     }
 }
 
@@ -9470,22 +9469,21 @@ static void VULKAN_ReleaseWindow(
         return;
     }
 
-    if (windowData->swapchainData != NULL) {
-        VULKAN_Wait(driverData);
+    VULKAN_Wait(driverData);
 
-        for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
-            if (windowData->swapchainData->inFlightFences[i] != NULL) {
-                VULKAN_ReleaseFence(
-                    driverData,
-                    windowData->swapchainData->inFlightFences[i]);
-            }
+    for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
+        if (windowData->inFlightFences[i] != NULL) {
+            VULKAN_ReleaseFence(
+                driverData,
+                windowData->inFlightFences[i]);
         }
-
-        VULKAN_INTERNAL_DestroySwapchain(
-            (VulkanRenderer *)driverData,
-            windowData);
     }
 
+    VULKAN_INTERNAL_DestroySwapchain(
+        (VulkanRenderer *)driverData,
+        windowData);
+
+
     SDL_LockMutex(renderer->windowLock);
     for (i = 0; i < renderer->claimedWindowCount; i += 1) {
         if (renderer->claimedWindows[i]->window == window) {
@@ -9502,23 +9500,21 @@ static void VULKAN_ReleaseWindow(
     SDL_RemoveEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
 }
 
-static bool VULKAN_INTERNAL_RecreateSwapchain(
+static Uint32 VULKAN_INTERNAL_RecreateSwapchain(
     VulkanRenderer *renderer,
     WindowData *windowData)
 {
     Uint32 i;
 
-    if (windowData->swapchainData != NULL) {
-        if (!VULKAN_Wait((SDL_GPURenderer *)renderer)) {
-            return false;
-        }
+    if (!VULKAN_Wait((SDL_GPURenderer *)renderer)) {
+        return false;
+    }
 
-        for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
-            if (windowData->swapchainData->inFlightFences[i] != NULL) {
-                VULKAN_ReleaseFence(
-                    (SDL_GPURenderer *)renderer,
-                    windowData->swapchainData->inFlightFences[i]);
-            }
+    for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
+        if (windowData->inFlightFences[i] != NULL) {
+            VULKAN_ReleaseFence(
+                (SDL_GPURenderer *)renderer,
+                windowData->inFlightFences[i]);
         }
     }
 
@@ -9529,56 +9525,63 @@ static bool VULKAN_INTERNAL_RecreateSwapchain(
 static bool VULKAN_AcquireSwapchainTexture(
     SDL_GPUCommandBuffer *commandBuffer,
     SDL_Window *window,
-    SDL_GPUTexture **swapchainTexture)
+    SDL_GPUTexture **swapchainTexture,
+    Uint32 *swapchainTextureWidth,
+    Uint32 *swapchainTextureHeight)
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
     Uint32 swapchainImageIndex;
     WindowData *windowData;
-    VulkanSwapchainData *swapchainData;
     VkResult acquireResult = VK_SUCCESS;
     VulkanTextureContainer *swapchainTextureContainer = NULL;
     VulkanPresentData *presentData;
 
     *swapchainTexture = NULL;
+    if (swapchainTextureWidth) {
+        *swapchainTextureWidth = 0;
+    }
+    if (swapchainTextureHeight) {
+        *swapchainTextureHeight = 0;
+    }
 
     windowData = VULKAN_INTERNAL_FetchWindowData(window);
     if (windowData == NULL) {
         SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false)
     }
 
-    swapchainData = windowData->swapchainData;
-
-    // Window is claimed but swapchain is invalid!
-    if (swapchainData == NULL) {
-        if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) {
-            // Window is minimized, don't bother
+    // If window data marked as needing swapchain recreate, try to recreate
+    if (windowData->needsSwapchainRecreate) {
+        Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
+        if (!recreateSwapchainResult) {
+            return false;
+        } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
+            // Edge case, texture is filled in with NULL but not an error
             return true;
         }
+    }
 
-        // Let's try to recreate
-        VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
-        swapchainData = windowData->swapchainData;
-
-        if (swapchainData == NULL) {
-            return false;
-        }
+    if (swapchainTextureWidth) {
+        *swapchainTextureWidth = windowData->width;
+    }
+    if (swapchainTextureHeight) {
+        *swapchainTextureHeight = windowData->height;
     }
 
-    if (swapchainData->inFlightFences[swapchainData->frameCounter] != NULL) {
-        if (swapchainData->presentMode == VK_PRESENT_MODE_FIFO_KHR) {
+    if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
+        if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) {
             // In VSYNC mode, block until the least recent presented frame is done
             if (!VULKAN_WaitForFences(
                 (SDL_GPURenderer *)renderer,
                 true,
-                &swapchainData->inFlightFences[swapchainData->frameCounter],
+                &windowData->inFlightFences[windowData->frameCounter],
                 1)) {
                 return false;
             }
         } else {
             if (!VULKAN_QueryFence(
                     (SDL_GPURenderer *)renderer,
-                    swapchainData->inFlightFences[swapchainData->frameCounter])) {
+                    windowData->inFlightFences[windowData->frameCounter])) {
                 /*
                  * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled,
                  * return true to indicate that there is no error but rendering should be skipped
@@ -9589,47 +9592,35 @@ static bool VULKAN_AcquireSwapchainTexture(
 
         VULKAN_ReleaseFence(
             (SDL_GPURenderer *)renderer,
-            swapchainData->inFlightFences[swapchainData->frameCounter]);
+            windowData->inFlightFences[windowData->frameCounter]);
 
-        swapchainData->inFlightFences[swapchainData->frameCounter] = NULL;
-    }
-
-    // If window data marked as needing swapchain recreate, try to recreate
-    if (windowData->needsSwapchainRecreate) {
-        if (!VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData)) {
-            return false;
-        }
-
-        swapchainData = windowData->swapchainData;
-
-        if (swapchainData == NULL) {
-            return false;
-        }
+        windowData->inFlightFences[windowData->frameCounter] = NULL;
     }
 
     // Finally, try to acquire!
     acquireResult = renderer->vkAcquireNextImageKHR(
         renderer->logicalDevice,
-        swapchainData->swapchain,
+        windowData->swapchain,
         SDL_MAX_UINT64,
-        swapchainData->imageAvailableSemaphore[swapchainData->frameCounter],
+        windowData->imageAvailableSemaphore[windowData->frameCounter],
         VK_NULL_HANDLE,
         &swapchainImageIndex);
 
     // Acquisition is invalid, let's try to recreate
     if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) {
-        VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
-        swapchainData = windowData->swapchainData;
-
-        if (swapchainData == NULL) {
+        Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
+        if (!recreateSwapchainResult) {
             return false;
+        } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
+            // Edge case, texture is filled in with NULL but not an error
+            return true;
         }
 
         acquireResult = renderer->vkAcquireNextImageKHR(
             renderer->logicalDevice,
-            swapchainData->swapchain,
+            windowData->swapchain,
             SDL_MAX_UINT64,
-            swapchainData->imageAvailableSemaphore[swapchainData->frameCounter],
+            windowData->imageAvailableSemaphore[windowData->frameCounter],
             VK_NULL_HANDLE,
             &swapchainImageIndex);
 
@@ -9638,7 +9629,7 @@ static bool VULKAN_AcquireSwapchainTexture(
         }
     }
 
-    swapchainTextureContainer = &swapchainData->textureContainers[swapchainImageIndex];
+    swapchainTextureContainer = &windowData->textureContainers[swapchainImageIndex];
 
     // We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes
 
@@ -9695,7 +9686,7 @@ static bool VULKAN_AcquireSwapchainTexture(
     }
 
     vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] =
-        swapchainData->imageAvailableSemaphore[swapchainData->frameCounter];
+        windowData->imageAvailableSemaphore[windowData->frameCounter];
     vulkanCommandBuffer->waitSemaphoreCount += 1;
 
     if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) {
@@ -9706,7 +9697,7 @@ static bool VULKAN_AcquireSwapchainTexture(
     }
 
     vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] =
-        swapchainData->renderFinishedSemaphore[swapchainData->frameCounter];
+        windowData->renderFinishedSemaphore[windowData->frameCounter];
     vulkanCommandBuffer->signalSemaphoreCount += 1;
 
     *swapchainTexture = (SDL_GPUTexture *)swapchainTextureContainer;
@@ -9724,13 +9715,9 @@ static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat(
         SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID)
     }
 
-    if (windowData->swapchainData == NULL) {
-        SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, swapchain is currently invalid!", SDL_GPU_TEXTUREFORMAT_INVALID)
-    }
-
     return SwapchainCompositionToSDLFormat(
         windowData->swapchainComposition,
-        windowData->swapchainData->usingFallbackFormat);
+        windowData->usingFallbackFormat);
 }
 
 static bool VULKAN_SetSwapchainParameters(
@@ -9757,9 +9744,16 @@ static bool VULKAN_SetSwapchainParameters(
     windowData->presentMode = presentMode;
     windowData->swapchainComposition = swapchainComposition;
 
-    return VULKAN_INTERNAL_RecreateSwapchain(
-        (VulkanRenderer *)driverData,
-        windowData);
+    Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
+    if (!recreateSwapchainResult) {
+        return false;
+    } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
+        // Edge case, swapchain extent is (0, 0) but this is not an error
+        windowData->needsSwapchainRecreate = true;
+        return true;
+    }
+
+    return true;
 }
 
 // Submission structure
@@ -10109,7 +10103,7 @@ static bool VULKAN_Submit(
     for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
         swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex;
         swapchainTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
-            &vulkanCommandBuffer->presentDatas[j].windowData->swapchainData->textureContainers[swapchainImageIndex],
+            &vulkanCommandBuffer->presentDatas[j].windowData->textureContainers[swapchainImageIndex],
             0,
             0);
 
@@ -10180,9 +10174,9 @@ static bool VULKAN_Submit(
         presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
         presentInfo.pNext = NULL;
         presentInfo.pWaitSemaphores =
-            &presentData->windowData->swapchainData->renderFinishedSemaphore[presentData->windowData->swapchainData->frameCounter];
+            &presentData->windowData->renderFinishedSemaphore[presentData->windowData->frameCounter];
         presentInfo.waitSemaphoreCount = 1;
-        presentInfo.pSwapchains = &presentData->windowData->swapchainData->swapchain;
+        presentInfo.pSwapchains = &presentData->windowData->swapchain;
         presentInfo.swapchainCount = 1;
         presentInfo.pImageIndices = &presentData->swapchainImageIndex;
         presentInfo.pResults = NULL;
@@ -10191,16 +10185,18 @@ static bool VULKAN_Submit(
             renderer->unifiedQueue,
             &presentInfo);
 
-        presentData->windowData->swapchainData->frameCounter =
-            (presentData->windowData->swapchainData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
+        presentData->windowData->frameCounter =
+            (presentData->windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
 
         if (presentResult != VK_SUCCESS) {
-            result = VULKAN_INTERNAL_RecreateSwapchain(
-                renderer,
-                presentData->windowData);
+            if (presentResult == VK_ERROR_OUT_OF_DATE_KHR) {
+                presentData->windowData->needsSwapchainRecreate = true;
+            } else {
+                CHECK_VULKAN_ERROR_AND_RETURN(presentResult, vkQueuePresentKHR, false)
+            }
         } else {
             // If presenting, the swapchain is using the in-flight fence
-            presentData->windowData->swapchainData->inFlightFences[presentData->windowData->swapchainData->frameCounter] = (SDL_GPUFence*)vulkanCommandBuffer->inFlightFence;
+            presentData->windowData->inFlightFences[presentData->windowData->frameCounter] = (SDL_GPUFence*)vulkanCommandBuffer->inFlightFence;
 
             (void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount);
         }

+ 9 - 10
src/render/gpu/SDL_render_gpu.c

@@ -957,7 +957,8 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
     GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
 
     SDL_GPUTexture *swapchain;
-    bool result = SDL_AcquireGPUSwapchainTexture(data->state.command_buffer, renderer->window, &swapchain);
+    Uint32 swapchain_texture_width, swapchain_texture_height;
+    bool result = SDL_AcquireGPUSwapchainTexture(data->state.command_buffer, renderer->window, &swapchain, &swapchain_texture_width, &swapchain_texture_height);
 
     if (!result) {
         SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to acquire swapchain texture: %s", SDL_GetError());
@@ -967,8 +968,6 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
         goto submit;
     }
 
-    SDL_GPUTextureFormat swapchain_fmt = SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window);
-
     SDL_GPUBlitInfo blit_info;
     SDL_zero(blit_info);
 
@@ -976,18 +975,13 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
     blit_info.source.w = data->backbuffer.width;
     blit_info.source.h = data->backbuffer.height;
     blit_info.destination.texture = swapchain;
-    blit_info.destination.w = renderer->output_pixel_w;
-    blit_info.destination.h = renderer->output_pixel_h;
+    blit_info.destination.w = swapchain_texture_width;
+    blit_info.destination.h = swapchain_texture_height;
     blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE;
     blit_info.filter = SDL_GPU_FILTER_LINEAR;
 
     SDL_BlitGPUTexture(data->state.command_buffer, &blit_info);
 
-    if (renderer->output_pixel_w != data->backbuffer.width || renderer->output_pixel_h != data->backbuffer.height || swapchain_fmt != data->backbuffer.format) {
-        SDL_ReleaseGPUTexture(data->device, data->backbuffer.texture);
-        CreateBackbuffer(data, renderer->output_pixel_w, renderer->output_pixel_h, swapchain_fmt);
-    }
-
 // *** FIXME ***
 // This is going to block if there is ever a frame in flight.
 // We should do something similar to FNA3D
@@ -1006,6 +1000,11 @@ submit:
     SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
 #endif
 
+    if (swapchain != NULL && (swapchain_texture_width != data->backbuffer.width || swapchain_texture_height != data->backbuffer.height)) {
+        SDL_ReleaseGPUTexture(data->device, data->backbuffer.texture);
+        CreateBackbuffer(data, swapchain_texture_width, swapchain_texture_height, SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window));
+    }
+
     data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device);
 
     return true;

+ 1 - 1
test/testgpu_simple_clear.c

@@ -82,7 +82,7 @@ SDL_AppResult SDL_AppIterate(void *appstate)
 	}
 
     SDL_GPUTexture *swapchainTexture;
-	if (!SDL_AcquireGPUSwapchainTexture(cmdbuf, state->windows[0], &swapchainTexture)) {
+	if (!SDL_AcquireGPUSwapchainTexture(cmdbuf, state->windows[0], &swapchainTexture, NULL, NULL)) {
         SDL_Log("SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError());
         return SDL_APP_FAILURE;
     }

+ 2 - 4
test/testgpu_spinning_cube.c

@@ -333,7 +333,7 @@ Render(SDL_Window *window, const int windownum)
     SDL_GPURenderPass *pass;
     SDL_GPUBufferBinding vertex_binding;
     SDL_GPUBlitInfo blit_info;
-    int drawablew, drawableh;
+    Uint32 drawablew, drawableh;
 
     /* Acquire the swapchain texture */
 
@@ -342,7 +342,7 @@ Render(SDL_Window *window, const int windownum)
         SDL_Log("Failed to acquire command buffer :%s", SDL_GetError());
         quit(2);
     }
-    if (!SDL_AcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture)) {
+    if (!SDL_AcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture, &drawablew, &drawableh)) {
         SDL_Log("Failed to acquire swapchain texture: %s", SDL_GetError());
         quit(2);
     }
@@ -353,8 +353,6 @@ Render(SDL_Window *window, const int windownum)
         return;
     }
 
-    SDL_GetWindowSizeInPixels(window, &drawablew, &drawableh);
-
     /*
     * Do some rotation with Euler angles. It is not a fixed axis as
     * quaterions would be, but the effect is cool.