feat(render): Phase U.3 — GPU clip-plane gate (gl_ClipDistance), no-clip default
Adds the GPU mechanism to clip drawing to a per-cell screen-space convex
region via gl_ClipDistance, consumed by the mesh + terrain vertex shaders.
This is the MECHANISM only — every instance defaults to slot 0 (no-clip /
pass-all) and terrain to count 0, so the running game renders IDENTICALLY to
pre-U.3 (verified: offline launch compiles both shaders and reaches steady
state; no GL errors). U.4 populates real clip data from portal visibility.
Binding contract (define once, both sides obey):
- mesh_modern.vert: SSBO binding=2 CellClip[] (shared per-frame regions, slot 0
reserved no-clip) + SSBO binding=3 uint[] per-instance slot, indexed by the
IDENTICAL gl_BaseInstanceARB+gl_InstanceID used for binding=0. binding=0/1
untouched.
- terrain_modern.vert: UBO binding=2 TerrainClip { int count; vec4 planes[8]; }
for the single OutsideView region (UBO namespace; SceneLighting is UBO
binding=1, so binding=2 is free and does not collide with the mesh SSBO
binding=2). count 0 = ungated.
- Both redeclare out gl_PerVertex { vec4 gl_Position; float gl_ClipDistance[8]; }
and set unused planes (i >= count) to +1.0 so they pass everything.
CellClip std430 layout (144 bytes/slot): count@0, 3 pad uints@4/8/12,
planes[8]@16 (vec4 stride 16). Terrain UBO std140: count@0 (padded to 16),
planes[8]@16 → 144 bytes. Verified by ClipFrameLayoutTests (8 new tests).
Pieces:
- ClipFrame: per-frame container + uploader for the SHARED clip data (binding=2
SSBO + terrain UBO). NoClip() = slot 0 + terrain count 0. AppendSlot /
SetTerrainClip pack std430/std140 bytes for U.4. UploadShared binds both.
- WbDrawDispatcher + EnvCellRenderer: each owns its binding=3 zero buffer
(all-zeros sized to its instance count → slot 0), re-binds binding=2 from the
shared ClipFrame id (or an internal no-clip fallback if unwired) before MDI.
gl_ClipDistance is per-vertex, so the single glMultiDrawElementsIndirect per
group is preserved — no draw splitting.
- TerrainModernRenderer: binds the terrain clip UBO (shared or no-clip fallback)
before its draw.
- GameWindow: glEnable(GL_CLIP_DISTANCE0..7) once at init (unused planes pass-all
so always-on avoids per-draw thrash); per frame builds ClipFrame.NoClip(),
UploadShared, and hands the buffer ids to the three renderers (tiny diff; U.4
swaps NoClip() for the real portal-visibility frame).
Gate: dotnet build green; App suite 134/134; offline launch confirms both
shaders compile + link with no GL errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0b125830fe
commit
bf2e559369
8 changed files with 797 additions and 1 deletions
|
|
@ -166,6 +166,14 @@ public sealed class GameWindow : IDisposable
|
|||
private AcDream.App.Rendering.Wb.EnvCellRenderer? _envCellRenderer;
|
||||
private AcDream.App.Rendering.Wb.WbFrustum? _envCellFrustum;
|
||||
|
||||
// Phase U.3: the shared per-frame clip data (binding=2 mesh SSBO + terrain
|
||||
// UBO). In U.3 this is rebuilt as ClipFrame.NoClip() every frame and uploaded
|
||||
// once before terrain/entities draw, so the whole scene renders ungated
|
||||
// (identical to pre-U.3). The buffer ids are handed to the three renderers so
|
||||
// each re-binds binding=2 immediately before its own draw. U.4 replaces the
|
||||
// NoClip() frame with one built from the portal-visibility result.
|
||||
private ClipFrame? _clipFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Phase 6.4: per-entity animation playback state for entities whose
|
||||
/// MotionTable resolved to a real cycle. The render loop ticks each
|
||||
|
|
@ -1079,6 +1087,17 @@ public sealed class GameWindow : IDisposable
|
|||
_gl.ClearColor(0.05f, 0.10f, 0.18f, 1.0f);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
|
||||
// Phase U.3: enable all 8 hardware clip planes once at startup. The mesh
|
||||
// and terrain vertex shaders write gl_ClipDistance[0..7] every frame;
|
||||
// unused planes are set to +1.0 (pass-all) when a region's plane count is
|
||||
// below 8, so leaving all 8 enabled permanently is correct and avoids
|
||||
// per-draw glEnable/glDisable thrash. In U.3 every region is no-clip, so
|
||||
// nothing is actually clipped — the running game renders identically. U.4
|
||||
// populates real clip regions; the enables stay as-is.
|
||||
// (EnableCap.ClipDistance0 == GL_CLIP_DISTANCE0 0x3000; +i selects plane i.)
|
||||
for (int _cp = 0; _cp < ClipFrame.MaxPlanes; _cp++)
|
||||
_gl.Enable(EnableCap.ClipDistance0 + _cp);
|
||||
|
||||
string shadersDir = Path.Combine(AppContext.BaseDirectory, "Rendering", "Shaders");
|
||||
|
||||
// Phase N.5b: terrain_modern shader pair — bindless texture handles +
|
||||
|
|
@ -7261,6 +7280,21 @@ public sealed class GameWindow : IDisposable
|
|||
goto SkipWorldGeometry;
|
||||
}
|
||||
|
||||
// Phase U.3: build + upload the SHARED per-frame clip data once,
|
||||
// ahead of both terrain and entity draws. In U.3 this is the no-clip
|
||||
// frame (slot 0 only, terrain count 0) so the whole scene renders
|
||||
// ungated — bit-identical to pre-U.3. UploadShared binds binding=2
|
||||
// (mesh SSBO) + binding=2 (terrain UBO); each renderer below re-binds
|
||||
// its binding=2 defensively from the ids we hand it. The single
|
||||
// _clipFrame instance reuses its GL buffers across frames (NoClip is
|
||||
// cheap CPU-only state we copy into it). U.4 swaps NoClip() for the
|
||||
// real portal-visibility frame here.
|
||||
_clipFrame ??= ClipFrame.NoClip();
|
||||
_clipFrame.UploadShared(_gl);
|
||||
_wbDrawDispatcher?.SetClipRegionSsbo(_clipFrame.RegionSsbo);
|
||||
_envCellRenderer?.SetClipRegionSsbo(_clipFrame.RegionSsbo);
|
||||
_terrain?.SetClipUbo(_clipFrame.TerrainUbo);
|
||||
|
||||
// Phase N.5b: wrap Draw in CPU stopwatch for [TERRAIN-DIAG] rollup
|
||||
// (gated on ACDREAM_WB_DIAG=1, same env var as [WB-DIAG]). Stopwatch
|
||||
// is cheap; only the periodic Console.WriteLine is gated.
|
||||
|
|
@ -10659,6 +10693,7 @@ public sealed class GameWindow : IDisposable
|
|||
_audioEngine?.Dispose(); // Phase E.2: stop all voices, close AL context
|
||||
_wbDrawDispatcher?.Dispose();
|
||||
_envCellRenderer?.Dispose(); // Phase A8
|
||||
_clipFrame?.Dispose(); // Phase U.3
|
||||
_skyRenderer?.Dispose(); // depends on sampler cache; dispose first
|
||||
_samplerCache?.Dispose();
|
||||
_textureCache?.Dispose();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue