Files
game_engine/docs/superpowers/specs/2026-03-26-instancing-design.md
tolelom c5f6511fc2 docs: add GPU instancing design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:12:11 +09:00

3.0 KiB

GPU Instancing Design

Overview

동일 메시를 수천 개 렌더링할 때 단일 draw call로 처리하는 인스턴싱 시스템. per-entity UBO 대신 인스턴스 버퍼에 모델 행렬을 패킹.

Scope

  • InstanceData 구조체 (모델 행렬 + 추가 per-instance 데이터)
  • InstanceBuffer 관리 (동적 크기, 매 프레임 업로드)
  • 인스턴스드 렌더 파이프라인 (기존 mesh_shader 확장)
  • 기존 파이프라인과 병행 (인스턴싱은 옵션)

InstanceData

#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct InstanceData {
    pub model: [[f32; 4]; 4],  // 모델 행렬 (64 bytes)
    pub color: [f32; 4],       // per-instance 색상/틴트 (16 bytes)
}
// Total: 80 bytes per instance

셰이더에서 vertex attribute로 전달 (location 4~8).

InstanceBuffer

pub struct InstanceBuffer {
    buffer: wgpu::Buffer,
    capacity: usize,        // 현재 버퍼 용량 (인스턴스 수)
    pub count: usize,       // 실제 인스턴스 수
}
  • new(device, initial_capacity) — VERTEX | COPY_DST 버퍼
  • update(device, queue, instances: &[InstanceData]) — 용량 초과 시 재생성, write_buffer
  • 인스턴스 데이터는 CPU에서 매 프레임 빌드 (Transform 순회)

Instanced Shader (instanced_shader.wgsl)

기존 mesh_shader.wgsl 기반 + 인스턴스 입력:

struct InstanceInput {
    @location(4) model_0: vec4<f32>,
    @location(5) model_1: vec4<f32>,
    @location(6) model_2: vec4<f32>,
    @location(7) model_3: vec4<f32>,
    @location(8) color: vec4<f32>,
};

@vertex
fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput {
    let model = mat4x4<f32>(instance.model_0, instance.model_1, instance.model_2, instance.model_3);
    let world_pos = model * vec4<f32>(vertex.position, 1.0);
    // ...
}

Pipeline

기존 create_mesh_pipeline에 instance attributes 추가:

  • slot 0: MeshVertex (48 bytes, per-vertex)
  • slot 1: InstanceData (80 bytes, per-instance, step_mode: Instance)
pub fn create_instanced_pipeline(
    device: &wgpu::Device,
    format: wgpu::TextureFormat,
    light_layout: &wgpu::BindGroupLayout,
    texture_layout: &wgpu::BindGroupLayout,
) -> wgpu::RenderPipeline;

Render

rpass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
rpass.set_vertex_buffer(1, instance_buffer.buffer.slice(..));
rpass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
rpass.draw_indexed(0..mesh.num_indices, 0, 0..instance_buffer.count as u32);

File Structure

  • crates/voltex_renderer/src/instancing.rs — InstanceData, InstanceBuffer, pipeline, InstanceData::LAYOUT
  • crates/voltex_renderer/src/instanced_shader.wgsl — 인스턴스드 셰이더
  • crates/voltex_renderer/src/lib.rs — 모듈 추가

Testing

  • InstanceData 크기: size_of == 80
  • InstanceData::LAYOUT: attribute 수, stride 검증
  • InstanceBuffer: capacity 확장 로직 (GPU 없이 로직만)
  • sort by mesh for batching (같은 메시끼리 모아서 단일 draw call)