Files
dusk/.claude/display-spritebatch.md
T
2026-06-16 12:29:36 -05:00

3.6 KiB

Display -- SpriteBatch

Source: src/dusk/display/spritebatch/

See also: .claude/display-mesh.md, .claude/display-texture.md


Overview

The SpriteBatch is the primary 2D rendering primitive. It accumulates axis-aligned quads (sprites) into a shared vertex buffer and draws them in batches. All 2D rendering in the engine -- UI frames, text, tilemaps, HUD -- goes through the global SPRITEBATCH.

The batch flushes automatically when the per-flush limit is reached, or explicitly via spriteBatchFlush().


Limits

Constant Value Meaning
SPRITEBATCH_SPRITES_MAX 512 Total sprites in the vertex buffer
SPRITEBATCH_FLUSH_COUNT 16 Number of auto-flush segments
SPRITEBATCH_SPRITES_MAX_PER_FLUSH 32 Sprites per auto-flush segment
SPRITEBATCH_VERTEX_COUNT 3072 Total vertices (512 * QUAD_VERTEX_COUNT)

Sprite structure

typedef struct {
  vec3 min;    // minimum XYZ corner of the quad in world/screen space
  vec3 max;    // maximum XYZ corner of the quad in world/screen space
  vec2 uvMin;  // minimum UV (top-left in [0,1] texture space)
  vec2 uvMax;  // maximum UV (bottom-right in [0,1] texture space)
} spritebatchsprite_t;

Z in min and max controls draw depth (further from camera = higher Z in a typical orthographic setup). For flat 2D, set min.z = max.z = 0.


SpriteBatch struct

typedef struct {
  mesh_t mesh;
  int32_t spriteCount;
  int32_t spriteFlush;
  shader_t *shader;
  shadermaterial_t material;
} spritebatch_t;

extern spritebatch_t SPRITEBATCH;
extern meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];

SPRITEBATCH_VERTICES is a separate global (not embedded in the struct) for platform alignment requirements.


API

errorret_t spriteBatchInit();
errorret_t spriteBatchDispose();

// Clear the buffer and reset state. Call before starting a new batch.
void spriteBatchClear();

// Append sprites to the buffer. Flushes automatically when the per-flush
// segment fills. shader + material are used on the next flush.
errorret_t spriteBatchBuffer(
  const spritebatchsprite_t *sprites,
  const uint32_t count,
  shader_t *shader,
  const shadermaterial_t material
);

// Upload and draw all buffered sprites. Binds shader and applies
// material if set. No-op if the buffer is empty.
errorret_t spriteBatchFlush();

Typical usage

// Beginning of a 2D render pass:
spriteBatchClear();

// Build sprites (e.g. via tilesetTileGetUV, then fill spritebatchsprite_t):
spritebatchsprite_t s;
glm_vec3_copy((vec3){ x, y, 0 }, s.min);
glm_vec3_copy((vec3){ x + w, y + h, 0 }, s.max);
glm_vec2_copy(uvMin, s.uvMin);
glm_vec2_copy(uvMax, s.uvMax);

shadermaterial_t mat = { .unlit = { .texture = myTexture } };
spriteBatchBuffer(&s, 1, myShader, mat);

// End of pass -- flush remaining sprites:
spriteBatchFlush();

Relationship to other systems

  • Text rendering (textDraw) internally calls spriteBatchBuffer for each glyph and requires a final spriteBatchFlush() after drawing.
  • UI frames (uiFrameDraw) push 9 quads to the batch without flushing -- the caller or uitextboxDraw is responsible for the flush.
  • ECS renderables of type ENTITY_RENDERABLE_TYPE_SPRITEBATCH are drawn via the spritebatch in the scene render pipeline.

Notes

  • spriteBatchBuffer changes the batch's shader and material fields. If you mix different shaders or textures in one batch, add an explicit spriteBatchFlush() call between groups to avoid draws with the wrong material.
  • The vertex buffer is a static global -- SPRITEBATCH_VERTICES must not be written from multiple threads.