Files
codegirl-games/.github/issues/enable-sdl-gpu-buffer-cycling.md
T
2026-08-09 08:29:30 +00:00

2.5 KiB

Enable SDL GPU buffer cycling for per-frame sprite uploads

Summary

end_frame overwrites the same transfer buffer and vertex buffer every frame, but both SDL calls currently pass cycle = false:

sdl.MapGPUTransferBuffer(app.device, app.transfer_buffer, false)
sdl.UploadToGPUBuffer(copy_pass, src, dst, false)

SDL documents cycling as the mechanism that rotates to an unbound internal resource when the previous frame still references the current one. Enabling it avoids an unnecessary resource dependency and makes the overwrite pattern explicitly safe.

Evidence

A temporary paired benchmark used:

  • Odin dev-2026-05-nightly:ea5175d with -debug -o:speed
  • SDL 3.4.12 and Vulkan/Lavapipe
  • 128 animated sprites
  • Ten order-alternated samples of 400 frames per mode
Mode Median frame time
Cycling disabled 4.869 ms
Cycling enabled 4.834 ms

Cycling improved median frame time by approximately 0.7%. This is a small performance change, but it also follows SDL's documented resource-reuse model.

Reproduction harness

Run the committed full-frame harness on the baseline commit and candidate commit:

make perf-frame \
	PERF_FRAME_SCENARIO=0 \
	PERF_ODIN_FLAGS="-debug -o:speed"

Keep all PERF_FRAME_* values unchanged. Compare median_ms_per_frame; the harness waits for GPU idle before stopping each trial timer.

Suggested fix

Cycle both resources that are fully overwritten each frame:

map_ptr := sdl.MapGPUTransferBuffer(
	app.device,
	app.transfer_buffer,
	true, // rotate if the previous frame still binds this transfer buffer
)

// Write the complete [0, n * SPRITE_VERTS_SIZE) range, then unmap.
sdl.UnmapGPUTransferBuffer(app.device, app.transfer_buffer)

copy_pass := sdl.BeginGPUCopyPass(cmd)
sdl.UploadToGPUBuffer(
	copy_pass,
	src,
	dst,
	true, // rotate the destination vertex buffer if it is still bound
)
sdl.EndGPUCopyPass(copy_pass)

Cycling makes previous contents undefined, so this remains correct only because the renderer writes the complete vertex range used by the frame before drawing. Do not enable cycling for partial updates that depend on untouched data.

Acceptance criteria

  • Both MapGPUTransferBuffer and UploadToGPUBuffer use cycle = true.
  • The complete submitted vertex range is rewritten every frame.
  • Existing engine tests and examples continue to pass.
  • A full-frame benchmark confirms no regression on a hardware GPU backend.
  • Add a comment explaining why cycling is safe for this full-overwrite path.