refactor(app): compose atomic frame roots
Move the complete update/render construction graph into a typed Phase-8 owner, explicitly carry the content dependencies it consumes, and publish both roots through one exact lease. Extract lifecycle resource sampling and frame-owned late bindings so partial startup and shutdown withdraw the same generation without window callbacks. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
826f9ea9b5
commit
3628aeb520
21 changed files with 1052 additions and 374 deletions
|
|
@ -22,7 +22,8 @@ public sealed class GameWindow :
|
|||
IGameWindowWorldRenderPublication,
|
||||
IGameWindowInteractionRetainedUiPublication,
|
||||
IGameWindowLivePresentationPublication,
|
||||
IGameWindowSessionPlayerPublication
|
||||
IGameWindowSessionPlayerPublication,
|
||||
IGameWindowFrameRootPublication
|
||||
{
|
||||
private static double ClientTimerNow() =>
|
||||
System.Diagnostics.Stopwatch.GetTimestamp()
|
||||
|
|
@ -103,6 +104,8 @@ public sealed class GameWindow :
|
|||
private SessionPlayerRuntimeBindings? _sessionPlayerBindings;
|
||||
private AcDream.App.Diagnostics.FrameScreenshotController? _frameScreenshots;
|
||||
private AcDream.App.Diagnostics.WorldLifecycleAutomationController? _worldLifecycleAutomation;
|
||||
private FrameRootRuntimeBindings? _frameRootBindings;
|
||||
private IDisposable? _frameGraphPublication;
|
||||
private AcDream.App.Rendering.GpuFrameFlightController? _gpuFrameFlights;
|
||||
private readonly AcDream.App.Rendering.GameFrameGraphSlot _frameGraphs = new();
|
||||
private readonly AcDream.App.Rendering.GameRenderResourceLifetime
|
||||
|
|
@ -1040,6 +1043,23 @@ public sealed class GameWindow :
|
|||
_sessionPlayerBindings = result.RuntimeBindings;
|
||||
}
|
||||
|
||||
void IGameWindowFrameRootPublication.PublishFrameRoots(
|
||||
FrameRootResult result)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(result);
|
||||
if (_worldLifecycleAutomation is not null
|
||||
|| _frameRootBindings is not null
|
||||
|| _frameGraphPublication is not null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The GameWindow composition shell already owns frame roots.");
|
||||
}
|
||||
|
||||
_worldLifecycleAutomation = result.LifecycleAutomation;
|
||||
_frameRootBindings = result.RuntimeBindings;
|
||||
_frameGraphPublication = result.FrameGraphPublication;
|
||||
}
|
||||
|
||||
private static void PublishCompositionOwner<T>(
|
||||
ref T? destination,
|
||||
T value,
|
||||
|
|
@ -1355,268 +1375,53 @@ public sealed class GameWindow :
|
|||
interactionUi,
|
||||
livePresentation);
|
||||
|
||||
var teleportRenderState =
|
||||
new AcDream.App.Rendering.LocalPlayerTeleportRenderStateSource(
|
||||
sessionPlayer.LocalTeleport);
|
||||
var renderLoginState = new AcDream.App.Rendering.RenderLoginStateSource(
|
||||
_options.LiveMode,
|
||||
_localPlayerMode);
|
||||
var renderFrameGlState = new AcDream.App.Rendering.RenderFrameGlStateController(
|
||||
new AcDream.App.Rendering.SilkRenderFrameGlStateApi(_gl!));
|
||||
var renderFrameLivePreparation =
|
||||
new AcDream.App.Rendering.RuntimeRenderFrameLivePreparation(
|
||||
_textureCache,
|
||||
_wbMeshAdapter,
|
||||
_worldReveal,
|
||||
teleportRenderState,
|
||||
renderLoginState,
|
||||
new AcDream.App.Rendering.LiveLoginRevealCellSource(
|
||||
_liveEntities!,
|
||||
_localPlayerIdentity),
|
||||
_particleRenderer,
|
||||
_frameProfiler,
|
||||
_frameDiag);
|
||||
var renderFrameResources =
|
||||
new AcDream.App.Rendering.RenderFrameResourceController(
|
||||
_gpuFrameFlights!,
|
||||
new AcDream.App.Rendering.RuntimeRenderFrameBeginResources(
|
||||
_textureCache,
|
||||
_wbDrawDispatcher,
|
||||
_envCellRenderer,
|
||||
_portalDepthMask,
|
||||
_textRenderer,
|
||||
_uiHost?.TextRenderer,
|
||||
_clipFrame,
|
||||
_terrain,
|
||||
_sceneLightingUbo,
|
||||
_frameProfiler,
|
||||
_gl!),
|
||||
new AcDream.App.Rendering.RuntimeRenderFrameClearPhase(
|
||||
_gl!,
|
||||
WorldTime,
|
||||
Weather,
|
||||
teleportRenderState,
|
||||
_particleVisibility,
|
||||
worldRenderDiagnostics,
|
||||
renderFrameGlState),
|
||||
renderFrameLivePreparation);
|
||||
var renderWeatherFrame =
|
||||
new AcDream.App.Rendering.RenderWeatherFrameController(
|
||||
WorldTime,
|
||||
Weather);
|
||||
var skyPesFrame = new AcDream.App.Rendering.SkyPesFrameController(
|
||||
_scriptRunner!,
|
||||
_particleSink!,
|
||||
_effectPoses,
|
||||
_entityEffects);
|
||||
var worldFrameEnvironment =
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameEnvironmentPreparation(
|
||||
FrameRootResult frameRoots = new FrameRootCompositionPhase(
|
||||
new FrameRootDependencies(
|
||||
_options,
|
||||
WorldTime,
|
||||
Lighting,
|
||||
_wbDrawDispatcher,
|
||||
_envCellRenderer,
|
||||
_sceneLightingUbo,
|
||||
_renderRange,
|
||||
skyPesFrame);
|
||||
var worldRenderFrameBuilder =
|
||||
new AcDream.App.Rendering.WorldRenderFrameBuilder(
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameCameraSource(
|
||||
_cameraController!,
|
||||
sessionPlayer.LocalTeleport),
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameVisibilityPreparation(
|
||||
_retailSelectionScene,
|
||||
_particleVisibility,
|
||||
_terrain,
|
||||
_worldReveal,
|
||||
_envCellFrustum),
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameSettingsPreview(
|
||||
_runtimeSettings,
|
||||
_audioEngine,
|
||||
_cameraController!,
|
||||
_displayFramePacing),
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameRootSource(
|
||||
_physicsEngine,
|
||||
_cellVisibility,
|
||||
_localPlayerMode,
|
||||
_chaseCameraInput,
|
||||
_playerControllerSlot,
|
||||
_liveWorldOrigin),
|
||||
worldFrameEnvironment,
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameAnimatedEntitySource(
|
||||
_animatedEntities,
|
||||
_staticAnimationScheduler,
|
||||
_equippedChildRenderer),
|
||||
new AcDream.App.Rendering.RuntimeWorldFrameBuildingSource(
|
||||
_landblockPresentationPipeline!,
|
||||
_cellVisibility));
|
||||
var terrainDrawDiagnostics =
|
||||
new AcDream.App.Rendering.TerrainDrawDiagnosticsController(
|
||||
_frameDiag,
|
||||
worldRenderDiagnostics,
|
||||
new AcDream.App.Rendering.RuntimeFramePipelineDiagnosticFactsSource(
|
||||
_terrain,
|
||||
_landblockPresentationPipeline,
|
||||
renderFrameLivePreparation,
|
||||
_wbDrawDispatcher,
|
||||
_streamingController,
|
||||
_liveEntities!,
|
||||
_worldState),
|
||||
_renderDiagnosticLog);
|
||||
var retailPViewCells = new AcDream.App.Rendering.RetailPViewCellSource(
|
||||
_cellVisibility);
|
||||
var retailPViewPassExecutor =
|
||||
new AcDream.App.Rendering.RetailPViewPassExecutor(
|
||||
_gl!,
|
||||
renderFrameGlState,
|
||||
new AcDream.App.Rendering.SilkRetailPViewFramebufferSource(
|
||||
_window!),
|
||||
_clipFrame!,
|
||||
_terrain,
|
||||
_envCellRenderer!,
|
||||
_wbDrawDispatcher!,
|
||||
_skyRenderer,
|
||||
_particleSystem,
|
||||
_particleRenderer,
|
||||
_portalDepthMask,
|
||||
_retailAlphaQueue,
|
||||
worldRenderDiagnostics,
|
||||
terrainDrawDiagnostics);
|
||||
var worldSceneDiagnostics =
|
||||
new AcDream.App.Rendering.WorldSceneDiagnosticsController(
|
||||
worldRenderDiagnostics,
|
||||
new AcDream.App.Rendering.RuntimeWorldScenePViewDiagnosticSource(
|
||||
_playerControllerSlot,
|
||||
_physicsEngine,
|
||||
_cellVisibility),
|
||||
_worldSceneDebugState,
|
||||
_debugLines,
|
||||
_window!,
|
||||
_input!,
|
||||
WorldTime,
|
||||
Weather,
|
||||
Lighting,
|
||||
_worldEnvironment,
|
||||
_physicsEngine,
|
||||
_cellVisibility,
|
||||
_localPlayerMode,
|
||||
_localPlayerIdentity,
|
||||
_chaseCameraInput,
|
||||
_playerControllerSlot,
|
||||
_debugVmRenderFacts,
|
||||
_debugVm is not null);
|
||||
var worldScenePasses = new AcDream.App.Rendering.WorldScenePassExecutor(
|
||||
_gl!,
|
||||
renderFrameGlState,
|
||||
_clipFrame!,
|
||||
_wbDrawDispatcher!,
|
||||
_envCellRenderer!,
|
||||
_terrain,
|
||||
terrainDrawDiagnostics,
|
||||
_skyRenderer,
|
||||
_particleSystem,
|
||||
_particleRenderer);
|
||||
var worldSceneRenderer = new AcDream.App.Rendering.WorldSceneRenderer(
|
||||
renderFrameResources,
|
||||
renderLoginState,
|
||||
_worldEnvironment,
|
||||
worldRenderFrameBuilder,
|
||||
new AcDream.App.Rendering.RuntimeWorldSceneEntitySource(_worldState),
|
||||
_retailSelectionScene,
|
||||
_retailAlphaQueue,
|
||||
_particleVisibility,
|
||||
new AcDream.App.Rendering.WorldScenePViewRenderer(
|
||||
new AcDream.App.Rendering.RetailPViewRenderer(),
|
||||
retailPViewPassExecutor,
|
||||
retailPViewPassExecutor),
|
||||
retailPViewCells,
|
||||
worldScenePasses,
|
||||
_renderRange,
|
||||
worldSceneDiagnostics);
|
||||
if (_frameScreenshots is { } screenshots
|
||||
&& _options.AutomationArtifactDirectory is { } artifactDirectory)
|
||||
{
|
||||
void UiProbeLog(string message) =>
|
||||
Console.WriteLine("[UI-PROBE] " + message);
|
||||
_worldLifecycleAutomation =
|
||||
new AcDream.App.Diagnostics.WorldLifecycleAutomationController(
|
||||
() => _worldReveal?.Snapshot ?? default,
|
||||
() => _worldReveal?.PortalMaterializationCount ?? 0,
|
||||
CaptureWorldLifecycleResourceSnapshot,
|
||||
screenshots,
|
||||
artifactDirectory,
|
||||
UiProbeLog);
|
||||
interactionUi.LateBindings.AdoptLateOwnerBinding(
|
||||
"world lifecycle automation",
|
||||
interactionUi.LateBindings.Automation.Bind(
|
||||
_worldLifecycleAutomation));
|
||||
}
|
||||
AcDream.App.Rendering.IRetainedGameplayUiFrame? retainedGameplayUi =
|
||||
_options.RetailUi && _retailUiRuntime is not null
|
||||
? new AcDream.App.Rendering.RetainedGameplayUiFrame(
|
||||
_retailUiRuntime,
|
||||
_input)
|
||||
: null;
|
||||
AcDream.App.Rendering.IPrivateFrameScreenshot? privateScreenshot =
|
||||
_frameScreenshots is not null
|
||||
? new AcDream.App.Rendering.PrivateFrameScreenshot(
|
||||
_frameScreenshots)
|
||||
: null;
|
||||
var privatePresentation =
|
||||
new AcDream.App.Rendering.PrivatePresentationRenderer(
|
||||
new AcDream.App.Rendering.LocalPlayerPortalViewport(
|
||||
sessionPlayer.LocalTeleport,
|
||||
_cameraController!),
|
||||
renderFrameResources,
|
||||
_paperdollFramePresenter,
|
||||
retainedGameplayUi,
|
||||
_devToolsFramePresenter,
|
||||
privateScreenshot);
|
||||
var framePreparation =
|
||||
new AcDream.App.Rendering.RenderFramePreparationController(
|
||||
renderFrameResources,
|
||||
_devToolsFramePresenter,
|
||||
renderWeatherFrame);
|
||||
var renderFrameOrchestrator =
|
||||
new AcDream.App.Rendering.RenderFrameOrchestrator(
|
||||
_gpuFrameFlights!,
|
||||
framePreparation,
|
||||
worldSceneRenderer,
|
||||
privatePresentation,
|
||||
_renderFrameDiagnostics!,
|
||||
(AcDream.App.Rendering.IRenderFrameFailureRecovery?)
|
||||
_devToolsFramePresenter
|
||||
?? AcDream.App.Rendering.NullRenderFrameFailureRecovery.Instance);
|
||||
var liveFrameCoordinator = new AcDream.App.World.RetailLiveFrameCoordinator(
|
||||
sessionPlayer.LiveObjectFrame,
|
||||
_worldState,
|
||||
sessionPlayer.LiveSession,
|
||||
sessionPlayer.LocalPlayerFrame,
|
||||
sessionPlayer.LiveSpatialReconciler);
|
||||
var cameraFrame = new AcDream.App.Rendering.CameraFrameController(
|
||||
_cameraController!,
|
||||
_inputCapture,
|
||||
_cameraInput,
|
||||
sessionPlayer.LocalPlayerFrameRuntime,
|
||||
_chaseCameraInput,
|
||||
sessionPlayer.LocalPlayerFrame,
|
||||
sessionPlayer.LiveSpatialReconciler,
|
||||
new AcDream.App.Combat.CombatCameraTargetSource(
|
||||
_liveWorldOrigin,
|
||||
_particleVisibility,
|
||||
_effectPoses,
|
||||
_renderRange,
|
||||
_runtimeSettings,
|
||||
Combat,
|
||||
_displayFramePacing,
|
||||
_worldSceneDebugState,
|
||||
_retailAlphaQueue,
|
||||
_frameProfiler,
|
||||
_frameDiag,
|
||||
_renderDiagnosticLog,
|
||||
_debugVmRenderFacts,
|
||||
_inputCapture,
|
||||
_cameraInput,
|
||||
_selection,
|
||||
_worldSelectionQuery!));
|
||||
var updateFrameOrchestrator = new AcDream.App.Update.UpdateFrameOrchestrator(
|
||||
new AcDream.App.Update.LiveEntityTeardownFramePhase(
|
||||
livePresentation.LiveEntities),
|
||||
new AcDream.App.Update.ConsoleUpdateFrameFailureSink(),
|
||||
_updateFrameClock,
|
||||
new AcDream.App.Update.PhysicsScriptClockPublisher(_scriptRunner!),
|
||||
sessionPlayer.StreamingFrame,
|
||||
sessionPlayer.GameplayInput,
|
||||
liveFrameCoordinator,
|
||||
new AcDream.App.Update.LiveEntityLivenessFramePhase(
|
||||
sessionPlayer.Liveness,
|
||||
new AcDream.App.Update.StopwatchClientMonotonicTimeSource()),
|
||||
sessionPlayer.LocalTeleport,
|
||||
new AcDream.App.Update.PlayerModeAutoEntryFramePhase(
|
||||
sessionPlayer.PlayerModeAutoEntry),
|
||||
cameraFrame);
|
||||
_frameGraphs.Publish(updateFrameOrchestrator, renderFrameOrchestrator);
|
||||
_animatedEntities,
|
||||
_updateFrameClock,
|
||||
Combat,
|
||||
_frameGraphs,
|
||||
Console.WriteLine),
|
||||
this).Compose(
|
||||
hostInputCamera,
|
||||
contentEffectsAudio,
|
||||
settingsDevTools,
|
||||
worldRender,
|
||||
interactionUi,
|
||||
livePresentation,
|
||||
sessionPlayer);
|
||||
|
||||
AcDream.App.Net.LiveSessionStartResult liveStart =
|
||||
sessionPlayer.SessionHost.Start(_options);
|
||||
frameRoots.SessionHost.Start(_options);
|
||||
switch (liveStart.Status)
|
||||
{
|
||||
case AcDream.App.Net.LiveSessionStartStatus.MissingCredentials:
|
||||
|
|
@ -1649,54 +1454,6 @@ public sealed class GameWindow :
|
|||
out _);
|
||||
}
|
||||
|
||||
private AcDream.App.Diagnostics.WorldLifecycleResourceSnapshot
|
||||
CaptureWorldLifecycleResourceSnapshot()
|
||||
{
|
||||
var meshManager = _wbMeshAdapter?.MeshManager;
|
||||
var mesh = meshManager?.Diagnostics ?? default;
|
||||
AcDream.App.Rendering.RenderFrameDiagnosticsSnapshot render =
|
||||
_renderFrameDiagnostics?.Snapshot
|
||||
?? AcDream.App.Rendering.RenderFrameDiagnosticsSnapshot.Initial;
|
||||
GCMemoryInfo memory = GC.GetGCMemoryInfo();
|
||||
return new AcDream.App.Diagnostics.WorldLifecycleResourceSnapshot(
|
||||
LoadedLandblocks: _worldState.LoadedLandblockIds.Count,
|
||||
WorldEntities: _worldState.Entities.Count,
|
||||
AnimatedEntities: _animatedEntities.Count,
|
||||
VisibleLandblocks: render.VisibleLandblocks,
|
||||
TotalLandblocks: render.TotalLandblocks,
|
||||
LiveEntities: _liveEntities?.Count ?? 0,
|
||||
MaterializedLiveEntities: _liveEntities?.MaterializedCount ?? 0,
|
||||
PendingLiveTeardowns: _liveEntities?.PendingTeardownCount ?? 0,
|
||||
PendingLandblockRetirements:
|
||||
_streamingController?.PendingRetirementCount ?? 0,
|
||||
ParticleEmitters: _particleSystem?.ActiveEmitterCount ?? 0,
|
||||
Particles: _particleSystem?.ActiveParticleCount ?? 0,
|
||||
ParticleBindings: _particleSink?.ActiveBindingCount ?? 0,
|
||||
ParticleOwners: _particleSink?.TrackedOwnerCount ?? 0,
|
||||
EffectOwners: _entityEffects?.ReadyOwnerCount ?? 0,
|
||||
LightOwners: _liveEntityLights?.TrackedOwnerCount ?? 0,
|
||||
ScriptOwners: _scriptRunner?.ActiveOwnerCount ?? 0,
|
||||
ActiveScripts: _scriptRunner?.ActiveScriptCount ?? 0,
|
||||
MeshRenderData: mesh.RenderData,
|
||||
MeshAtlasArrays: mesh.AtlasArrays,
|
||||
MeshEstimatedBytes: mesh.EstimatedBytes,
|
||||
StagedMeshUploads: _wbMeshAdapter?.StagedUploadBacklog ?? 0,
|
||||
StagedMeshBytes: _wbMeshAdapter?.StagedUploadBytes ?? 0,
|
||||
TrackedGpuBytes: AcDream.App.Rendering.Wb.GpuMemoryTracker.AllocatedBytes,
|
||||
TrackedGpuBuffers: AcDream.App.Rendering.Wb.GpuMemoryTracker.BufferCount,
|
||||
TrackedGpuTextures: AcDream.App.Rendering.Wb.GpuMemoryTracker.TextureCount,
|
||||
OwnedCompositeTextures: _textureCache?.OwnedBindlessTextureCount ?? 0,
|
||||
CompositeTextureOwners: _textureCache?.TextureOwnerCount ?? 0,
|
||||
ActiveParticleTextures: _textureCache?.ActiveParticleTextureCount ?? 0,
|
||||
ParticleTextureOwners: _textureCache?.ParticleTextureOwnerCount ?? 0,
|
||||
CompositeWarmupPending: _wbDrawDispatcher?.LastCompositeWarmupPendingCount ?? 0,
|
||||
ManagedBytes: GC.GetTotalMemory(forceFullCollection: false),
|
||||
ManagedCommittedBytes: memory.TotalCommittedBytes,
|
||||
Fps: render.Fps,
|
||||
FrameMilliseconds: render.FrameMilliseconds,
|
||||
LastFrameProfile: _frameProfiler.LastReport);
|
||||
}
|
||||
|
||||
// IsEntityCurrentlyMoving REMOVED (2026-07-09): it powered a cache-bypass
|
||||
// narrowing that dropped settled-open doors / faded-out walls back onto the
|
||||
// stale Tier-1 rest-pose cache entry (the door/fade "flip-back"). See the
|
||||
|
|
@ -1871,7 +1628,20 @@ public sealed class GameWindow :
|
|||
[
|
||||
new("world frame composition", () =>
|
||||
{
|
||||
_frameGraphs.Withdraw();
|
||||
IDisposable? publication = _frameGraphPublication;
|
||||
if (publication is null)
|
||||
return;
|
||||
publication.Dispose();
|
||||
_frameGraphPublication = null;
|
||||
}),
|
||||
new("frame-root bindings", () =>
|
||||
{
|
||||
FrameRootRuntimeBindings? bindings = _frameRootBindings;
|
||||
if (bindings is null)
|
||||
return;
|
||||
bindings.Dispose();
|
||||
_frameRootBindings = null;
|
||||
_worldLifecycleAutomation = null;
|
||||
}),
|
||||
new("session/player bindings", () =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue