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);