Snow mesh: build once and cache per world tile; LOD, morph and culling at draw time (no rebuild on camera move) #1496

Open
opened 2026-11-08 06:18:30 +00:00 by sky_line · 0 comments
Owner

Problem

The snow surface mesh (#783, clean_weather_snowmesh.comp, LLCleanWeatherVK::recordMeshBuild) is rebuilt from scratch when the camera moves more than 0.25 m or turns more than 8 degrees (RenderVKWeatherSnowMeshRebuildMove, kMeshRebuildTurnCos), and on every grid snap. On an RTX 3060 at 960x511 that build is about 21 ms (#1494's capture). A walking camera pays it every 2-3 frames.

The rebuild exists because the mesh bakes in camera-dependent data at build time:

  1. The grids follow the camera. The inner grid (512^2) and the outer ring (256^2) are centred on the camera and snap as it moves.
  2. LOD is decided at build time. Stride 1/2/4 per 4x4 block is chosen by distance from the camera (inner: 10 / 20 m), the stride morphs are baked into the vertices, and the inner-to-ring geomorph band is a radius around the camera.
  3. The view cone is culled at build time. Blocks behind the view cone, plus a margin, are not built at all.

(#1494's fix, fix-weather-perf de854254b2, already stops the every-4th-frame interval build when nothing changed. That leaves only the camera triggers.)

Proposal: build once, cache per world tile (CDLOD / geometry-clipmap style)

  • World-anchored tiles: each 8x8 tile, which the patch tile table already uses, is built once at full resolution. It is keyed by world tile coordinate plus a snow version stamp that bumps when that tile's grid texels change by more than the existing 2 mm threshold. The grid textures use wrap-around addressing, so a snap moves no data and no vertex.
  • LOD at draw time: pre-built index lists per stride for each tile. The stride is chosen per tile when drawing, and the stride morph and the inner-to-ring geomorph are done in the vertex shader from the live camera distance, as water waves already fade.
  • Culling at draw time: a small GPU pass each frame culls tiles into the indirect draw, for the main view and for each sun cascade.
  • Rebuilds only where needed: only tiles entering the area, or whose snow changed (the local patches), are built. A full build happens only on teleport or first entry.
  • Joins across tiles: skirts, apron and joins must agree across tiles built in different frames. A tile's build reads its neighbours' texels, as patches do today. A neighbour's version change invalidates edge tiles.
  • Memory: a VRAM budget and eviction for cached tiles (no unbounded growth). Allocation failure falls back to the current path, never crashes.

Done when

  • A walking or turning camera in settled snow causes no full builds. c_snow_mesh_build p95 is close to its p50, and the per-frame cost is the tile cull plus edge tiles.
  • The image matches today's mesh: no seams, no pops at stride or ring hand-overs, same AO and cascade behaviour. This is checked with today's mesh kept behind a Persist 0 A/B toggle until the new path matches, then the old path is deleted.
  • VRAM is bounded and measured.
  • A source regression check is added.
## Problem The snow surface mesh (#783, `clean_weather_snowmesh.comp`, `LLCleanWeatherVK::recordMeshBuild`) is rebuilt from scratch when the camera moves more than 0.25 m or turns more than 8 degrees (`RenderVKWeatherSnowMeshRebuildMove`, `kMeshRebuildTurnCos`), and on every grid snap. On an RTX 3060 at 960x511 that build is about 21 ms (#1494's capture). A walking camera pays it every 2-3 frames. The rebuild exists because the mesh bakes in camera-dependent data at build time: 1. **The grids follow the camera.** The inner grid (512^2) and the outer ring (256^2) are centred on the camera and snap as it moves. 2. **LOD is decided at build time.** Stride 1/2/4 per 4x4 block is chosen by distance from the camera (inner: 10 / 20 m), the stride morphs are baked into the vertices, and the inner-to-ring geomorph band is a radius around the camera. 3. **The view cone is culled at build time.** Blocks behind the view cone, plus a margin, are not built at all. (#1494's fix, `fix-weather-perf` de854254b2, already stops the every-4th-frame interval build when nothing changed. That leaves only the camera triggers.) ## Proposal: build once, cache per world tile (CDLOD / geometry-clipmap style) - **World-anchored tiles:** each 8x8 tile, which the patch tile table already uses, is built once at full resolution. It is keyed by world tile coordinate plus a snow version stamp that bumps when that tile's grid texels change by more than the existing 2 mm threshold. The grid textures use wrap-around addressing, so a snap moves no data and no vertex. - **LOD at draw time:** pre-built index lists per stride for each tile. The stride is chosen per tile when drawing, and the stride morph and the inner-to-ring geomorph are done in the vertex shader from the live camera distance, as water waves already fade. - **Culling at draw time:** a small GPU pass each frame culls tiles into the indirect draw, for the main view and for each sun cascade. - **Rebuilds only where needed:** only tiles entering the area, or whose snow changed (the local patches), are built. A full build happens only on teleport or first entry. - **Joins across tiles:** skirts, apron and joins must agree across tiles built in different frames. A tile's build reads its neighbours' texels, as patches do today. A neighbour's version change invalidates edge tiles. - **Memory:** a VRAM budget and eviction for cached tiles (no unbounded growth). Allocation failure falls back to the current path, never crashes. ## Done when - A walking or turning camera in settled snow causes no full builds. `c_snow_mesh_build` p95 is close to its p50, and the per-frame cost is the tile cull plus edge tiles. - The image matches today's mesh: no seams, no pops at stride or ring hand-overs, same AO and cascade behaviour. This is checked with today's mesh kept behind a Persist 0 A/B toggle until the new path matches, then the old path is deleted. - VRAM is bounded and measured. - A source regression check is added.
sky_line added this to the Slipstream project 2026-11-08 06:18:30 +00:00
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#1496
No description provided.