I am putting a game together using SDL3 as it looks excellent; well done to everyone involved.
I have a strange issue when using more than one texture. For example this code:
Transform transform;
transform.set_position(vec2(-2.0f, 0.0f));
transform.set_origin(_cog_sprite->origin());
_draw_list.push_back(std::move(DrawEntry::make(_cog_sprite, transform, 1)));
transform.set_position(vec2(2.0f, 0.0f));
transform.set_origin(_cog_sprite->origin());
_draw_list.push_back(std::move(DrawEntry::make(_cog_sprite, transform, -10)));
works as expected:
However when I use two different sprites, with different textures, then both sprites appear to use the same model
matrix:
Transform transform;
transform.set_position(vec2(-2.0f, 0.0f));
transform.set_origin(_cog_sprite->origin());
_draw_list.push_back(std::move(DrawEntry::make(_cog_sprite, transform, 1)));
transform.set_position(vec2(2.0f, 0.0f));
transform.set_origin(_house_sprite->origin());
_draw_list.push_back(std::move(DrawEntry::make(_house_sprite, transform, -10)));
My shaders use an array of sprite uniforms, which just contain the model matrix, and the index into the sprite uniform is passed as a vertex attribute:
cbuffer u0 : register(b0, space1)
{
float4x4 view_proj;
};
StructuredBuffer<float4x4> sprite_uniforms : register(t0, space0);
SpriteFragIn main(SpriteVertIn input)
{
SpriteFragIn output;
float4x4 model = sprite_uniforms[input.uniform_index];
output.position = mul(view_proj, mul(model, float4(input.position, 0.0f, 1.0f)));
output.colour = input.colour;
output.tex_coord = input.tex_coord;
output.render_flags = input.render_flags;
return output;
}
At the moment I am using a single render pass to draw all sprites, and once that is complete I am uploading the vertex, index and uniform buffers in a copy pass.
void SpriteRenderer::frame_start(SDL_GPUCommandBuffer* command_buffer,
SDL_GPUTexture* target_texture,
Camera const& camera,
size_t frame) {
SDL_assert(_render_pass == nullptr);
_uniform_buffers.reset(frame);
_vertex_buffers.reset(frame);
_index_buffers.reset(frame);
_num_draw_calls = 0;
// Set the global uniform buffer
SDL_PushGPUVertexUniformData(command_buffer, 0, &camera.view_proj_matrix(), sizeof(mat4));
SDL_GPUColorTargetInfo const color_target_info = {
.texture = target_texture,
.clear_color = SDL_FColor{ 0.0f, 0.5f, 0.0f, 1.0f },
.load_op = SDL_GPU_LOADOP_CLEAR,
.store_op = SDL_GPU_STOREOP_STORE,
};
_render_pass = SDL_BeginGPURenderPass(command_buffer, &color_target_info, 1, nullptr);
SDL_BindGPUGraphicsPipeline(_render_pass, _pipeline.pipeline());
auto const viewport = camera.viewport();
SDL_SetGPUViewport(_render_pass, &viewport);
SDL_GPUBuffer* vertex_storage_buffer = _uniform_buffers.buffer();
SDL_BindGPUVertexStorageBuffers(_render_pass, 0, &vertex_storage_buffer, 1);
SDL_GPUBufferBinding const vertex_buffer_binding = {
.buffer = _vertex_buffers.buffer(),
.offset = 0,
};
SDL_BindGPUVertexBuffers(_render_pass, 0, &vertex_buffer_binding, 1);
SDL_GPUBufferBinding const index_buffer_binding = {
.buffer = _index_buffers.buffer(),
.offset = 0,
};
SDL_BindGPUIndexBuffer(_render_pass, &index_buffer_binding, SDL_GPU_INDEXELEMENTSIZE_32BIT);
}
Uint32 SpriteRenderer::draw(DrawListIterator begin, DrawListIterator end) {
SDL_assert(_render_pass != nullptr);
...
SDL_GPUTextureSamplerBinding const fragment_sampler_binding = {
.texture = texture,
.sampler = g_app_state.game->samplers().linear_clamp(),
};
SDL_BindGPUFragmentSamplers(_render_pass, 0, &fragment_sampler_binding, 1);
SDL_DrawGPUIndexedPrimitives(_render_pass, num_sprites * 6, 1, 0, 0, 0);
}
bool SpriteRenderer::frame_end(SDL_GPUCommandBuffer* command_buffer) {
SDL_assert(_render_pass != nullptr);
SDL_EndGPURenderPass(_render_pass);
_render_pass = nullptr;
auto* copy_pass = SDL_BeginGPUCopyPass(command_buffer);
if (!_uniform_buffers.upload(copy_pass)) {
log_error("Failed to upload sprite uniform buffer");
return false;
}
if (!_vertex_buffers.upload(copy_pass)) {
log_error("Failed to upload sprite vertex buffer");
return false;
}
if (!_index_buffers.upload(copy_pass)) {
log_error("Failed to upload sprite index buffer");
return false;
}
SDL_EndGPUCopyPass(copy_pass);
g_app_state.game->timer().profile_draws_add(_num_draw_calls);
return true;
}
I have debugged the GPU buffer content using the Xcode Metal Debugger and they all appear to be correct.
Vertex buffer:
Index buffer:
Uniform buffer:
Can anyone suggest why the wrong uniform is being used when the texture changes?