mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-04-21 14:55:39 +00:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -118,6 +118,36 @@ struct SDL_Texture
|
||||
SDL_Texture *next;
|
||||
};
|
||||
|
||||
// Define the GPU render state structure
|
||||
typedef struct SDL_GPURenderStateUniformBuffer
|
||||
{
|
||||
Uint32 slot_index;
|
||||
void *data;
|
||||
Uint32 length;
|
||||
} SDL_GPURenderStateUniformBuffer;
|
||||
|
||||
// Define the GPU render state structure
|
||||
struct SDL_GPURenderState
|
||||
{
|
||||
SDL_Renderer *renderer;
|
||||
|
||||
Uint32 last_command_generation; // last command queue generation this state was in.
|
||||
|
||||
SDL_GPUShader *fragment_shader;
|
||||
|
||||
int num_sampler_bindings;
|
||||
SDL_GPUTextureSamplerBinding *sampler_bindings;
|
||||
|
||||
int num_storage_textures;
|
||||
SDL_GPUTexture **storage_textures;
|
||||
|
||||
int num_storage_buffers;
|
||||
SDL_GPUBuffer **storage_buffers;
|
||||
|
||||
int num_uniform_buffers;
|
||||
SDL_GPURenderStateUniformBuffer *uniform_buffers;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SDL_RENDERCMD_NO_OP,
|
||||
@@ -158,6 +188,7 @@ typedef struct SDL_RenderCommand
|
||||
SDL_Texture *texture;
|
||||
SDL_ScaleMode texture_scale_mode;
|
||||
SDL_TextureAddressMode texture_address_mode;
|
||||
SDL_GPURenderState *gpu_render_state;
|
||||
} draw;
|
||||
struct
|
||||
{
|
||||
@@ -282,6 +313,7 @@ struct SDL_Renderer
|
||||
SDL_FColor color; /**< Color for drawing operations values */
|
||||
SDL_BlendMode blendMode; /**< The drawing blend mode */
|
||||
SDL_TextureAddressMode texture_address_mode;
|
||||
SDL_GPURenderState *gpu_render_state;
|
||||
|
||||
SDL_RenderCommand *render_commands;
|
||||
SDL_RenderCommand *render_commands_tail;
|
||||
|
||||
@@ -27,40 +27,10 @@
|
||||
|
||||
#include "../SDL_sysrender.h"
|
||||
|
||||
struct GPU_PipelineCacheKeyStruct
|
||||
{
|
||||
Uint64 blend_mode : 28;
|
||||
Uint64 frag_shader : 4;
|
||||
Uint64 vert_shader : 4;
|
||||
Uint64 attachment_format : 6;
|
||||
Uint64 primitive_type : 3;
|
||||
};
|
||||
|
||||
typedef union GPU_PipelineCacheKeyConverter
|
||||
{
|
||||
struct GPU_PipelineCacheKeyStruct as_struct;
|
||||
Uint64 as_uint64;
|
||||
} GPU_PipelineCacheKeyConverter;
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(GPU_PipelineCacheKeyConverter_Size, sizeof(GPU_PipelineCacheKeyConverter) <= sizeof(Uint64));
|
||||
|
||||
static Uint32 SDLCALL HashPipelineCacheKey(void *userdata, const void *key)
|
||||
{
|
||||
const GPU_PipelineParameters *params = (const GPU_PipelineParameters *) key;
|
||||
GPU_PipelineCacheKeyConverter cvt;
|
||||
cvt.as_uint64 = 0;
|
||||
cvt.as_struct.blend_mode = params->blend_mode;
|
||||
cvt.as_struct.frag_shader = params->frag_shader;
|
||||
cvt.as_struct.vert_shader = params->vert_shader;
|
||||
cvt.as_struct.attachment_format = params->attachment_format;
|
||||
cvt.as_struct.primitive_type = params->primitive_type;
|
||||
|
||||
// 64-bit uint hash function stolen from taisei (which stole it from somewhere else)
|
||||
Uint64 x = cvt.as_uint64;
|
||||
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
|
||||
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
|
||||
x = x ^ (x >> 31);
|
||||
return (Uint32)(x & 0xffffffff);
|
||||
return SDL_murmur3_32(params, sizeof(*params), 0);
|
||||
}
|
||||
|
||||
static bool SDLCALL MatchPipelineCacheKey(void *userdata, const void *a, const void *b)
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef struct GPU_PipelineParameters
|
||||
GPU_VertexShaderID vert_shader;
|
||||
SDL_GPUTextureFormat attachment_format;
|
||||
SDL_GPUPrimitiveType primitive_type;
|
||||
SDL_GPUShader *custom_frag_shader;
|
||||
} GPU_PipelineParameters;
|
||||
|
||||
typedef struct GPU_PipelineCache
|
||||
|
||||
@@ -541,6 +541,8 @@ static void Draw(
|
||||
}
|
||||
|
||||
SDL_GPURenderPass *pass = data->state.render_pass;
|
||||
SDL_GPURenderState *custom_state = cmd->data.draw.gpu_render_state;
|
||||
SDL_GPUShader *custom_frag_shader = custom_state ? custom_state->fragment_shader : NULL;
|
||||
GPU_VertexShaderID v_shader;
|
||||
GPU_FragmentShaderID f_shader;
|
||||
|
||||
@@ -570,12 +572,18 @@ static void Draw(
|
||||
f_shader = FRAG_SHADER_COLOR;
|
||||
}
|
||||
|
||||
if (custom_frag_shader) {
|
||||
f_shader = FRAG_SHADER_TEXTURE_CUSTOM;
|
||||
data->shaders.frag_shaders[FRAG_SHADER_TEXTURE_CUSTOM] = custom_frag_shader;
|
||||
}
|
||||
|
||||
GPU_PipelineParameters pipe_params;
|
||||
SDL_zero(pipe_params);
|
||||
pipe_params.blend_mode = cmd->data.draw.blend;
|
||||
pipe_params.vert_shader = v_shader;
|
||||
pipe_params.frag_shader = f_shader;
|
||||
pipe_params.primitive_type = prim;
|
||||
pipe_params.custom_frag_shader = custom_frag_shader;
|
||||
|
||||
if (data->state.render_target) {
|
||||
pipe_params.attachment_format = ((GPU_TextureData *)data->state.render_target->internal)->format;
|
||||
@@ -590,15 +598,34 @@ static void Draw(
|
||||
|
||||
SDL_BindGPUGraphicsPipeline(pass, pipe);
|
||||
|
||||
Uint32 sampler_slot = 0;
|
||||
if (cmd->data.draw.texture) {
|
||||
GPU_TextureData *tdata = (GPU_TextureData *)cmd->data.draw.texture->internal;
|
||||
SDL_GPUTextureSamplerBinding sampler_bind;
|
||||
SDL_zero(sampler_bind);
|
||||
sampler_bind.sampler = *SamplerPointer(data, cmd->data.draw.texture_address_mode, cmd->data.draw.texture_scale_mode);
|
||||
sampler_bind.texture = tdata->texture;
|
||||
SDL_BindGPUFragmentSamplers(pass, 0, &sampler_bind, 1);
|
||||
SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1);
|
||||
}
|
||||
if (custom_state) {
|
||||
if (custom_state->num_sampler_bindings > 0) {
|
||||
SDL_BindGPUFragmentSamplers(pass, sampler_slot, custom_state->sampler_bindings, custom_state->num_sampler_bindings);
|
||||
}
|
||||
if (custom_state->num_storage_textures > 0) {
|
||||
SDL_BindGPUFragmentStorageTextures(pass, 0, custom_state->storage_textures, custom_state->num_storage_textures);
|
||||
}
|
||||
if (custom_state->num_storage_buffers > 0) {
|
||||
SDL_BindGPUFragmentStorageBuffers(pass, 0, custom_state->storage_buffers, custom_state->num_storage_buffers);
|
||||
}
|
||||
if (custom_state->num_uniform_buffers > 0) {
|
||||
for (int i = 0; i < custom_state->num_uniform_buffers; i++) {
|
||||
SDL_GPURenderStateUniformBuffer *ub = &custom_state->uniform_buffers[i];
|
||||
SDL_PushGPUFragmentUniformData(data->state.command_buffer, ub->slot_index, ub->data, ub->length);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PushFragmentUniforms(data, cmd);
|
||||
}
|
||||
PushFragmentUniforms(data, cmd);
|
||||
|
||||
SDL_GPUBufferBinding buffer_bind;
|
||||
SDL_zero(buffer_bind);
|
||||
|
||||
@@ -196,6 +196,9 @@ bool GPU_InitShaders(GPU_Shaders *shaders, SDL_GPUDevice *device)
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_arraysize(frag_shader_sources); ++i) {
|
||||
if (i == FRAG_SHADER_TEXTURE_CUSTOM) {
|
||||
continue;
|
||||
}
|
||||
shaders->frag_shaders[i] = CompileShader(
|
||||
&frag_shader_sources[i], device, SDL_GPU_SHADERSTAGE_FRAGMENT);
|
||||
if (shaders->frag_shaders[i] == NULL) {
|
||||
@@ -215,6 +218,9 @@ void GPU_ReleaseShaders(GPU_Shaders *shaders, SDL_GPUDevice *device)
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_arraysize(shaders->frag_shaders); ++i) {
|
||||
if (i == FRAG_SHADER_TEXTURE_CUSTOM) {
|
||||
continue;
|
||||
}
|
||||
SDL_ReleaseGPUShader(device, shaders->frag_shaders[i]);
|
||||
shaders->frag_shaders[i] = NULL;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ typedef enum
|
||||
FRAG_SHADER_TEXTURE_RGBA,
|
||||
FRAG_SHADER_TEXTURE_RGB_PIXELART,
|
||||
FRAG_SHADER_TEXTURE_RGBA_PIXELART,
|
||||
FRAG_SHADER_TEXTURE_CUSTOM,
|
||||
|
||||
NUM_FRAG_SHADERS,
|
||||
} GPU_FragmentShaderID;
|
||||
|
||||
Reference in New Issue
Block a user