Skip to content

[Batch 5] Chunk block data GPU storage buffer #389

@MichaelFisher1997

Description

@MichaelFisher1997

Summary

Upload chunk block data (1 byte per block) to a GPU storage buffer so that a compute meshing shader can read blocks directly. This is the foundation for GPU-driven mesh generation.

Depends on: #384 (transfer queue for uploading block data efficiently)

Current State

Block data lives only in CPU memory (Chunk.blocks: [16][256][16]BlockType). Meshing reads blocks on CPU worker threads, produces vertex arrays, uploads vertices to GPU. The GPU never sees raw block data.

Target State

A GPU-visible storage buffer holds block data for all loaded chunks. Layout:

Buffer: [chunk_0_blocks | chunk_1_blocks | ... | chunk_N_blocks]
Each chunk: 16 × 256 × 16 = 65536 bytes (BlockType is u8)
Total: 16384 chunks × 64KB = 1GB max

A compute meshing shader (future issue) can read blocks from this buffer and output vertices directly on the GPU — no CPU meshing, no vertex upload.

Implementation Plan

Step 1: Block data buffer management

const GpuBlockBuffer = struct {
    buffer: BufferHandle,
    capacity: usize,            // max chunks
    slot_size: usize,           // 65536 bytes per chunk
    allocator: FreeListAllocator,  // which slots are in use
    
    pub fn allocate(self: *GpuBlockBuffer, chunk_index: u32) !usize;
    pub fn free(self: *GpuBlockBuffer, slot: usize) void;
    pub fn upload(self: *GpuBlockBuffer, slot: usize, blocks: []const u8) !void;
};

Step 2: Chunk index mapping

  • Need a mapping: chunk coordinate (cx, cz) → buffer slot index
  • Could use the same index as ChunkStorage HashMap (iterate and assign)
  • Or: sparse mapping with a lookup texture/buffer

Step 3: Upload integration

Step 4: Lighting data (optional, same buffer or separate)

  • sky_light and block_light could go in the same buffer (appended after blocks)
  • Or a separate buffer for light data
  • Keep it simple for this issue: blocks only, light stays on CPU for now

Step 5: Integration with chunk lifecycle

  • WorldStreamer: after chunk generates, call gpu_block_buffer.upload()
  • WorldStreamer: when chunk unloads, call gpu_block_buffer.free()
  • Player modifies block: update both CPU chunk and GPU buffer (small sub-region upload)

Files to Create

  • src/world/gpu_block_buffer.zig — GPU block data buffer management

Files to Modify

  • src/world/world_renderer.zig — own and manage GpuBlockBuffer
  • src/world/world_streamer.zig — upload block data on generate, free on unload
  • src/game/player.zig — update GPU buffer on block break/place

Testing

  • Block data uploads for all chunks within render distance
  • Slots freed correctly on chunk unload
  • Block modifications reflected in GPU buffer (verify by reading back)
  • Memory bounded: doesn't exceed max allocation
  • No performance regression from upload overhead
  • Works with 0 chunks, 1 chunk, 16384 chunks

Roadmap: docs/PERFORMANCE_ROADMAP.md — Batch 5, Issue 4A-1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions