Added support for custom shaders with the GPU renderer

Added an example of MSDF font rendering with the SDL 2D renderer
This commit is contained in:
Sam Lantinga 2025-03-13 16:41:58 -07:00
parent eb56c8af85
commit 2aee105b43
19 changed files with 1460 additions and 34 deletions

View file

@ -346,6 +346,16 @@ static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
return true;
}
static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state)
{
SDL_Renderer *renderer = state->renderer;
if (state->last_command_generation == renderer->render_command_generation) {
// the current command queue depends on this state, flush the queue now before it changes
return FlushRenderCommands(renderer);
}
return true;
}
bool SDL_FlushRenderer(SDL_Renderer *renderer)
{
if (!FlushRenderCommands(renderer)) {
@ -577,6 +587,10 @@ static SDL_RenderCommand *PrepQueueCmdDraw(SDL_Renderer *renderer, const SDL_Ren
cmd->data.draw.texture_scale_mode = texture->scaleMode;
}
cmd->data.draw.texture_address_mode = SDL_TEXTURE_ADDRESS_CLAMP;
cmd->data.draw.gpu_render_state = renderer->gpu_render_state;
if (renderer->gpu_render_state) {
renderer->gpu_render_state->last_command_generation = renderer->render_command_generation;
}
}
}
return cmd;
@ -5824,3 +5838,142 @@ bool SDL_GetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode *scale
}
return true;
}
SDL_GPURenderState *SDL_CreateGPURenderState(SDL_Renderer *renderer, SDL_GPURenderStateDesc *desc)
{
CHECK_RENDERER_MAGIC(renderer, false);
if (!desc) {
SDL_InvalidParamError("desc");
return NULL;
}
if (desc->version < sizeof(*desc)) {
// Update this to handle older versions of this interface
SDL_SetError("Invalid desc, should be initialized with SDL_INIT_INTERFACE()");
return NULL;
}
if (!desc->fragment_shader) {
SDL_SetError("desc->fragment_shader is required");
return NULL;
}
SDL_GPUDevice *device = (SDL_GPUDevice *)SDL_GetPointerProperty(renderer->props, SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);
if (!device) {
SDL_SetError("Renderer isn't associated with a GPU device");
return NULL;
}
SDL_GPURenderState *state = (SDL_GPURenderState *)SDL_calloc(1, sizeof(*state));
if (!state) {
return NULL;
}
state->renderer = renderer;
state->fragment_shader = desc->fragment_shader;
if (desc->num_sampler_bindings > 0) {
state->sampler_bindings = (SDL_GPUTextureSamplerBinding *)SDL_calloc(desc->num_sampler_bindings, sizeof(*state->sampler_bindings));
if (!state->sampler_bindings) {
SDL_DestroyGPURenderState(state);
return NULL;
}
SDL_memcpy(state->sampler_bindings, desc->sampler_bindings, desc->num_sampler_bindings * sizeof(*state->sampler_bindings));
state->num_sampler_bindings = desc->num_sampler_bindings;
}
if (desc->num_storage_textures > 0) {
state->storage_textures = (SDL_GPUTexture **)SDL_calloc(desc->num_storage_textures, sizeof(*state->storage_textures));
if (!state->storage_textures) {
SDL_DestroyGPURenderState(state);
return NULL;
}
SDL_memcpy(state->storage_textures, desc->storage_textures, desc->num_storage_textures * sizeof(*state->storage_textures));
state->num_storage_textures = desc->num_storage_textures;
}
if (desc->num_storage_buffers > 0) {
state->storage_buffers = (SDL_GPUBuffer **)SDL_calloc(desc->num_storage_buffers, sizeof(*state->storage_buffers));
if (!state->storage_buffers) {
SDL_DestroyGPURenderState(state);
return NULL;
}
SDL_memcpy(state->storage_buffers, desc->storage_buffers, desc->num_storage_buffers * sizeof(*state->storage_buffers));
state->num_storage_buffers = desc->num_storage_buffers;
}
return state;
}
bool SDL_SetGPURenderStateFragmentUniformData(SDL_GPURenderState *state, Uint32 slot_index, const void *data, Uint32 length)
{
if (!state) {
return SDL_InvalidParamError("state");
}
if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) {
return false;
}
for (int i = 0; i < state->num_uniform_buffers; i++) {
SDL_GPURenderStateUniformBuffer *buffer = &state->uniform_buffers[i];
if (buffer->slot_index == slot_index) {
void *new_data = SDL_realloc(buffer->data, length);
if (!new_data) {
return false;
}
SDL_memcpy(new_data, data, length);
buffer->data = new_data;
buffer->length = length;
return true;
}
}
SDL_GPURenderStateUniformBuffer *buffers = (SDL_GPURenderStateUniformBuffer *)SDL_realloc(state->uniform_buffers, (state->num_uniform_buffers + 1) * sizeof(*state->uniform_buffers));
if (!buffers) {
return false;
}
SDL_GPURenderStateUniformBuffer *buffer = &buffers[state->num_uniform_buffers];
buffer->slot_index = slot_index;
buffer->length = length;
buffer->data = SDL_malloc(length);
if (!buffer->data) {
SDL_free(buffers);
return false;
}
SDL_memcpy(buffer->data, data, length);
state->uniform_buffers = buffers;
++state->num_uniform_buffers;
return true;
}
bool SDL_SetRenderGPUState(SDL_Renderer *renderer, SDL_GPURenderState *state)
{
CHECK_RENDERER_MAGIC(renderer, false);
renderer->gpu_render_state = state;
return true;
}
void SDL_DestroyGPURenderState(SDL_GPURenderState *state)
{
if (!state) {
return;
}
FlushRenderCommandsIfGPURenderStateNeeded(state);
if (state->num_uniform_buffers > 0) {
for (int i = 0; i < state->num_uniform_buffers; i++) {
SDL_free(state->uniform_buffers[i].data);
}
SDL_free(state->uniform_buffers);
}
SDL_free(state->sampler_bindings);
SDL_free(state->storage_textures);
SDL_free(state->storage_buffers);
SDL_free(state);
}