SDL: GPU: More robust error reporting (#10958)

From be401dd1e35c08baaf44000f031b81951698fc10 Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Fri, 27 Sep 2024 00:30:18 -0700
Subject: [PATCH] GPU: More robust error reporting (#10958)

---------

Co-authored-by: Ethan Lee <flibitijibibo@gmail.com>
Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
---
 include/SDL3/SDL_gpu.h          |  63 +--
 src/dynapi/SDL_dynapi_procs.h   |   8 +-
 src/gpu/SDL_gpu.c               |  68 ++--
 src/gpu/SDL_sysgpu.h            |  11 +-
 src/gpu/d3d11/SDL_gpu_d3d11.c   | 309 +++++++-------
 src/gpu/d3d12/SDL_gpu_d3d12.c   | 380 +++++++++--------
 src/gpu/metal/SDL_gpu_metal.m   |  86 ++--
 src/gpu/vulkan/SDL_gpu_vulkan.c | 700 ++++++++++++++++----------------
 src/render/gpu/SDL_render_gpu.c |  18 +-
 test/testgpu_simple_clear.c     |   7 +-
 test/testgpu_spinning_cube.c    |  21 +-
 11 files changed, 816 insertions(+), 855 deletions(-)

diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index 04099f240859d..a17dd5661d777 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -877,15 +877,15 @@ 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, AcquireSwapchainTexture will block if too many frames
+ *   this present mode, AcquireGPUSwapchainTexture will block if too many frames
  *   are in flight.
  * - IMMEDIATE: Immediately presents. Lowest latency option, but tearing may
- *   occur. When using this mode, AcquireSwapchainTexture will return NULL if
+ *   occur. When using this mode, AcquireGPUSwapchainTexture will return NULL if
  *   too many frames are in flight.
  * - 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, AcquireSwapchainTexture will return NULL if too many frames
+ *   this mode, AcquireGPUSwapchainTexture will return NULL if too many frames
  *   are in flight.
  *
  * \since This enum is available since SDL 3.0.0
@@ -1623,6 +1623,7 @@ typedef struct SDL_GPUBlitInfo {
     Uint8 padding2;
     Uint8 padding3;
 } SDL_GPUBlitInfo;
+
 /* Binding structs */
 
 /**
@@ -1731,7 +1732,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GPUSupportsProperties(
  * \param debug_mode enable debug mode properties and validations.
  * \param name the preferred GPU driver, or NULL to let SDL pick the optimal
  *             driver.
- * \returns a GPU context on success or NULL on failure.
+ * \returns a GPU context on success or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -1778,7 +1779,7 @@ extern SDL_DECLSPEC SDL_GPUDevice *SDLCALL SDL_CreateGPUDevice(
  *   use for all vertex semantics, default is "TEXCOORD".
  *
  * \param props the properties to use.
- * \returns a GPU context on success or NULL on failure.
+ * \returns a GPU context on success or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -1904,7 +1905,7 @@ extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_GetGPUShaderFormats(SDL_GPUD
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the compute pipeline to
  *                   create.
- * \returns a compute pipeline object on success, or NULL on failure.
+ * \returns a compute pipeline object on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -1921,7 +1922,7 @@ extern SDL_DECLSPEC SDL_GPUComputePipeline *SDLCALL SDL_CreateGPUComputePipeline
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the graphics pipeline to
  *                   create.
- * \returns a graphics pipeline object on success, or NULL on failure.
+ * \returns a graphics pipeline object on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -1939,7 +1940,7 @@ extern SDL_DECLSPEC SDL_GPUGraphicsPipeline *SDLCALL SDL_CreateGPUGraphicsPipeli
  *
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the sampler to create.
- * \returns a sampler object on success, or NULL on failure.
+ * \returns a sampler object on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -2008,7 +2009,7 @@ extern SDL_DECLSPEC SDL_GPUSampler *SDLCALL SDL_CreateGPUSampler(
  *
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the shader to create.
- * \returns a shader object on success, or NULL on failure.
+ * \returns a shader object on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -2034,7 +2035,7 @@ extern SDL_DECLSPEC SDL_GPUShader *SDLCALL SDL_CreateGPUShader(
  *
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the texture to create.
- * \returns a texture object on success, or NULL on failure.
+ * \returns a texture object on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -2064,7 +2065,7 @@ extern SDL_DECLSPEC SDL_GPUTexture *SDLCALL SDL_CreateGPUTexture(
  *
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the buffer to create.
- * \returns a buffer object on success, or NULL on failure.
+ * \returns a buffer object on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -2093,7 +2094,7 @@ extern SDL_DECLSPEC SDL_GPUBuffer *SDLCALL SDL_CreateGPUBuffer(
  * \param device a GPU Context.
  * \param createinfo a struct describing the state of the transfer buffer to
  *                   create.
- * \returns a transfer buffer on success, or NULL on failure.
+ * \returns a transfer buffer on success, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -2301,7 +2302,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUGraphicsPipeline(
  * acquired on.
  *
  * \param device a GPU context.
- * \returns a command buffer.
+ * \returns a command buffer, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -2967,7 +2968,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_EndGPUComputePass(
  * \param device a GPU context.
  * \param transfer_buffer a transfer buffer.
  * \param cycle if true, cycles the transfer buffer if it is already bound.
- * \returns the address of the mapped transfer buffer memory.
+ * \returns the address of the mapped transfer buffer memory, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  */
@@ -3183,7 +3184,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_BlitGPUTexture(
  * \param device a GPU context.
  * \param window an SDL_Window.
  * \param swapchain_composition the swapchain composition to check.
- * \returns true if supported, false if unsupported (or on error).
+ * \returns true if supported, false if unsupported.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3202,7 +3203,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WindowSupportsGPUSwapchainComposition(
  * \param device a GPU context.
  * \param window an SDL_Window.
  * \param present_mode the presentation mode to check.
- * \returns true if supported, false if unsupported (or on error).
+ * \returns true if supported, false if unsupported.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3226,7 +3227,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_WindowSupportsGPUPresentMode(
  *
  * \param device a GPU context.
  * \param window an SDL_Window.
- * \returns true on success, otherwise false.
+ * \returns true on success, or false on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3283,6 +3284,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUSwapchainParameters(
 
 /**
  * Obtains the texture format of the swapchain for the given window.
+ * Note that this format can change if the swapchain parameters change.
  *
  * \param device a GPU context.
  * \param window an SDL_Window that has been claimed.
@@ -3300,16 +3302,15 @@ 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. May return NULL under certain conditions. This
- * is not necessarily an error. This texture is managed by the implementation
- * and must not be freed by the user. You MUST NOT call this function from any
+ * buffer used to acquire it. The swapchain texture handle can be NULL under certain conditions. This
+ * is not necessarily an error. If this function returns false then there is an error. This texture is managed by the implementation
+ * and must not be freed by the user. The texture dimensions will be the height and width of the claimed window. 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 w a pointer filled in with the swapchain width.
- * \param h a pointer filled in with the swapchain height.
- * \returns a swapchain texture.
+ * \param swapchainTexture a pointer filled in with a swapchain texture handle
+ * \returns true on success, false on error.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3317,11 +3318,10 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
  * \sa SDL_SubmitGPUCommandBuffer
  * \sa SDL_SubmitGPUCommandBufferAndAcquireFence
  */
-extern SDL_DECLSPEC SDL_GPUTexture *SDLCALL SDL_AcquireGPUSwapchainTexture(
+extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture(
     SDL_GPUCommandBuffer *command_buffer,
     SDL_Window *window,
-    Uint32 *w,
-    Uint32 *h);
+    SDL_GPUTexture **swapchainTexture);
 
 /**
  * Submits a command buffer so its commands can be processed on the GPU.
@@ -3334,6 +3334,7 @@ extern SDL_DECLSPEC SDL_GPUTexture *SDLCALL SDL_AcquireGPUSwapchainTexture(
  * command in a subsequent submission begins executing.
  *
  * \param command_buffer a command buffer.
+ * \returns true on success, false on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3341,7 +3342,7 @@ extern SDL_DECLSPEC SDL_GPUTexture *SDLCALL SDL_AcquireGPUSwapchainTexture(
  * \sa SDL_AcquireGPUSwapchainTexture
  * \sa SDL_SubmitGPUCommandBufferAndAcquireFence
  */
-extern SDL_DECLSPEC void SDLCALL SDL_SubmitGPUCommandBuffer(
+extern SDL_DECLSPEC bool SDLCALL SDL_SubmitGPUCommandBuffer(
     SDL_GPUCommandBuffer *command_buffer);
 
 /**
@@ -3357,7 +3358,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_SubmitGPUCommandBuffer(
  * command in a subsequent submission begins executing.
  *
  * \param command_buffer a command buffer.
- * \returns a fence associated with the command buffer.
+ * \returns a fence associated with the command buffer, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
@@ -3373,12 +3374,13 @@ extern SDL_DECLSPEC SDL_GPUFence *SDLCALL SDL_SubmitGPUCommandBufferAndAcquireFe
  * Blocks the thread until the GPU is completely idle.
  *
  * \param device a GPU context.
+ * \returns true on success, false on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_WaitForGPUFences
  */
-extern SDL_DECLSPEC void SDLCALL SDL_WaitForGPUIdle(
+extern SDL_DECLSPEC bool SDLCALL SDL_WaitForGPUIdle(
     SDL_GPUDevice *device);
 
 /**
@@ -3389,13 +3391,14 @@ extern SDL_DECLSPEC void SDLCALL SDL_WaitForGPUIdle(
  *                 fences to be signaled.
  * \param fences an array of fences to wait on.
  * \param num_fences the number of fences in the fences array.
+ * \returns true on success, false on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_SubmitGPUCommandBufferAndAcquireFence
  * \sa SDL_WaitForGPUIdle
  */
-extern SDL_DECLSPEC void SDLCALL SDL_WaitForGPUFences(
+extern SDL_DECLSPEC bool SDLCALL SDL_WaitForGPUFences(
     SDL_GPUDevice *device,
     bool wait_all,
     SDL_GPUFence *const *fences,
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index f0986cd3ac929..0aaa592b049c8 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(SDL_GPUTexture*,SDL_AcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a, SDL_Window *b, Uint32 *c, Uint32 *d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(bool,SDL_AcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a, SDL_Window *b, SDL_GPUTexture **c),(a,b,c),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)
@@ -953,7 +953,7 @@ SDL_DYNAPI_PROC(bool,SDL_StopHapticRumble,(SDL_Haptic *a),(a),return)
 SDL_DYNAPI_PROC(bool,SDL_StopTextInput,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(bool,SDL_StorageReady,(SDL_Storage *a),(a),return)
 SDL_DYNAPI_PROC(SDL_GUID,SDL_StringToGUID,(const char *a),(a),return)
-SDL_DYNAPI_PROC(void,SDL_SubmitGPUCommandBuffer,(SDL_GPUCommandBuffer *a),(a),)
+SDL_DYNAPI_PROC(bool,SDL_SubmitGPUCommandBuffer,(SDL_GPUCommandBuffer *a),(a),return)
 SDL_DYNAPI_PROC(SDL_GPUFence*,SDL_SubmitGPUCommandBufferAndAcquireFence,(SDL_GPUCommandBuffer *a),(a),return)
 SDL_DYNAPI_PROC(bool,SDL_SurfaceHasAlternateImages,(SDL_Surface *a),(a),return)
 SDL_DYNAPI_PROC(bool,SDL_SurfaceHasColorKey,(SDL_Surface *a),(a),return)
@@ -1006,8 +1006,8 @@ SDL_DYNAPI_PROC(void,SDL_WaitCondition,(SDL_Condition *a, SDL_Mutex *b),(a,b),)
 SDL_DYNAPI_PROC(bool,SDL_WaitConditionTimeout,(SDL_Condition *a, SDL_Mutex *b, Sint32 c),(a,b,c),return)
 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(void,SDL_WaitForGPUFences,(SDL_GPUDevice *a, bool b, SDL_GPUFence *const *c, Uint32 d),(a,b,c,d),)
-SDL_DYNAPI_PROC(void,SDL_WaitForGPUIdle,(SDL_GPUDevice *a),(a),)
+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_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)
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index 16e04b41ae35e..fb9c4d107afb9 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -28,16 +28,22 @@
         return retval;                      \
     }
 
-#define CHECK_COMMAND_BUFFER                                       \
+#define CHECK_COMMAND_BUFFER                                        \
     if (((CommandBufferCommonHeader *)command_buffer)->submitted) { \
-        SDL_assert_release(!"Command buffer already submitted!");  \
-        return;                                                    \
+        SDL_assert_release(!"Command buffer already submitted!");   \
+        return;                                                     \
     }
 
-#define CHECK_COMMAND_BUFFER_RETURN_NULL                           \
+#define CHECK_COMMAND_BUFFER_RETURN_FALSE                           \
     if (((CommandBufferCommonHeader *)command_buffer)->submitted) { \
-        SDL_assert_release(!"Command buffer already submitted!");  \
-        return NULL;                                               \
+        SDL_assert_release(!"Command buffer already submitted!");   \
+        return false;                                               \
+    }
+
+#define CHECK_COMMAND_BUFFER_RETURN_NULL                            \
+    if (((CommandBufferCommonHeader *)command_buffer)->submitted) { \
+        SDL_assert_release(!"Command buffer already submitted!");   \
+        return NULL;                                                \
     }
 
 #define CHECK_ANY_PASS_IN_PROGRESS(msg, retval)                                 \
@@ -2594,65 +2600,59 @@ SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(
         window);
 }
 
-SDL_GPUTexture *SDL_AcquireGPUSwapchainTexture(
+bool SDL_AcquireGPUSwapchainTexture(
     SDL_GPUCommandBuffer *command_buffer,
     SDL_Window *window,
-    Uint32 *w,
-    Uint32 *h)
+    SDL_GPUTexture **swapchainTexture)
 {
     if (command_buffer == NULL) {
         SDL_InvalidParamError("command_buffer");
-        return NULL;
+        return false;
     }
     if (window == NULL) {
         SDL_InvalidParamError("window");
-        return NULL;
-    }
-    if (w == NULL) {
-        SDL_InvalidParamError("w");
-        return NULL;
+        return false;
     }
-    if (h == NULL) {
-        SDL_InvalidParamError("h");
-        return NULL;
+    if (swapchainTexture == NULL) {
+        SDL_InvalidParamError("swapchainTexture");
+        return false;
     }
 
     if (COMMAND_BUFFER_DEVICE->debug_mode) {
-        CHECK_COMMAND_BUFFER_RETURN_NULL
-        CHECK_ANY_PASS_IN_PROGRESS("Cannot acquire a swapchain texture during a pass!", NULL)
+        CHECK_COMMAND_BUFFER_RETURN_FALSE
+        CHECK_ANY_PASS_IN_PROGRESS("Cannot acquire a swapchain texture during a pass!", false)
     }
 
     return COMMAND_BUFFER_DEVICE->AcquireSwapchainTexture(
         command_buffer,
         window,
-        w,
-        h);
+        swapchainTexture);
 }
 
-void SDL_SubmitGPUCommandBuffer(
+bool SDL_SubmitGPUCommandBuffer(
     SDL_GPUCommandBuffer *command_buffer)
 {
     CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
 
     if (command_buffer == NULL) {
         SDL_InvalidParamError("command_buffer");
-        return;
+        return false;
     }
 
     if (COMMAND_BUFFER_DEVICE->debug_mode) {
-        CHECK_COMMAND_BUFFER
+        CHECK_COMMAND_BUFFER_RETURN_FALSE
         if (
             commandBufferHeader->render_pass.in_progress ||
             commandBufferHeader->compute_pass.in_progress ||
             commandBufferHeader->copy_pass.in_progress) {
             SDL_assert_release(!"Cannot submit command buffer while a pass is in progress!");
-            return;
+            return false;
         }
     }
 
     commandBufferHeader->submitted = true;
 
-    COMMAND_BUFFER_DEVICE->Submit(
+    return COMMAND_BUFFER_DEVICE->Submit(
         command_buffer);
 }
 
@@ -2683,28 +2683,28 @@ SDL_GPUFence *SDL_SubmitGPUCommandBufferAndAcquireFence(
         command_buffer);
 }
 
-void SDL_WaitForGPUIdle(
+bool SDL_WaitForGPUIdle(
     SDL_GPUDevice *device)
 {
-    CHECK_DEVICE_MAGIC(device, );
+    CHECK_DEVICE_MAGIC(device, false);
 
-    device->Wait(
+    return device->Wait(
         device->driverData);
 }
 
-void SDL_WaitForGPUFences(
+bool SDL_WaitForGPUFences(
     SDL_GPUDevice *device,
     bool wait_all,
     SDL_GPUFence *const *fences,
     Uint32 num_fences)
 {
-    CHECK_DEVICE_MAGIC(device, );
+    CHECK_DEVICE_MAGIC(device, false);
     if (fences == NULL && num_fences > 0) {
         SDL_InvalidParamError("fences");
-        return;
+        return false;
     }
 
-    device->WaitForFences(
+    return device->WaitForFences(
         device->driverData,
         wait_all,
         fences,
diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h
index 5ae9b95a84767..e2e60cffb3041 100644
--- a/src/gpu/SDL_sysgpu.h
+++ b/src/gpu/SDL_sysgpu.h
@@ -648,22 +648,21 @@ struct SDL_GPUDevice
     SDL_GPUCommandBuffer *(*AcquireCommandBuffer)(
         SDL_GPURenderer *driverData);
 
-    SDL_GPUTexture *(*AcquireSwapchainTexture)(
+    bool (*AcquireSwapchainTexture)(
         SDL_GPUCommandBuffer *commandBuffer,
         SDL_Window *window,
-        Uint32 *w,
-        Uint32 *h);
+        SDL_GPUTexture **swapchainTexture);
 
-    void (*Submit)(
+    bool (*Submit)(
         SDL_GPUCommandBuffer *commandBuffer);
 
     SDL_GPUFence *(*SubmitAndAcquireFence)(
         SDL_GPUCommandBuffer *commandBuffer);
 
-    void (*Wait)(
+    bool (*Wait)(
         SDL_GPURenderer *driverData);
 
-    void (*WaitForFences)(
+    bool (*WaitForFences)(
         SDL_GPURenderer *driverData,
         bool waitAll,
         SDL_GPUFence *const *fences,
diff --git a/src/gpu/d3d11/SDL_gpu_d3d11.c b/src/gpu/d3d11/SDL_gpu_d3d11.c
index 78b77a401b7b5..5a5210112c3e0 100644
--- a/src/gpu/d3d11/SDL_gpu_d3d11.c
+++ b/src/gpu/d3d11/SDL_gpu_d3d11.c
@@ -108,20 +108,18 @@ static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87,
 
 // Macros
 
-#define ERROR_LOG(msg)                                       \
-    if (FAILED(res)) {                                       \
-        D3D11_INTERNAL_LogError(renderer->device, msg, res); \
-    }
+#define SET_ERROR_AND_RETURN(fmt, msg, ret)           \
+    if (renderer->debugMode) {                        \
+        SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \
+    }                                                 \
+    SDL_SetError(fmt, msg);                           \
+    return ret;                                       \
 
-#define ERROR_LOG_RETURN(msg, ret)                           \
-    if (FAILED(res)) {                                       \
-        D3D11_INTERNAL_LogError(renderer->device, msg, res); \
-        return ret;                                          \
-    }
+#define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret)
 
-#define ERROR_SET_RETURN(msg, ret)                           \
+#define CHECK_D3D11_ERROR_AND_RETURN(msg, ret)               \
     if (FAILED(res)) {                                       \
-        D3D11_INTERNAL_SetError(renderer->device, msg, res); \
+        D3D11_INTERNAL_SetError(renderer, msg, res);         \
         return ret;                                          \
     }
 
@@ -146,7 +144,7 @@ static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87,
 
 // Forward Declarations
 
-static void D3D11_Wait(SDL_GPURenderer *driverData);
+static bool D3D11_Wait(SDL_GPURenderer *driverData);
 static void D3D11_ReleaseWindow(
     SDL_GPURenderer *driverData,
     SDL_Window *window);
@@ -800,7 +798,7 @@ struct D3D11Renderer
 // Logging
 
 static void D3D11_INTERNAL_SetError(
-    ID3D11Device1 *device,
+    D3D11Renderer *renderer,
     const char *msg,
     HRESULT res)
 {
@@ -811,7 +809,7 @@ static void D3D11_INTERNAL_SetError(
     DWORD dwChars; // Number of chars returned.
 
     if (res == DXGI_ERROR_DEVICE_REMOVED) {
-        res = ID3D11Device_GetDeviceRemovedReason(device);
+        res = ID3D11Device_GetDeviceRemovedReason(renderer->device);
     }
 
     // Try to get the message from the system errors.
@@ -831,6 +829,9 @@ static void D3D11_INTERNAL_SetError(
 
     // No message? Screw it, just post the code.
     if (dwChars == 0) {
+        if (renderer->debugMode) {
+            SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: " HRESULT_FMT, msg, res);
+        }
         SDL_SetError("%s! Error Code: " HRESULT_FMT, msg, res);
         return;
     }
@@ -850,61 +851,10 @@ static void D3D11_INTERNAL_SetError(
     // Ensure null-terminated string
     wszMsgBuff[dwChars] = '\0';
 
-    SDL_SetError("%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
-}
-
-static void D3D11_INTERNAL_LogError(
-    ID3D11Device1 *device,
-    const char *msg,
-    HRESULT res)
-{
-#define MAX_ERROR_LEN 1024 // FIXME: Arbitrary!
-
-    // Buffer for text, ensure space for \0 terminator after buffer
-    char wszMsgBuff[MAX_ERROR_LEN + 1];
-    DWORD dwChars; // Number of chars returned.
-
-    if (res == DXGI_ERROR_DEVICE_REMOVED) {
-        res = ID3D11Device_GetDeviceRemovedReason(device);
-    }
-
-    // Try to get the message from the system errors.
-#ifdef _WIN32
-    dwChars = FormatMessageA(
-        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-        NULL,
-        res,
-        0,
-        wszMsgBuff,
-        MAX_ERROR_LEN,
-        NULL);
-#else
-    // FIXME: Do we have error strings in dxvk-native? -flibit
-    dwChars = 0;
-#endif
-
-    // No message? Screw it, just post the code.
-    if (dwChars == 0) {
-        SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: " HRESULT_FMT, msg, res);
-        return;
-    }
-
-    // Ensure valid range
-    dwChars = SDL_min(dwChars, MAX_ERROR_LEN);
-
-    // Trim whitespace from tail of message
-    while (dwChars > 0) {
-        if (wszMsgBuff[dwChars - 1] <= ' ') {
-            dwChars--;
-        } else {
-            break;
-        }
+    if (renderer->debugMode) {
+        SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
     }
-
-    // Ensure null-terminated string
-    wszMsgBuff[dwChars] = '\0';
-
-    SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
+    SDL_SetError("%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
 }
 
 // Helper Functions
@@ -1348,7 +1298,7 @@ static ID3D11BlendState *D3D11_INTERNAL_FetchBlendState(
         renderer->device,
         &blendDesc,
         &result);
-    ERROR_LOG_RETURN("Could not create blend state", NULL);
+    CHECK_D3D11_ERROR_AND_RETURN("Could not create blend state", NULL);
 
     return result;
 }
@@ -1386,7 +1336,7 @@ static ID3D11DepthStencilState *D3D11_INTERNAL_FetchDepthStencilState(
         renderer->device,
         &dsDesc,
         &result);
-    ERROR_LOG_RETURN("Could not create depth-stencil state", NULL);
+    CHECK_D3D11_ERROR_AND_RETURN("Could not create depth-stencil state", NULL);
 
     return result;
 }
@@ -1417,7 +1367,7 @@ static ID3D11RasterizerState *D3D11_INTERNAL_FetchRasterizerState(
         renderer->device,
         &rasterizerDesc,
         &result);
-    ERROR_LOG_RETURN("Could not create rasterizer state", NULL);
+    CHECK_D3D11_ERROR_AND_RETURN("Could not create rasterizer state", NULL);
 
     return result;
 }
@@ -1486,8 +1436,8 @@ static ID3D11InputLayout *D3D11_INTERNAL_FetchInputLayout(
         shaderByteLength,
         &result);
     if (FAILED(res)) {
-        SDL_SetError("Could not create input layout! Error: " HRESULT_FMT, res);
         SDL_stack_free(elementDescs);
+        CHECK_D3D11_ERROR_AND_RETURN("Could not create input layout!", NULL)
         return NULL;
     }
 
@@ -1522,10 +1472,7 @@ static ID3D11DeviceChild *D3D11_INTERNAL_CreateID3D11Shader(
             codeSize,
             NULL,
             (ID3D11VertexShader **)&handle);
-        if (FAILED(res)) {
-            D3D11_INTERNAL_LogError(renderer->device, "Could not create vertex shader", res);
-            return NULL;
-        }
+        CHECK_D3D11_ERROR_AND_RETURN("Could not create vertex shader", NULL)
     } else if (stage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
         res = ID3D11Device_CreatePixelShader(
             renderer->device,
@@ -1533,10 +1480,7 @@ static ID3D11DeviceChild *D3D11_INTERNAL_CreateID3D11Shader(
             codeSize,
             NULL,
             (ID3D11PixelShader **)&handle);
-        if (FAILED(res)) {
-            D3D11_INTERNAL_LogError(renderer->device, "Could not create pixel shader", res);
-            return NULL;
-        }
+        CHECK_D3D11_ERROR_AND_RETURN("Could not create pixel shader", NULL)
     } else if (stage == SDL_GPU_SHADERSTAGE_COMPUTE) {
         res = ID3D11Device_CreateComputeShader(
             renderer->device,
@@ -1544,10 +1488,7 @@ static ID3D11DeviceChild *D3D11_INTERNAL_CreateID3D11Shader(
             codeSize,
             NULL,
             (ID3D11ComputeShader **)&handle);
-        if (FAILED(res)) {
-            D3D11_INTERNAL_LogError(renderer->device, "Could not create compute shader", res);
-            return NULL;
-        }
+        CHECK_D3D11_ERROR_AND_RETURN("Could not create compute shader", NULL)
     }
 
     if (pBytecode != NULL) {
@@ -1576,7 +1517,6 @@ static SDL_GPUComputePipeline *D3D11_CreateComputePipeline(
         NULL,
         NULL);
     if (shader == NULL) {
-        SDL_SetError("Failed to create compute pipeline!");
         return NULL;
     }
 
@@ -1609,6 +1549,10 @@ static SDL_GPUGraphicsPipeline *D3D11_CreateGraphicsPipeline(
         createinfo->target_info.num_color_targets,
         createinfo->target_info.color_target_descriptions);
 
+    if (pipeline->colorTargetBlendState == NULL) {
+        return NULL;
+    }
+
     pipeline->numColorTargets = createinfo->target_info.num_color_targets;
     for (Sint32 i = 0; i < pipeline->numColorTargets; i += 1) {
         pipeline->colorTargetFormats[i] = SDLToD3D11_TextureFormat[createinfo->target_info.color_target_descriptions[i].format];
@@ -1627,6 +1571,10 @@ static SDL_GPUGraphicsPipeline *D3D11_CreateGraphicsPipeline(
         renderer,
         createinfo->depth_stencil_state);
 
+    if (pipeline->depthStencilState == NULL) {
+        return NULL;
+    }
+
     pipeline->hasDepthStencilTarget = createinfo->target_info.has_depth_stencil_target;
     pipeline->depthStencilTargetFormat = SDLToD3D11_TextureFormat[createinfo->target_info.depth_stencil_format];
 
@@ -1637,6 +1585,10 @@ static SDL_GPUGraphicsPipeline *D3D11_CreateGraphicsPipeline(
         renderer,
         createinfo->rasterizer_state);
 
+    if (pipeline->rasterizerState == NULL) {
+        return NULL;
+    }
+
     // Shaders
 
     pipeline->vertexShader = (ID3D11VertexShader *)vertShader->handle;
@@ -1875,7 +1827,7 @@ static SDL_GPUSampler *D3D11_CreateSampler(
         renderer->device,
         &samplerDesc,
         &samplerStateHandle);
-    ERROR_SET_RETURN("Could not create sampler state", NULL);
+    CHECK_D3D11_ERROR_AND_RETURN("Could not create sampler state", NULL);
 
     d3d11Sampler = (D3D11Sampler *)SDL_malloc(sizeof(D3D11Sampler));
     d3d11Sampler->handle = samplerStateHandle;
@@ -1901,7 +1853,7 @@ SDL_GPUShader *D3D11_CreateShader(
         createinfo->entrypoint,
         createinfo->stage == SDL_GPU_SHADERSTAGE_VERTEX ? &bytecode : NULL,
         createinfo->stage == SDL_GPU_SHADERSTAGE_VERTEX ? &bytecodeSize : NULL);
-    if (!handle) {
+    if (handle == NULL) {
         return NULL;
     }
 
@@ -1994,7 +1946,7 @@ static D3D11Texture *D3D11_INTERNAL_CreateTexture(
             &desc2D,
             initialData,
             (ID3D11Texture2D **)&textureHandle);
-        ERROR_L

(Patch may be truncated, please check the link at the top of this post.)