From 941b0a8ea4eb00c066b69fb10f4f79b97db0ef2c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 5 Oct 2025 06:44:15 -0700 Subject: [PATCH] Added support for external GPU textures to the GPU renderer --- include/SDL3/SDL_render.h | 65 ++++++++++++++--------- src/render/gpu/SDL_render_gpu.c | 91 ++++++++++++++++++++++----------- 2 files changed, 103 insertions(+), 53 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 195e580290..015e244cfb 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -765,6 +765,19 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende * VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL associated with the texture, if * you want to wrap an existing texture. * + * With the GPU renderer: + * + * - `SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER`: the SDL_GPUTexture associated with the texture, if you want to wrap an existing texture. + * - `SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_UV_NUMBER`: the SDL_GPUTexture + * associated with the UV plane of an NV12 texture, if you want to wrap an + * existing texture. + * - `SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_U_NUMBER`: the SDL_GPUTexture + * associated with the U plane of a YUV texture, if you want to wrap an + * existing texture. + * - `SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_V_NUMBER`: the SDL_GPUTexture + * associated with the V plane of a YUV texture, if you want to wrap an + * existing texture. + * * \param renderer the rendering context. * \param props the properties to use. * \returns the created texture or NULL on failure; call SDL_GetError() for @@ -783,30 +796,34 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende */ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props); -#define SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER "SDL.texture.create.colorspace" -#define SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER "SDL.texture.create.format" -#define SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER "SDL.texture.create.access" -#define SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER "SDL.texture.create.width" -#define SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER "SDL.texture.create.height" -#define SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER "SDL.texture.create.palette" -#define SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT "SDL.texture.create.SDR_white_point" -#define SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT "SDL.texture.create.HDR_headroom" -#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER "SDL.texture.create.d3d11.texture" -#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER "SDL.texture.create.d3d11.texture_u" -#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER "SDL.texture.create.d3d11.texture_v" -#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_POINTER "SDL.texture.create.d3d12.texture" -#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER "SDL.texture.create.d3d12.texture_u" -#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER "SDL.texture.create.d3d12.texture_v" -#define SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER "SDL.texture.create.metal.pixelbuffer" -#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_NUMBER "SDL.texture.create.opengl.texture" -#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_UV_NUMBER "SDL.texture.create.opengl.texture_uv" -#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER "SDL.texture.create.opengl.texture_u" -#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_V_NUMBER "SDL.texture.create.opengl.texture_v" -#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER "SDL.texture.create.opengles2.texture" -#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_UV_NUMBER "SDL.texture.create.opengles2.texture_uv" -#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER "SDL.texture.create.opengles2.texture_u" -#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER "SDL.texture.create.opengles2.texture_v" -#define SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER "SDL.texture.create.vulkan.texture" +#define SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER "SDL.texture.create.colorspace" +#define SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER "SDL.texture.create.format" +#define SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER "SDL.texture.create.access" +#define SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER "SDL.texture.create.width" +#define SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER "SDL.texture.create.height" +#define SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER "SDL.texture.create.palette" +#define SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT "SDL.texture.create.SDR_white_point" +#define SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT "SDL.texture.create.HDR_headroom" +#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER "SDL.texture.create.d3d11.texture" +#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER "SDL.texture.create.d3d11.texture_u" +#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER "SDL.texture.create.d3d11.texture_v" +#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_POINTER "SDL.texture.create.d3d12.texture" +#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER "SDL.texture.create.d3d12.texture_u" +#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER "SDL.texture.create.d3d12.texture_v" +#define SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER "SDL.texture.create.metal.pixelbuffer" +#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_NUMBER "SDL.texture.create.opengl.texture" +#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_UV_NUMBER "SDL.texture.create.opengl.texture_uv" +#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER "SDL.texture.create.opengl.texture_u" +#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_V_NUMBER "SDL.texture.create.opengl.texture_v" +#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER "SDL.texture.create.opengles2.texture" +#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_UV_NUMBER "SDL.texture.create.opengles2.texture_uv" +#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER "SDL.texture.create.opengles2.texture_u" +#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER "SDL.texture.create.opengles2.texture_v" +#define SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER "SDL.texture.create.vulkan.texture" +#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER "SDL.texture.create.gpu.texture" +#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_UV_POINTER "SDL.texture.create.gpu.texture_uv" +#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_U_POINTER "SDL.texture.create.gpu.texture_u" +#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_V_POINTER "SDL.texture.create.gpu.texture_v" /** * Get the properties associated with a texture. diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index a91ddb8de0..5e0e6a9887 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -133,6 +133,7 @@ typedef struct GPU_PaletteData typedef struct GPU_TextureData { + bool external_texture; SDL_GPUTexture *texture; SDL_GPUTextureFormat format; void *pixels; @@ -142,11 +143,14 @@ typedef struct GPU_TextureData #ifdef SDL_HAVE_YUV // YV12 texture support bool yuv; + bool external_texture_u; + bool external_texture_v; SDL_GPUTexture *textureU; SDL_GPUTexture *textureV; // NV12 texture support bool nv12; + bool external_texture_nv; SDL_GPUTexture *textureNV; #endif } GPU_TextureData; @@ -261,6 +265,12 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ SDL_GPUTextureFormat format; SDL_GPUTextureUsageFlags usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; + data = (GPU_TextureData *)SDL_calloc(1, sizeof(*data)); + if (!data) { + return false; + } + texture->internal = data; + switch (texture->format) { case SDL_PIXELFORMAT_INDEX8: case SDL_PIXELFORMAT_YV12: @@ -292,11 +302,7 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ return SDL_SetError("Texture format %s not supported by SDL_GPU", SDL_GetPixelFormatName(texture->format)); } - - data = (GPU_TextureData *)SDL_calloc(1, sizeof(*data)); - if (!data) { - return false; - } + data->format = format; if (texture->access == SDL_TEXTUREACCESS_STREAMING) { size_t size; @@ -326,7 +332,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; } - texture->internal = data; SDL_GPUTextureCreateInfo tci; SDL_zero(tci); tci.format = format; @@ -336,11 +341,16 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ tci.width = texture->w; tci.height = texture->h; tci.sample_count = SDL_GPU_SAMPLECOUNT_1; + tci.props = create_props; - data->format = format; - data->texture = SDL_CreateGPUTexture(renderdata->device, &tci); - if (!data->texture) { - return false; + data->texture = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER, NULL); + if (data->texture) { + data->external_texture = true; + } else { + data->texture = SDL_CreateGPUTexture(renderdata->device, &tci); + if (!data->texture) { + return false; + } } SDL_PropertiesID props = SDL_GetTextureProperties(texture); @@ -354,15 +364,25 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ tci.width = (tci.width + 1) / 2; tci.height = (tci.height + 1) / 2; - data->textureU = SDL_CreateGPUTexture(renderdata->device, &tci); - if (!data->textureU) { - return false; + data->textureU = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_U_POINTER, NULL); + if (data->textureU) { + data->external_texture_u = true; + } else { + data->textureU = SDL_CreateGPUTexture(renderdata->device, &tci); + if (!data->textureU) { + return false; + } } SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_U_POINTER, data->textureU); - data->textureV = SDL_CreateGPUTexture(renderdata->device, &tci); - if (!data->textureV) { - return false; + data->textureV = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_V_POINTER, NULL); + if (data->textureV) { + data->external_texture_v = true; + } else { + data->textureV = SDL_CreateGPUTexture(renderdata->device, &tci); + if (!data->textureV) { + return false; + } } SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_V_POINTER, data->textureU); @@ -378,17 +398,22 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ data->nv12 = true; - tci.width = ((tci.width + 1) / 2); - tci.height = ((tci.height + 1) / 2); - if (texture->format == SDL_PIXELFORMAT_P010) { - tci.format = SDL_GPU_TEXTUREFORMAT_R16G16_UNORM; + data->textureNV = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_UV_POINTER, NULL); + if (data->textureNV) { + data->external_texture_nv = true; } else { - tci.format = SDL_GPU_TEXTUREFORMAT_R8G8_UNORM; - } + tci.width = ((tci.width + 1) / 2); + tci.height = ((tci.height + 1) / 2); + if (texture->format == SDL_PIXELFORMAT_P010) { + tci.format = SDL_GPU_TEXTUREFORMAT_R16G16_UNORM; + } else { + tci.format = SDL_GPU_TEXTUREFORMAT_R8G8_UNORM; + } - data->textureNV = SDL_CreateGPUTexture(renderdata->device, &tci); - if (!data->textureNV) { - return false; + data->textureNV = SDL_CreateGPUTexture(renderdata->device, &tci); + if (!data->textureNV) { + return false; + } } SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_UV_POINTER, data->textureNV); @@ -1484,11 +1509,19 @@ static void GPU_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) return; } - SDL_ReleaseGPUTexture(renderdata->device, data->texture); + if (!data->external_texture) { + SDL_ReleaseGPUTexture(renderdata->device, data->texture); + } #ifdef SDL_HAVE_YUV - SDL_ReleaseGPUTexture(renderdata->device, data->textureU); - SDL_ReleaseGPUTexture(renderdata->device, data->textureV); - SDL_ReleaseGPUTexture(renderdata->device, data->textureNV); + if (!data->external_texture_u) { + SDL_ReleaseGPUTexture(renderdata->device, data->textureU); + } + if (!data->external_texture_v) { + SDL_ReleaseGPUTexture(renderdata->device, data->textureV); + } + if (!data->external_texture_nv) { + SDL_ReleaseGPUTexture(renderdata->device, data->textureNV); + } #endif SDL_free(data->pixels); SDL_free(data);