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
Roadmap: docs/PERFORMANCE_ROADMAP.md — Batch 5, Issue 4A-1
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:
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
Step 2: Chunk index mapping
(cx, cz)→ buffer slot indexChunkStorageHashMap (iterate and assign)Step 3: Upload integration
Step 4: Lighting data (optional, same buffer or separate)
sky_lightandblock_lightcould go in the same buffer (appended after blocks)Step 5: Integration with chunk lifecycle
WorldStreamer: after chunk generates, callgpu_block_buffer.upload()WorldStreamer: when chunk unloads, callgpu_block_buffer.free()Files to Create
src/world/gpu_block_buffer.zig— GPU block data buffer managementFiles to Modify
src/world/world_renderer.zig— own and manage GpuBlockBuffersrc/world/world_streamer.zig— upload block data on generate, free on unloadsrc/game/player.zig— update GPU buffer on block break/placeTesting
Roadmap:
docs/PERFORMANCE_ROADMAP.md— Batch 5, Issue 4A-1