perf(pipeline): per-frame hatch+wipeout frustum cull at draw time (Phase 2.3)

The Phase 2.1 step-4 attempt at culling hatches inside
`synced_hatch_models` was wrong: GPU hatch/wipeout buffers are
uploaded on geometry_epoch only (see render.rs — they're "static
buffers"), so any CPU-side cull at build time would freeze the
visible set at the geometry epoch boundary and never re-evaluate on
pan. Reverted that approach; the build-time function now returns the
full visible set again and the cache key is back to geometry_epoch.

The actual per-frame frustum cull moves into the existing draw-time
skip-flag machinery:

- `compute_hatch_lod` now also folds in `aabb_offscreen` (ORed with
  the existing Phase 3.3 sub-pixel skip), so out-of-view hatches are
  skipped at the draw call.
- New `compute_wipeout_lod` + `wipeout_skip_flags` mirror the hatch
  path. The wipeout draw loop honors the flag.
- `Primitive::prepare` calls the new compute step alongside the rest.

Per-frame projection + skip is a few hundred microseconds on dense
docs; far cheaper than re-uploading GPU buffers per pan tick.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-05-17 15:02:07 +03:00
commit b70c9f9262
3 changed files with 52 additions and 63 deletions

View file

@ -148,6 +148,7 @@ impl shader::Primitive for Primitive {
pipeline.compute_wipeout_scissors(self.uniforms.view_proj, clip_size.width, clip_size.height);
pipeline.compute_image_scissors(self.uniforms.view_proj, clip_size.width, clip_size.height);
pipeline.compute_hatch_lod(self.uniforms.view_proj, clip_size.width, clip_size.height);
pipeline.compute_wipeout_lod(self.uniforms.view_proj, clip_size.width, clip_size.height);
pipeline.compute_mesh_lod(self.uniforms.view_proj, clip_size.width, clip_size.height);
if self.show_viewcube {
pipeline.viewcube.upload(