From afdf325fb4090e93a124519d1a3bc1fbe0ba9025 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+thatcosmonaut@users.noreply.github.com> Date: Mon, 30 Sep 2024 10:23:19 -0700 Subject: [PATCH] GPU: Add swapchain dimension out params (#11003) --- include/SDL3/SDL_gpu.h | 22 +- src/dynapi/SDL_dynapi_procs.h | 2 +- src/gpu/SDL_gpu.c | 12 +- src/gpu/SDL_sysgpu.h | 4 +- src/gpu/d3d11/SDL_gpu_d3d11.c | 23 +- src/gpu/d3d12/SDL_gpu_d3d12.c | 26 +- src/gpu/metal/SDL_gpu_metal.m | 16 +- src/gpu/vulkan/SDL_gpu_vulkan.c | 500 ++++++++++++++++---------------- src/render/gpu/SDL_render_gpu.c | 19 +- test/testgpu_simple_clear.c | 2 +- test/testgpu_spinning_cube.c | 6 +- 11 files changed, 347 insertions(+), 285 deletions(-) diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h index 123ffdcb3..a986a2f2c 100644 --- a/include/SDL3/SDL_gpu.h +++ b/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. diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index bdba993f1..cfaec9dad 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/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) diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c index aedb81259..a1210a0ea 100644 --- a/src/gpu/SDL_gpu.c +++ b/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( diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h index 5388696d1..ce48a5eb2 100644 --- a/src/gpu/SDL_sysgpu.h +++ b/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); diff --git a/src/gpu/d3d11/SDL_gpu_d3d11.c b/src/gpu/d3d11/SDL_gpu_d3d11.c index a7472fc13..39bbd9e2f 100644 --- a/src/gpu/d3d11/SDL_gpu_d3d11.c +++ b/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 diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c index 20a6367ee..6807c8f01 100644 --- a/src/gpu/d3d12/SDL_gpu_d3d12.c +++ b/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 diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index 70d594006..fd118bc69 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/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) { diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 280b2bacc..81e476a9e 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/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(windowData->textureContainers); + windowData->textureContainers = NULL; + + if (windowData->swapchain) { + renderer->vkDestroySwapchainKHR( + renderer->logicalDevice, + windowData->swapchain, + NULL); + windowData->swapchain = VK_NULL_HANDLE; } - SDL_free(swapchainData->textureContainers); - - renderer->vkDestroySwapchainKHR( - renderer->logicalDevice, - swapchainData->swapchain, - NULL); - - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL); + if (windowData->surface) { + renderer->vkDestroySurfaceKHR( + renderer->instance, + windowData->surface, + NULL); + windowData->surface = VK_NULL_HANDLE; + } for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { - renderer->vkDestroySemaphore( - renderer->logicalDevice, - swapchainData->imageAvailableSemaphore[i], - NULL); + if (windowData->imageAvailableSemaphore[i]) { + renderer->vkDestroySemaphore( + renderer->logicalDevice, + windowData->imageAvailableSemaphore[i], + NULL); + windowData->imageAvailableSemaphore[i] = VK_NULL_HANDLE; + } - renderer->vkDestroySemaphore( - renderer->logicalDevice, - swapchainData->renderFinishedSemaphore[i], - NULL); + if (windowData->renderFinishedSemaphore[i]) { + renderer->vkDestroySemaphore( + renderer->logicalDevice, + windowData->renderFinishedSemaphore[i], + NULL); + windowData->renderFinishedSemaphore[i] = VK_NULL_HANDLE; + } } - - windowData->swapchainData = NULL; - SDL_free(swapchainData); } 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 - return true; - } - - // Let's try to recreate - VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); - swapchainData = windowData->swapchainData; - - if (swapchainData == NULL) { + // 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; } } - if (swapchainData->inFlightFences[swapchainData->frameCounter] != NULL) { - if (swapchainData->presentMode == VK_PRESENT_MODE_FIFO_KHR) { + 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 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); } diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index e735d1e30..c9fa0fbf2 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/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; diff --git a/test/testgpu_simple_clear.c b/test/testgpu_simple_clear.c index 93d9d60c7..940dd37d6 100644 --- a/test/testgpu_simple_clear.c +++ b/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; } diff --git a/test/testgpu_spinning_cube.c b/test/testgpu_spinning_cube.c index 2e646d494..9fe01a1a8 100644 --- a/test/testgpu_spinning_cube.c +++ b/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.