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
|
lifecycle, allocation, allowed scope, every mutation claim, and all gate
|
||||||
counts. A failed lens gets one bounded fix contract; a third fix round stops
|
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.
|
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
|
/// subPart contributes its own <see cref="CachedBatch"/> entries, with
|
||||||
/// <see cref="RestPose"/> already containing the
|
/// <see cref="RestPose"/> already containing the
|
||||||
/// <c>subPart.PartTransform * meshRef.PartTransform</c> product.
|
/// <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
|
/// <see cref="Group"/> is a registration-checked fast handle into the
|
||||||
/// dispatcher's bounded group table; a retired or synthetic entry falls back
|
/// dispatcher's bounded group table; a retired or synthetic entry falls back
|
||||||
/// to key lookup and refreshes this array entry.
|
/// to key lookup and refreshes this array entry.
|
||||||
|
|
@ -25,7 +23,6 @@ internal readonly record struct CachedBatch(
|
||||||
GroupKey Key,
|
GroupKey Key,
|
||||||
GpuTextureSlot TextureSlot,
|
GpuTextureSlot TextureSlot,
|
||||||
Matrix4x4 RestPose,
|
Matrix4x4 RestPose,
|
||||||
Vector3 LocalSortCenter = default,
|
|
||||||
WbDrawDispatcher.InstanceGroup? Group = null,
|
WbDrawDispatcher.InstanceGroup? Group = null,
|
||||||
long GroupRegistration = 0);
|
long GroupRegistration = 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,13 +143,12 @@ public sealed partial class WbDrawDispatcher
|
||||||
/// one needs to reach the alpha queue: <see cref="IsOpaque"/> (so the
|
/// one needs to reach the alpha queue: <see cref="IsOpaque"/> (so the
|
||||||
/// populator can route without re-deriving it from
|
/// populator can route without re-deriving it from
|
||||||
/// <see cref="GroupKey.Translucency"/>) and <see cref="LocalSortCenter"/>
|
/// <see cref="GroupKey.Translucency"/>) and <see cref="LocalSortCenter"/>
|
||||||
/// (the authored GfxObj sort center — the same value
|
/// (the authored GfxObj sort center consumed by the live per-cell CYpt
|
||||||
/// <c>InstanceGroup.LocalSortCenters</c> carries per instance today; S4-c2
|
/// sort; the classic grouped path has no corresponding sidecar).
|
||||||
/// deleted its one alpha-ordering consumer, <c>RetailAlphaOrdering.ComputeViewerDistance</c>,
|
/// <c>WalkStaticStreamPopulator</c> transforms it through the part model
|
||||||
/// but S4-c3a restores its retail use: <c>WalkStaticStreamPopulator</c>
|
/// and retains the resulting squared CYpt key in
|
||||||
/// transforms it through the part model and retains the resulting
|
/// <see cref="SortDistanceSq"/> before the per-cell stable sort. The key
|
||||||
/// squared CYpt key in <see cref="SortDistanceSq"/> before the per-cell
|
/// feeds two FIFO alpha lists; neither queue sorts.
|
||||||
/// stable sort. The key feeds two FIFO alpha lists; neither queue sorts.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly record struct WalkClassifiedBatch(
|
internal readonly record struct WalkClassifiedBatch(
|
||||||
GroupKey Key,
|
GroupKey Key,
|
||||||
|
|
|
||||||
|
|
@ -1818,8 +1818,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
||||||
visibleInstanceCount: 0,
|
visibleInstanceCount: 0,
|
||||||
immediateInstanceCount: 0,
|
immediateInstanceCount: 0,
|
||||||
deferTransparent: false,
|
deferTransparent: false);
|
||||||
camPos);
|
|
||||||
_cpuStopwatch.Stop();
|
_cpuStopwatch.Stop();
|
||||||
if (diag) MaybeFlushDiag();
|
if (diag) MaybeFlushDiag();
|
||||||
return;
|
return;
|
||||||
|
|
@ -1841,8 +1840,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
||||||
visibleInstanceCount: 0,
|
visibleInstanceCount: 0,
|
||||||
immediateInstanceCount: 0,
|
immediateInstanceCount: 0,
|
||||||
deferTransparent,
|
deferTransparent);
|
||||||
camPos);
|
|
||||||
_cpuStopwatch.Stop();
|
_cpuStopwatch.Stop();
|
||||||
if (diag) MaybeFlushDiag();
|
if (diag) MaybeFlushDiag();
|
||||||
return;
|
return;
|
||||||
|
|
@ -1850,7 +1848,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
|
|
||||||
_opaqueDraws.Sort(CompareOpaqueSubmissionOrder);
|
_opaqueDraws.Sort(CompareOpaqueSubmissionOrder);
|
||||||
if (deferTransparent)
|
if (deferTransparent)
|
||||||
DeferTransparentGroups(camPos, vp);
|
DeferTransparentGroups(vp);
|
||||||
else
|
else
|
||||||
_translucentDraws.Sort(CompareTransparentSubmissionOrder);
|
_translucentDraws.Sort(CompareTransparentSubmissionOrder);
|
||||||
|
|
||||||
|
|
@ -1969,8 +1967,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
ObserveClassifiedDispatcherSubmission(observeCurrentPath,
|
||||||
totalInstances,
|
totalInstances,
|
||||||
immediateInstances,
|
immediateInstances,
|
||||||
deferTransparent,
|
deferTransparent);
|
||||||
camPos);
|
|
||||||
|
|
||||||
// Campaign V slice V11: every per-frame upload is a frame ring slice
|
// Campaign V slice V11: every per-frame upload is a frame ring slice
|
||||||
// bound through the borrowed world pass, which retires the buffer-set
|
// bound through the borrowed world pass, which retires the buffer-set
|
||||||
|
|
@ -2153,8 +2150,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
private void ObserveCurrentDispatcherSubmission(
|
private void ObserveCurrentDispatcherSubmission(
|
||||||
int visibleInstanceCount,
|
int visibleInstanceCount,
|
||||||
int immediateInstanceCount,
|
int immediateInstanceCount,
|
||||||
bool deferTransparent,
|
bool deferTransparent)
|
||||||
Vector3 cameraWorldPosition)
|
|
||||||
{
|
{
|
||||||
ICurrentRenderDispatcherObserver? observer =
|
ICurrentRenderDispatcherObserver? observer =
|
||||||
_currentRenderSceneObserver;
|
_currentRenderSceneObserver;
|
||||||
|
|
@ -2168,7 +2164,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
deferTransparent,
|
deferTransparent,
|
||||||
_opaqueDraws,
|
_opaqueDraws,
|
||||||
_translucentDraws,
|
_translucentDraws,
|
||||||
cameraWorldPosition,
|
|
||||||
_alphaFingerprintScratch);
|
_alphaFingerprintScratch);
|
||||||
observer.ObserveDispatcherSubmission(in submission);
|
observer.ObserveDispatcherSubmission(in submission);
|
||||||
}
|
}
|
||||||
|
|
@ -2177,16 +2172,14 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
bool observeCurrentPath,
|
bool observeCurrentPath,
|
||||||
int visibleInstanceCount,
|
int visibleInstanceCount,
|
||||||
int immediateInstanceCount,
|
int immediateInstanceCount,
|
||||||
bool deferTransparent,
|
bool deferTransparent)
|
||||||
Vector3 cameraWorldPosition)
|
|
||||||
{
|
{
|
||||||
if (observeCurrentPath)
|
if (observeCurrentPath)
|
||||||
{
|
{
|
||||||
ObserveCurrentDispatcherSubmission(
|
ObserveCurrentDispatcherSubmission(
|
||||||
visibleInstanceCount,
|
visibleInstanceCount,
|
||||||
immediateInstanceCount,
|
immediateInstanceCount,
|
||||||
deferTransparent,
|
deferTransparent);
|
||||||
cameraWorldPosition);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2197,7 +2190,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
bool deferTransparent,
|
bool deferTransparent,
|
||||||
IReadOnlyList<InstanceGroup> opaque,
|
IReadOnlyList<InstanceGroup> opaque,
|
||||||
IReadOnlyList<InstanceGroup> transparent,
|
IReadOnlyList<InstanceGroup> transparent,
|
||||||
Vector3 cameraWorldPosition,
|
|
||||||
List<AlphaFingerprint> alphaScratch)
|
List<AlphaFingerprint> alphaScratch)
|
||||||
{
|
{
|
||||||
// The no-VAO production early-out deliberately skips group
|
// The no-VAO production early-out deliberately skips group
|
||||||
|
|
@ -2226,7 +2218,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
RenderSceneHash128 transparentDigest =
|
RenderSceneHash128 transparentDigest =
|
||||||
BuildTransparentSubmissionDigest(
|
BuildTransparentSubmissionDigest(
|
||||||
acceptedTransparent,
|
acceptedTransparent,
|
||||||
cameraWorldPosition,
|
|
||||||
alphaScratch);
|
alphaScratch);
|
||||||
RenderSceneHash128 transparentSetDigest =
|
RenderSceneHash128 transparentSetDigest =
|
||||||
BuildOpaqueSubmissionDigest(acceptedTransparent);
|
BuildOpaqueSubmissionDigest(acceptedTransparent);
|
||||||
|
|
@ -2285,11 +2276,8 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
|
|
||||||
private static RenderSceneHash128 BuildTransparentSubmissionDigest(
|
private static RenderSceneHash128 BuildTransparentSubmissionDigest(
|
||||||
IReadOnlyList<InstanceGroup> groups,
|
IReadOnlyList<InstanceGroup> groups,
|
||||||
Vector3 cameraWorldPosition,
|
|
||||||
List<AlphaFingerprint> scratch)
|
List<AlphaFingerprint> scratch)
|
||||||
{
|
{
|
||||||
_ = cameraWorldPosition; // S4-c2: the digest reflects retail's FIFO
|
|
||||||
// submission order, not a distance sort.
|
|
||||||
scratch.Clear();
|
scratch.Clear();
|
||||||
for (int groupIndex = 0;
|
for (int groupIndex = 0;
|
||||||
groupIndex < groups.Count;
|
groupIndex < groups.Count;
|
||||||
|
|
@ -2395,7 +2383,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
hash.Add(group.Matrices[index]);
|
hash.Add(group.Matrices[index]);
|
||||||
hash.Add(group.LocalSortCenters[index]);
|
|
||||||
hash.Add(group.Slots[index]);
|
hash.Add(group.Slots[index]);
|
||||||
InstanceLightSet lights = group.LightSets[index];
|
InstanceLightSet lights = group.LightSets[index];
|
||||||
for (int lightIndex = 0;
|
for (int lightIndex = 0;
|
||||||
|
|
@ -2410,11 +2397,8 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
hash.Add(group.SelectionLighting[index]);
|
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!;
|
RetailAlphaQueue queue = _alphaQueue!;
|
||||||
if (_deferredAlpha.Count == 0)
|
if (_deferredAlpha.Count == 0)
|
||||||
_deferredAlphaViewProjection = viewProjection;
|
_deferredAlphaViewProjection = viewProjection;
|
||||||
|
|
@ -2858,7 +2842,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
internal static void ApplyCacheHit(
|
internal static void ApplyCacheHit(
|
||||||
EntityCacheEntry entry,
|
EntityCacheEntry entry,
|
||||||
Matrix4x4 entityWorld,
|
Matrix4x4 entityWorld,
|
||||||
Action<GroupKey, Matrix4x4, Vector3> appendInstance)
|
Action<GroupKey, Matrix4x4> appendInstance)
|
||||||
{
|
{
|
||||||
foreach (var cached in entry.Batches)
|
foreach (var cached in entry.Batches)
|
||||||
{
|
{
|
||||||
|
|
@ -2869,8 +2853,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
// out of sync with it.
|
// out of sync with it.
|
||||||
appendInstance(
|
appendInstance(
|
||||||
cached.Key,
|
cached.Key,
|
||||||
cached.RestPose * entityWorld,
|
cached.RestPose * entityWorld);
|
||||||
cached.LocalSortCenter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2899,7 +2882,7 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
GroupRegistration = group.Registration,
|
GroupRegistration = group.Registration,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
AppendInstanceToGroup(group!, model, cached.LocalSortCenter);
|
AppendInstanceToGroup(group!, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3012,11 +2995,10 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AppendInstanceToGroup(
|
private void AppendInstanceToGroup(
|
||||||
GroupKey key,
|
GroupKey key,
|
||||||
Matrix4x4 model,
|
Matrix4x4 model)
|
||||||
Vector3 localSortCenter)
|
|
||||||
{
|
{
|
||||||
InstanceGroup grp = GetOrCreateInstanceGroup(key);
|
InstanceGroup grp = GetOrCreateInstanceGroup(key);
|
||||||
AppendInstanceToGroup(grp, model, localSortCenter);
|
AppendInstanceToGroup(grp, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InstanceGroup GetOrCreateInstanceGroup(GroupKey key)
|
private InstanceGroup GetOrCreateInstanceGroup(GroupKey key)
|
||||||
|
|
@ -3047,12 +3029,10 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
|
|
||||||
private void AppendInstanceToGroup(
|
private void AppendInstanceToGroup(
|
||||||
InstanceGroup grp,
|
InstanceGroup grp,
|
||||||
Matrix4x4 model,
|
Matrix4x4 model)
|
||||||
Vector3 localSortCenter)
|
|
||||||
{
|
{
|
||||||
grp.LastUsedFrame = _groupFrame;
|
grp.LastUsedFrame = _groupFrame;
|
||||||
grp.Matrices.Add(model);
|
grp.Matrices.Add(model);
|
||||||
grp.LocalSortCenters.Add(localSortCenter);
|
|
||||||
grp.SubmissionOrders.Add(_nextInstanceSubmissionOrder++);
|
grp.SubmissionOrders.Add(_nextInstanceSubmissionOrder++);
|
||||||
grp.Slots.Add(_currentEntitySlot); // Phase U.4 — parallel to Matrices
|
grp.Slots.Add(_currentEntitySlot); // Phase U.4 — parallel to Matrices
|
||||||
AppendCurrentLightSet(grp); // Fix B — 8 ints per instance, 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);
|
InstanceGroup grp = GetOrCreateInstanceGroup(key);
|
||||||
grp.Matrices.Add(model);
|
grp.Matrices.Add(model);
|
||||||
grp.LocalSortCenters.Add(renderData.SortCenter);
|
|
||||||
grp.SubmissionOrders.Add(_nextInstanceSubmissionOrder++);
|
grp.SubmissionOrders.Add(_nextInstanceSubmissionOrder++);
|
||||||
grp.Slots.Add(_currentEntitySlot); // Phase U.4 — parallel to Matrices
|
grp.Slots.Add(_currentEntitySlot); // Phase U.4 — parallel to Matrices
|
||||||
AppendCurrentLightSet(grp); // Fix B — 8 ints per instance, 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,
|
key,
|
||||||
texSlot,
|
texSlot,
|
||||||
restPose,
|
restPose,
|
||||||
renderData.SortCenter,
|
|
||||||
grp,
|
grp,
|
||||||
grp.Registration));
|
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 float SortDistance; // squared distance from camera to first instance, for opaque sort
|
||||||
public readonly List<Matrix4x4> Matrices = new();
|
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
|
// Retail CShadowPart::insertion_sort (0x006B5130) is stable for equal
|
||||||
// CYpt. Material groups erase authored entity/part/batch traversal
|
// CYpt. Material groups erase authored entity/part/batch traversal
|
||||||
// unless that order is retained explicitly. SubmissionOrders[i] is
|
// unless that order is retained explicitly. SubmissionOrders[i] is
|
||||||
|
|
@ -3771,7 +3744,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
public void ClearPerInstanceData()
|
public void ClearPerInstanceData()
|
||||||
{
|
{
|
||||||
Matrices.Clear();
|
Matrices.Clear();
|
||||||
LocalSortCenters.Clear();
|
|
||||||
SubmissionOrders.Clear();
|
SubmissionOrders.Clear();
|
||||||
Slots.Clear();
|
Slots.Clear();
|
||||||
LightSets.Clear();
|
LightSets.Clear();
|
||||||
|
|
@ -3785,7 +3757,6 @@ public sealed partial class WbDrawDispatcher : IDisposable
|
||||||
{
|
{
|
||||||
ClearPerInstanceData();
|
ClearPerInstanceData();
|
||||||
Matrices.TrimExcess();
|
Matrices.TrimExcess();
|
||||||
LocalSortCenters.TrimExcess();
|
|
||||||
SubmissionOrders.TrimExcess();
|
SubmissionOrders.TrimExcess();
|
||||||
Slots.TrimExcess();
|
Slots.TrimExcess();
|
||||||
LightSets.TrimExcess();
|
LightSets.TrimExcess();
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,54 @@ public sealed class WalkStaticStreamPopulatorTests
|
||||||
Assert.Contains("0x005A06B7..0x005A0720", register, StringComparison.Ordinal);
|
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()
|
private static string FindRepoRoot()
|
||||||
{
|
{
|
||||||
string? directory = AppContext.BaseDirectory;
|
string? directory = AppContext.BaseDirectory;
|
||||||
|
|
@ -907,7 +955,7 @@ public sealed class WalkStaticStreamPopulatorTests
|
||||||
"DeferTransparentGroups",
|
"DeferTransparentGroups",
|
||||||
BindingFlags.Instance | BindingFlags.NonPublic)!;
|
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.Equal(0, fx.AlphaQueue!.PendingCount);
|
||||||
Assert.Contains(
|
Assert.Contains(
|
||||||
|
|
@ -947,7 +995,6 @@ public sealed class WalkStaticStreamPopulatorTests
|
||||||
Translucency = TranslucencyKind.AlphaBlend,
|
Translucency = TranslucencyKind.AlphaBlend,
|
||||||
};
|
};
|
||||||
group.Matrices.Add(Matrix4x4.Identity);
|
group.Matrices.Add(Matrix4x4.Identity);
|
||||||
group.LocalSortCenters.Add(Vector3.Zero);
|
|
||||||
group.SubmissionOrders.Add(0);
|
group.SubmissionOrders.Add(0);
|
||||||
group.Slots.Add(0);
|
group.Slots.Add(0);
|
||||||
group.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
group.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
using AcDream.App.Rendering.Scene;
|
using AcDream.App.Rendering.Scene;
|
||||||
using AcDream.App.Rendering.Wb;
|
using AcDream.App.Rendering.Wb;
|
||||||
using AcDream.Core.Meshing;
|
using AcDream.Core.Meshing;
|
||||||
|
|
@ -104,7 +105,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: true,
|
deferTransparent: true,
|
||||||
opaque: [],
|
opaque: [],
|
||||||
transparent: [first, second],
|
transparent: [first, second],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
var reversedMaterialGroups =
|
var reversedMaterialGroups =
|
||||||
WbDrawDispatcher.CreateDispatcherSubmission(
|
WbDrawDispatcher.CreateDispatcherSubmission(
|
||||||
|
|
@ -113,7 +113,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: true,
|
deferTransparent: true,
|
||||||
opaque: [],
|
opaque: [],
|
||||||
transparent: [second, first],
|
transparent: [second, first],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
|
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
|
|
@ -128,7 +127,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: true,
|
deferTransparent: true,
|
||||||
opaque: [],
|
opaque: [],
|
||||||
transparent: [first, second],
|
transparent: [first, second],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
|
|
||||||
Assert.NotEqual(
|
Assert.NotEqual(
|
||||||
|
|
@ -154,7 +152,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: false,
|
deferTransparent: false,
|
||||||
opaque: [],
|
opaque: [],
|
||||||
transparent: [],
|
transparent: [],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
CurrentRenderDispatcherSubmission actual =
|
CurrentRenderDispatcherSubmission actual =
|
||||||
WbDrawDispatcher.CreateDispatcherSubmission(
|
WbDrawDispatcher.CreateDispatcherSubmission(
|
||||||
|
|
@ -163,7 +160,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: false,
|
deferTransparent: false,
|
||||||
opaque: [stale],
|
opaque: [stale],
|
||||||
transparent: [stale],
|
transparent: [stale],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
|
|
||||||
Assert.Equal(expected, actual);
|
Assert.Equal(expected, actual);
|
||||||
|
|
@ -188,7 +184,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: true,
|
deferTransparent: true,
|
||||||
opaque: [],
|
opaque: [],
|
||||||
transparent: [ordinary],
|
transparent: [ordinary],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
CurrentRenderDispatcherSubmission buildingSubmission =
|
CurrentRenderDispatcherSubmission buildingSubmission =
|
||||||
WbDrawDispatcher.CreateDispatcherSubmission(
|
WbDrawDispatcher.CreateDispatcherSubmission(
|
||||||
|
|
@ -197,7 +192,6 @@ public class InstanceGroupClearTests
|
||||||
deferTransparent: true,
|
deferTransparent: true,
|
||||||
opaque: [],
|
opaque: [],
|
||||||
transparent: [building],
|
transparent: [building],
|
||||||
cameraWorldPosition: Vector3.Zero,
|
|
||||||
alphaScratch: scratch);
|
alphaScratch: scratch);
|
||||||
|
|
||||||
Assert.NotEqual(
|
Assert.NotEqual(
|
||||||
|
|
@ -208,6 +202,63 @@ public class InstanceGroupClearTests
|
||||||
buildingSubmission.TransparentSetDigest);
|
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]
|
[Fact]
|
||||||
public void CachedGroupHandle_RequiresLiveMatchingRegistration()
|
public void CachedGroupHandle_RequiresLiveMatchingRegistration()
|
||||||
{
|
{
|
||||||
|
|
@ -392,7 +443,6 @@ public class InstanceGroupClearTests
|
||||||
Translucency = TranslucencyKind.AlphaBlend,
|
Translucency = TranslucencyKind.AlphaBlend,
|
||||||
};
|
};
|
||||||
group.Matrices.Add(Matrix4x4.CreateTranslation(10f, 0f, 0f));
|
group.Matrices.Add(Matrix4x4.CreateTranslation(10f, 0f, 0f));
|
||||||
group.LocalSortCenters.Add(Vector3.Zero);
|
|
||||||
group.SubmissionOrders.Add(submissionOrder);
|
group.SubmissionOrders.Add(submissionOrder);
|
||||||
group.Slots.Add(0u);
|
group.Slots.Add(0u);
|
||||||
group.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
group.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
||||||
|
|
@ -404,9 +454,9 @@ public class InstanceGroupClearTests
|
||||||
}
|
}
|
||||||
|
|
||||||
// #193 (regression from #188, 2026-07-09): WbDrawDispatcher's InstanceGroup holds
|
// #193 (regression from #188, 2026-07-09): WbDrawDispatcher's InstanceGroup holds
|
||||||
// nine per-instance parallel lists — Matrices, LocalSortCenters,
|
// eight per-instance parallel lists — Matrices, SubmissionOrders, Slots,
|
||||||
// SubmissionOrders, Slots, LightSets, IndoorFlags, Opacities, and
|
// LightSets, IndoorFlags, Opacities, DetailCategories, and
|
||||||
// DetailCategories, SelectionLighting — appended in lockstep
|
// SelectionLighting — appended in lockstep
|
||||||
// (one entry per drawn instance) every frame. The
|
// (one entry per drawn instance) every frame. The
|
||||||
// per-frame reset must clear ALL of them. #188 added Opacities but left it out of
|
// 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
|
// 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();
|
var grp = new WbDrawDispatcher.InstanceGroup();
|
||||||
grp.Matrices.Add(Matrix4x4.Identity);
|
grp.Matrices.Add(Matrix4x4.Identity);
|
||||||
grp.LocalSortCenters.Add(Vector3.Zero);
|
|
||||||
grp.SubmissionOrders.Add(0);
|
grp.SubmissionOrders.Add(0);
|
||||||
grp.Slots.Add(1u);
|
grp.Slots.Add(1u);
|
||||||
grp.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
grp.LightSets.Add(WbDrawDispatcher.InstanceLightSet.Disabled);
|
||||||
|
|
@ -430,7 +479,6 @@ public class InstanceGroupClearTests
|
||||||
grp.ClearPerInstanceData();
|
grp.ClearPerInstanceData();
|
||||||
|
|
||||||
Assert.Empty(grp.Matrices);
|
Assert.Empty(grp.Matrices);
|
||||||
Assert.Empty(grp.LocalSortCenters);
|
|
||||||
Assert.Empty(grp.SubmissionOrders);
|
Assert.Empty(grp.SubmissionOrders);
|
||||||
Assert.Empty(grp.Slots);
|
Assert.Empty(grp.Slots);
|
||||||
Assert.Empty(grp.LightSets);
|
Assert.Empty(grp.LightSets);
|
||||||
|
|
|
||||||
|
|
@ -417,8 +417,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
||||||
uint firstIndex,
|
uint firstIndex,
|
||||||
int indexCount,
|
int indexCount,
|
||||||
uint texSlot,
|
uint texSlot,
|
||||||
Matrix4x4? restPose = null,
|
Matrix4x4? restPose = null)
|
||||||
Vector3? localSortCenter = null)
|
|
||||||
{
|
{
|
||||||
var key = new GroupKey(
|
var key = new GroupKey(
|
||||||
FirstIndex: firstIndex,
|
FirstIndex: firstIndex,
|
||||||
|
|
@ -431,8 +430,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
||||||
return new CachedBatch(
|
return new CachedBatch(
|
||||||
key,
|
key,
|
||||||
new AcDream.App.Rendering.Gpu.GpuTextureSlot(texSlot),
|
new AcDream.App.Rendering.Gpu.GpuTextureSlot(texSlot),
|
||||||
restPose ?? Matrix4x4.Identity,
|
restPose ?? Matrix4x4.Identity);
|
||||||
localSortCenter ?? Vector3.Zero);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -488,8 +486,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
||||||
// Production code: this is the !isAnimated && _cache.TryGet branch
|
// Production code: this is the !isAnimated && _cache.TryGet branch
|
||||||
// at the top of the per-entity loop body in Draw.
|
// at the top of the per-entity loop body in Draw.
|
||||||
var groups = new Dictionary<GroupKey, List<Matrix4x4>>();
|
var groups = new Dictionary<GroupKey, List<Matrix4x4>>();
|
||||||
var sortCenters = new List<Vector3>();
|
void AppendInstance(GroupKey k, Matrix4x4 m)
|
||||||
void AppendInstance(GroupKey k, Matrix4x4 m, Vector3 localSortCenter)
|
|
||||||
{
|
{
|
||||||
if (!groups.TryGetValue(k, out var list))
|
if (!groups.TryGetValue(k, out var list))
|
||||||
{
|
{
|
||||||
|
|
@ -497,7 +494,6 @@ public sealed class WbDrawDispatcherBucketingTests
|
||||||
groups[k] = list;
|
groups[k] = list;
|
||||||
}
|
}
|
||||||
list.Add(m);
|
list.Add(m);
|
||||||
sortCenters.Add(localSortCenter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(cache.TryGet(EntityId, LandblockId, out var entryHit));
|
Assert.True(cache.TryGet(EntityId, LandblockId, out var entryHit));
|
||||||
|
|
@ -519,35 +515,6 @@ public sealed class WbDrawDispatcherBucketingTests
|
||||||
// appended matrix must equal entityWorld.
|
// appended matrix must equal entityWorld.
|
||||||
foreach (var (_, list) in groups)
|
foreach (var (_, list) in groups)
|
||||||
Assert.Equal(entityWorld, list[0]);
|
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]
|
[Fact]
|
||||||
|
|
@ -788,7 +755,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
||||||
const uint EntityId = 100;
|
const uint EntityId = 100;
|
||||||
const int MeshRefCount = 3;
|
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))
|
if (!groups.TryGetValue(k, out var list))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue