Skip to content

ENG2-P2-14: GPU-driven culling / multi_draw_indirect path (Metal-gated stretch) #733

Description

@aram-devdocs

Parent

  • Program: ENG2 — GoudEngine v2 Rebuild (see the pinned master tracking issue)
  • Phase / Milestone: Phase 2 — Render Core v2 (eng2-p2-render-core)
  • Batch / Group: New in design-expansion.md — Metal-gated stretch goal, sequenced after core instancing/culling work
  • Runbook spec: docs/src/runbook/phases/phase-2.md (committed with the roadmap)

Summary

Evaluate GPU-driven culling via multi_draw_indirect/compute-driven submission for the 30k+ visible target on the Metal backend; implement it if wgpu's Metal support is viable, otherwise ship the CPU spatial-index path (ENG2-P2-02) as the answer and document the indirect path as a future capability.

Architecture Context

Layer: Layer 2 (Libs) — libs/graphics/backend/wgpu_backend/ (see tools/lint_layers.rs — downward-only deps).
Modules/types touched:

  • goud_engine/src/libs/graphics/backend/wgpu_backend/init.rs — currently requests wgpu::Features::empty(); this issue evaluates whether MULTI_DRAW_INDIRECT/INDIRECT_FIRST_INSTANCE can be requested and are actually supported on the Metal adapter.
  • If viable: new compute-pass culling shader that writes a draw-indirect buffer, plus the indirect-draw submission path in the render loop.

Boundary constraints (only those that apply):

  • Raw GPU calls stay in libs/graphics/backend/ — no wgpu:: outside it.

Pattern to follow: RFC-0005 (ENG2-P0-09) decision item on CPU spatial index vs GPU compute culling — this issue is the execution of whichever branch the RFC (and this issue's own Metal feasibility check) lands on.

Scope

  • Check wgpu::Features::MULTI_DRAW_INDIRECT and INDIRECT_FIRST_INSTANCE support on the Metal backend/adapter actually used (confirm against what init.rs requests today — currently Features::empty()).
  • If Metal support is acceptable: implement a compute pass that culls instances and writes a draw-indirect buffer, collapsing CPU submit cost toward O(1).
  • If Metal support is not acceptable: document the finding as an ADR/decision note, keep the CPU spatial-index path (ENG2-P2-02) as the shipped answer, and record the indirect path as a documented future GPU-backend capability (not silently dropped).
  • Tests: spec test at goud_engine/tests/spec/ + unit/isolation coverage for the new code paths.
  • Docs/rules updates if this invalidates .agents/rules/graphics-patterns.md or docs/src/*.

Acceptance Criteria

  • Metal feature-support finding is documented (pass/fail) with evidence from actual adapter feature queries, not assumption.
  • Acceptance (per design-expansion.md): S2 at 30k visible meets the 120-FPS gate with CPU submit ≤ 1ms — via whichever path (GPU-indirect or CPU spatial-index) actually achieves it.
  • If the indirect path ships: draw-indirect buffer content is correctness-tested against the CPU culling result (same visible set).
  • If the indirect path does not ship: a decision record explains why and what future GPU-backend capability would unlock it.
  • cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings clean; cargo test green; ./codegen.sh && git diff --exit-code (drift gate)

Breaking Change & Throne Follow-up

None — additive/optional path. If implemented, it is an alternate submission mode gated by hardware capability detection, not a replacement that removes the CPU path.

Blocked By

ENG2-P2-02 (CPU spatial index — the fallback/comparison baseline), ENG2-P2-04 (uniform v2 dynamic-offset layout — indirect draws need compatible per-object addressing).

Files Likely Touched

  • New/Modified/Generated: goud_engine/src/libs/graphics/backend/wgpu_backend/init.rs, new compute-culling shader + indirect-draw submission path (if shipped), decision-record doc (if not shipped)

Agent Notes

  • This is an explicitly Metal-gated stretch goal per design-expansion.md — do not block Phase 2 gate closure (ENG2-P2-12) on this landing; the CPU spatial-index path (ENG2-P2-02) is the guaranteed-shipped answer regardless of this issue's outcome.
  • backend/wgpu_backend/init.rs currently sets required_features: wgpu::Features::empty() (verified in current source) — no indirect-draw features are requested today, so this issue's first job is determining whether the Metal adapter actually supports them before writing any compute-culling code.
  • RFC-0005 (ENG2-P0-09) decision item: "instanced draws vs multi_draw_indirect (Metal feature check)" — this issue is the execution of that RFC decision point, so the RFC's conclusion should already have flagged the direction; verify it hasn't changed before starting.
  • Acceptance is explicitly "whichever path achieves it" — this issue's success criterion is the S2 30k-visible/120-FPS/CPU-submit-≤1ms number, not "ship GPU-driven culling" as an end in itself.

Verification

cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings
cargo test
./codegen.sh && git diff --exit-code

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions