Epic: visibility-buffer opaque pipeline (explore for FPS) #256

Open
opened 2026-10-02 23:44:46 +00:00 by sky_line · 1 comment
Owner

Epic — exploratory, no timeline. Track a visibility-buffer (VB) opaque pipeline as a future option for more FPS, mainly in dense, zoomed-out multi-sim views.

Why

Our opaque path is GPU-driven deferred: compute cull, then multi-draw indirect into a G-buffer, then compute lighting (clean_deferred_light.comp). That's already per-pixel for lighting. The remaining costs grow with geometry complexity:

  • Tiny-triangle waste: GPUs shade 2×2 pixel blocks, so a triangle covering one or two pixels still runs the shader on the whole block and throws the extra work away. Distant sims are mostly sub-pixel or few-pixel triangles, and the G-buffer pass still samples materials and writes about 20 B per pixel for every one of those wasted pixels.
  • G-buffer bandwidth at 4K: the G-buffer is written in full and read back in the lighting pass every frame.
  • Overdraw: material work is spent on pixels that are later covered, limited only by the Hi-Z / prepass.

A VB pass writes only instanceID + triangleID (about 4–8 B per pixel), which makes the geometry pass almost free per pixel. Material evaluation and lighting then run exactly once per visible pixel, in compute. This is the approach Nanite and other modern GPU-driven engines take for dense geometry.

Sketch

  1. VB raster: the existing compute cull and MDI write a packed uint (instance/draw id + primitive id) plus depth. There's no material work in this pass; it doesn't fetch textures except for alpha-masked geometry, which is a separate pass or binned.
  2. Material resolve (compute): per pixel, fetch the triangle's 3 vertices through the instance SSBO and mega-VBO. Rebuild barycentrics analytically, including derivatives for texture LOD (a known VB difficulty). Evaluate the material bindlessly, then either:
    • write a slim G-buffer for the existing deferred lighting (lowest risk), or
    • fuse material and lighting in one pass.
  3. Material binning: classify pixels by material/shader type (tile classification or a sort) to avoid divergence. This covers PBR vs. legacy Blinn-Phong and the other variants.
  4. Keep forward for alpha-blended surfaces, water and particles, as now.

Open questions / risks

  • Rigged meshes: the pre-skinned vertex stream must be addressable per triangle. The pre-skin buffers exist, but the vertex fetch path needs checking.
  • Derivatives/LOD for the analytic barycentrics vs. the hardware quad derivatives (TAA/FSR jitter interplay).
  • Legacy SL material variety: shiny, fullbright, glow, texture animation, planar/planar-mapped UVs, media. All must move into the resolve.
  • Alpha-mask cost (alpha test needs the texture in the VB pass).
  • Interaction with VRS, the ray-tracing (RT) consumers (the shadow mask reads the G-buffer and depth), SSR, and the picking path.
  • A measurement gate: profile G-buffer pass time vs. lighting in dense multi-sim scenes first. Only pursue this if the geometry/G-buffer pass is the bottleneck there.

Exit criteria (for exploring)

  • A prototype behind a setting, opaque PBR only.
  • A/B GPU timings on a dense 5×5 multi-sim view and a typical single-sim scene, at 1080p and 4K.
**Epic — exploratory, no timeline.** Track a visibility-buffer (VB) opaque pipeline as a future option for more FPS, mainly in dense, zoomed-out multi-sim views. ## Why Our opaque path is GPU-driven deferred: compute cull, then multi-draw indirect into a G-buffer, then compute lighting (`clean_deferred_light.comp`). That's already per-pixel for lighting. The remaining costs grow with geometry complexity: - **Tiny-triangle waste:** GPUs shade 2×2 pixel blocks, so a triangle covering one or two pixels still runs the shader on the whole block and throws the extra work away. Distant sims are mostly sub-pixel or few-pixel triangles, and the G-buffer pass still samples materials and writes about 20 B per pixel for every one of those wasted pixels. - **G-buffer bandwidth at 4K:** the G-buffer is written in full and read back in the lighting pass every frame. - **Overdraw:** material work is spent on pixels that are later covered, limited only by the Hi-Z / prepass. A VB pass writes only `instanceID + triangleID` (about 4–8 B per pixel), which makes the geometry pass almost free per pixel. Material evaluation and lighting then run exactly once per visible pixel, in compute. This is the approach Nanite and other modern GPU-driven engines take for dense geometry. ## Sketch 1. **VB raster:** the existing compute cull and MDI write a packed `uint` (instance/draw id + primitive id) plus depth. There's no material work in this pass; it doesn't fetch textures except for alpha-masked geometry, which is a separate pass or binned. 2. **Material resolve (compute):** per pixel, fetch the triangle's 3 vertices through the instance SSBO and mega-VBO. Rebuild barycentrics analytically, including derivatives for texture LOD (a known VB difficulty). Evaluate the material bindlessly, then either: - write a slim G-buffer for the existing deferred lighting (lowest risk), or - fuse material and lighting in one pass. 3. **Material binning:** classify pixels by material/shader type (tile classification or a sort) to avoid divergence. This covers PBR vs. legacy Blinn-Phong and the other variants. 4. **Keep forward** for alpha-blended surfaces, water and particles, as now. ## Open questions / risks - Rigged meshes: the pre-skinned vertex stream must be addressable per triangle. The pre-skin buffers exist, but the vertex fetch path needs checking. - Derivatives/LOD for the analytic barycentrics vs. the hardware quad derivatives (TAA/FSR jitter interplay). - Legacy SL material variety: shiny, fullbright, glow, texture animation, planar/planar-mapped UVs, media. All must move into the resolve. - Alpha-mask cost (alpha test needs the texture in the VB pass). - Interaction with VRS, the ray-tracing (RT) consumers (the shadow mask reads the G-buffer and depth), SSR, and the picking path. - A measurement gate: profile G-buffer pass time vs. lighting in dense multi-sim scenes first. Only pursue this if the geometry/G-buffer pass is the bottleneck there. ## Exit criteria (for exploring) - A prototype behind a setting, opaque PBR only. - A/B GPU timings on a dense 5×5 multi-sim view and a typical single-sim scene, at 1080p and 4K.
sky_line added this to the Slipstream project 2026-10-06 15:04:13 +00:00
Author
Owner

Analysis 2026-10-10 (after the #259 prepass A/B)

Geometry passes today: busy sim, prepass ON, RTX 5090 at 4K with FSR, GPU 10.7 ms total. g_prepass 0.83, g_gbuf_static 1.25, g_gbuf_rigged ~0.25, so about 2.3 ms.

Expected gain here: a visibility-buffer raster pass costs about what the prepass does (~0.8 ms), plus a compute material resolve (~0.5-1 ms at 4K, estimate). Net gain in this scene is ~0.5-1 ms.

Why that's modest: the prepass already gives one material evaluation per visible pixel (EQUAL test). What a visibility buffer adds on top:

  1. 2x2 quad waste on tiny triangles. Big in dense, zoomed-out views: multi-sim, high draw distance, distant avatars.
  2. G-buffer bandwidth: ~20 B/px written and read vs. 4-8 B/px.
  3. One geometry pass instead of prepass + G-buffer vertex work.
  4. A common base for RT, AO and binned legacy materials.

Gate: take one GPU timer sample in a dense, zoomed-out multi-sim view (mainland or a big build from above, high draw distance, prepass ON).

  • If prepass + G-buffer is over ~4 ms or the top item, pursue this. Start with a static-opaque-only prototype whose resolve writes the existing G-buffer, so lighting, AO, the RT mask and VRS are untouched; then rigged, then alpha-mask.
  • If it is ~2-3 ms, bigger levers come first: idle-avatar pre-skin/weld/refit (#632, ~3.5 ms in crowds), probe tracing (~2.9 ms), async overlap (#633).

Order: remove the forward fallback (#640) first or as part of this, so only one world path needs converting.

The A/B behind this is in #259.

## Analysis 2026-10-10 (after the #259 prepass A/B) **Geometry passes today:** busy sim, prepass ON, RTX 5090 at 4K with FSR, GPU 10.7 ms total. `g_prepass` 0.83, `g_gbuf_static` 1.25, `g_gbuf_rigged` ~0.25, so about **2.3 ms**. **Expected gain here:** a visibility-buffer raster pass costs about what the prepass does (~0.8 ms), plus a compute material resolve (~0.5-1 ms at 4K, estimate). Net gain in this scene is **~0.5-1 ms**. **Why that's modest:** the prepass already gives one material evaluation per visible pixel (EQUAL test). What a visibility buffer adds on top: 1. **2x2 quad waste on tiny triangles.** Big in dense, zoomed-out views: multi-sim, high draw distance, distant avatars. 2. **G-buffer bandwidth:** ~20 B/px written and read vs. 4-8 B/px. 3. **One geometry pass** instead of prepass + G-buffer vertex work. 4. **A common base** for RT, AO and binned legacy materials. **Gate:** take one GPU timer sample in a dense, zoomed-out multi-sim view (mainland or a big build from above, high draw distance, prepass ON). - If prepass + G-buffer is over ~4 ms or the top item, pursue this. Start with a **static-opaque-only prototype** whose resolve writes the existing G-buffer, so lighting, AO, the RT mask and VRS are untouched; then rigged, then alpha-mask. - If it is ~2-3 ms, bigger levers come first: idle-avatar pre-skin/weld/refit (#632, ~3.5 ms in crowds), probe tracing (~2.9 ms), async overlap (#633). **Order:** remove the forward fallback (#640) first or as part of this, so only one world path needs converting. The A/B behind this is in #259.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sky_line/Slipstream#256
No description provided.