SDL: GPU: recreate swapchain on window pixel size change event (#10985)

From fc242abbd2bddface0fd9ffba47957d978c21bf4 Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Sat, 28 Sep 2024 18:09:56 -0700
Subject: [PATCH] GPU: recreate swapchain on window pixel size change event
 (#10985)

---
 include/SDL3/SDL_gpu.h          |   2 +-
 src/gpu/d3d11/SDL_gpu_d3d11.c   |  69 ++++++++------
 src/gpu/d3d12/SDL_gpu_d3d12.c   | 161 ++++++++++++++++----------------
 src/gpu/vulkan/SDL_gpu_vulkan.c |  43 +++------
 4 files changed, 136 insertions(+), 139 deletions(-)

diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index a70a399eabee6..5983ce66713b9 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -3263,7 +3263,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WindowSupportsGPUPresentMode(
  * Claims a window, creating a swapchain structure for it.
  *
  * This must be called before SDL_AcquireGPUSwapchainTexture is called using
- * the window.
+ * the window. You should only call this function from the thread that created the window.
  *
  * The swapchain will be created with SDL_GPU_SWAPCHAINCOMPOSITION_SDR and
  * SDL_GPU_PRESENTMODE_VSYNC. If you want to have different swapchain
diff --git a/src/gpu/d3d11/SDL_gpu_d3d11.c b/src/gpu/d3d11/SDL_gpu_d3d11.c
index 90402ede29203..a7472fc136c80 100644
--- a/src/gpu/d3d11/SDL_gpu_d3d11.c
+++ b/src/gpu/d3d11/SDL_gpu_d3d11.c
@@ -482,6 +482,7 @@ typedef struct D3D11WindowData
     DXGI_COLOR_SPACE_TYPE swapchainColorSpace;
     SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
     Uint32 frameCounter;
+    bool needsSwapchainRecreate;
 } D3D11WindowData;
 
 typedef struct D3D11Shader
@@ -5021,6 +5022,18 @@ static D3D11WindowData *D3D11_INTERNAL_FetchWindowData(
     return (D3D11WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL);
 }
 
+static bool D3D11_INTERNAL_OnWindowResize(void *userdata, SDL_Event *e)
+{
+    SDL_Window *w = (SDL_Window *)userdata;
+    D3D11WindowData *data;
+    if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED && e->window.windowID == SDL_GetWindowID(w)) {
+        data = D3D11_INTERNAL_FetchWindowData(w);
+        data->needsSwapchainRecreate = true;
+    }
+
+    return true;
+}
+
 static bool D3D11_INTERNAL_InitializeSwapchainTexture(
     D3D11Renderer *renderer,
     IDXGISwapChain *swapchain,
@@ -5089,7 +5102,6 @@ static bool D3D11_INTERNAL_CreateSwapchain(
     SDL_GPUPresentMode presentMode)
 {
     HWND dxgiHandle;
-    int width, height;
     Uint32 i;
     DXGI_SWAP_CHAIN_DESC swapchainDesc;
     DXGI_FORMAT swapchainFormat;
@@ -5105,9 +5117,6 @@ static bool D3D11_INTERNAL_CreateSwapchain(
     dxgiHandle = (HWND)windowData->window;
 #endif
 
-    // Get the window size
-    SDL_GetWindowSize(windowData->window, &width, &height);
-
     swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
 
     // Initialize the swapchain buffer descriptor
@@ -5216,6 +5225,10 @@ static bool D3D11_INTERNAL_CreateSwapchain(
         return false;
     }
 
+    int w, h;
+    SDL_SyncWindow(windowData->window);
+    SDL_GetWindowSizeInPixels(windowData->window, &w, &h);
+
     // Initialize dummy container, width/height will be filled out in AcquireSwapchainTexture
     SDL_zerop(&windowData->textureContainer);
     windowData->textureContainer.textures = SDL_calloc(1, sizeof(D3D11Texture *));
@@ -5231,6 +5244,8 @@ static bool D3D11_INTERNAL_CreateSwapchain(
     windowData->textureContainer.header.info.num_levels = 1;
     windowData->textureContainer.header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
     windowData->textureContainer.header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
+    windowData->textureContainer.header.info.width = w;
+    windowData->textureContainer.header.info.height = h;
 
     windowData->texture.container = &windowData->textureContainer;
     windowData->texture.containerIndex = 0;
@@ -5240,32 +5255,41 @@ static bool D3D11_INTERNAL_CreateSwapchain(
 
 static bool D3D11_INTERNAL_ResizeSwapchain(
     D3D11Renderer *renderer,
-    D3D11WindowData *windowData,
-    Sint32 width,
-    Sint32 height)
+    D3D11WindowData *windowData)
 {
+    D3D11_Wait((SDL_GPURenderer *)renderer);
+
     // Release the old RTV
     ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetViews[0]);
     SDL_free(windowData->texture.subresources[0].colorTargetViews);
     SDL_free(windowData->texture.subresources);
 
+    int w, h;
+    SDL_SyncWindow(windowData->window);
+    SDL_GetWindowSizeInPixels(windowData->window, &w, &h);
+
     // Resize the swapchain
     HRESULT res = IDXGISwapChain_ResizeBuffers(
         windowData->swapchain,
         0, // Keep buffer count the same
-        width,
-        height,
+        w,
+        h,
         DXGI_FORMAT_UNKNOWN, // Keep the old format
         renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
     CHECK_D3D11_ERROR_AND_RETURN("Could not resize swapchain buffers", false);
 
     // Create the texture object for the swapchain
-    return D3D11_INTERNAL_InitializeSwapchainTexture(
+    bool result = D3D11_INTERNAL_InitializeSwapchainTexture(
         renderer,
         windowData->swapchain,
         windowData->swapchainFormat,
         (windowData->swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat,
         &windowData->texture);
+
+    windowData->textureContainer.header.info.width = w;
+    windowData->textureContainer.header.info.height = h;
+    windowData->needsSwapchainRecreate = !result;
+    return result;
 }
 
 static bool D3D11_SupportsSwapchainComposition(
@@ -5350,14 +5374,13 @@ static bool D3D11_ClaimWindow(
     D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window);
 
     if (windowData == NULL) {
-        windowData = (D3D11WindowData *)SDL_malloc(sizeof(D3D11WindowData));
+        windowData = (D3D11WindowData *)SDL_calloc(1, sizeof(D3D11WindowData));
         windowData->window = window;
 
         if (D3D11_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) {
             SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
 
             SDL_LockMutex(renderer->windowLock);
-
             if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
                 renderer->claimedWindowCapacity *= 2;
                 renderer->claimedWindows = SDL_realloc(
@@ -5366,9 +5389,10 @@ static bool D3D11_ClaimWindow(
             }
             renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
             renderer->claimedWindowCount += 1;
-
             SDL_UnlockMutex(renderer->windowLock);
 
+            SDL_AddEventWatch(D3D11_INTERNAL_OnWindowResize, window);
+
             return true;
         } else {
             SDL_free(windowData);
@@ -5439,6 +5463,7 @@ static void D3D11_ReleaseWindow(
     SDL_free(windowData);
 
     SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA);
+    SDL_RemoveEventWatch(D3D11_INTERNAL_OnWindowResize, window);
 }
 
 static bool D3D11_AcquireSwapchainTexture(
@@ -5449,8 +5474,6 @@ static bool D3D11_AcquireSwapchainTexture(
     D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
     D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer;
     D3D11WindowData *windowData;
-    DXGI_SWAP_CHAIN_DESC swapchainDesc;
-    int windowW, windowH;
     HRESULT res;
 
     *swapchainTexture = NULL;
@@ -5460,16 +5483,8 @@ static bool D3D11_AcquireSwapchainTexture(
         SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false)
     }
 
-    // Check for window size changes and resize the swapchain if needed.
-    IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc);
-    SDL_GetWindowSize(window, &windowW, &windowH);
-
-    if ((UINT)windowW != swapchainDesc.BufferDesc.Width || (UINT)windowH != swapchainDesc.BufferDesc.Height) {
-        if (!D3D11_INTERNAL_ResizeSwapchain(
-            renderer,
-            windowData,
-            windowW,
-            windowH)) {
+    if (windowData->needsSwapchainRecreate) {
+        if (!D3D11_INTERNAL_ResizeSwapchain(renderer, windowData)) {
             return false;
         }
     }
@@ -5511,10 +5526,6 @@ static bool D3D11_AcquireSwapchainTexture(
         (void **)&windowData->texture.handle);
     CHECK_D3D11_ERROR_AND_RETURN("Could not acquire swapchain!", false);
 
-    // Update the texture container dimensions
-    windowData->textureContainer.header.info.width = windowW;
-    windowData->textureContainer.header.info.height = windowH;
-
     // Set up presentation
     if (d3d11CommandBuffer->windowDataCount == d3d11CommandBuffer->windowDataCapacity) {
         d3d11CommandBuffer->windowDataCapacity += 1;
diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c
index 37cedb86a8e65..20a6367ee48f4 100644
--- a/src/gpu/d3d12/SDL_gpu_d3d12.c
+++ b/src/gpu/d3d12/SDL_gpu_d3d12.c
@@ -544,7 +544,6 @@ typedef struct D3D12WindowData
     SDL_Window *window;
 #if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
     D3D12XBOX_FRAME_PIPELINE_TOKEN frameToken;
-    Uint32 swapchainWidth, swapchainHeight;
 #else
     IDXGISwapChain3 *swapchain;
 #endif
@@ -555,6 +554,7 @@ typedef struct D3D12WindowData
 
     D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT];
     SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
+    bool needsSwapchainRecreate;
 } D3D12WindowData;
 
 typedef struct D3D12PresentData
@@ -5973,6 +5973,18 @@ static D3D12WindowData *D3D12_INTERNAL_FetchWindowData(
     return (D3D12WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL);
 }
 
+static bool D3D12_INTERNAL_OnWindowResize(void *userdata, SDL_Event *e)
+{
+    SDL_Window *w = (SDL_Window *)userdata;
+    D3D12WindowData *data;
+    if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED && e->window.windowID == SDL_GetWindowID(w)) {
+        data = D3D12_INTERNAL_FetchWindowData(w);
+        data->needsSwapchainRecreate = true;
+    }
+
+    return true;
+}
+
 static bool D3D12_SupportsSwapchainComposition(
     SDL_GPURenderer *driverData,
     SDL_Window *window,
@@ -6063,7 +6075,8 @@ static bool D3D12_INTERNAL_CreateSwapchain(
     D3D12Texture *texture;
 
     // Get the swapchain size
-    SDL_GetWindowSize(windowData->window, &width, &height);
+    SDL_SyncWindow(windowData->window);
+    SDL_GetWindowSizeInPixels(windowData->window, &width, &height);
 
     // Create the swapchain textures
     SDL_zero(createInfo);
@@ -6091,8 +6104,6 @@ static bool D3D12_INTERNAL_CreateSwapchain(
     windowData->swapchainComposition = swapchain_composition;
     windowData->swapchainColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
     windowData->frameCounter = 0;
-    windowData->swapchainWidth = width;
-    windowData->swapchainHeight = height;
 
     // Precache blit pipelines for the swapchain format
     for (Uint32 i = 0; i < 5; i += 1) {
@@ -6126,35 +6137,31 @@ static void D3D12_INTERNAL_DestroySwapchain(
     }
 }
 
-static bool D3D12_INTERNAL_ResizeSwapchainIfNeeded(
+static bool D3D12_INTERNAL_ResizeSwapchain(
     D3D12Renderer *renderer,
     D3D12WindowData *windowData)
 {
-    int w, h;
-    SDL_GetWindowSize(windowData->window, &w, &h);
-
-    if (w != windowData->swapchainWidth || h != windowData->swapchainHeight) {
-        // Wait so we don't release in-flight views
-        D3D12_Wait((SDL_GPURenderer *)renderer);
-
-        // Present a black screen
-        renderer->commandQueue->PresentX(0, NULL, NULL);
+    // Wait so we don't release in-flight views
+    D3D12_Wait((SDL_GPURenderer *)renderer);
 
-        // Clean up the previous swapchain textures
-        for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
-            D3D12_INTERNAL_DestroyTexture(
-                renderer,
-                windowData->textureContainers[i].activeTexture);
-        }
+    // Present a black screen
+    renderer->commandQueue->PresentX(0, NULL, NULL);
 
-        // Create a new swapchain
-        D3D12_INTERNAL_CreateSwapchain(
+    // Clean up the previous swapchain textures
+    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
+        D3D12_INTERNAL_DestroyTexture(
             renderer,
-            windowData,
-            windowData->swapchainComposition,
-            windowData->present_mode);
+            windowData->textureContainers[i].activeTexture);
     }
 
+    // Create a new swapchain
+    D3D12_INTERNAL_CreateSwapchain(
+        renderer,
+        windowData,
+        windowData->swapchainComposition,
+        windowData->present_mode);
+
+    windowData->needsSwapchainRecreate = false;
     return true;
 }
 #else
@@ -6273,58 +6280,58 @@ static bool D3D12_INTERNAL_InitializeSwapchainTexture(
     return true;
 }
 
-static bool D3D12_INTERNAL_ResizeSwapchainIfNeeded(
+static bool D3D12_INTERNAL_ResizeSwapchain(
     D3D12Renderer *renderer,
     D3D12WindowData *windowData)
 {
-    DXGI_SWAP_CHAIN_DESC swapchainDesc;
-    int w, h;
+    // Wait so we don't release in-flight views
+    D3D12_Wait((SDL_GPURenderer *)renderer);
 
-    IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc);
-    SDL_GetWindowSize(windowData->window, &w, &h);
+    // Release views and clean up
+    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
+        D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
+            renderer,
+            &windowData->textureContainers[i].activeTexture->srvHandle);
+        D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
+            renderer,
+            &windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]);
 
-    if ((UINT)w != swapchainDesc.BufferDesc.Width || (UINT)h != swapchainDesc.BufferDesc.Height) {
-        // Wait so we don't release in-flight views
-        D3D12_Wait((SDL_GPURenderer *)renderer);
+        SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles);
+        SDL_free(windowData->textureContainers[i].activeTexture->subresources);
+        SDL_free(windowData->textureContainers[i].activeTexture);
+        SDL_free(windowData->textureContainers[i].textures);
+    }
 
-        // Release views and clean up
-        for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
-            D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
-                renderer,
-                &windowData->textureContainers[i].activeTexture->srvHandle);
-            D3D12_INTERNAL_ReleaseCpuDescriptorHandle(
+    int w, h;
+    SDL_SyncWindow(windowData->window);
+    SDL_GetWindowSizeInPixels(
+        windowData->window,
+        &w,
+        &h);
+
+    // Resize the swapchain
+    HRESULT res = IDXGISwapChain_ResizeBuffers(
+        windowData->swapchain,
+        0, // Keep buffer count the same
+        w,
+        h,
+        DXGI_FORMAT_UNKNOWN, // Keep the old format
+        renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
+    CHECK_D3D12_ERROR_AND_RETURN("Could not resize swapchain buffers", false)
+
+    // Create texture object for the swapchain
+    for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
+        if (!D3D12_INTERNAL_InitializeSwapchainTexture(
                 renderer,
-                &windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles[0]);
-
-            SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].rtvHandles);
-            SDL_free(windowData->textureContainers[i].activeTexture->subresources);
-            SDL_free(windowData->textureContainers[i].activeTexture);
-            SDL_free(windowData->textureContainers[i].textures);
-        }
-
-        // Resize the swapchain
-        HRESULT res = IDXGISwapChain_ResizeBuffers(
-            windowData->swapchain,
-            0, // Keep buffer count the same
-            w,
-            h,
-            DXGI_FORMAT_UNKNOWN, // Keep the old format
-            renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
-        CHECK_D3D12_ERROR_AND_RETURN("Could not resize swapchain buffers", false)
-
-        // Create texture object for the swapchain
-        for (Uint32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
-            if (!D3D12_INTERNAL_InitializeSwapchainTexture(
-                    renderer,
-                    windowData->swapchain,
-                    windowData->swapchainComposition,
-                    i,
-                    &windowData->textureContainers[i])) {
-                return false;
-            }
+                windowData->swapchain,
+                windowData->swapchainComposition,
+                i,
+                &windowData->textureContainers[i])) {
+            return false;
         }
     }
 
+    windowData->needsSwapchainRecreate = false;
     return true;
 }
 
@@ -6358,7 +6365,6 @@ static bool D3D12_INTERNAL_CreateSwapchain(
     SDL_GPUPresentMode presentMode)
 {
     HWND dxgiHandle;
-    int width, height;
     DXGI_SWAP_CHAIN_DESC1 swapchainDesc;
     DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreenDesc;
     DXGI_FORMAT swapchainFormat;
@@ -6374,9 +6380,6 @@ static bool D3D12_INTERNAL_CreateSwapchain(
     dxgiHandle = (HWND)windowData->window;
 #endif
 
-    // Get the window size
-    SDL_GetWindowSize(windowData->window, &width, &height);
-
     swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
 
     // Initialize the swapchain buffer descriptor
@@ -6529,7 +6532,6 @@ static bool D3D12_ClaimWindow(
             SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
 
             SDL_LockMutex(renderer->windowLock);
-
             if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
                 renderer->claimedWindowCapacity *= 2;
                 renderer->claimedWindows = (D3D12WindowData **)SDL_realloc(
@@ -6538,9 +6540,10 @@ static bool D3D12_ClaimWindow(
             }
             renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
             renderer->claimedWindowCount += 1;
-
             SDL_UnlockMutex(renderer->windowLock);
 
+            SDL_AddEventWatch(D3D12_INTERNAL_OnWindowResize, window);
+
             return true;
         } else {
             SDL_free(windowData);
@@ -6589,6 +6592,7 @@ static void D3D12_ReleaseWindow(
 
     SDL_free(windowData);
     SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA);
+    SDL_RemoveEventWatch(D3D12_INTERNAL_OnWindowResize, window);
 }
 
 static bool D3D12_SetSwapchainParameters(
@@ -6918,10 +6922,11 @@ static bool D3D12_AcquireSwapchainTexture(
         SET_STRING_ERROR_AND_RETURN("Cannot acquire swapchain texture from an unclaimed window!", false)
     }
 
-    res = D3D12_INTERNAL_ResizeSwapchainIfNeeded(
-        renderer,
-        windowData);
-    CHECK_D3D12_ERROR_AND_RETURN("Could not resize swapchain", false);
+    if (windowData->needsSwapchainRecreate) {
+        if (!D3D12_INTERNAL_ResizeSwapchain(renderer, windowData)) {
+            return false;
+        }
+    }
 
     if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
         if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) {
@@ -6932,7 +6937,7 @@ static bool D3D12_AcquireSwapchainTexture(
                 &windowData->inFlightFences[windowData->frameCounter],
                 1)) {
                 return false;
-                }
+            }
         } else {
             if (!D3D12_QueryFence(
                     (SDL_GPURenderer *)renderer,
diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c
index 84c073657aca9..280b2bacca9f3 100644
--- a/src/gpu/vulkan/SDL_gpu_vulkan.c
+++ b/src/gpu/vulkan/SDL_gpu_vulkan.c
@@ -1159,6 +1159,7 @@ struct VulkanRenderer
     SDL_Mutex *acquireUniformBufferLock;
     SDL_Mutex *renderPassFetchLock;
     SDL_Mutex *framebufferFetchLock;
+    SDL_Mutex *windowLock;
 
     Uint8 defragInProgress;
 
@@ -4493,35 +4494,6 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
         &drawableWidth,
         &drawableHeight);
 
-    if (drawableWidth < (Sint32)swapchainSupportDetails.capabilities.minImageExtent.width ||
-        drawableWidth > (Sint32)swapchainSupportDetails.capabilities.maxImageExtent.width ||
-        drawableHeight < (Sint32)swapchainSupportDetails.capabilities.minImageExtent.height ||
-        drawableHeight > (Sint32)swapchainSupportDetails.capabilities.maxImageExtent.height) {
-        if (swapchainSupportDetails.capabilities.currentExtent.width != SDL_MAX_UINT32) {
-            drawableWidth = VULKAN_INTERNAL_clamp(
-                drawableWidth,
-                (Sint32)swapchainSupportDetails.capabilities.minImageExtent.width,
-                (Sint32)swapchainSupportDetails.capabilities.maxImageExtent.width);
-            drawableHeight = VULKAN_INTERNAL_clamp(
-                drawableHeight,
-                (Sint32)swapchainSupportDetails.capabilities.minImageExtent.height,
-                (Sint32)swapchainSupportDetails.capabilities.maxImageExtent.height);
-        } else {
-            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);
-            SET_STRING_ERROR_AND_RETURN("No fallback swapchain size available!", false);
-        }
-    }
-
     swapchainData->imageCount = MAX_FRAMES_IN_FLIGHT;
 
     if (swapchainSupportDetails.capabilities.maxImageCount > 0 &&
@@ -4833,6 +4805,7 @@ static void VULKAN_DestroyDevice(
     SDL_DestroyMutex(renderer->acquireUniformBufferLock);
     SDL_DestroyMutex(renderer->renderPassFetchLock);
     SDL_DestroyMutex(renderer->framebufferFetchLock);
+    SDL_DestroyMutex(renderer->windowLock);
 
     renderer->vkDestroyDevice(renderer->logicalDevice, NULL);
     renderer->vkDestroyInstance(renderer->instance, NULL);
@@ -9356,7 +9329,7 @@ static bool VULKAN_INTERNAL_OnWindowResize(void *userdata, SDL_Event *e)
 {
     SDL_Window *w = (SDL_Window *)userdata;
     WindowData *data;
-    if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
+    if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED && e->window.windowID == SDL_GetWindowID(w)) {
         data = VULKAN_INTERNAL_FetchWindowData(w);
         data->needsSwapchainRecreate = true;
     }
@@ -9460,6 +9433,7 @@ static bool VULKAN_ClaimWindow(
         if (VULKAN_INTERNAL_CreateSwapchain(renderer, windowData)) {
             SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
 
+            SDL_LockMutex(renderer->windowLock);
             if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
                 renderer->claimedWindowCapacity *= 2;
                 renderer->claimedWindows = SDL_realloc(
@@ -9469,6 +9443,7 @@ static bool VULKAN_ClaimWindow(
 
             renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
             renderer->claimedWindowCount += 1;
+            SDL_UnlockMutex(renderer->windowLock);
 
             SDL_AddEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
 
@@ -9511,6 +9486,7 @@ static void VULKAN_ReleaseWindow(
             windowData);
     }
 
+    SDL_LockMutex(renderer->windowLock);
     for (i = 0; i < renderer->claimedWindowCount; i += 1) {
         if (renderer->claimedWindows[i]->window == window) {
             renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1];
@@ -9518,6 +9494,7 @@ static void VULKAN_ReleaseWindow(
             break;
         }
     }
+    SDL_UnlockMutex(renderer->windowLock);
 
     SDL_free(windowData);
 
@@ -9619,7 +9596,10 @@ static bool VULKAN_AcquireSwapchainTexture(
 
     // If window data marked as needing swapchain recreate, try to recreate
     if (windowData->needsSwapchainRecreate) {
-        VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
+        if (!VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData)) {
+            return false;
+        }
+
         swapchainData = windowData->swapchainData;
 
         if (swapchainData == NULL) {
@@ -11340,6 +11320,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S
     renderer->acquireUniformBufferLock = SDL_CreateMutex();
     renderer->renderPassFetchLock = SDL_CreateMutex();
     renderer->framebufferFetchLock = SDL_CreateMutex();
+    renderer->windowLock = SDL_CreateMutex();
 
     /*
      * Create submitted command buffer list