diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 0b6e7fbb34..17bed4514d 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -283,6 +283,12 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window * present synchronized with the refresh rate. This property can take any * value that is supported by SDL_SetRenderVSync() for the renderer. * + * With the SDL GPU renderer: + * + * - `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 provide DXIL shaders to SDL_GPURenderState, optional. + * - `SDL_PROP_RENDERER_CREATE_GPU_SHADERS_MSL_BOOLEAN`: the app is able to provide MSL shaders to SDL_GPURenderState, optional. + * * With the vulkan renderer: * * - `SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER`: the VkInstance to use @@ -319,6 +325,9 @@ 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_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" #define SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER "SDL.renderer.create.vulkan.instance" #define SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER "SDL.renderer.create.vulkan.surface" #define SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER "SDL.renderer.create.vulkan.physical_device" @@ -326,6 +335,31 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_ #define SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.create.vulkan.graphics_queue_family_index" #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. + * + * 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. + * + * If no available GPU driver supports any of the specified shader formats, this function will fail. + * + * \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. + * \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. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CreateRendererWithProperties + * \sa SDL_GetGPUShaderFormats + * \sa SDL_CreateGPUShader + * \sa SDL_CreateGPURenderState + * \sa SDL_SetRenderGPUState + */ +extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateGPURenderer(SDL_Window *window, SDL_GPUShaderFormat format_flags, SDL_GPUDevice **device); + /** * Create a 2D software rendering context for a surface. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c53dcd7793..44f9d0e062 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1249,6 +1249,7 @@ SDL3_0.0.0 { SDL_SetRenderTextureAddressMode; SDL_GetRenderTextureAddressMode; SDL_GetGPUDeviceProperties; + SDL_CreateGPURenderer; # 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 6fa3944607..a12f3fbf24 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1274,3 +1274,4 @@ #define SDL_SetRenderTextureAddressMode SDL_SetRenderTextureAddressMode_REAL #define SDL_GetRenderTextureAddressMode SDL_GetRenderTextureAddressMode_REAL #define SDL_GetGPUDeviceProperties SDL_GetGPUDeviceProperties_REAL +#define SDL_CreateGPURenderer SDL_CreateGPURenderer_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 8c1158e186..d7988ac2b0 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1282,3 +1282,4 @@ 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) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 80b0cfd295..3cb5c8d2b8 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -1187,6 +1187,37 @@ 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) +{ + if (!device) { + SDL_InvalidParamError("device"); + return NULL; + } + + *device = NULL; + SDL_Renderer *renderer; + + SDL_PropertiesID props = SDL_CreateProperties(); + 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"); + + 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_Renderer *SDL_CreateSoftwareRenderer(SDL_Surface *surface) { #ifdef SDL_VIDEO_RENDER_SW diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index 35a5e71398..1b4ca96c55 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -1128,21 +1128,26 @@ static void GPU_DestroyRenderer(SDL_Renderer *renderer) } for (Uint32 i = 0; i < SDL_arraysize(data->samplers); ++i) { - SDL_ReleaseGPUSampler(data->device, data->samplers[i]); + if (data->samplers[i]) { + SDL_ReleaseGPUSampler(data->device, data->samplers[i]); + } } if (data->backbuffer.texture) { SDL_ReleaseGPUTexture(data->device, data->backbuffer.texture); } - if (renderer->window) { + if (renderer->window && data->device) { SDL_ReleaseWindowFromGPUDevice(data->device, renderer->window); } ReleaseVertexBuffer(data); GPU_DestroyPipelineCache(&data->pipeline_cache); - GPU_ReleaseShaders(&data->shaders, data->device); - SDL_DestroyGPUDevice(data->device); + + if (data->device) { + GPU_ReleaseShaders(&data->shaders, data->device); + SDL_DestroyGPUDevice(data->device); + } SDL_free(data); } diff --git a/src/render/gpu/SDL_shaders_gpu.c b/src/render/gpu/SDL_shaders_gpu.c index f09a40e468..e4dc5e890b 100644 --- a/src/render/gpu/SDL_shaders_gpu.c +++ b/src/render/gpu/SDL_shaders_gpu.c @@ -244,9 +244,24 @@ SDL_GPUShader *GPU_GetFragmentShader(GPU_Shaders *shaders, GPU_FragmentShaderID void GPU_FillSupportedShaderFormats(SDL_PropertiesID props) { - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, HAVE_SPIRV_SHADERS); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, HAVE_DXIL60_SHADERS); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, HAVE_METAL_SHADERS); + bool custom_shaders = false; + if (SDL_GetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_GPU_SHADERS_SPIRV_BOOLEAN, false)) { + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, HAVE_SPIRV_SHADERS); + custom_shaders = true; + } + if (SDL_GetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_GPU_SHADERS_DXIL_BOOLEAN, false)) { + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, HAVE_DXIL60_SHADERS); + custom_shaders = true; + } + if (SDL_GetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_GPU_SHADERS_MSL_BOOLEAN, false)) { + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, HAVE_METAL_SHADERS); + custom_shaders = true; + } + if (!custom_shaders) { + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, HAVE_SPIRV_SHADERS); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, HAVE_DXIL60_SHADERS); + SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, HAVE_METAL_SHADERS); + } } #endif // SDL_VIDEO_RENDER_GPU diff --git a/test/testgpurender_effects.c b/test/testgpurender_effects.c index 62df028dab..b10700b743 100644 --- a/test/testgpurender_effects.c +++ b/test/testgpurender_effects.c @@ -148,12 +148,6 @@ static bool InitGPURenderState(void) SDL_GPURenderStateDesc desc; int i; - device = (SDL_GPUDevice *)SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL); - if (!device) { - SDL_Log("Couldn't get GPU device"); - return false; - } - formats = SDL_GetGPUShaderFormats(device); if (formats == SDL_GPU_SHADERFORMAT_INVALID) { SDL_Log("Couldn't get supported shader formats: %s", SDL_GetError()); @@ -250,8 +244,8 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) return SDL_APP_FAILURE; } - renderer = SDL_CreateRenderer(window, "gpu"); - if (!renderer) { + renderer = SDL_CreateGPURenderer(window, SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, &device); + if (!renderer || !device) { SDL_Log("Couldn't create renderer: %s", SDL_GetError()); return SDL_APP_FAILURE; }