test(render): foliage-wind receiver fallback guard asserts branch shape; cascade-0 publish covered (Campaign VM VM6 review 5 nits)
Round-5 narrow re-review of 754d59d9: APPROVE (verified to the SPIR-V
disassembly; shadows-on path proven opcode-identical). Four
non-blocking nits.
N1 (test robustness): T3 (ReceiverVertexShadersFallBackToThePlainLightDirectionWhenTheShadowFlagIsClear)
was satisfied by comment prose -- round 5's own explanatory comments
quote the exact plain-pipeline direction substring
("-uLights[i].dirAndRange.xyz, ... matched bit-for-bit"), so the
substring-only assertion passed even with the code mutated. Fixed:
comment lines are now stripped (new StripLineComments helper) before
any assertion, and a new ordered regex per shader asserts the exact
branch SHAPE -- shadowGatedOff ? -uLights[...] : normalize(uShadow...)
-- not just substring presence. Mutation-tested locally against the
rewritten test: (1) swapping the ternary's true/false operands --
FAILED (previously passed); (2) deleting the fallback entirely,
collapsing to the pre-round-5 buggy expression -- FAILED (previously
passed). Original file restored and reverified passing after each
mutation.
N2 (doc accuracy): "numerically the plain pipeline" overstated the
round-5 fix in three places (plan doc, mesh_atmospheric.vert,
terrain_atmospheric.vert). The direction expression is bit-for-bit;
the SUM is not, because the atmospheric shaders' split ambient+point
vs directional accumulation (and terrain's two varyings vs the plain
pipeline's one) reassociates float summation order by ~1 ulp -- which
is exactly the measured mean |Delta| 0.007 the coordinator's own
pixel-proof evidence already recorded (well under the 65 px noise
floor). All three rewritten to say the receiver "matches the plain
pipeline to within float summation-order rounding (measured mean
|Delta| 0.007 on the offline scene)."
N3 (coverage): RenderPrepared's own cascadeCount == 0 exit (the F2
fix) had no direct test even though RenderPrepared already has 8
direct call sites in this file. Added one: ResidentMaximumReachMeters
at/below CameraNearMeters, passed straight to RenderPrepared with an
otherwise-fully-valid environment (so the fitter, not the environment
gate, is what returns zero cascades), asserting IsBindableFor true /
IsValidFor false.
N4 (latent): RenderPrepared's OWN "if (!environment.ShouldRender)"
exit is a fourth bufferless-disabled path -- unreachable via Render
(whose own gate already validates ShouldRender first) but the same
shape, and RenderPrepared is called directly by tests and any future
caller. Took the preferred fix: publishes the disabled binding there
too, via the same helper, so every exit on a frame that draws the
world publishes when the pack's AtmosphericFrame is bound. Also made
EvaluateGateAndPublishDisabledBinding self-contained: it now resets
_currentFrameBinding to Disabled on its own entry instead of relying
on Render having done so first (idempotent with Render's own reset).
Regenerated SPIR-V: mesh_atmospheric.vert and terrain_atmospheric.vert
are comment-only changes (N2), so only the manifest's source hashes
changed -- compiled .spv bytes are unchanged, consistent with round 3's
precedent for comment-only shader edits.
Verify: Release build 0 warnings/0 errors. App hermetic-lane filter
6,055/0 failed. Full hermetic-filtered solution: 15,283/0 failed
across 15 projects.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
9a49c66d47
commit
1f15124271
7 changed files with 154 additions and 25 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using AcDream.App.Plugins;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Gpu;
|
||||
|
|
@ -1225,28 +1226,38 @@ public sealed class AtmosphericPostProcessGraphTests
|
|||
[Fact]
|
||||
public void ReceiverVertexShadersFallBackToThePlainLightDirectionWhenTheShadowFlagIsClear()
|
||||
{
|
||||
// Campaign VM VM6 review fix round 5 (F1 BLOCKER, test T3): pins
|
||||
// the shader SOURCE TEXT so a future edit that removes the
|
||||
// flag-gated fallback (reintroducing "a shadow-gated-off frame
|
||||
// lights outdoor terrain/objects from the disabled block's
|
||||
// (0,0,1) placeholder instead of the authored sun") fails this
|
||||
// test instead of only showing up in a pixel capture. Both
|
||||
// receiver vertex shaders must (1) test the SAME flags bit
|
||||
// acdreamDirectionalShadowVisibility already tests, and (2)
|
||||
// reference the SAME uLights-derived expression the corresponding
|
||||
// plain (mesh_modern.vert / terrain_modern.vert) pipeline uses,
|
||||
// proving the fallback branch is not some other, unverified
|
||||
// direction.
|
||||
// Campaign VM VM6 review fix round 5 (F1 BLOCKER, test T3), hardened
|
||||
// round 6 (N1): the original version of this test asserted only
|
||||
// that the plain-pipeline direction SUBSTRING appeared somewhere
|
||||
// in the receiver file — but the round-5 fix's own explanatory
|
||||
// comments ALSO quote that exact substring in prose ("(-uLights[i]
|
||||
// .dirAndRange.xyz, ... matched bit-for-bit)"), so the check was
|
||||
// satisfied by the COMMENT even with the CODE mutated. Mutation-
|
||||
// tested locally against this rewritten test: (1) swapping the
|
||||
// ternary's true/false operands — PASSED the old substring-only
|
||||
// test, FAILS this one; (2) deleting the fallback entirely
|
||||
// (always taking the shadow-block direction, i.e. collapsing the
|
||||
// ternary to just its false arm) — PASSED the old test, FAILS
|
||||
// this one. Comment lines are stripped before matching, and the
|
||||
// assertion is the exact branch SHAPE — the gated-off condition,
|
||||
// then the plain uLights expression on the TRUE arm, then
|
||||
// uShadowLightDirectionAndSource on the FALSE arm — via an
|
||||
// ordered regex, not a substring search that a comment can also
|
||||
// satisfy.
|
||||
string shaderRoot = Path.Combine(
|
||||
RepositoryRoot(),
|
||||
"src",
|
||||
"AcDream.App",
|
||||
"Rendering",
|
||||
"Shaders");
|
||||
string meshAtmospheric = File.ReadAllText(Path.Combine(shaderRoot, "mesh_atmospheric.vert"));
|
||||
string meshModern = File.ReadAllText(Path.Combine(shaderRoot, "mesh_modern.vert"));
|
||||
string terrainAtmospheric = File.ReadAllText(Path.Combine(shaderRoot, "terrain_atmospheric.vert"));
|
||||
string terrainModern = File.ReadAllText(Path.Combine(shaderRoot, "terrain_modern.vert"));
|
||||
string meshAtmospheric = StripLineComments(
|
||||
File.ReadAllText(Path.Combine(shaderRoot, "mesh_atmospheric.vert")));
|
||||
string meshModern = StripLineComments(
|
||||
File.ReadAllText(Path.Combine(shaderRoot, "mesh_modern.vert")));
|
||||
string terrainAtmospheric = StripLineComments(
|
||||
File.ReadAllText(Path.Combine(shaderRoot, "terrain_atmospheric.vert")));
|
||||
string terrainModern = StripLineComments(
|
||||
File.ReadAllText(Path.Combine(shaderRoot, "terrain_modern.vert")));
|
||||
|
||||
const string flagTest = "(uShadowTextureAndFlags.w & 1u) == 0u";
|
||||
Assert.Contains(flagTest, meshAtmospheric, StringComparison.Ordinal);
|
||||
|
|
@ -1254,7 +1265,8 @@ public sealed class AtmosphericPostProcessGraphTests
|
|||
|
||||
// mesh: both files negate the per-light uniform inline (no
|
||||
// intermediate variable), so the literal expression matches
|
||||
// string-for-string.
|
||||
// string-for-string once comments can no longer supply a false
|
||||
// positive.
|
||||
const string meshPlainDirection = "-uLights[i].dirAndRange.xyz";
|
||||
Assert.Contains(meshPlainDirection, meshModern, StringComparison.Ordinal);
|
||||
Assert.Contains(meshPlainDirection, meshAtmospheric, StringComparison.Ordinal);
|
||||
|
|
@ -1269,8 +1281,46 @@ public sealed class AtmosphericPostProcessGraphTests
|
|||
"-" + terrainLightUniform,
|
||||
terrainAtmospheric,
|
||||
StringComparison.Ordinal);
|
||||
|
||||
// Branch-shape guard (N1): fails on a swapped ternary or a
|
||||
// deleted fallback even though the substrings above would still
|
||||
// be present somewhere in the (comment-stripped) file.
|
||||
var meshBranchShape = new Regex(
|
||||
@"shadowGatedOff\s*\?\s*-uLights\[i\]\.dirAndRange\.xyz\s*:\s*normalize\(uShadowLightDirectionAndSource\.xyz\)",
|
||||
RegexOptions.Singleline);
|
||||
Assert.True(
|
||||
meshBranchShape.IsMatch(meshAtmospheric),
|
||||
"mesh_atmospheric.vert must branch exactly: "
|
||||
+ "shadowGatedOff ? -uLights[i].dirAndRange.xyz : normalize(uShadowLightDirectionAndSource.xyz)");
|
||||
|
||||
var terrainBranchShape = new Regex(
|
||||
@"shadowGatedOff\s*\?\s*-uLights\[0\]\.dirAndRange\.xyz\s*:\s*normalize\(uShadowLightDirectionAndSource\.xyz\)",
|
||||
RegexOptions.Singleline);
|
||||
Assert.True(
|
||||
terrainBranchShape.IsMatch(terrainAtmospheric),
|
||||
"terrain_atmospheric.vert must branch exactly: "
|
||||
+ "shadowGatedOff ? -uLights[0].dirAndRange.xyz : normalize(uShadowLightDirectionAndSource.xyz)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Campaign VM VM6 review fix round 6 (N1): removes everything from
|
||||
/// the first <c>//</c> on each line onward. GLSL has no string
|
||||
/// literals in these files, so a per-line truncation is safe and
|
||||
/// cannot itself corrupt real code — it exists specifically so a
|
||||
/// shader-text assertion cannot be satisfied by a comment that quotes
|
||||
/// the expected code as prose instead of by the code itself.
|
||||
/// </summary>
|
||||
private static string StripLineComments(string glsl)
|
||||
{
|
||||
string[] lines = glsl.Split('\n');
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
int index = lines[i].IndexOf("//", StringComparison.Ordinal);
|
||||
if (index >= 0)
|
||||
lines[i] = lines[i][..index];
|
||||
}
|
||||
return string.Join('\n', lines);
|
||||
}
|
||||
[Fact]
|
||||
public void FirstAdvanceSnapsExactlyToTheWeatherTargetInsteadOfEasingFromZero()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue