From 3ee39f6c3f2c20d49097fd4716d29972040c5de9 Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Wed, 11 Dec 2024 11:16:35 -0800
Subject: [PATCH] GPU: Revise swapchain acquisition (#11633)
---------
Co-authored-by: Lucas Murray <22484+lmurray@users.noreply.github.com>
---
include/SDL3/SDL_gpu.h | 100 ++++++++++++++++++++++--------
src/dynapi/SDL_dynapi.sym | 2 +
src/dynapi/SDL_dynapi_overrides.h | 2 +
src/dynapi/SDL_dynapi_procs.h | 2 +
src/gpu/SDL_gpu.c | 62 ++++++++++++++++--
src/gpu/SDL_sysgpu.h | 13 ++++
src/gpu/d3d12/SDL_gpu_d3d12.c | 67 ++++++++++++++++++--
src/gpu/metal/SDL_gpu_metal.m | 71 ++++++++++++++++++---
src/gpu/vulkan/SDL_gpu_vulkan.c | 69 ++++++++++++++++++---
9 files changed, 337 insertions(+), 51 deletions(-)
diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index 8b7417f16be62..40f11a5fc227f 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -1068,8 +1068,7 @@ typedef enum SDL_GPUSamplerAddressMode
* Specifies the timing that will be used to present swapchain textures to the
* OS.
*
- * Note that this value affects the behavior of
- * SDL_AcquireGPUSwapchainTexture. VSYNC mode will always be supported.
+ * VSYNC mode will always be supported.
* IMMEDIATE and MAILBOX modes may not be supported on certain systems.
*
* It is recommended to query SDL_WindowSupportsGPUPresentMode after claiming
@@ -1077,17 +1076,12 @@ typedef enum SDL_GPUSamplerAddressMode
*
* - VSYNC: Waits for vblank before presenting. No tearing is possible. If
* there is a pending image to present, the new image is enqueued for
- * presentation. Disallows tearing at the cost of visual latency. When using
- * this present mode, AcquireGPUSwapchainTexture will block if too many
- * frames are in flight.
+ * presentation. Disallows tearing at the cost of visual latency.
* - IMMEDIATE: Immediately presents. Lowest latency option, but tearing may
- * occur. When using this mode, AcquireGPUSwapchainTexture will fill the
- * swapchain texture pointer with NULL if too many frames are in flight.
+ * occur.
* - MAILBOX: Waits for vblank before presenting. No tearing is possible. If
* there is a pending image to present, the pending image is replaced by the
- * new image. Similar to VSYNC, but with reduced visual latency. When using
- * this mode, AcquireGPUSwapchainTexture will fill the swapchain texture
- * pointer with NULL if too many frames are in flight.
+ * new image. Similar to VSYNC, but with reduced visual latency.
*
* \since This enum is available since SDL 3.1.3
*
@@ -3442,6 +3436,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WindowSupportsGPUPresentMode(
* \returns true on success, or false on failure; call SDL_GetError() for more
* information.
*
+ * \threadsafety This function should only be called from the thread that created the window.
+ *
* \since This function is available since SDL 3.1.3.
*
* \sa SDL_AcquireGPUSwapchainTexture
@@ -3501,8 +3497,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUSwapchainParameters(
*
* The default value when the device is created is 2. This means that after
* you have submitted 2 frames for presentation, if the GPU has not finished
- * working on the first frame, SDL_AcquireGPUSwapchainTexture() will block or
- * return false depending on the present mode.
+ * working on the first frame, SDL_AcquireGPUSwapchainTexture() will fill the swapchain texture pointer with NULL,
+ * and SDL_WaitAndAcquireGPUSwapchainTexture() will block.
*
* Higher values increase throughput at the expense of visual latency. Lower
* values decrease visual latency at the expense of throughput.
@@ -3514,9 +3510,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUSwapchainParameters(
*
* \param device a GPU context.
* \param allowed_frames_in_flight the maximum number of frames that can be
- * pending on the GPU before
- * AcquireSwapchainTexture blocks or returns
- * false.
+ * pending on the GPU.
* \returns true if successful, false on error; call SDL_GetError() for more
* information.
*
@@ -3547,20 +3541,17 @@ 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 filled in
- * with NULL under certain conditions. This is not necessarily an error. If
- * this function returns false then there is an error.
+ * buffer used to acquire it.
+ *
+ * This function will fill the swapchain texture handle with NULL if too many frames are in flight.
+ * This is not an error.
+ * The best practice is to call SDL_CancelGPUCommandBuffer if the swapchain texture
+ * handle is NULL to avoid enqueuing needless work on the GPU.
*
* 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.
*
- * When using SDL_GPU_PRESENTMODE_VSYNC, this function will block if too many
- * frames are in flight. Otherwise, this function will fill the swapchain
- * texture handle with NULL if too many frames are in flight. The best
- * practice is to call SDL_CancelGPUCommandBuffer if the swapchain texture
- * handle is NULL to avoid enqueuing needless work on the GPU.
- *
* \param command_buffer a command buffer.
* \param window a window that has been claimed.
* \param swapchain_texture a pointer filled in with a swapchain texture
@@ -3572,14 +3563,17 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
* \returns true on success, false on error; call SDL_GetError() for more
* information.
*
+ * \threadsafety This function should only be called from the thread that created the window.
+ *
* \since This function is available since SDL 3.1.3.
*
- * \sa SDL_GPUPresentMode
* \sa SDL_ClaimWindowForGPUDevice
* \sa SDL_SubmitGPUCommandBuffer
* \sa SDL_SubmitGPUCommandBufferAndAcquireFence
* \sa SDL_CancelGPUCommandBuffer
* \sa SDL_GetWindowSizeInPixels
+ * \sa SDL_WaitForGPUSwapchain
+ * \sa SDL_SetGPUAllowedFramesInFlight
*/
extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture(
SDL_GPUCommandBuffer *command_buffer,
@@ -3588,6 +3582,62 @@ extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture(
Uint32 *swapchain_texture_width,
Uint32 *swapchain_texture_height);
+/**
+ * Blocks the thread until a swapchain texture is available to be acquired.
+ *
+ * \param device a GPU context.
+ * \param window a window that has been claimed.
+ * \returns true on success, false on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \threadsafety This function should only be called from the thread that created the window.
+ *
+ * \since This function is available since SDL 3.2.0.
+ *
+ * \sa SDL_AcquireGPUSwapchainTexture
+ * \sa SDL_SetGPUAllowedFramesInFlight
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_WaitForGPUSwapchain(
+ SDL_GPUDevice *device,
+ SDL_Window *window);
+
+/**
+ * Blocks the thread until a swapchain texture is available to be acquired, and then acquires it.
+ *
+ * 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. It is an error to call SDL_CancelGPUCommandBuffer() after a swapchain texture is acquired.
+ *
+ * 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 swapchain_texture a pointer filled in with a swapchain texture
+ * handle.
+ * \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.
+ *
+ * \threadsafety This function should only be called from the thread that created the window.
+ *
+ * \since This function is available since SDL 3.2.0.
+ *
+ * \sa SDL_SubmitGPUCommandBuffer
+ * \sa SDL_SubmitGPUCommandBufferAndAcquireFence
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_WaitAndAcquireGPUSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ 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.sym b/src/dynapi/SDL_dynapi.sym
index cba2c637298b0..d575bcef93794 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -974,6 +974,7 @@ SDL3_0.0.0 {
SDL_WaitEventTimeout;
SDL_WaitForGPUFences;
SDL_WaitForGPUIdle;
+ SDL_WaitForGPUSwapchain;
SDL_WaitProcess;
SDL_WaitSemaphore;
SDL_WaitSemaphoreTimeout;
@@ -1205,6 +1206,7 @@ SDL3_0.0.0 {
SDL_RunOnMainThread;
SDL_SetGPUAllowedFramesInFlight;
SDL_RenderTextureAffine;
+ SDL_WaitAndAcquireGPUSwapchainTexture;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 7dd37385ad352..608d59ca1ecb1 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -999,6 +999,7 @@
#define SDL_WaitEventTimeout SDL_WaitEventTimeout_REAL
#define SDL_WaitForGPUFences SDL_WaitForGPUFences_REAL
#define SDL_WaitForGPUIdle SDL_WaitForGPUIdle_REAL
+#define SDL_WaitForGPUSwapchain SDL_WaitForGPUSwapchain_REAL
#define SDL_WaitProcess SDL_WaitProcess_REAL
#define SDL_WaitSemaphore SDL_WaitSemaphore_REAL
#define SDL_WaitSemaphoreTimeout SDL_WaitSemaphoreTimeout_REAL
@@ -1230,3 +1231,4 @@
#define SDL_RunOnMainThread SDL_RunOnMainThread_REAL
#define SDL_SetGPUAllowedFramesInFlight SDL_SetGPUAllowedFramesInFlight_REAL
#define SDL_RenderTextureAffine SDL_RenderTextureAffine_REAL
+#define SDL_WaitAndAcquireGPUSwapchainTexture SDL_WaitAndAcquireGPUSwapchainTexture_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 1a9acd50010f4..4611595282db8 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1009,6 +1009,7 @@ SDL_DYNAPI_PROC(bool,SDL_WaitEvent,(SDL_Event *a),(a),return)
SDL_DYNAPI_PROC(bool,SDL_WaitEventTimeout,(SDL_Event *a, Sint32 b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_WaitForGPUFences,(SDL_GPUDevice *a, bool b, SDL_GPUFence *const *c, Uint32 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(bool,SDL_WaitForGPUIdle,(SDL_GPUDevice *a),(a),return)
+SDL_DYNAPI_PROC(bool,SDL_WaitForGPUSwapchain,(SDL_GPUDevice *a, SDL_Window *b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_WaitProcess,(SDL_Process *a, bool b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_WaitSemaphore,(SDL_Semaphore *a),(a),)
SDL_DYNAPI_PROC(bool,SDL_WaitSemaphoreTimeout,(SDL_Semaphore *a, Sint32 b),(a,b),return)
@@ -1236,3 +1237,4 @@ SDL_DYNAPI_PROC(bool,SDL_IsMainThread,(void),(),return)
SDL_DYNAPI_PROC(bool,SDL_RunOnMainThread,(SDL_MainThreadCallback a,void *b,bool c),(a,b,c),return)
SDL_DYNAPI_PROC(bool,SDL_SetGPUAllowedFramesInFlight,(SDL_GPUDevice *a,Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_RenderTextureAffine,(SDL_Renderer *a,SDL_Texture *b,const SDL_FRect *c,const SDL_FPoint *d,const SDL_FPoint *e,const SDL_FPoint *f),(a,b,c,d,e,f),return)
+SDL_DYNAPI_PROC(bool,SDL_WaitAndAcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a,SDL_Window *b,SDL_GPUTexture **c,Uint32 *d,Uint32 *e),(a,b,c,d,e),return)
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index 5763e793dd729..bc1cb1a288182 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -2694,16 +2694,13 @@ bool SDL_AcquireGPUSwapchainTexture(
CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
if (command_buffer == NULL) {
- SDL_InvalidParamError("command_buffer");
- return false;
+ return SDL_InvalidParamError("command_buffer");
}
if (window == NULL) {
- SDL_InvalidParamError("window");
- return false;
+ return SDL_InvalidParamError("window");
}
if (swapchain_texture == NULL) {
- SDL_InvalidParamError("swapchain_texture");
- return false;
+ return SDL_InvalidParamError("swapchain_texture");
}
if (COMMAND_BUFFER_DEVICE->debug_mode) {
@@ -2725,6 +2722,59 @@ bool SDL_AcquireGPUSwapchainTexture(
return result;
}
+bool SDL_WaitForGPUSwapchain(
+ SDL_GPUDevice *device,
+ SDL_Window *window)
+{
+ CHECK_DEVICE_MAGIC(device, false);
+
+ if (window == NULL) {
+ return SDL_InvalidParamError("window");
+ }
+
+ return device->WaitForSwapchain(
+ device->driverData,
+ window);
+}
+
+bool SDL_WaitAndAcquireGPUSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height)
+{
+ CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
+
+ if (command_buffer == NULL) {
+ return SDL_InvalidParamError("command_buffer");
+ }
+ if (window == NULL) {
+ return SDL_InvalidParamError("window");
+ }
+ if (swapchain_texture == NULL) {
+ return SDL_InvalidParamError("swapchain_texture");
+ }
+
+ if (COMMAND_BUFFER_DEVICE->debug_mode) {
+ CHECK_COMMAND_BUFFER_RETURN_FALSE
+ CHECK_ANY_PASS_IN_PROGRESS("Cannot acquire a swapchain texture during a pass!", false)
+ }
+
+ bool result = COMMAND_BUFFER_DEVICE->WaitAndAcquireSwapchainTexture(
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+
+ if (*swapchain_texture != NULL){
+ commandBufferHeader->swapchain_texture_acquired = true;
+ }
+
+ return result;
+}
+
bool SDL_SubmitGPUCommandBuffer(
SDL_GPUCommandBuffer *command_buffer)
{
diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h
index 2cf8f6459b74a..901fd9aba2aaa 100644
--- a/src/gpu/SDL_sysgpu.h
+++ b/src/gpu/SDL_sysgpu.h
@@ -811,6 +811,17 @@ struct SDL_GPUDevice
Uint32 *swapchainTextureWidth,
Uint32 *swapchainTextureHeight);
+ bool (*WaitForSwapchain)(
+ SDL_GPURenderer *driverData,
+ SDL_Window *window);
+
+ bool (*WaitAndAcquireSwapchainTexture)(
+ SDL_GPUCommandBuffer *commandBuffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchainTexture,
+ Uint32 *swapchainTextureWidth,
+ Uint32 *swapchainTextureHeight);
+
bool (*Submit)(
SDL_GPUCommandBuffer *commandBuffer);
@@ -937,6 +948,8 @@ struct SDL_GPUDevice
ASSIGN_DRIVER_FUNC(GetSwapchainTextureFormat, name) \
ASSIGN_DRIVER_FUNC(AcquireCommandBuffer, name) \
ASSIGN_DRIVER_FUNC(AcquireSwapchainTexture, name) \
+ ASSIGN_DRIVER_FUNC(WaitForSwapchain, name) \
+ ASSIGN_DRIVER_FUNC(WaitAndAcquireSwapchainTexture, name)\
ASSIGN_DRIVER_FUNC(Submit, name) \
ASSIGN_DRIVER_FUNC(SubmitAndAcquireFence, name) \
ASSIGN_DRIVER_FUNC(Cancel, name) \
diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c
index 7b024f0b024b6..f42c72bf80169 100644
--- a/src/gpu/d3d12/SDL_gpu_d3d12.c
+++ b/src/gpu/d3d12/SDL_gpu_d3d12.c
@@ -7124,7 +7124,32 @@ static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer(
return (SDL_GPUCommandBuffer *)commandBuffer;
}
-static bool D3D12_AcquireSwapchainTexture(
+static bool D3D12_WaitForSwapchain(
+ SDL_GPURenderer *driverData,
+ SDL_Window *window)
+{
+ D3D12Renderer *renderer = (D3D12Renderer *)driverData;
+ D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
+
+ if (windowData == NULL) {
+ SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false);
+ }
+
+ if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
+ if (!D3D12_WaitForFences(
+ driverData,
+ true,
+ &windowData->inFlightFences[windowData->frameCounter],
+ 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool D3D12_INTERNAL_AcquireSwapchainTexture(
+ bool block,
SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window,
SDL_GPUTexture **swapchainTexture,
@@ -7164,7 +7189,7 @@ static bool D3D12_AcquireSwapchainTexture(
}
if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
- if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) {
+ if (block) {
// In VSYNC mode, block until the least recent presented frame is done
if (!D3D12_WaitForFences(
(SDL_GPURenderer *)renderer,
@@ -7174,13 +7199,11 @@ static bool D3D12_AcquireSwapchainTexture(
return false;
}
} else {
+ // If we are not blocking and the least recent fence is not signaled,
+ // return true to indicate that there is no error but rendering should be skipped.
if (!D3D12_QueryFence(
(SDL_GPURenderer *)renderer,
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
- */
return true;
}
}
@@ -7238,6 +7261,38 @@ static bool D3D12_AcquireSwapchainTexture(
return true;
}
+static bool D3D12_AcquireSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height
+) {
+ return D3D12_INTERNAL_AcquireSwapchainTexture(
+ false,
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+}
+
+static bool D3D12_WaitAndAcquireSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height
+) {
+ return D3D12_INTERNAL_AcquireSwapchainTexture(
+ true,
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+}
+
static void D3D12_INTERNAL_PerformPendingDestroys(D3D12Renderer *renderer)
{
SDL_LockMutex(renderer->disposeLock);
diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m
index 981355a3ad88b..be64b366ff14c 100644
--- a/src/gpu/metal/SDL_gpu_metal.m
+++ b/src/gpu/metal/SDL_gpu_metal.m
@@ -3671,7 +3671,34 @@ static void METAL_ReleaseWindow(
}
}
-static bool METAL_AcquireSwapchainTexture(
+static bool METAL_WaitForSwapchain(
+ SDL_GPURenderer *driverData,
+ SDL_Window *window)
+{
+ @autoreleasepool {
+ MetalRenderer *renderer = (MetalRenderer *)driverData;
+ MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window);
+
+ if (windowData == NULL) {
+ SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false);
+ }
+
+ if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
+ if (!METAL_WaitForFences(
+ driverData,
+ true,
+ &windowData->inFlightFences[windowData->frameCounter],
+ 1)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+static bool METAL_INTERNAL_AcquireSwapchainTexture(
+ bool block,
SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window,
SDL_GPUTexture **texture,
@@ -3709,8 +3736,8 @@ static bool METAL_AcquireSwapchainTexture(
}
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 (block) {
+ // If we are blocking, just wait for the fence!
if (!METAL_WaitForFences(
(SDL_GPURenderer *)renderer,
true,
@@ -3719,13 +3746,11 @@ static bool METAL_AcquireSwapchainTexture(
return false;
}
} else {
+ // If we are not blocking and the least recent fence is not signaled,
+ // return true to indicate that there is no error but rendering should be skipped.
if (!METAL_QueryFence(
(SDL_GPURenderer *)metalCommandBuffer->renderer,
windowData->inFlightFences[windowData->frameCounter])) {
- /*
- * In IMMEDIATE mode, if the least recent fence is not signaled,
- * return true to indicate that there is no error but rendering should be skipped
- */
return true;
}
}
@@ -3757,6 +3782,38 @@ static bool METAL_AcquireSwapchainTexture(
}
}
+static bool METAL_AcquireSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height
+) {
+ return METAL_INTERNAL_AcquireSwapchainTexture(
+ false,
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+}
+
+static bool METAL_WaitAndAcquireSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height
+) {
+ return METAL_INTERNAL_AcquireSwapchainTexture(
+ true,
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+}
+
static SDL_GPUTextureFormat METAL_GetSwapchainTextureFormat(
SDL_GPURenderer *driverData,
SDL_Window *window)
diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c
index 359aecdfeb0f8..277e4246b454e 100644
--- a/src/gpu/vulkan/SDL_gpu_vulkan.c
+++ b/src/gpu/vulkan/SDL_gpu_vulkan.c
@@ -9655,7 +9655,32 @@ static Uint32 VULKAN_INTERNAL_RecreateSwapchain(
return VULKAN_INTERNAL_CreateSwapchain(renderer, windowData);
}
-static bool VULKAN_AcquireSwapchainTexture(
+static bool VULKAN_WaitForSwapchain(
+ SDL_GPURenderer *driverData,
+ SDL_Window *window)
+{
+ VulkanRenderer *renderer = (VulkanRenderer *)driverData;
+ WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
+
+ if (windowData == NULL) {
+ SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false);
+ }
+
+ if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
+ if (!VULKAN_WaitForFences(
+ driverData,
+ true,
+ &windowData->inFlightFences[windowData->frameCounter],
+ 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool VULKAN_INTERNAL_AcquireSwapchainTexture(
+ bool block,
SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window,
SDL_GPUTexture **swapchainTexture,
@@ -9708,8 +9733,8 @@ static bool VULKAN_AcquireSwapchainTexture(
}
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 (block) {
+ // If we are blocking, just wait for the fence!
if (!VULKAN_WaitForFences(
(SDL_GPURenderer *)renderer,
true,
@@ -9718,13 +9743,11 @@ static bool VULKAN_AcquireSwapchainTexture(
return false;
}
} else {
+ // If we are not blocking and the least recent fence is not signaled,
+ // return true to indicate that there is no error but rendering should be skipped.
if (!VULKAN_QueryFence(
(SDL_GPURenderer *)renderer,
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
- */
return true;
}
}
@@ -9843,6 +9866,38 @@ static bool VULKAN_AcquireSwapchainTexture(
return true;
}
+static bool VULKAN_AcquireSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height
+) {
+ return VULKAN_INTERNAL_AcquireSwapchainTexture(
+ false,
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+}
+
+static bool VULKAN_WaitAndAcquireSwapchainTexture(
+ SDL_GPUCommandBuffer *command_buffer,
+ SDL_Window *window,
+ SDL_GPUTexture **swapchain_texture,
+ Uint32 *swapchain_texture_width,
+ Uint32 *swapchain_texture_height
+) {
+ return VULKAN_INTERNAL_AcquireSwapchainTexture(
+ true,
+ command_buffer,
+ window,
+ swapchain_texture,
+ swapchain_texture_width,
+ swapchain_texture_height);
+}
+
static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat(
SDL_GPURenderer *driverData,
SDL_Window *window)