acdream/src/AcDream.App/Rendering/Wb/BindlessSupport.cs
Erik da56063be5 fix(N.5b): black terrain — switch to uvec2 handle + sampler constructor
Symptom: terrain renders pure black in modern path (legacy renderer
correct). Diagnostic at TerrainModernRenderer.Draw showed:
  glProgramUniformHandle(prog=4, loc=5, handle=0x100251xxx) → GL_INVALID_OPERATION (0x0502)
on both terrain and alpha sampler uniforms.

Root cause: the `uniform sampler2DArray` + glProgramUniformHandleARB
combination is rejected by the NVIDIA Windows driver in this configuration.
The handle is valid and resident; the uniform location is valid; the
program is valid; but the driver refuses to bind a 64-bit handle to a
sampler uniform via the program-uniform path.

Fix: switch to N.5's mesh_modern pattern — pass each 64-bit handle as a
`uniform uvec2` (low + high 32-bit halves) and construct the sampler at
the use site via the GLSL `sampler2DArray(handle)` constructor. This
form is what ARB_bindless_texture documents as universally supported and
is what N.5 already uses successfully.

Files:
- terrain_modern.frag: replace `uniform sampler2DArray uTerrain/uAlpha`
  with `uniform uvec2 uTerrainHandle/uAlphaHandle` + `#define`s
- TerrainModernRenderer.cs: cache uvec2 uniform locations; set via
  `glProgramUniform2(program, loc, low32, high32)` per frame
- BindlessSupport.cs: remove now-unused `SetSamplerHandleUniform`,
  leave a comment noting why the helper was retired
- GameWindow.cs: also strip the temporary [TERRAIN-DBG] cursor-wrap
  print added during the perf-baseline investigation

Build green; 114/114 tests in N.5+N.5b filter still pass; user-verified
terrain renders correctly in modern path post-fix. Captured fresh perf
baseline:
- Legacy:  cpu_us median  1.5  / p95  3.0  (1 chunk = 1 glDrawElements)
- Modern:  cpu_us median  6.4-7.0 / p95  9-14 (51 visible LBs, 1 MDI call)

Modern is ~4× slower on CPU at radius=5 because the chunked legacy path
already collapsed the scene to one draw call. The architectural wins
(zero glBindTexture/frame; constant-cost dispatch as A.5 raises radius)
will be documented in T10's perf baseline doc; the spec's
"≥10% lower CPU" acceptance criterion is invalid at radius=5 and needs
revision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 12:53:21 +02:00

64 lines
2.4 KiB
C#

using Silk.NET.OpenGL;
using Silk.NET.OpenGL.Extensions.ARB;
namespace AcDream.App.Rendering.Wb;
/// <summary>
/// Thin wrapper around <see cref="ArbBindlessTexture"/> + capability detection
/// for the modern rendering path. Constructed once at startup via
/// <see cref="TryCreate"/>, which returns false if the extension isn't present.
/// </summary>
public sealed class BindlessSupport
{
private readonly ArbBindlessTexture _ext;
private BindlessSupport(ArbBindlessTexture extension)
{
_ext = extension;
}
public static bool TryCreate(GL gl, out BindlessSupport? support)
{
if (gl.TryGetExtension<ArbBindlessTexture>(out var ext))
{
support = new BindlessSupport(ext);
return true;
}
support = null;
return false;
}
/// <summary>Get a 64-bit bindless handle for the texture and make it resident.
/// Idempotent: handle is the same for a given texture name.</summary>
public ulong GetResidentHandle(uint textureName)
{
ulong h = _ext.GetTextureHandle(textureName);
if (!_ext.IsTextureHandleResident(h))
_ext.MakeTextureHandleResident(h);
return h;
}
/// <summary>Release residency for a handle. Call before deleting the underlying texture.</summary>
public void MakeNonResident(ulong handle)
{
if (_ext.IsTextureHandleResident(handle))
_ext.MakeTextureHandleNonResident(handle);
}
// Phase N.5b note: a `SetSamplerHandleUniform` wrapper was added in T6
// and removed when terrain rendering surfaced GL_INVALID_OPERATION on
// NVIDIA Windows for the `uniform sampler2DArray` + glProgramUniformHandleARB
// combination. The replacement pattern (uvec2 handle uniform + GLSL
// sampler-from-handle constructor — see terrain_modern.frag) lives at the
// call site via plain `_gl.ProgramUniform2(program, loc, low, high)`. If
// you re-introduce a sampler-handle helper, restrict it to drivers known
// to accept the direct sampler-uniform path.
/// <summary>Detect <c>GL_ARB_shader_draw_parameters</c> in addition to bindless.
/// N.5's vertex shader uses <c>gl_BaseInstanceARB</c> and <c>gl_DrawIDARB</c>
/// from this extension.</summary>
public bool HasShaderDrawParameters(GL gl)
{
return gl.IsExtensionPresent("GL_ARB_shader_draw_parameters");
}
}