feat(rendering): compare the incremental shadow scene

Construct Slice F's non-drawing scene only for lifecycle automation, drain accepted static and live deltas at the final update boundary, and compare exact current-path fingerprints at cadence and checkpoints. Publish bounded mismatch, journal, index, digest, and memory evidence without changing normal launches or draw submission.

Release: 8,211 passed, 5 skipped.
This commit is contained in:
Erik 2026-07-24 22:24:30 +02:00
parent ff5d86175f
commit 0eb6648589
21 changed files with 937 additions and 20 deletions

View file

@ -6,6 +6,7 @@ using AcDream.App.Interaction;
using AcDream.App.Physics;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Residency;
using AcDream.App.Rendering.Scene;
using AcDream.App.Rendering.Selection;
using AcDream.App.Rendering.Sky;
using AcDream.App.Rendering.Vfx;
@ -80,6 +81,7 @@ internal sealed record LivePresentationResult(
RetailStaticAnimatingObjectScheduler StaticAnimationScheduler,
WorldGenerationAvailabilityState WorldAvailability,
GpuWorldState WorldState,
RenderSceneShadowRuntime? RenderSceneShadow,
LiveEntityRuntime LiveEntities,
ProjectileController ProjectileController,
LiveEntityProjectionWithdrawalController ProjectionWithdrawal,
@ -196,6 +198,20 @@ internal sealed class LivePresentationCompositionPhase
try
{
CompositionAcquisitionScope.CompositionAcquisitionLease<
RenderSceneShadowRuntime>? renderSceneShadowLease = null;
if (interaction.RetainedUi?.Screenshots is not null
&& d.Options.AutomationArtifactDirectory is not null)
{
renderSceneShadowLease = scope.Acquire(
"shadow render scene",
() => new RenderSceneShadowRuntime(
RenderSceneGeneration.FromRaw(1)),
static value => value.Dispose());
}
RenderSceneShadowRuntime? renderSceneShadow =
renderSceneShadowLease?.Resource;
var componentLifecycle =
new DeferredLiveEntityRuntimeComponentLifecycle();
var wbSpawnAdapter = new LandblockSpawnAdapter(foundation.MeshAdapter);
@ -333,20 +349,33 @@ internal sealed class LivePresentationCompositionPhase
"developer world-entity source",
devWorldEntities.BindOwned(worldState));
}
liveEntities = new LiveEntityRuntime(
worldState,
new CompositeLiveEntityResourceLifecycle(
var resourceOwners =
new List<CompositeLiveEntityResourceLifecycle.Owner>
{
new(
entity => _ = entitySpawnAdapter.OnCreate(entity),
entity => _ = entitySpawnAdapter.OnRemove(entity)),
new(
entityScriptActivator.OnCreate,
entityScriptActivator.OnRemove)),
entityScriptActivator.OnRemove),
};
if (renderSceneShadow is not null)
{
resourceOwners.Add(new(
renderSceneShadow.OnLiveResourceRegistered,
renderSceneShadow.OnLiveResourceUnregistered));
}
liveEntities = new LiveEntityRuntime(
worldState,
new CompositeLiveEntityResourceLifecycle(
[.. resourceOwners]),
componentLifecycle);
var liveRuntimeLease = scope.Own(
"canonical live-entity runtime",
liveEntities,
static runtime => runtime.Clear());
LiveRenderProjectionJournal? liveRenderProjections =
renderSceneShadow?.BindLiveRuntime(liveEntities);
Fault(LivePresentationCompositionPoint.CanonicalRuntimeCreated);
bindings.Adopt(
@ -376,6 +405,13 @@ internal sealed class LivePresentationCompositionPhase
liveEntities,
wbVisibility,
"WB projection visibility");
if (liveRenderProjections is not null)
{
bindings.BindProjectionVisibility(
liveEntities,
liveRenderProjections.OnProjectionVisibilityChanged,
"shadow render projection visibility");
}
Action<LiveEntityRecord, bool> particleVisibility = (record, visible) =>
{
if (record.WorldEntity is { } entity)
@ -511,6 +547,15 @@ internal sealed class LivePresentationCompositionPhase
bindings.BindProjectionPoseReady(
equippedLease.Resource,
lightsLease.Resource.OnAttachedPoseReady);
if (liveRenderProjections is not null)
{
bindings.BindProjectionPoseReady(
equippedLease.Resource,
liveRenderProjections.OnProjectionPoseReady);
bindings.BindProjectionRemoved(
equippedLease.Resource,
liveRenderProjections.OnProjectionRemoved);
}
bindings.Adopt(
"entity-effect animation hooks",
content.HookRegistrations.RegisterOwned(entityEffects));
@ -529,6 +574,8 @@ internal sealed class LivePresentationCompositionPhase
staticAnimationScheduler,
worldAvailability,
worldState,
renderSceneShadow,
renderSceneShadowLease,
liveEntities,
projectileController,
projectionWithdrawal,
@ -574,6 +621,9 @@ internal sealed class LivePresentationCompositionPhase
RetailStaticAnimatingObjectScheduler staticAnimationScheduler,
WorldGenerationAvailabilityState worldAvailability,
GpuWorldState worldState,
RenderSceneShadowRuntime? renderSceneShadow,
CompositionAcquisitionScope.CompositionAcquisitionLease<
RenderSceneShadowRuntime>? renderSceneShadowLease,
LiveEntityRuntime liveEntities,
ProjectileController projectileController,
LiveEntityProjectionWithdrawalController projectionWithdrawal,
@ -818,7 +868,8 @@ internal sealed class LivePresentationCompositionPhase
worldState,
landblockRetirementOwner,
landblockLoaded.OnLandblockLoaded,
landblockRenderPublisher.PrepareAfterRenderPins);
landblockRenderPublisher.PrepareAfterRenderPins,
renderSceneShadow?.StaticProjections);
Fault(LivePresentationCompositionPoint.LandblockPipelineCreated);
var clipFrameLease = scope.Acquire(
@ -965,6 +1016,7 @@ internal sealed class LivePresentationCompositionPhase
staticAnimationScheduler,
worldAvailability,
worldState,
renderSceneShadow,
liveEntities,
projectileController,
projectionWithdrawal,
@ -1010,6 +1062,7 @@ internal sealed class LivePresentationCompositionPhase
_publication.PublishLivePresentation(result);
liveRuntimeLease.Transfer();
renderSceneShadowLease?.Transfer();
lightsLease.Transfer();
equippedLease.Transfer();
presentationLease.Transfer();