SDL: gpu: make NULL object releases no-ops

From 7ee8160922a7305e9d14b5c9e62a559e6fc38d08 Mon Sep 17 00:00:00 2001
From: Andrei Sabalenka <[EMAIL REDACTED]>
Date: Mon, 29 Jun 2026 20:28:14 +0300
Subject: [PATCH] gpu: make NULL object releases no-ops

These functions already return immediately for NULL objects in normal builds, but it was done through CHECK_PARAM. When building with SDL_DISABLE_INVALID_PARAMS, these checks were compiled out, and NULL resources could reach backend release functions and cause segfault.

Always return on NULL so API behaves consistently regardless of SDL_DISABLE_INVALID_PARAMS.
---
 include/SDL3/SDL_gpu.h | 24 +++++++++++++++++++++
 src/gpu/SDL_gpu.c      | 48 +++++++++++++++++++++---------------------
 2 files changed, 48 insertions(+), 24 deletions(-)

diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index c4fc914638f8d..447852bff125e 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -3060,6 +3060,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_PopGPUDebugGroup(
  *
  * You must not reference the texture after calling this function.
  *
+ * It is safe to pass NULL for `texture`, in that case this function
+ * is a no-op.
+ *
  * \param device a GPU context.
  * \param texture a texture to be destroyed.
  *
@@ -3074,6 +3077,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUTexture(
  *
  * You must not reference the sampler after calling this function.
  *
+ * It is safe to pass NULL for `sampler`, in that case this function
+ * is a no-op.
+ *
  * \param device a GPU context.
  * \param sampler a sampler to be destroyed.
  *
@@ -3088,6 +3094,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUSampler(
  *
  * You must not reference the buffer after calling this function.
  *
+ * It is safe to pass NULL for `buffer`, in that case this function
+ * is a no-op.
+ *
  * \param device a GPU context.
  * \param buffer a buffer to be destroyed.
  *
@@ -3102,6 +3111,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUBuffer(
  *
  * You must not reference the transfer buffer after calling this function.
  *
+ * It is safe to pass NULL for `transfer_buffer`, in that case this
+ * function is a no-op.
+ *
  * \param device a GPU context.
  * \param transfer_buffer a transfer buffer to be destroyed.
  *
@@ -3116,6 +3128,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUTransferBuffer(
  *
  * You must not reference the compute pipeline after calling this function.
  *
+ * It is safe to pass NULL for `compute_pipeline`, in that case this
+ * function is a no-op.
+ *
  * \param device a GPU context.
  * \param compute_pipeline a compute pipeline to be destroyed.
  *
@@ -3130,6 +3145,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUComputePipeline(
  *
  * You must not reference the shader after calling this function.
  *
+ * It is safe to pass NULL for `shader`, in that case this function
+ * is a no-op.
+ *
  * \param device a GPU context.
  * \param shader a shader to be destroyed.
  *
@@ -3144,6 +3162,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseGPUShader(
  *
  * You must not reference the graphics pipeline after calling this function.
  *
+ * It is safe to pass NULL for `graphics_pipeline`, in that case this
+ * function is a no-op.
+ *
  * \param device a GPU context.
  * \param graphics_pipeline a graphics pipeline to be destroyed.
  *
@@ -4487,6 +4508,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_QueryGPUFence(
  *
  * You must not reference the fence after calling this function.
  *
+ * It is safe to pass NULL for `fence`, in that case this function
+ * is a no-op.
+ *
  * \param device a GPU context.
  * \param fence a fence.
  *
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index e980905b30f63..d326ca1fc7f8d 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -1549,12 +1549,12 @@ void SDL_ReleaseGPUTexture(
     SDL_GPUDevice *device,
     SDL_GPUTexture *texture)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(texture == NULL) {
+    if(texture == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseTexture(
         device->driverData,
         texture);
@@ -1564,12 +1564,12 @@ void SDL_ReleaseGPUSampler(
     SDL_GPUDevice *device,
     SDL_GPUSampler *sampler)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(sampler == NULL) {
+    if(sampler == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseSampler(
         device->driverData,
         sampler);
@@ -1579,12 +1579,12 @@ void SDL_ReleaseGPUBuffer(
     SDL_GPUDevice *device,
     SDL_GPUBuffer *buffer)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(buffer == NULL) {
+    if(buffer == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseBuffer(
         device->driverData,
         buffer);
@@ -1594,12 +1594,12 @@ void SDL_ReleaseGPUTransferBuffer(
     SDL_GPUDevice *device,
     SDL_GPUTransferBuffer *transfer_buffer)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(transfer_buffer == NULL) {
+    if(transfer_buffer == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseTransferBuffer(
         device->driverData,
         transfer_buffer);
@@ -1609,12 +1609,12 @@ void SDL_ReleaseGPUShader(
     SDL_GPUDevice *device,
     SDL_GPUShader *shader)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(shader == NULL) {
+    if(shader == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseShader(
         device->driverData,
         shader);
@@ -1624,12 +1624,12 @@ void SDL_ReleaseGPUComputePipeline(
     SDL_GPUDevice *device,
     SDL_GPUComputePipeline *compute_pipeline)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(compute_pipeline == NULL) {
+    if(compute_pipeline == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseComputePipeline(
         device->driverData,
         compute_pipeline);
@@ -1639,12 +1639,12 @@ void SDL_ReleaseGPUGraphicsPipeline(
     SDL_GPUDevice *device,
     SDL_GPUGraphicsPipeline *graphics_pipeline)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(graphics_pipeline == NULL) {
+    if(graphics_pipeline == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseGraphicsPipeline(
         device->driverData,
         graphics_pipeline);
@@ -3504,12 +3504,12 @@ void SDL_ReleaseGPUFence(
     SDL_GPUDevice *device,
     SDL_GPUFence *fence)
 {
-    CHECK_DEVICE_MAGIC(device, );
-
-    CHECK_PARAM(fence == NULL) {
+    if(fence == NULL) {
         return;
     }
 
+    CHECK_DEVICE_MAGIC(device, );
+
     device->ReleaseFence(
         device->driverData,
         fence);