From a864dcac25f8d6aa1991a24642ca04d9a90c5fc6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 1 Oct 2025 14:36:56 -0700
Subject: [PATCH] Added support for using the GPU renderer as an offscreen
renderer
SDL_CreateGPURenderer() now allows passing in an existing GPU device and passing in a NULL window to create an offscreen renderer.
Also renamed SDL_SetRenderGPUState() to SDL_SetGPURenderState().
---
include/SDL3/SDL_render.h | 54 +-
src/dynapi/SDL_dynapi.sym | 3 +-
src/dynapi/SDL_dynapi_overrides.h | 3 +-
src/dynapi/SDL_dynapi_procs.h | 5 +-
src/render/SDL_render.c | 55 +-
src/render/gpu/SDL_render_gpu.c | 185 ++-
test/CMakeLists.txt | 2 +-
test/testgpu/build-shaders.sh | 119 +-
test/testgpu/cube.frag.dxil.h | 238 +++
test/testgpu/cube.frag.hlsl | 17 +
test/testgpu/cube.frag.msl.h | 33 +
test/testgpu/cube.frag.spv.h | 34 +
test/testgpu/cube.glsl | 31 -
test/testgpu/cube.hlsl | 31 -
test/testgpu/cube.hlsli | 13 +
test/testgpu/cube.metal | 38 -
test/testgpu/cube.vert.dxil.h | 339 ++++
test/testgpu/cube.vert.hlsl | 15 +
test/testgpu/cube.vert.msl.h | 54 +
test/testgpu/cube.vert.spv.h | 88 +
test/testgpu/overlay.frag.dxil.h | 307 ++++
test/testgpu/overlay.frag.hlsl | 10 +
test/testgpu/overlay.frag.msl.h | 39 +
test/testgpu/overlay.frag.spv.h | 66 +
test/testgpu/overlay.hlsli | 5 +
test/testgpu/overlay.vert.dxil.h | 288 ++++
test/testgpu/overlay.vert.hlsl | 25 +
test/testgpu/overlay.vert.msl.h | 146 ++
test/testgpu/overlay.vert.spv.h | 103 ++
test/testgpu/testgpu_dxil.h | 876 ----------
test/testgpu/testgpu_metallib.h | 2584 -----------------------------
test/testgpu/testgpu_spirv.h | 150 --
test/testgpu_spinning_cube.c | 392 ++++-
test/testgpurender_effects.c | 14 +-
test/testgpurender_msdf.c | 8 +-
test/testsymbols.c | 3 +-
36 files changed, 2391 insertions(+), 3982 deletions(-)
create mode 100644 test/testgpu/cube.frag.dxil.h
create mode 100644 test/testgpu/cube.frag.hlsl
create mode 100644 test/testgpu/cube.frag.msl.h
create mode 100644 test/testgpu/cube.frag.spv.h
delete mode 100644 test/testgpu/cube.glsl
delete mode 100644 test/testgpu/cube.hlsl
create mode 100644 test/testgpu/cube.hlsli
delete mode 100644 test/testgpu/cube.metal
create mode 100644 test/testgpu/cube.vert.dxil.h
create mode 100644 test/testgpu/cube.vert.hlsl
create mode 100644 test/testgpu/cube.vert.msl.h
create mode 100644 test/testgpu/cube.vert.spv.h
create mode 100644 test/testgpu/overlay.frag.dxil.h
create mode 100644 test/testgpu/overlay.frag.hlsl
create mode 100644 test/testgpu/overlay.frag.msl.h
create mode 100644 test/testgpu/overlay.frag.spv.h
create mode 100644 test/testgpu/overlay.hlsli
create mode 100644 test/testgpu/overlay.vert.dxil.h
create mode 100644 test/testgpu/overlay.vert.hlsl
create mode 100644 test/testgpu/overlay.vert.msl.h
create mode 100644 test/testgpu/overlay.vert.spv.h
delete mode 100644 test/testgpu/testgpu_dxil.h
delete mode 100644 test/testgpu/testgpu_metallib.h
delete mode 100644 test/testgpu/testgpu_spirv.h
diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index 6023b40b64b3d..84c6d647718bd 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -74,6 +74,13 @@ extern "C" {
*/
#define SDL_SOFTWARE_RENDERER "software"
+/**
+ * The name of the GPU renderer.
+ *
+ * \since This macro is available since SDL 3.4.0.
+ */
+#define SDL_GPU_RENDERER "gpu"
+
/**
* Vertex structure.
*
@@ -285,6 +292,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window
*
* With the SDL GPU renderer (since SDL 3.4.0):
*
+ * - `SDL_PROP_RENDERER_CREATE_GPU_DEVICE_POINTER`: the device to use with the renderer, optional.
* - `SDL_PROP_RENDERER_CREATE_GPU_SHADERS_SPIRV_BOOLEAN`: the app is able to
* provide SPIR-V shaders to SDL_GPURenderState, optional.
* - `SDL_PROP_RENDERER_CREATE_GPU_SHADERS_DXIL_BOOLEAN`: the app is able to
@@ -328,6 +336,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_
#define SDL_PROP_RENDERER_CREATE_SURFACE_POINTER "SDL.renderer.create.surface"
#define SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER "SDL.renderer.create.output_colorspace"
#define SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER "SDL.renderer.create.present_vsync"
+#define SDL_PROP_RENDERER_CREATE_GPU_DEVICE_POINTER "SDL.renderer.create.gpu.device"
#define SDL_PROP_RENDERER_CREATE_GPU_SHADERS_SPIRV_BOOLEAN "SDL.renderer.create.gpu.shaders_spirv"
#define SDL_PROP_RENDERER_CREATE_GPU_SHADERS_DXIL_BOOLEAN "SDL.renderer.create.gpu.shaders_dxil"
#define SDL_PROP_RENDERER_CREATE_GPU_SHADERS_MSL_BOOLEAN "SDL.renderer.create.gpu.shaders_msl"
@@ -339,35 +348,40 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_
#define SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.create.vulkan.present_queue_family_index"
/**
- * Create a 2D GPU rendering context for a window, with support for the
- * specified shader format.
+ * Create a 2D GPU rendering context.
*
- * This is a convenience function to create a SDL GPU backed renderer,
- * intended to be used with SDL_GPURenderState. The resulting renderer will
- * support shaders in one of the specified shader formats.
+ * The GPU device to use is passed in as a parameter. If this is NULL, then a device will be created normally and can be retrieved using SDL_GetGPURendererDevice().
*
- * If no available GPU driver supports any of the specified shader formats,
- * this function will fail.
+ * The window to use is passed in as a parameter. If this is NULL, then this will become an offscreen renderer. In that case, you should call SDL_SetRenderTarget() to setup rendering to a texture, and then call SDL_RenderPresent() normally to complete drawing a frame.
*
- * \param window the window where rendering is displayed.
- * \param format_flags a bitflag indicating which shader formats the app is
- * able to provide.
- * \param device a pointer filled with the associated GPU device, or NULL on
- * error.
+ * \param device the GPU device to use with the renderer, or NULL to create a device.
+ * \param window the window where rendering is displayed, or NULL to create an offscreen renderer.
* \returns a valid rendering context or NULL if there was an error; call
* SDL_GetError() for more information.
*
- * \threadsafety This function should only be called on the main thread.
+ * \threadsafety If this function is called with a valid GPU device, it should be called on the thread that created the device. If this function is called with a valid window, it should be called on the thread that created the window.
*
* \since This function is available since SDL 3.4.0.
*
* \sa SDL_CreateRendererWithProperties
- * \sa SDL_GetGPUShaderFormats
+ * \sa SDL_GetGPURendererDevice
* \sa SDL_CreateGPUShader
* \sa SDL_CreateGPURenderState
- * \sa SDL_SetRenderGPUState
+ * \sa SDL_SetGPURenderState
*/
-extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateGPURenderer(SDL_Window *window, SDL_GPUShaderFormat format_flags, SDL_GPUDevice **device);
+extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateGPURenderer(SDL_GPUDevice *device, SDL_Window *window);
+
+/**
+ * Return the GPU device used by a renderer.
+ *
+ * \param renderer the rendering context.
+ * \returns the GPU device used by the renderer, or NULL if the renderer is not a GPU renderer; call SDL_GetError() for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_GetGPURendererDevice(SDL_Renderer *renderer);
/**
* Create a 2D software rendering context for a surface.
@@ -382,7 +396,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateGPURenderer(SDL_Window *win
* \returns a valid rendering context or NULL if there was an error; call
* SDL_GetError() for more information.
*
- * \threadsafety This function should only be called on the main thread.
+ * \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.2.0.
*
@@ -2888,7 +2902,7 @@ typedef struct SDL_GPURenderStateCreateInfo
*
* \sa SDL_CreateGPURenderState
* \sa SDL_SetGPURenderStateFragmentUniforms
- * \sa SDL_SetRenderGPUState
+ * \sa SDL_SetGPURenderState
* \sa SDL_DestroyGPURenderState
*/
typedef struct SDL_GPURenderState SDL_GPURenderState;
@@ -2907,7 +2921,7 @@ typedef struct SDL_GPURenderState SDL_GPURenderState;
* \since This function is available since SDL 3.4.0.
*
* \sa SDL_SetGPURenderStateFragmentUniforms
- * \sa SDL_SetRenderGPUState
+ * \sa SDL_SetGPURenderState
* \sa SDL_DestroyGPURenderState
*/
extern SDL_DECLSPEC SDL_GPURenderState * SDLCALL SDL_CreateGPURenderState(SDL_Renderer *renderer, SDL_GPURenderStateCreateInfo *createinfo);
@@ -2948,7 +2962,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPURenderStateFragmentUniforms(SDL_GPURe
*
* \since This function is available since SDL 3.4.0.
*/
-extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderGPUState(SDL_Renderer *renderer, SDL_GPURenderState *state);
+extern SDL_DECLSPEC bool SDLCALL SDL_SetGPURenderState(SDL_Renderer *renderer, SDL_GPURenderState *state);
/**
* Destroy custom GPU render state.
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 99dd2411f5392..967a424970b79 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1240,7 +1240,7 @@ SDL3_0.0.0 {
SDL_GetDefaultTextureScaleMode;
SDL_CreateGPURenderState;
SDL_SetGPURenderStateFragmentUniforms;
- SDL_SetRenderGPUState;
+ SDL_SetGPURenderState;
SDL_DestroyGPURenderState;
SDL_SetWindowProgressState;
SDL_SetWindowProgressValue;
@@ -1259,6 +1259,7 @@ SDL3_0.0.0 {
SDL_GetGPUTextureFormatFromPixelFormat;
SDL_SetTexturePalette;
SDL_GetTexturePalette;
+ SDL_GetGPURendererDevice;
# 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 2785c06441567..b0693c5471d73 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1265,7 +1265,7 @@
#define SDL_GetDefaultTextureScaleMode SDL_GetDefaultTextureScaleMode_REAL
#define SDL_CreateGPURenderState SDL_CreateGPURenderState_REAL
#define SDL_SetGPURenderStateFragmentUniforms SDL_SetGPURenderStateFragmentUniforms_REAL
-#define SDL_SetRenderGPUState SDL_SetRenderGPUState_REAL
+#define SDL_SetGPURenderState SDL_SetGPURenderState_REAL
#define SDL_DestroyGPURenderState SDL_DestroyGPURenderState_REAL
#define SDL_SetWindowProgressState SDL_SetWindowProgressState_REAL
#define SDL_SetWindowProgressValue SDL_SetWindowProgressValue_REAL
@@ -1285,3 +1285,4 @@
#define JNI_OnLoad JNI_OnLoad_REAL
#define SDL_SetTexturePalette SDL_SetTexturePalette_REAL
#define SDL_GetTexturePalette SDL_GetTexturePalette_REAL
+#define SDL_GetGPURendererDevice SDL_GetGPURendererDevice_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index bf8bb35029c27..18195db8a8f63 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1273,7 +1273,7 @@ SDL_DYNAPI_PROC(bool,SDL_SetDefaultTextureScaleMode,(SDL_Renderer *a,SDL_ScaleMo
SDL_DYNAPI_PROC(bool,SDL_GetDefaultTextureScaleMode,(SDL_Renderer *a,SDL_ScaleMode *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_GPURenderState*,SDL_CreateGPURenderState,(SDL_Renderer *a,SDL_GPURenderStateCreateInfo *b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateFragmentUniforms,(SDL_GPURenderState *a,Uint32 b,const void *c,Uint32 d),(a,b,c,d),return)
-SDL_DYNAPI_PROC(bool,SDL_SetRenderGPUState,(SDL_Renderer *a,SDL_GPURenderState *b),(a,b),return)
+SDL_DYNAPI_PROC(bool,SDL_SetGPURenderState,(SDL_Renderer *a,SDL_GPURenderState *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_DestroyGPURenderState,(SDL_GPURenderState *a),(a),)
SDL_DYNAPI_PROC(bool,SDL_SetWindowProgressState,(SDL_Window *a,SDL_ProgressState b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_SetWindowProgressValue,(SDL_Window *a,float b),(a,b),return)
@@ -1282,7 +1282,7 @@ SDL_DYNAPI_PROC(float,SDL_GetWindowProgressValue,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(bool,SDL_SetRenderTextureAddressMode,(SDL_Renderer *a,SDL_TextureAddressMode b,SDL_TextureAddressMode c),(a,b,c),return)
SDL_DYNAPI_PROC(bool,SDL_GetRenderTextureAddressMode,(SDL_Renderer *a,SDL_TextureAddressMode *b,SDL_TextureAddressMode *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetGPUDeviceProperties,(SDL_GPUDevice *a),(a),return)
-SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateGPURenderer,(SDL_Window *a,SDL_GPUShaderFormat b,SDL_GPUDevice **c),(a,b,c),return)
+SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateGPURenderer,(SDL_GPUDevice *a,SDL_Window *b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_PutAudioStreamPlanarData,(SDL_AudioStream *a,const void * const*b,int c,int d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_GetEventDescription,(const SDL_Event *a,char *b,int c),(a,b,c),return)
SDL_DYNAPI_PROC(bool,SDL_PutAudioStreamDataNoCopy,(SDL_AudioStream *a,const void *b,int c,SDL_AudioStreamDataCompleteCallback d,void *e),(a,b,c,d,e),return)
@@ -1293,3 +1293,4 @@ SDL_DYNAPI_PROC(SDL_GPUTextureFormat,SDL_GetGPUTextureFormatFromPixelFormat,(SDL
SDL_DYNAPI_PROC(Sint32,JNI_OnLoad,(JavaVM *a, void *b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_SetTexturePalette,(SDL_Texture *a,SDL_Palette *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_Palette*,SDL_GetTexturePalette,(SDL_Texture *a),(a),return)
+SDL_DYNAPI_PROC(SDL_GPUDevice*,SDL_GetGPURendererDevice,(SDL_Renderer *a),(a),return)
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index ebe5fd4008bbf..513076f1aeb9c 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -1047,11 +1047,17 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
const char *hint;
SDL_PropertiesID new_props;
- CHECK_PARAM((!window && !surface) || (window && surface)) {
+ // The GPU renderer is the only one that can be created without a window or surface
+ CHECK_PARAM(!window && !surface && (!driver_name || SDL_strcmp(driver_name, SDL_GPU_RENDERER) != 0)) {
SDL_InvalidParamError("window");
return NULL;
}
+ CHECK_PARAM(window && surface) {
+ SDL_SetError("A renderer can't target both a window and surface");
+ return NULL;
+ }
+
CHECK_PARAM(window && SDL_WindowHasSurface(window)) {
SDL_SetError("Surface already associated with window");
return NULL;
@@ -1269,37 +1275,32 @@ SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name)
return renderer;
}
-SDL_Renderer *SDL_CreateGPURenderer(SDL_Window *window, SDL_GPUShaderFormat format_flags, SDL_GPUDevice **device)
+SDL_Renderer *SDL_CreateGPURenderer(SDL_GPUDevice *device, SDL_Window *window)
{
- CHECK_PARAM(!device) {
- SDL_InvalidParamError("device");
- return NULL;
- }
-
- *device = NULL;
SDL_Renderer *renderer;
SDL_PropertiesID props = SDL_CreateProperties();
+ SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_GPU_DEVICE_POINTER, device);
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
- if (format_flags & SDL_GPU_SHADERFORMAT_SPIRV) {
- SDL_SetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_GPU_SHADERS_SPIRV_BOOLEAN, true);
- }
- if (format_flags & SDL_GPU_SHADERFORMAT_DXIL) {
- SDL_SetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_GPU_SHADERS_DXIL_BOOLEAN, true);
- }
- if (format_flags & SDL_GPU_SHADERFORMAT_MSL) {
- SDL_SetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_GPU_SHADERS_MSL_BOOLEAN, true);
- }
- SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, "gpu");
+ SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, SDL_GPU_RENDERER);
renderer = SDL_CreateRendererWithProperties(props);
- if (renderer) {
- *device = (SDL_GPUDevice *)SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);
- }
SDL_DestroyProperties(props);
return renderer;
}
+SDL_GPUDevice *SDL_GetGPURendererDevice(SDL_Renderer *renderer)
+{
+ CHECK_RENDERER_MAGIC(renderer, NULL);
+
+ SDL_GPUDevice *device = SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);
+ if (!device) {
+ SDL_SetError("Renderer isn't a GPU renderer");
+ return NULL;
+ }
+ return device;
+}
+
SDL_Renderer *SDL_CreateSoftwareRenderer(SDL_Surface *surface)
{
#ifdef SDL_VIDEO_RENDER_SW
@@ -1365,8 +1366,8 @@ bool SDL_GetRenderOutputSize(SDL_Renderer *renderer, int *w, int *h)
} else if (renderer->window) {
return SDL_GetWindowSizeInPixels(renderer->window, w, h);
} else {
- SDL_assert(!"This should never happen");
- return SDL_SetError("Renderer doesn't support querying output size");
+ // We don't have any output size, this might be an offscreen-only renderer
+ return true;
}
}
@@ -5548,7 +5549,11 @@ bool SDL_RenderPresent(SDL_Renderer *renderer)
CHECK_RENDERER_MAGIC(renderer, false);
CHECK_PARAM(renderer->target) {
- return SDL_SetError("You can't present on a render target");
+ if (!renderer->window && SDL_strcmp(renderer->name, SDL_GPU_RENDERER) == 0) {
+ // We're an offscreen renderer, we must submit the command queue
+ } else {
+ return SDL_SetError("You can't present on a render target");
+ }
}
if (renderer->transparent_window) {
@@ -6218,7 +6223,7 @@ bool SDL_SetGPURenderStateFragmentUniforms(SDL_GPURenderState *state, Uint32 slo
return true;
}
-bool SDL_SetRenderGPUState(SDL_Renderer *renderer, SDL_GPURenderState *state)
+bool SDL_SetGPURenderState(SDL_Renderer *renderer, SDL_GPURenderState *state)
{
CHECK_RENDERER_MAGIC(renderer, false);
diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c
index 7fe9848df8078..1d13cf016d54a 100644
--- a/src/render/gpu/SDL_render_gpu.c
+++ b/src/render/gpu/SDL_render_gpu.c
@@ -83,6 +83,7 @@ static const float INPUTTYPE_HDR10 = 3;
typedef struct GPU_RenderData
{
+ bool external_device;
SDL_GPUDevice *device;
GPU_Shaders shaders;
GPU_PipelineCache pipeline_cache;
@@ -1415,34 +1416,37 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
{
GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
- SDL_GPUTexture *swapchain;
- Uint32 swapchain_texture_width, swapchain_texture_height;
- bool result = SDL_WaitAndAcquireGPUSwapchainTexture(data->state.command_buffer, renderer->window, &swapchain, &swapchain_texture_width, &swapchain_texture_height);
+ if (renderer->window) {
+ SDL_GPUTexture *swapchain;
+ Uint32 swapchain_texture_width, swapchain_texture_height;
+ bool result = SDL_WaitAndAcquireGPUSwapchainTexture(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());
- }
+ if (!result) {
+ SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to acquire swapchain texture: %s", SDL_GetError());
+ }
- if (swapchain != NULL) {
- SDL_GPUBlitInfo blit_info;
- SDL_zero(blit_info);
+ if (swapchain != NULL) {
+ SDL_GPUBlitInfo blit_info;
+ SDL_zero(blit_info);
- blit_info.source.texture = data->backbuffer.texture;
- blit_info.source.w = data->backbuffer.width;
- blit_info.source.h = data->backbuffer.height;
- blit_info.destination.texture = swapchain;
- 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;
+ blit_info.source.texture = data->backbuffer.texture;
+ blit_info.source.w = data->backbuffer.width;
+ blit_info.source.h = data->backbuffer.height;
+ blit_info.destination.texture = swapchain;
+ 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);
+ SDL_BlitGPUTexture(data->state.command_buffer, &blit_info);
- SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
+ SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
- if (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));
+ if (swapchain_texture_width != data->backbuffer.width || swapchain_texture_height != data->backbuffer.height) {
+ CreateBackbuffer(data, swapchain_texture_width, swapchain_texture_height, SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window));
+ }
+ } else {
+ SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
}
} else {
SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
@@ -1486,7 +1490,7 @@ static void GPU_DestroyRenderer(SDL_Renderer *renderer)
}
if (data->state.command_buffer) {
- SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
+ SDL_CancelGPUCommandBuffer(data->state.command_buffer);
data->state.command_buffer = NULL;
}
@@ -1509,7 +1513,9 @@ static void GPU_DestroyRenderer(SDL_Renderer *renderer)
if (data->device) {
GPU_ReleaseShaders(&data->shaders, data->device);
- SDL_DestroyGPUDevice(data->device);
+ if (!data->external_device) {
+ SDL_DestroyGPUDevice(data->device);
+ }
}
SDL_free(data);
@@ -1551,6 +1557,14 @@ static bool GPU_SetVSync(SDL_Renderer *renderer, const int vsync)
GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
SDL_GPUPresentMode mode = SDL_GPU_PRESENTMODE_VSYNC;
+ if (!renderer->window) {
+ if (!vsync) {
+ return true;
+ } else {
+ return SDL_Unsupported();
+ }
+ }
+
if (!ChoosePresentMode(data->device, renderer->window, vsync, &mode)) {
return false;
}
@@ -1575,8 +1589,8 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
SDL_SetupRendererColorspace(renderer, create_props);
if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
- renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR &&
- renderer->output_colorspace != SDL_COLORSPACE_HDR10) {
+ renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
+ /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) {
return SDL_SetError("Unsupported output colorspace");
}
@@ -1614,40 +1628,45 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
renderer->window = window;
renderer->name = GPU_RenderDriver.name;
- bool debug = SDL_GetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, false);
- bool lowpower = SDL_GetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, false);
+ data->device = SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_GPU_DEVICE_POINTER, NULL);
+ if (data->device) {
+ data->external_device = true;
+ } else {
+ bool debug = SDL_GetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, false);
+ bool lowpower = SDL_GetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, false);
- // Prefer environment variables/hints if they exist, otherwise defer to properties
- debug = SDL_GetHintBoolean(SDL_HINT_RENDER_GPU_DEBUG, debug);
- lowpower = SDL_GetHintBoolean(SDL_HINT_RENDER_GPU_LOW_POWER, lowpower);
+ // Prefer environment variables/hints if they exist, otherwise defer to properties
+ debug = SDL_GetHintBoolean(SDL_HINT_RENDER_GPU_DEBUG, debug);
+ lowpower = SDL_GetHintBoolean(SDL_HINT_RENDER_GPU_LOW_POWER, lowpower);
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, debug);
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, lowpower);
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, debug);
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, lowpower);
- // Set hints for the greatest hardware compatibility
- // This property allows using the renderer on Intel Haswell and Broadwell GPUs.
- if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN)) {
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN, true);
- }
- // These properties allow using the renderer on more Android devices.
- if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN)) {
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN, false);
- }
- if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN)) {
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN, false);
- }
- if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN)) {
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN, false);
- }
- if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN)) {
- SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN, false);
- }
+ // Set hints for the greatest hardware compatibility
+ // This property allows using the renderer on Intel Haswell and Broadwell GPUs.
+ if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN)) {
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN, true);
+ }
+ // These properties allow using the renderer on more Android devices.
+ if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN)) {
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN, false);
+ }
+ if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN)) {
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN, false);
+ }
+ if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN)) {
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN, false);
+ }
+ if (!SDL_HasProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN)) {
+ SDL_SetBooleanProperty(create_props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN, false);
+ }
- GPU_FillSupportedShaderFormats(create_props);
- data->device = SDL_CreateGPUDeviceWithProperties(create_props);
+ GPU_FillSupportedShaderFormats(create_props);
+ data->device = SDL_CreateGPUDeviceWithProperties(create_props);
- if (!data->device) {
- return false;
+ if (!data->device) {
+ return false;
+ }
}
if (!GPU_InitShaders(&data->shaders, data->device)) {
@@ -1663,30 +1682,39 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
return false;
}
- if (!SDL_ClaimWindowForGPUDevice(data->device, window)) {
- return false;
- }
+ if (window) {
+ if (!SDL_ClaimWindowForGPUDevice(data->device, window)) {
+ return false;
+ }
- switch (renderer->output_colorspace) {
- case SDL_COLORSPACE_SRGB_LINEAR:
- data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR;
- break;
- case SDL_COLORSPACE_HDR10:
- data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084;
- break;
- case SDL_COLORSPACE_SRGB:
- default:
- data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
- break;
- }
- data->swapchain.present_mode = SDL_GPU_PRESENTMODE_VSYNC;
+ switch (renderer->output_colorspace) {
+ case SDL_COLORSPACE_SRGB_LINEAR:
+ data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR;
+ break;
+ case SDL_COLORSPACE_HDR10:
+ data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084;
+ break;
+ case SDL_COLORSPACE_SRGB:
+ default:
+ data->swapchain.composition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
+ break;
+ }
+ data->swapchain.present_mode = SDL_GPU_PRESENTMODE_VSYNC;
- int vsync = (int)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0);
- ChoosePresentMode(data->device, window, vsync, &data->swapchain.present_mode);
+ int vsync = (int)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0);
+ ChoosePresentMode(data->device, window, vsync, &data->swapchain.present_mode);
- SDL_SetGPUSwapchainParameters(data->device, window, data->swapchain.composition, data->swapchain.present_mode);
+ SDL_SetGPUSwapchainParameters(data->device, window, data->swapchain.composition, data->swapchain.present_mode);
- SDL_SetGPUAllowedFramesInFlight(data->device, 1);
+ SDL_SetGPUAllowedFramesInFlight(data->device, 1);
+
+ int w, h;
+ SDL_GetWindowSizeInPixels(window, &w, &h);
+
+ if (!CreateBackbuffer(data, w, h, SDL_GetGPUSwapchainTextureFormat(data->device, window))) {
+ return false;
+ }
+ }
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA32);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA32);
@@ -1711,13 +1739,6 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
data->state.viewport.max_depth = 1;
data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device);
- int w, h;
- SDL_GetWindowSizeInPixels(window, &w, &h);
-
- if (
(Patch may be truncated, please check the link at the top of this post.)