refactor(render): delete superseded alpha sidecars
Remove the dead classic-group CachedBatch/InstanceGroup sort-center sidecar and its cache, append, reset, and diagnostic-digest plumbing. Remove the discarded camera parameter chain from grouped alpha deferral and dispatcher submission diagnostics while preserving walk/particle CYpt keys, FIFO submission ordinals, and opaque camera distance. Add production-facing reflection/source pins for the deleted shapes and camera chain, retained per-cell and opaque owners, the exact three AlphaFlushCounts reasons, and deleted alpha-order symbols. Update directly affected cache/digest/group lifecycle tests and record the complete section 22 result. Gates: Release 0W/0E; focused App 132/132 plus Core 29/29; new pins 4/4; real allocation pins 2/2 at 0 B; shaders 32/32; hermetic 16755/16755; InstalledDat exactly 385 pass, 10 documented fail, 1 skip; diff-check clean. Mutations: InstanceGroup and CachedBatch sidecars fail their Assert.Null pins; Defer and digest camera parameters fail method-shape pins; false count prose fails the exact-reason pin; RetailAlphaOrdering resurrection fails the deletion pin. Each was restored independently before the final matrix. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a0523e6057
commit
26e97ba412
7 changed files with 224 additions and 105 deletions
|
|
@ -1990,3 +1990,93 @@ call reachability, diagnostic-digest consequences, cache/reset/storage
|
|||
lifecycle, allocation, allowed scope, every mutation claim, and all gate
|
||||
counts. A failed lens gets one bounded fix contract; a third fix round stops
|
||||
and is written up. Dual PASS lands c3b and unlocks G3.
|
||||
|
||||
### 22.7 S4-c3b implementation result (implementer, 2026-09-04)
|
||||
|
||||
PASS for the bounded implementation return. The classic grouped dispatcher no
|
||||
longer carries `CachedBatch.LocalSortCenter` or
|
||||
`InstanceGroup.LocalSortCenters`; their constructor/cache-hit/append/reset/
|
||||
release and diagnostic-hash plumbing is gone. The discarded alpha camera
|
||||
parameter is also absent from `DeferTransparentGroups`,
|
||||
`BuildTransparentSubmissionDigest`, `CreateDispatcherSubmission`,
|
||||
`ObserveCurrentDispatcherSubmission`, and
|
||||
`ObserveClassifiedDispatcherSubmission`, with no camera tombstone remaining.
|
||||
The diagnostic transparent digest therefore hashes only rendered state plus the
|
||||
live submission ordinal, as intended.
|
||||
|
||||
Production-facing reflection/source pins prove the deletion and retain the
|
||||
separate live owners: `WalkClassifiedBatch.LocalSortCenter`/
|
||||
`SortDistanceSq`, the transformed authored-center per-cell stable descending
|
||||
sort, and `PartitionInstanceGroups(cameraWorldPosition)` feeding opaque
|
||||
`InstanceGroup.SortDistance`. Existing cache-hit matrix composition,
|
||||
transparent submission-order digest, group lifecycle, walk/particle merge,
|
||||
queue/router, allocation, and shader/state tests remain green. The
|
||||
`AlphaFlushCounts_*` source-truth pin keeps exactly the content-empty replay,
|
||||
AP-238 `(cell,list)` coalescing, and AP-239/AP-240 list-membership reasons and
|
||||
explicitly preserves the fact that the former broad “per instance versus per
|
||||
subset” explanation was false. Static production truth rejects resurrection of
|
||||
`RetailAlphaOrdering`, `FlushFartherThan`, alpha-submit `viewerDistance`, or a
|
||||
discarded camera tombstone.
|
||||
|
||||
Automated return:
|
||||
|
||||
- The first `dotnet build AcDream.slnx -c Release --no-restore` invocation did
|
||||
not reach compilation because the fresh worktree had no
|
||||
`obj/project.assets.json` files (`NETSDK1004`, 57 projects). One
|
||||
`dotnet restore AcDream.slnx` populated those assets; the Release rerun then
|
||||
passed with 0 warnings / 0 errors. This was dependency preparation, not a
|
||||
product retry.
|
||||
- focused App
|
||||
`InstanceGroupClearTests|WalkStaticStreamPopulatorTests|ParticleRendererRouteTests|WalkFrameDriverTests|RetailAlphaQueueTests|RetailParticleGeometryClassifierTests`
|
||||
— PASS, 132/132; focused Core
|
||||
`WbDrawDispatcherBucketingTests|EntityClassificationCacheTests` — PASS,
|
||||
29/29 (161/161 aggregate);
|
||||
- the four new deletion/truth pins — PASS, 4/4 after every mutation restore;
|
||||
- real warmed
|
||||
`ProductionCellObjectParticleMerge_WarmedPathAllocatesZeroBytes|ProductionImmediateMesh_WarmedDrawImmediateParticleSubmissionRhiDoesNotAllocate`
|
||||
— PASS, 2/2, both observing 0 managed bytes;
|
||||
- `VulkanShaderDescriptorContractTests|VulkanShaderManifestTests|RenderPackSpirvValidatorTests`
|
||||
— PASS, 32/32; no shader, SPIR-V, manifest, RHI, or pipeline file changed;
|
||||
- official no-retry 14-assembly hermetic gate — PASS, 16,755 passed / 0
|
||||
skipped / 0 failed; evidence
|
||||
`artifacts/s4-c3b-hermetic-20260904`;
|
||||
- InstalledDat with explicit `C:\Turbine\Asheron's Call`, inclusive of the
|
||||
retained KnownFailures — 385 passed / 10 expected failed / 1 skipped. The
|
||||
failures were exactly TowerAscent, the two #383 layout sweeps,
|
||||
`Oh_doorway_still_first_frame_diff` (#458), and the six
|
||||
`AlphaFlushCounts_*`; the skip remained `Sweep_the_lookin_gate_decodes_against_the_street_fixture`.
|
||||
No new failure appeared; evidence
|
||||
`artifacts/s4-c3b-installed-inclusive-20260904`.
|
||||
|
||||
Mutation ledger (each mutation was applied alone and restored by patch before
|
||||
the next; the restored pin set passed 4/4):
|
||||
|
||||
1. Restoring `InstanceGroup.LocalSortCenters` first failed
|
||||
`ClassicGroupedShapes_DeleteDeadSortCenterSidecarsButRetainWalkCyptKey` at
|
||||
its second `Assert.Null`: expected null; actual
|
||||
`List<Vector3> LocalSortCenters`.
|
||||
2. Restoring `CachedBatch.LocalSortCenter` first failed the same pin at its
|
||||
first `Assert.Null`: expected null; actual `Vector3 LocalSortCenter`.
|
||||
3. Restoring the `DeferTransparentGroups(Vector3, Matrix4x4)` parameter and
|
||||
tombstone first failed
|
||||
`AlphaSubmissionShapes_DeleteDeadCameraChainButRetainOpaqueCameraDistance`:
|
||||
`Assert.Equal` expected `[Matrix4x4]`, actual `[Vector3, Matrix4x4]`.
|
||||
4. Restoring `cameraWorldPosition` on
|
||||
`BuildTransparentSubmissionDigest` first failed the same pin's next
|
||||
`Assert.DoesNotContain`: the parameter collection contained
|
||||
`Vector3 cameraWorldPosition` at position 1.
|
||||
5. Replacing the exact three-reason prose with a false broad fourth reason
|
||||
first failed
|
||||
`AlphaFlushCountKnownFailure_StatesOnlyTheThreeExactReasons` at
|
||||
`Assert.Contains`: the required `former broad "per instance versus per
|
||||
subset" explanation was false.` substring was absent.
|
||||
6. Restoring an executable `RetailAlphaOrdering` symbol first failed
|
||||
`ClassicGroupedAlphaSourceTruth_DeletesDeadOrderingChainAndPreservesLiveOwners`
|
||||
at `Assert.DoesNotContain`: substring `RetailAlphaOrdering` was found.
|
||||
|
||||
The implementation changes only the three allowed production files, the two
|
||||
named App test files, the one directly affected Core rendering test file, and
|
||||
this result. AD-119 and AP-238 through AP-243, queue/router/list membership,
|
||||
capacities, barriers, particles, per-cell ordering, building/private/portal
|
||||
distances, shaders/RHI, package/content, and graphical behavior are unchanged.
|
||||
No graphical client was launched.
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ namespace AcDream.App.Rendering.Wb;
|
|||
/// subPart contributes its own <see cref="CachedBatch"/> entries, with
|
||||
/// <see cref="RestPose"/> already containing the
|
||||
/// <c>subPart.PartTransform * meshRef.PartTransform</c> product.
|
||||
/// <see cref="LocalSortCenter"/> preserves the authored GfxObj key used by
|
||||
/// retail's delayed-alpha viewer-distance ordering on cache hits.
|
||||
/// <see cref="Group"/> is a registration-checked fast handle into the
|
||||
/// dispatcher's bounded group table; a retired or synthetic entry falls back
|
||||
/// to key lookup and refreshes this array entry.
|
||||
|
|
@ -25,7 +23,6 @@ internal readonly record struct CachedBatch(
|
|||
GroupKey Key,
|
||||
GpuTextureSlot TextureSlot,
|
||||
Matrix4x4 RestPose,
|
||||
Vector3 LocalSortCenter = default,
|
||||
WbDrawDispatcher.InstanceGroup? Group = null,
|
||||
long GroupRegistration = 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -143,13 +143,12 @@ public sealed partial class WbDrawDispatcher
|
|||
/// one needs to reach the alpha queue: <see cref="IsOpaque"/> (so the
|
||||
/// populator can route without re-deriving it from
|
||||
/// <see cref="GroupKey.Translucency"/>) and <see cref="LocalSortCenter"/>
|
||||
/// (the authored GfxObj sort center — the same value
|
||||
/// <c>InstanceGroup.LocalSortCenters</c> carries per instance today; S4-c2
|
||||
/// deleted its one alpha-ordering consumer, <c>RetailAlphaOrdering.ComputeViewerDistance</c>,
|
||||
/// but S4-c3a restores its retail use: <c>WalkStaticStreamPopulator</c>
|
||||
/// transforms it through the part model and retains the resulting
|
||||
/// squared CYpt key in <see cref="SortDistanceSq"/> before the per-cell
|
||||
/// stable sort. The key feeds two FIFO alpha lists; neither queue sorts.
|
||||
/// (the authored GfxObj sort center consumed by the live per-cell CYpt
|
||||
/// sort; the classic grouped path has no corresponding sidecar).
|
||||
/// <c>WalkStaticStreamPopulator</c> transforms it through the part model
|
||||
/// and retains the resulting squared CYpt key in
|
||||
/// <see cref="SortDistanceSq"/> before the per-cell stable sort. The key
|
||||
/// feeds two FIFO alpha lists; neither queue sorts.
|
||||
/// </summary>
|
||||
internal readonly record struct WalkClassifiedBatch(
|
||||
GroupKey Key,
|
||||
|
|
|
|||
|
|
@ -1818,8 +1818,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
||||
visibleInstanceCount: 0,
|
||||
immediateInstanceCount: 0,
|
||||
deferTransparent: false,
|
||||
camPos);
|
||||
deferTransparent: false);
|
||||
_cpuStopwatch.Stop();
|
||||
if (diag) MaybeFlushDiag();
|
||||
return;
|
||||
|
|
@ -1841,8 +1840,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
||||
visibleInstanceCount: 0,
|
||||
immediateInstanceCount: 0,
|
||||
deferTransparent,
|
||||
camPos);
|
||||
deferTransparent);
|
||||
_cpuStopwatch.Stop();
|
||||
if (diag) MaybeFlushDiag();
|
||||
return;
|
||||
|
|
@ -1850,7 +1848,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
|
||||
_opaqueDraws.Sort(CompareOpaqueSubmissionOrder);
|
||||
if (deferTransparent)
|
||||
DeferTransparentGroups(camPos, vp);
|
||||
DeferTransparentGroups(vp);
|
||||
else
|
||||
_translucentDraws.Sort(CompareTransparentSubmissionOrder);
|
||||
|
||||
|
|
@ -1969,8 +1967,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
||||
totalInstances,
|
||||
immediateInstances,
|
||||
deferTransparent,
|
||||
camPos);
|
||||
deferTransparent);
|
||||
|
||||
// Campaign V slice V11: every per-frame upload is a frame ring slice
|
||||
// bound through the borrowed world pass, which retires the buffer-set
|
||||
|
|
@ -2153,8 +2150,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
private void ObserveCurrentDispatcherSubmission(
|
||||
int visibleInstanceCount,
|
||||
int immediateInstanceCount,
|
||||
bool deferTransparent,
|
||||
Vector3 cameraWorldPosition)
|
||||
bool deferTransparent)
|
||||
{
|
||||
ICurrentRenderDispatcherObserver? observer =
|
||||
_currentRenderSceneObserver;
|
||||
|
|
@ -2168,7 +2164,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
deferTransparent,
|
||||
_opaqueDraws,
|
||||
_translucentDraws,
|
||||
cameraWorldPosition,
|
||||
_alphaFingerprintScratch);
|
||||
observer.ObserveDispatcherSubmission(in submission);
|
||||
}
|
||||
|
|
@ -2177,16 +2172,14 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
bool observeCurrentPath,
|
||||
int visibleInstanceCount,
|
||||
int immediateInstanceCount,
|
||||
bool deferTransparent,
|
||||
Vector3 cameraWorldPosition)
|
||||
bool deferTransparent)
|
||||
{
|
||||
if (observeCurrentPath)
|
||||
{
|
||||
ObserveCurrentDispatcherSubmission(
|
||||
visibleInstanceCount,
|
||||
immediateInstanceCount,
|
||||
deferTransparent,
|
||||
cameraWorldPosition);
|
||||
deferTransparent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2197,7 +2190,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
bool deferTransparent,
|
||||
IReadOnlyList<InstanceGroup> opaque,
|
||||
IReadOnlyList<InstanceGroup> transparent,
|
||||
Vector3 cameraWorldPosition,
|
||||
List<AlphaFingerprint> alphaScratch)
|
||||
{
|
||||
// The no-VAO production early-out deliberately skips group
|
||||
|
|
@ -2226,7 +2218,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
RenderSceneHash128 transparentDigest =
|
||||
BuildTransparentSubmissionDigest(
|
||||
acceptedTransparent,
|
||||
cameraWorldPosition,
|
||||
alphaScratch);
|
||||
RenderSceneHash128 transparentSetDigest =
|
||||
BuildOpaqueSubmissionDigest(acceptedTransparent);
|
||||
|
|
@ -2285,11 +2276,8 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
|
||||
private static RenderSceneHash128 BuildTransparentSubmissionDigest(
|
||||
IReadOnlyList<InstanceGroup> groups,
|
||||
Vector3 cameraWorldPosition,
|
||||
List<AlphaFingerprint> scratch)
|
||||
{
|
||||
_ = cameraWorldPosition; // S4-c2: the digest reflects retail's FIFO
|
||||
// submission order, not a distance sort.
|
||||
scratch.Clear();
|
||||
for (int groupIndex = 0;
|
||||
groupIndex < groups.Count;
|
||||
|
|
@ -2395,7 +2383,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
int index)
|
||||
{
|
||||
hash.Add(group.Matrices[index]);
|
||||
hash.Add(group.LocalSortCenters[index]);
|
||||
hash.Add(group.Slots[index]);
|
||||
InstanceLightSet lights = group.LightSets[index];
|
||||
for (int lightIndex = 0;
|
||||
|
|
@ -2410,11 +2397,8 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
hash.Add(group.SelectionLighting[index]);
|
||||
}
|
||||
|
||||
private void DeferTransparentGroups(Vector3 cameraWorldPosition, Matrix4x4 viewProjection)
|
||||
private void DeferTransparentGroups(Matrix4x4 viewProjection)
|
||||
{
|
||||
_ = cameraWorldPosition; // S4-c2: retail's queues are FIFO, not distance-sorted; kept as a
|
||||
// parameter so callers need not change (WalkClassify's sibling
|
||||
// call site still needs it for nothing else either).
|
||||
RetailAlphaQueue queue = _alphaQueue!;
|
||||
if (_deferredAlpha.Count == 0)
|
||||
_deferredAlphaViewProjection = viewProjection;
|
||||
|
|
@ -2858,7 +2842,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
internal static void ApplyCacheHit(
|
||||
EntityCacheEntry entry,
|
||||
Matrix4x4 entityWorld,
|
||||
Action<GroupKey, Matrix4x4, Vector3> appendInstance)
|
||||
Action<GroupKey, Matrix4x4> appendInstance)
|
||||
{
|
||||
foreach (var cached in entry.Batches)
|
||||
{
|
||||
|
|
@ -2869,8 +2853,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
// out of sync with it.
|
||||
appendInstance(
|
||||
cached.Key,
|
||||
cached.RestPose * entityWorld,
|
||||
cached.LocalSortCenter);
|
||||
cached.RestPose * entityWorld);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2899,7 +2882,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
GroupRegistration = group.Registration,
|
||||
};
|
||||
}
|
||||
AppendInstanceToGroup(group!, model, cached.LocalSortCenter);
|
||||
AppendInstanceToGroup(group!, model);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3012,11 +2995,10 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
/// </summary>
|
||||
private void AppendInstanceToGroup(
|
||||
GroupKey key,
|
||||
Matrix4x4 model,
|
||||
Vector3 localSortCenter)
|
||||
Matrix4x4 model)
|
||||
{
|
||||
InstanceGroup grp = GetOrCreateInstanceGroup(key);
|
||||
AppendInstanceToGroup(grp, model, localSortCenter);
|
||||
AppendInstanceToGroup(grp, model);
|
||||
}
|
||||
|
||||
private InstanceGroup GetOrCreateInstanceGroup(GroupKey key)
|
||||
|
|
@ -3047,12 +3029,10 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
|
||||
private void AppendInstanceToGroup(
|
||||
InstanceGroup grp,
|
||||
Matrix4x4 model,
|
||||
Vector3 localSortCenter)
|
||||
Matrix4x4 model)
|
||||
{
|
||||
grp.LastUsedFrame = _groupFrame;
|
||||
grp.Matrices.Add(model);
|
||||
grp.LocalSortCenters.Add(localSortCenter);
|
||||
grp.SubmissionOrders.Add(_nextInstanceSubmissionOrder++);
|
||||
grp.Slots.Add(_currentEntitySlot); // Phase U.4 — parallel to Matrices
|
||||
AppendCurrentLightSet(grp); // Fix B — 8 ints per instance, parallel to Matrices
|
||||
|
|
@ -3231,7 +3211,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
|
||||
InstanceGroup grp = GetOrCreateInstanceGroup(key);
|
||||
grp.Matrices.Add(model);
|
||||
grp.LocalSortCenters.Add(renderData.SortCenter);
|
||||
grp.SubmissionOrders.Add(_nextInstanceSubmissionOrder++);
|
||||
grp.Slots.Add(_currentEntitySlot); // Phase U.4 — parallel to Matrices
|
||||
AppendCurrentLightSet(grp); // Fix B — 8 ints per instance, parallel to Matrices
|
||||
|
|
@ -3241,7 +3220,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
key,
|
||||
texSlot,
|
||||
restPose,
|
||||
renderData.SortCenter,
|
||||
grp,
|
||||
grp.Registration));
|
||||
}
|
||||
|
|
@ -3712,11 +3690,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
public float SortDistance; // squared distance from camera to first instance, for opaque sort
|
||||
public readonly List<Matrix4x4> Matrices = new();
|
||||
|
||||
// Retail CPhysicsPart::CYpt uses the transformed GfxObj sort center,
|
||||
// not the entity origin. Parallel to Matrices so delayed-alpha
|
||||
// submissions retain the exact per-part key after material grouping.
|
||||
public readonly List<Vector3> LocalSortCenters = new();
|
||||
|
||||
// Retail CShadowPart::insertion_sort (0x006B5130) is stable for equal
|
||||
// CYpt. Material groups erase authored entity/part/batch traversal
|
||||
// unless that order is retained explicitly. SubmissionOrders[i] is
|
||||
|
|
@ -3771,7 +3744,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
public void ClearPerInstanceData()
|
||||
{
|
||||
Matrices.Clear();
|
||||
LocalSortCenters.Clear();
|
||||
SubmissionOrders.Clear();
|
||||
Slots.Clear();
|
||||
LightSets.Clear();
|
||||
|
|
@ -3785,7 +3757,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
|||
{
|
||||
ClearPerInstanceData();
|
||||
Matrices.TrimExcess();
|
||||
LocalSortCenters.TrimExcess();
|
||||
SubmissionOrders.TrimExcess();
|
||||
Slots.TrimExcess();
|
||||
LightSets.TrimExcess();
|
||||
|
|
|
|||
|
|
@ -345,6 +345,54 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Assert.Contains("0x005A06B7..0x005A0720", register, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassicGroupedAlphaSourceTruth_DeletesDeadOrderingChainAndPreservesLiveOwners()
|
||||
{
|
||||
string root = FindRepoRoot();
|
||||
string dispatcher = File.ReadAllText(Path.Combine(
|
||||
root, "src", "AcDream.App", "Rendering", "Wb", "WbDrawDispatcher.cs"));
|
||||
string cachedBatch = File.ReadAllText(Path.Combine(
|
||||
root, "src", "AcDream.App", "Rendering", "Wb", "CachedBatch.cs"));
|
||||
string walkClassifier = File.ReadAllText(Path.Combine(
|
||||
root, "src", "AcDream.App", "Rendering", "Wb", "WbDrawDispatcher.WalkClassify.cs"));
|
||||
string alphaProduction = dispatcher + cachedBatch + walkClassifier;
|
||||
|
||||
Assert.Contains(
|
||||
"Vector3.DistanceSquared(cameraWorldPosition, groupPosition)",
|
||||
dispatcher,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("group.SortDistance =", dispatcher, StringComparison.Ordinal);
|
||||
Assert.Contains("Vector3 LocalSortCenter", walkClassifier, StringComparison.Ordinal);
|
||||
Assert.Contains("float SortDistanceSq", walkClassifier, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("RetailAlphaOrdering", alphaProduction, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("FlushFartherThan(", alphaProduction, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("viewerDistance", alphaProduction, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("_ = cameraWorldPosition", dispatcher, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlphaFlushCountKnownFailure_StatesOnlyTheThreeExactReasons()
|
||||
{
|
||||
string root = FindRepoRoot();
|
||||
string alphaTranscript = File.ReadAllText(Path.Combine(
|
||||
root,
|
||||
"tests",
|
||||
"AcDream.App.Tests",
|
||||
"Rendering",
|
||||
"Walk",
|
||||
"WalkTraceConformanceTests.AlphaDepthTranscript.cs"));
|
||||
|
||||
Assert.Contains("THREE independent reasons", alphaTranscript, StringComparison.Ordinal);
|
||||
Assert.Contains("GfxObj/particle mesh content at all", alphaTranscript, StringComparison.Ordinal);
|
||||
Assert.Contains("AP-238 coalesces transparent", alphaTranscript, StringComparison.Ordinal);
|
||||
Assert.Contains("one token per <c>(cell,list)</c>", alphaTranscript, StringComparison.Ordinal);
|
||||
Assert.Contains("AP-239/AP-240 change", alphaTranscript, StringComparison.Ordinal);
|
||||
Assert.Contains(
|
||||
"former broad \"per instance versus per subset\" explanation was false.",
|
||||
alphaTranscript,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string FindRepoRoot()
|
||||
{
|
||||
string? directory = AppContext.BaseDirectory;
|
||||
|
|
@ -907,7 +955,7 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
"DeferTransparentGroups",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||
|
||||
defer.Invoke(fx.Dispatcher, [Vector3.Zero, Matrix4x4.Identity]);
|
||||
defer.Invoke(fx.Dispatcher, [Matrix4x4.Identity]);
|
||||
|
||||
Assert.Equal(0, fx.AlphaQueue!.PendingCount);
|
||||
Assert.Contains(
|
||||
|
|
@ -947,7 +995,6 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Translucency = TranslucencyKind.AlphaBlend,
|
||||
};
|
||||
group.Matrices.Add(Matrix4x4.Identity);
|
||||
group.LocalSortCenters.Add(Vector3.Zero);
|
||||
group.SubmissionOrders.Add(0);
|
||||
group.Slots.Add(0);
|
||||
group.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.Core.Meshing;
|
||||
|
|
@ -104,7 +105,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: true,
|
||||
opaque: [],
|
||||
transparent: [first, second],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
var reversedMaterialGroups =
|
||||
WbDrawDispatcher.CreateDispatcherSubmission(
|
||||
|
|
@ -113,7 +113,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: true,
|
||||
opaque: [],
|
||||
transparent: [second, first],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
|
||||
Assert.Equal(
|
||||
|
|
@ -128,7 +127,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: true,
|
||||
opaque: [],
|
||||
transparent: [first, second],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
|
||||
Assert.NotEqual(
|
||||
|
|
@ -154,7 +152,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: false,
|
||||
opaque: [],
|
||||
transparent: [],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
CurrentRenderDispatcherSubmission actual =
|
||||
WbDrawDispatcher.CreateDispatcherSubmission(
|
||||
|
|
@ -163,7 +160,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: false,
|
||||
opaque: [stale],
|
||||
transparent: [stale],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
|
|
@ -188,7 +184,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: true,
|
||||
opaque: [],
|
||||
transparent: [ordinary],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
CurrentRenderDispatcherSubmission buildingSubmission =
|
||||
WbDrawDispatcher.CreateDispatcherSubmission(
|
||||
|
|
@ -197,7 +192,6 @@ public class InstanceGroupClearTests
|
|||
deferTransparent: true,
|
||||
opaque: [],
|
||||
transparent: [building],
|
||||
cameraWorldPosition: Vector3.Zero,
|
||||
alphaScratch: scratch);
|
||||
|
||||
Assert.NotEqual(
|
||||
|
|
@ -208,6 +202,63 @@ public class InstanceGroupClearTests
|
|||
buildingSubmission.TransparentSetDigest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassicGroupedShapes_DeleteDeadSortCenterSidecarsButRetainWalkCyptKey()
|
||||
{
|
||||
Assert.Null(typeof(CachedBatch).GetProperty("LocalSortCenter"));
|
||||
Assert.Null(typeof(WbDrawDispatcher.InstanceGroup).GetField("LocalSortCenters"));
|
||||
|
||||
Type walkBatch = typeof(WbDrawDispatcher).GetNestedType(
|
||||
"WalkClassifiedBatch",
|
||||
BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("WalkClassifiedBatch was not found.");
|
||||
Assert.Equal(typeof(Vector3), walkBatch.GetProperty("LocalSortCenter")?.PropertyType);
|
||||
Assert.Equal(typeof(float), walkBatch.GetProperty("SortDistanceSq")?.PropertyType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlphaSubmissionShapes_DeleteDeadCameraChainButRetainOpaqueCameraDistance()
|
||||
{
|
||||
Type dispatcher = typeof(WbDrawDispatcher);
|
||||
MethodInfo defer = dispatcher.GetMethod(
|
||||
"DeferTransparentGroups",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("DeferTransparentGroups was not found.");
|
||||
MethodInfo transparentDigest = dispatcher.GetMethod(
|
||||
"BuildTransparentSubmissionDigest",
|
||||
BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("BuildTransparentSubmissionDigest was not found.");
|
||||
MethodInfo createSubmission = dispatcher.GetMethod(
|
||||
"CreateDispatcherSubmission",
|
||||
BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("CreateDispatcherSubmission was not found.");
|
||||
MethodInfo observeCurrent = dispatcher.GetMethod(
|
||||
"ObserveCurrentDispatcherSubmission",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("ObserveCurrentDispatcherSubmission was not found.");
|
||||
MethodInfo observeClassified = dispatcher.GetMethod(
|
||||
"ObserveClassifiedDispatcherSubmission",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("ObserveClassifiedDispatcherSubmission was not found.");
|
||||
|
||||
Assert.Equal([typeof(Matrix4x4)], defer.GetParameters().Select(static p => p.ParameterType));
|
||||
Assert.DoesNotContain(transparentDigest.GetParameters(), static p => p.ParameterType == typeof(Vector3));
|
||||
Assert.DoesNotContain(createSubmission.GetParameters(), static p => p.ParameterType == typeof(Vector3));
|
||||
Assert.DoesNotContain(observeCurrent.GetParameters(), static p => p.ParameterType == typeof(Vector3));
|
||||
Assert.DoesNotContain(observeClassified.GetParameters(), static p => p.ParameterType == typeof(Vector3));
|
||||
|
||||
MethodInfo partition = dispatcher.GetMethod(
|
||||
"PartitionInstanceGroups",
|
||||
BindingFlags.Static | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("PartitionInstanceGroups was not found.");
|
||||
Assert.Contains(
|
||||
partition.GetParameters(),
|
||||
static p => p.Name == "cameraWorldPosition" && p.ParameterType == typeof(Vector3));
|
||||
Assert.Equal(
|
||||
typeof(float),
|
||||
typeof(WbDrawDispatcher.InstanceGroup).GetField("SortDistance")?.FieldType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CachedGroupHandle_RequiresLiveMatchingRegistration()
|
||||
{
|
||||
|
|
@ -392,7 +443,6 @@ public class InstanceGroupClearTests
|
|||
Translucency = TranslucencyKind.AlphaBlend,
|
||||
};
|
||||
group.Matrices.Add(Matrix4x4.CreateTranslation(10f, 0f, 0f));
|
||||
group.LocalSortCenters.Add(Vector3.Zero);
|
||||
group.SubmissionOrders.Add(submissionOrder);
|
||||
group.Slots.Add(0u);
|
||||
group.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
||||
|
|
@ -404,9 +454,9 @@ public class InstanceGroupClearTests
|
|||
}
|
||||
|
||||
// #193 (regression from #188, 2026-07-09): WbDrawDispatcher's InstanceGroup holds
|
||||
// nine per-instance parallel lists — Matrices, LocalSortCenters,
|
||||
// SubmissionOrders, Slots, LightSets, IndoorFlags, Opacities, and
|
||||
// DetailCategories, SelectionLighting — appended in lockstep
|
||||
// eight per-instance parallel lists — Matrices, SubmissionOrders, Slots,
|
||||
// LightSets, IndoorFlags, Opacities, DetailCategories, and
|
||||
// SelectionLighting — appended in lockstep
|
||||
// (one entry per drawn instance) every frame. The
|
||||
// per-frame reset must clear ALL of them. #188 added Opacities but left it out of
|
||||
// the inline clear loop, so it grew one float per instance per frame forever; as
|
||||
|
|
@ -418,7 +468,6 @@ public class InstanceGroupClearTests
|
|||
{
|
||||
var grp = new WbDrawDispatcher.InstanceGroup();
|
||||
grp.Matrices.Add(Matrix4x4.Identity);
|
||||
grp.LocalSortCenters.Add(Vector3.Zero);
|
||||
grp.SubmissionOrders.Add(0);
|
||||
grp.Slots.Add(1u);
|
||||
grp.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
||||
|
|
@ -430,7 +479,6 @@ public class InstanceGroupClearTests
|
|||
grp.ClearPerInstanceData();
|
||||
|
||||
Assert.Empty(grp.Matrices);
|
||||
Assert.Empty(grp.LocalSortCenters);
|
||||
Assert.Empty(grp.SubmissionOrders);
|
||||
Assert.Empty(grp.Slots);
|
||||
Assert.Empty(grp.LightSets);
|
||||
|
|
|
|||
|
|
@ -417,8 +417,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
uint firstIndex,
|
||||
int indexCount,
|
||||
uint texSlot,
|
||||
Matrix4x4? restPose = null,
|
||||
Vector3? localSortCenter = null)
|
||||
Matrix4x4? restPose = null)
|
||||
{
|
||||
var key = new GroupKey(
|
||||
FirstIndex: firstIndex,
|
||||
|
|
@ -431,8 +430,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
return new CachedBatch(
|
||||
key,
|
||||
new AcDream.App.Rendering.Gpu.GpuTextureSlot(texSlot),
|
||||
restPose ?? Matrix4x4.Identity,
|
||||
localSortCenter ?? Vector3.Zero);
|
||||
restPose ?? Matrix4x4.Identity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -488,8 +486,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
// Production code: this is the !isAnimated && _cache.TryGet branch
|
||||
// at the top of the per-entity loop body in Draw.
|
||||
var groups = new Dictionary<GroupKey, List<Matrix4x4>>();
|
||||
var sortCenters = new List<Vector3>();
|
||||
void AppendInstance(GroupKey k, Matrix4x4 m, Vector3 localSortCenter)
|
||||
void AppendInstance(GroupKey k, Matrix4x4 m)
|
||||
{
|
||||
if (!groups.TryGetValue(k, out var list))
|
||||
{
|
||||
|
|
@ -497,7 +494,6 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
groups[k] = list;
|
||||
}
|
||||
list.Add(m);
|
||||
sortCenters.Add(localSortCenter);
|
||||
}
|
||||
|
||||
Assert.True(cache.TryGet(EntityId, LandblockId, out var entryHit));
|
||||
|
|
@ -519,35 +515,6 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
// appended matrix must equal entityWorld.
|
||||
foreach (var (_, list) in groups)
|
||||
Assert.Equal(entityWorld, list[0]);
|
||||
Assert.All(sortCenters, center => Assert.Equal(Vector3.Zero, center));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyCacheHit_PreservesAuthoredSortCenter()
|
||||
{
|
||||
Vector3 authoredCenter = new(1.25f, -2.5f, 7.75f);
|
||||
var entry = new EntityCacheEntry
|
||||
{
|
||||
EntityId = 100,
|
||||
LandblockHint = 0xA9B40000u,
|
||||
Batches =
|
||||
[
|
||||
MakeCachedBatch(
|
||||
ibo: 1,
|
||||
firstIndex: 0,
|
||||
indexCount: 6,
|
||||
texSlot: 0xAA,
|
||||
localSortCenter: authoredCenter),
|
||||
],
|
||||
};
|
||||
Vector3 observedCenter = default;
|
||||
|
||||
WbDrawDispatcher.ApplyCacheHit(
|
||||
entry,
|
||||
Matrix4x4.Identity,
|
||||
(_, _, center) => observedCenter = center);
|
||||
|
||||
Assert.Equal(authoredCenter, observedCenter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -788,7 +755,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
const uint EntityId = 100;
|
||||
const int MeshRefCount = 3;
|
||||
|
||||
void AppendInstance(GroupKey k, Matrix4x4 m, Vector3 localSortCenter)
|
||||
void AppendInstance(GroupKey k, Matrix4x4 m)
|
||||
{
|
||||
if (!groups.TryGetValue(k, out var list))
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue