refactor(render): extract world scene frame owner

Move fallback and PView world drawing, shared alpha, particles, visibility, selection, and diagnostics behind focused frame owners. Make exceptional frames failure-atomic by restoring the shared GL baseline and preserving primary alpha failures through cleanup.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-22 07:22:09 +02:00
parent 85239fb373
commit 28e1cf8029
25 changed files with 2679 additions and 844 deletions

View file

@ -74,9 +74,17 @@ internal sealed class RetailPViewParticleClassifications
/// cell shells/objects, then surviving dynamics. Landscape sky/terrain/weather
/// placement follows <c>LScape::draw @ 0x00506330</c>.
/// </summary>
internal sealed class RetailPViewPassExecutor : IRetailPViewPassExecutor
internal interface IOutdoorSceneParticleOwnerSource
{
IReadOnlySet<uint> OutdoorSceneParticleEntityIds { get; }
}
internal sealed class RetailPViewPassExecutor :
IRetailPViewPassExecutor,
IOutdoorSceneParticleOwnerSource
{
private readonly GL _gl;
private readonly IRenderFrameGlState _frameGlState;
private readonly IRetailPViewFramebufferSource _framebuffer;
private readonly ClipFrame _clipFrame;
private readonly TerrainModernRenderer? _terrain;
@ -101,6 +109,7 @@ internal sealed class RetailPViewPassExecutor : IRetailPViewPassExecutor
public RetailPViewPassExecutor(
GL gl,
IRenderFrameGlState frameGlState,
IRetailPViewFramebufferSource framebuffer,
ClipFrame clipFrame,
TerrainModernRenderer? terrain,
@ -115,6 +124,8 @@ internal sealed class RetailPViewPassExecutor : IRetailPViewPassExecutor
TerrainDrawDiagnosticsController terrainDiagnostics)
{
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
_frameGlState = frameGlState
?? throw new ArgumentNullException(nameof(frameGlState));
_framebuffer = framebuffer
?? throw new ArgumentNullException(nameof(framebuffer));
_clipFrame = clipFrame ?? throw new ArgumentNullException(nameof(clipFrame));
@ -136,6 +147,28 @@ internal sealed class RetailPViewPassExecutor : IRetailPViewPassExecutor
_particleClassifications.BeginFrame();
}
public void AbortFrame()
{
List<Exception>? failures = null;
TryAbort(_frameGlState.RestoreFrameDefaults);
TryAbort(_particleClassifications.BeginFrame);
TryAbort(_noSceneParticleEntityIds.Clear);
if (failures is { Count: > 0 })
throw new AggregateException("Retail PView pass abort failed.", failures);
void TryAbort(Action operation)
{
try
{
operation();
}
catch (Exception error)
{
(failures ??= []).Add(error);
}
}
}
public ClipFrameAssembly AssembleClipFrame(
PortalVisibilityFrame portalFrame,
ClipFrameAssembly reuseAssembly) =>