diag #176/#180: isolation toggles pin the residual stripes on the camera sweep, not the render
The #176 gate-2 failure ("stripes/triangles flickering when the camera
is pushed into walls; nothing when zoomed out") is NOT a render defect.
Isolation apparatus added this commit:
- ACDREAM_LIGHT_DEBUG shader modes (mesh_modern.vert/frag + uLightDebug
upload in EnvCellRenderer/WbDrawDispatcher): 1 = ambient-only,
2 = dynamics killed, 3 = raw vLit field (texture ignored). The
pattern SURVIVES mode 3 -> not texture; lives above the light data.
- ACDREAM_CLIP_DEBUG=1 (RenderingDiagnostics.ClipDebugNoShellTrim +
the EnvCellRenderer slot-fill gate): shell pass draws cells WHOLE
(retail's shape). Pattern survives -> the per-cell clip trim is
exonerated.
With every render suspect dead, an autonomous visual loop (synthetic
back-into-wall input + GDI window captures + the [flap-sweep] probe)
pinned the mechanism numerically: at a compressed moving boom the
camera-collision sweep is BISTABLE - consecutive sweeps with ~1.4 mm
input drift flip the first-contact solution 0.27 m along the boom
(pulledIn 0.27<->0.53, every ~5-10 frames, all 368k sweeps ok=True),
and at ~1700 fps unsynced every monitor refresh tear-interleaves the
two views = the stripe/hatch patterns. Filed as #180 with the retail
anchor: viewer_sought_position is STATEFUL (SmartBox 0x00452d75 feeds
the CURRENT swept viewer into CameraManager::UpdateCamera 0x00456660
and assigns the return to the sought, 0x00452d84) - the target
converges to the collided position instead of re-rolling the full
knife-edge ray per frame like our RetailChaseCamera does. SweepEye
itself ports update_viewer 0x00453ce0 faithfully and is exonerated.
Also recorded in #176: the site-A weenie light-registration leak (a
portal's I100 light stacked x2->x4 over one session as re-CreateObject
re-registered it under fresh entity ids).
The #176 lighting fix (d8984e87) remains live-verified; #176 re-gates
after #180 lands. ISSUES: #180 filed, #176 updated. Suites: Core
2599+2skip; toggles inert by default.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
d8984e877f
commit
6aabe0b5ac
6 changed files with 120 additions and 2 deletions
|
|
@ -46,6 +46,57 @@ Copy this block when adding a new issue:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## #180 — Camera-collision sweep bistable at a compressed boom → per-frame eye strobe (the #176 "stripes")
|
||||||
|
|
||||||
|
**Status:** OPEN — mechanism PINNED numerically; fix = port retail's stateful sought-position
|
||||||
|
**Severity:** HIGH (the visible flicker/stripe artifact the #176 gate keeps failing on; corridor camera constantly rides walls)
|
||||||
|
**Filed:** 2026-07-06
|
||||||
|
**Component:** camera / physics (NOT render — every render suspect eliminated by isolation)
|
||||||
|
|
||||||
|
**Description (user, Facility Hub):** "geometrical patterns, triangles, that get
|
||||||
|
weird stripes… especially when I push the camera to the wall or the openings of
|
||||||
|
the corridor. If I zoom out and the camera does not touch the walls I get no
|
||||||
|
pattern." Reproduced autonomously (synthetic back-into-wall + GDI window
|
||||||
|
captures, `launch-176-cameye.log`).
|
||||||
|
|
||||||
|
**Root cause (probe-pinned):** while the compressed chase boom moves along/near
|
||||||
|
a wall, the `SweepEye` first-contact solution is BISTABLE: consecutive
|
||||||
|
`[flap-sweep]` records show the sweep INPUT moving ~1.4 mm (the player's glide)
|
||||||
|
while the OUTPUT flips 0.27 m along the boom (`pulledIn` 0.27 ↔ 0.53, eye
|
||||||
|
(78.500,−38.633,−3.845) ↔ (78.669,−38.815,−3.938)), re-flipping every ~5–10
|
||||||
|
frames — a knife-edge graze on the corridor's double-faced slabs (the #137
|
||||||
|
threshold family, camera edition; all 368k sweeps returned ok=True — this is
|
||||||
|
NOT the fallback path). Each flip hard-cuts the whole view matrix; at ~1700 fps
|
||||||
|
with no vsync every monitor refresh tear-interleaves BOTH views → fine
|
||||||
|
stripe/hatch patterns over surfaces (worst near seams where the 0.27 m parallax
|
||||||
|
is largest), flickering with movement. Confirmed present in the pure light
|
||||||
|
field (`ACDREAM_LIGHT_DEBUG=3` still shows it → not texture) and with the
|
||||||
|
shell clip trim disabled (`ACDREAM_CLIP_DEBUG=1` → not the clip gate).
|
||||||
|
Explains the residual #176 flicker reports post-lighting-fix: one of the two
|
||||||
|
alternating eyes sees the purple under-room parallax, the other doesn't.
|
||||||
|
|
||||||
|
**Retail anchor (the fix):** retail's sought position is STATEFUL —
|
||||||
|
`SmartBox` (0x00452d75) calls `CameraManager::UpdateCamera(mgr, &ret,
|
||||||
|
&this->viewer)` with the CURRENT swept viewer and assigns the RETURN to
|
||||||
|
`viewer_sought_position` (0x00452d84): the next frame's target derives from
|
||||||
|
the collided position (converges; re-extends gradually), so mm-jitter never
|
||||||
|
re-tests the full-length knife-edge ray per frame. acdream's
|
||||||
|
`RetailChaseCamera` recomputes the full-length desired boom from scratch every
|
||||||
|
frame. Fix = read `CameraManager::UpdateCamera` (0x00456660) + the sought
|
||||||
|
derivation and port the stateful shape — NO damping band-aid on the swept
|
||||||
|
result without that reading (workaround rule).
|
||||||
|
|
||||||
|
**Files:** `src/AcDream.App/Rendering/RetailChaseCamera.cs` (boom/desired-eye),
|
||||||
|
`src/AcDream.App/Rendering/PhysicsCameraCollisionProbe.cs` (`SweepEye` — port
|
||||||
|
verbatim per update_viewer 0x00453ce0, exonerated), `[flap-sweep]` probe
|
||||||
|
(ACDREAM_PROBE_FLAP) is the apparatus.
|
||||||
|
|
||||||
|
**Acceptance:** camera pressed into walls/openings while moving → no
|
||||||
|
stripe/hatch patterns, no per-frame view jumps (consecutive-frame eye deltas
|
||||||
|
stay continuous in `[flap-sweep]`); camera glides along walls like retail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## #179 — Lightning flash has no indoor gate (dormant until weather strobes ship)
|
## #179 — Lightning flash has no indoor gate (dormant until weather strobes ship)
|
||||||
|
|
||||||
**Status:** OPEN (dormant — zero production impact today)
|
**Status:** OPEN (dormant — zero production impact today)
|
||||||
|
|
@ -179,7 +230,21 @@ gate passes, then strip.
|
||||||
**Gate:** stand in the `0x8A020164` corridor, turn back-and-forth across the seam — the
|
**Gate:** stand in the `0x8A020164` corridor, turn back-and-forth across the seam — the
|
||||||
purple wedge must hold steady (its faceted SHAPE is retail-correct and stays).
|
purple wedge must hold steady (its faceted SHAPE is retail-correct and stays).
|
||||||
|
|
||||||
**Status:** 🟡 FIX SHIPPED — pending user gate
|
**⚠️ GATE 2026-07-06 (evening): FAILED on a RESIDUAL that turned out to be a
|
||||||
|
SEPARATE mechanism — flickering stripe/triangle patterns when the camera is
|
||||||
|
pushed into walls/openings. Isolation toggles killed every render suspect
|
||||||
|
(texture via `ACDREAM_LIGHT_DEBUG=3`, shell clip trim via `ACDREAM_CLIP_DEBUG=1`,
|
||||||
|
coplanar dat pairs, double-draws, seals); the artifact is the CAMERA-COLLISION
|
||||||
|
sweep strobing the eye 0.27 m per few frames at a compressed boom → tear-
|
||||||
|
interleaved views = stripes. Split to #180 (camera/physics). The lighting half
|
||||||
|
of #176 (the pool tracking the camera via flood scoping) remains FIXED and
|
||||||
|
live-verified; re-gate #176 after #180 lands. Also filed from this arc: the
|
||||||
|
site-A weenie light-registration LEAK (a portal's I100 light stacked ×2→×4
|
||||||
|
over one session as CreateObject re-sends re-registered it under fresh
|
||||||
|
entity ids — `[seam-ent]` L= showed `01D4:I100` four times) — fold into the
|
||||||
|
A7 arc or fix with #180's gate.**
|
||||||
|
|
||||||
|
**Status:** 🟡 lighting fix SHIPPED + verified; residual flicker = #180 (camera strobe)
|
||||||
**Severity:** MEDIUM (visible artifact along every corridor seam in the M1.5 dungeon)
|
**Severity:** MEDIUM (visible artifact along every corridor seam in the M1.5 dungeon)
|
||||||
**Filed:** 2026-07-06
|
**Filed:** 2026-07-06
|
||||||
**Component:** render — floor-portal polygons / portal surface state
|
**Component:** render — floor-portal polygons / portal surface state
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ in flat uint vTextureLayer;
|
||||||
// alpha < 0.05 (skip empty fragments — large
|
// alpha < 0.05 (skip empty fragments — large
|
||||||
// transparent overdraw cost otherwise)
|
// transparent overdraw cost otherwise)
|
||||||
uniform int uRenderPass;
|
uniform int uRenderPass;
|
||||||
|
uniform int uLightDebug; // #176 stripe hunt (see mesh_modern.vert) — mode 3 handled here
|
||||||
|
|
||||||
// SceneLighting UBO — IDENTICAL layout to mesh_instanced.frag binding=1.
|
// SceneLighting UBO — IDENTICAL layout to mesh_instanced.frag binding=1.
|
||||||
struct Light {
|
struct Light {
|
||||||
|
|
@ -85,6 +86,14 @@ void main() {
|
||||||
// Per-vertex Gouraud lighting from the vertex shader (ambient + capped lights).
|
// Per-vertex Gouraud lighting from the vertex shader (ambient + capped lights).
|
||||||
vec3 lit = vLit;
|
vec3 lit = vLit;
|
||||||
|
|
||||||
|
// #176 stripe-hunt mode 3: show the raw per-vertex light field (texture
|
||||||
|
// ignored). Stripes visible HERE = a vertex-lighting artifact; absent =
|
||||||
|
// the pattern comes from texture/per-pixel machinery. Throwaway diagnostic.
|
||||||
|
if (uLightDebug == 3) {
|
||||||
|
FragColor = vec4(min(lit, vec3(1.0)), 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Lightning flash — additive scene bump (matches mesh_instanced.frag).
|
// Lightning flash — additive scene bump (matches mesh_instanced.frag).
|
||||||
lit += uFogParams.z * vec3(0.6, 0.6, 0.75);
|
lit += uFogParams.z * vec3(0.6, 0.6, 0.75);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,11 @@ uniform mat4 uViewProjection;
|
||||||
// uDrawIDOffset pattern in BaseObjectRenderManager.cs line 845.
|
// uDrawIDOffset pattern in BaseObjectRenderManager.cs line 845.
|
||||||
uniform int uDrawIDOffset;
|
uniform int uDrawIDOffset;
|
||||||
uniform int uLightingMode; // A7 Fix D: 0 = OBJECT (plain Lambert + sun), 1 = ENVCELL (half-Lambert wrap, no sun)
|
uniform int uLightingMode; // A7 Fix D: 0 = OBJECT (plain Lambert + sun), 1 = ENVCELL (half-Lambert wrap, no sun)
|
||||||
|
// #176 stripe-hunt isolation modes (ACDREAM_LIGHT_DEBUG, throwaway diagnostic):
|
||||||
|
// 0 = off; 1 = ambient-only vLit (all point/sun contributions killed);
|
||||||
|
// 2 = DYNAMIC point lights killed (purples + viewer fill off, statics stay);
|
||||||
|
// 3 = handled in the frag (raw vLit visualization, texture ignored).
|
||||||
|
uniform int uLightDebug;
|
||||||
|
|
||||||
// SceneLighting UBO — binding=1 in the UBO namespace (GL keeps the SSBO and UBO
|
// SceneLighting UBO — binding=1 in the UBO namespace (GL keeps the SSBO and UBO
|
||||||
// binding tables separate, so this coexists with the binding=1 BatchBuffer SSBO
|
// binding tables separate, so this coexists with the binding=1 BatchBuffer SSBO
|
||||||
|
|
@ -178,6 +183,7 @@ vec3 pointContribution(vec3 N, vec3 worldPos, GlobalLight L) {
|
||||||
// bake's 1/d³ distance-cube, which makes a tight concentrated pool. No per-light
|
// bake's 1/d³ distance-cube, which makes a tight concentrated pool. No per-light
|
||||||
// cap — D3D accumulates then saturates, which accumulateLights does via min(pointAcc,1).
|
// cap — D3D accumulates then saturates, which accumulateLights does via min(pointAcc,1).
|
||||||
if (L.coneAngleEtc.y > 0.5) {
|
if (L.coneAngleEtc.y > 0.5) {
|
||||||
|
if (uLightDebug == 2) return vec3(0.0); // #176 stripe hunt: dynamics killed
|
||||||
vec3 Ldir = toL / max(d, 1e-4);
|
vec3 Ldir = toL / max(d, 1e-4);
|
||||||
float ndl = max(0.0, dot(N, Ldir));
|
float ndl = max(0.0, dot(N, Ldir));
|
||||||
if (ndl <= 0.0) return vec3(0.0);
|
if (ndl <= 0.0) return vec3(0.0);
|
||||||
|
|
@ -214,6 +220,7 @@ vec3 pointContribution(vec3 N, vec3 worldPos, GlobalLight L) {
|
||||||
|
|
||||||
vec3 accumulateLights(vec3 N, vec3 worldPos, int instanceIndex) {
|
vec3 accumulateLights(vec3 N, vec3 worldPos, int instanceIndex) {
|
||||||
vec3 lit = uCellAmbient.xyz;
|
vec3 lit = uCellAmbient.xyz;
|
||||||
|
if (uLightDebug == 1) return lit; // #176 stripe hunt: ambient only
|
||||||
|
|
||||||
// SUN / directional — OBJECT path only (mode 0). retail's EnvCell path
|
// SUN / directional — OBJECT path only (mode 0). retail's EnvCell path
|
||||||
// (minimize_envcell_lighting) enables only dynamic lights, NEVER the sun, so
|
// (minimize_envcell_lighting) enables only dynamic lights, NEVER the sun, so
|
||||||
|
|
|
||||||
|
|
@ -878,6 +878,8 @@ public sealed unsafe class EnvCellRenderer : IDisposable
|
||||||
_shader.SetInt("uRenderPass", (int)renderPass);
|
_shader.SetInt("uRenderPass", (int)renderPass);
|
||||||
_shader.SetInt("uFilterByCell", 0);
|
_shader.SetInt("uFilterByCell", 0);
|
||||||
_shader.SetInt("uLightingMode", 1); // A7 Fix D D-3/D-4: EnvCell bake (wrap points, no sun)
|
_shader.SetInt("uLightingMode", 1); // A7 Fix D D-3/D-4: EnvCell bake (wrap points, no sun)
|
||||||
|
// #176 stripe-hunt isolation (ACDREAM_LIGHT_DEBUG) — throwaway diagnostic.
|
||||||
|
_shader.SetInt("uLightDebug", AcDream.Core.Rendering.RenderingDiagnostics.LightDebugMode);
|
||||||
|
|
||||||
// Phase U.4 ROOT-CAUSE FIX (cell-shell flicker / "transparent walls when
|
// Phase U.4 ROOT-CAUSE FIX (cell-shell flicker / "transparent walls when
|
||||||
// moving"): upload uViewProjection HERE rather than inheriting it from
|
// moving"): upload uViewProjection HERE rather than inheriting it from
|
||||||
|
|
@ -1306,7 +1308,10 @@ public sealed unsafe class EnvCellRenderer : IDisposable
|
||||||
// instanceClipSlot[i] tracks Instances[i] through the MDI BaseInstance.
|
// instanceClipSlot[i] tracks Instances[i] through the MDI BaseInstance.
|
||||||
if (_clipSlotData.Length < uniqueInstanceCount)
|
if (_clipSlotData.Length < uniqueInstanceCount)
|
||||||
_clipSlotData = new uint[Math.Max(_clipSlotData.Length * 2, uniqueInstanceCount)];
|
_clipSlotData = new uint[Math.Max(_clipSlotData.Length * 2, uniqueInstanceCount)];
|
||||||
if (_cellIdToSlot is null)
|
// #176 stripe-hunt isolation (ACDREAM_CLIP_DEBUG=1): force every shell
|
||||||
|
// instance to slot 0 (no-clip) — retail draws cell shells WHOLE.
|
||||||
|
if (_cellIdToSlot is null
|
||||||
|
|| AcDream.Core.Rendering.RenderingDiagnostics.ClipDebugNoShellTrim)
|
||||||
{
|
{
|
||||||
Array.Clear(_clipSlotData, 0, uniqueInstanceCount);
|
Array.Clear(_clipSlotData, 0, uniqueInstanceCount);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -910,6 +910,8 @@ public sealed unsafe class WbDrawDispatcher : IDisposable
|
||||||
// A7 Fix D D-3/D-4: object path — plain Lambert points + sun. MUST set
|
// A7 Fix D D-3/D-4: object path — plain Lambert points + sun. MUST set
|
||||||
// explicitly (shared GL uniform; EnvCellRenderer sets it to 1).
|
// explicitly (shared GL uniform; EnvCellRenderer sets it to 1).
|
||||||
_shader.SetInt("uLightingMode", 0);
|
_shader.SetInt("uLightingMode", 0);
|
||||||
|
// #176 stripe-hunt isolation (ACDREAM_LIGHT_DEBUG) — throwaway diagnostic.
|
||||||
|
_shader.SetInt("uLightDebug", AcDream.Core.Rendering.RenderingDiagnostics.LightDebugMode);
|
||||||
|
|
||||||
// #128 self-heal: fresh re-request dedup per Draw pass.
|
// #128 self-heal: fresh re-request dedup per Draw pass.
|
||||||
_missRequested.Clear();
|
_missRequested.Clear();
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,36 @@ public static class RenderingDiagnostics
|
||||||
public static IReadOnlySet<uint> SeamDrawTargetCells { get; } =
|
public static IReadOnlySet<uint> SeamDrawTargetCells { get; } =
|
||||||
ParseSeamDrawTargets(Environment.GetEnvironmentVariable("ACDREAM_PROBE_SEAMDRAW"));
|
ParseSeamDrawTargets(Environment.GetEnvironmentVariable("ACDREAM_PROBE_SEAMDRAW"));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// #176 stripe-hunt shader isolation mode (<c>ACDREAM_LIGHT_DEBUG</c>) —
|
||||||
|
/// throwaway diagnostic, uploaded as <c>uLightDebug</c> by EnvCellRenderer +
|
||||||
|
/// WbDrawDispatcher each pass. 0 = off; 1 = ambient-only vertex lighting
|
||||||
|
/// (all point/sun contributions killed); 2 = DYNAMIC point lights killed
|
||||||
|
/// (the intensity-100 portal purples + the viewer fill off; statics stay);
|
||||||
|
/// 3 = raw vLit visualization in the fragment shader (texture ignored).
|
||||||
|
/// Discriminates lighting-driven stripes (gone at 1/2, visible in the field
|
||||||
|
/// at 3) from texture/per-pixel machinery (survive 1). Settable for a
|
||||||
|
/// future DebugPanel mirror.
|
||||||
|
/// </summary>
|
||||||
|
public static int LightDebugMode { get; set; } =
|
||||||
|
int.TryParse(Environment.GetEnvironmentVariable("ACDREAM_LIGHT_DEBUG"), out var ldm) ? ldm : 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// #176 stripe-hunt isolation (<c>ACDREAM_CLIP_DEBUG=1</c>) — throwaway
|
||||||
|
/// diagnostic. When true, the EnvCell SHELL pass maps every instance to clip
|
||||||
|
/// slot 0 (the reserved no-clip region) instead of its cell's portal-slice
|
||||||
|
/// region — i.e. shells draw WHOLE, retail's shape (retail never clips cell
|
||||||
|
/// geometry; aperture exactness comes from the seal/punch depth writes +
|
||||||
|
/// far→near order, PView::DrawCells 0x005a4840). Discriminator for the
|
||||||
|
/// camera-against-wall stripe/hatch pattern: a knife-edge slice region (eye
|
||||||
|
/// on a portal plane / inside a wall) yields a clip plane near-parallel to
|
||||||
|
/// large surfaces → interpolated gl_ClipDistance ≈ 0 across them → per-pixel
|
||||||
|
/// sign dither = the hatch. Stripes gone with this on = the shell trim is
|
||||||
|
/// the mechanism. Entity/dispatcher clip routing is NOT affected.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ClipDebugNoShellTrim { get; set; } =
|
||||||
|
Environment.GetEnvironmentVariable("ACDREAM_CLIP_DEBUG") == "1";
|
||||||
|
|
||||||
/// <summary>Parse ACDREAM_PROBE_SEAMDRAW: "1"/"true"/empty → the default #176
|
/// <summary>Parse ACDREAM_PROBE_SEAMDRAW: "1"/"true"/empty → the default #176
|
||||||
/// Facility Hub set; otherwise a comma-separated hex cell-id list (same forgiving
|
/// Facility Hub set; otherwise a comma-separated hex cell-id list (same forgiving
|
||||||
/// grammar as <see cref="ParseDumpEntityIds"/>). Internal for unit tests.</summary>
|
/// grammar as <see cref="ParseDumpEntityIds"/>). Internal for unit tests.</summary>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue