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:
parent
85239fb373
commit
28e1cf8029
25 changed files with 2679 additions and 844 deletions
328
src/AcDream.App/Rendering/WorldScenePassExecutor.cs
Normal file
328
src/AcDream.App/Rendering/WorldScenePassExecutor.cs
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
using AcDream.App.Rendering.Sky;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.Vfx;
|
||||
using AcDream.Core.World;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
||||
internal interface IWorldScenePassExecutor
|
||||
{
|
||||
HashSet<uint>? TerrainVisibleCellIds { get; }
|
||||
|
||||
void BeginFrame();
|
||||
|
||||
void PrepareFlatWorldClip();
|
||||
|
||||
void DrawFlatSky(
|
||||
in WorldCameraFrame camera,
|
||||
in RenderFrameFoundation foundation,
|
||||
DayGroupData? activeDayGroup,
|
||||
float dayFraction);
|
||||
|
||||
void DrawFlatTerrain(in WorldCameraFrame camera, uint? playerLandblockId);
|
||||
|
||||
void DrawFlatEntities(
|
||||
in WorldCameraFrame camera,
|
||||
IEnumerable<(uint LandblockId, System.Numerics.Vector3 AabbMin,
|
||||
System.Numerics.Vector3 AabbMax,
|
||||
IReadOnlyList<WorldEntity> Entities,
|
||||
IReadOnlyDictionary<uint, WorldEntity>? AnimatedById)> entries,
|
||||
uint? playerLandblockId,
|
||||
HashSet<uint> animatedEntityIds);
|
||||
|
||||
string DrawPostWorldParticles(
|
||||
LoadedCell? clipRoot,
|
||||
ClipFrameAssembly? clipAssembly,
|
||||
in WorldCameraFrame camera,
|
||||
IReadOnlySet<uint> outdoorOwnerIds,
|
||||
string currentSignature);
|
||||
|
||||
void DrawFlatWeather(
|
||||
in WorldCameraFrame camera,
|
||||
in RenderFrameFoundation foundation,
|
||||
DayGroupData? activeDayGroup,
|
||||
float dayFraction);
|
||||
|
||||
void DisableClipDistances();
|
||||
|
||||
void AbortFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concrete GL leaf for the flat-world safety path and the post-world particle
|
||||
/// and weather passes. Retail PView frames remain owned by
|
||||
/// <see cref="RetailPViewRenderer"/> and <see cref="RetailPViewPassExecutor"/>.
|
||||
/// </summary>
|
||||
internal sealed class WorldScenePassExecutor : IWorldScenePassExecutor
|
||||
{
|
||||
private readonly GL _gl;
|
||||
private readonly IRenderFrameGlState _frameGlState;
|
||||
private readonly ClipFrame _clipFrame;
|
||||
private readonly WbDrawDispatcher _entities;
|
||||
private readonly EnvCellRenderer _environmentCells;
|
||||
private readonly TerrainModernRenderer? _terrain;
|
||||
private readonly TerrainDrawDiagnosticsController _terrainDiagnostics;
|
||||
private readonly SkyRenderer? _sky;
|
||||
private readonly ParticleSystem? _particles;
|
||||
private readonly ParticleRenderer? _particleRenderer;
|
||||
private readonly HashSet<uint> _visibleParticleOwners = [];
|
||||
private readonly HashSet<uint> _noExcludedParticleOwners = [];
|
||||
|
||||
public WorldScenePassExecutor(
|
||||
GL gl,
|
||||
IRenderFrameGlState frameGlState,
|
||||
ClipFrame clipFrame,
|
||||
WbDrawDispatcher entities,
|
||||
EnvCellRenderer environmentCells,
|
||||
TerrainModernRenderer? terrain,
|
||||
TerrainDrawDiagnosticsController terrainDiagnostics,
|
||||
SkyRenderer? sky,
|
||||
ParticleSystem? particles,
|
||||
ParticleRenderer? particleRenderer)
|
||||
{
|
||||
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
|
||||
_frameGlState = frameGlState
|
||||
?? throw new ArgumentNullException(nameof(frameGlState));
|
||||
_clipFrame = clipFrame ?? throw new ArgumentNullException(nameof(clipFrame));
|
||||
_entities = entities ?? throw new ArgumentNullException(nameof(entities));
|
||||
_environmentCells = environmentCells
|
||||
?? throw new ArgumentNullException(nameof(environmentCells));
|
||||
_terrain = terrain;
|
||||
_terrainDiagnostics = terrainDiagnostics
|
||||
?? throw new ArgumentNullException(nameof(terrainDiagnostics));
|
||||
_sky = sky;
|
||||
_particles = particles;
|
||||
_particleRenderer = particleRenderer;
|
||||
}
|
||||
|
||||
public HashSet<uint>? TerrainVisibleCellIds => _terrain?.VisibleCellIds;
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
_visibleParticleOwners.Clear();
|
||||
_clipFrame.Reset();
|
||||
_entities.ClearClipRouting();
|
||||
_environmentCells.SetClipRouting(null);
|
||||
}
|
||||
|
||||
public void PrepareFlatWorldClip()
|
||||
{
|
||||
_clipFrame.ReserveTerrainUploads(_gl, 1);
|
||||
_clipFrame.UploadRegions(_gl);
|
||||
TerrainClipBufferBinding terrainBinding = _clipFrame.UploadTerrainClip(_gl);
|
||||
_entities.SetClipRegionSsbo(_clipFrame.RegionSsbo);
|
||||
_environmentCells.SetClipRegionSsbo(_clipFrame.RegionSsbo);
|
||||
_terrain?.SetClipUbo(terrainBinding);
|
||||
}
|
||||
|
||||
public void DrawFlatSky(
|
||||
in WorldCameraFrame camera,
|
||||
in RenderFrameFoundation foundation,
|
||||
DayGroupData? activeDayGroup,
|
||||
float dayFraction)
|
||||
{
|
||||
_clipFrame.BindTerrainClip(_gl);
|
||||
EnableClipDistances();
|
||||
Exception? drawFailure = null;
|
||||
try
|
||||
{
|
||||
_sky?.RenderSky(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
dayFraction,
|
||||
activeDayGroup,
|
||||
foundation.Sky,
|
||||
foundation.EnvironOverrideActive);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
drawFailure = error;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
DisableClipDistances();
|
||||
}
|
||||
catch (Exception closeFailure) when (drawFailure is not null)
|
||||
{
|
||||
throw new AggregateException(
|
||||
"Sky drawing failed and its clip-distance bracket could not be closed.",
|
||||
drawFailure,
|
||||
closeFailure);
|
||||
}
|
||||
}
|
||||
|
||||
if (_particles is not null && _particleRenderer is not null)
|
||||
{
|
||||
_particleRenderer.Draw(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
ParticleRenderPass.SkyPreScene);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawFlatTerrain(
|
||||
in WorldCameraFrame camera,
|
||||
uint? playerLandblockId)
|
||||
{
|
||||
EnableClipDistances();
|
||||
_terrainDiagnostics.Begin();
|
||||
_terrain?.Draw(
|
||||
camera.Camera,
|
||||
camera.Frustum,
|
||||
neverCullLandblockId: playerLandblockId);
|
||||
_terrainDiagnostics.Complete();
|
||||
}
|
||||
|
||||
public void DrawFlatEntities(
|
||||
in WorldCameraFrame camera,
|
||||
IEnumerable<(uint LandblockId, System.Numerics.Vector3 AabbMin,
|
||||
System.Numerics.Vector3 AabbMax,
|
||||
IReadOnlyList<WorldEntity> Entities,
|
||||
IReadOnlyDictionary<uint, WorldEntity>? AnimatedById)> entries,
|
||||
uint? playerLandblockId,
|
||||
HashSet<uint> animatedEntityIds) =>
|
||||
_entities.Draw(
|
||||
camera.Camera,
|
||||
entries,
|
||||
camera.Frustum,
|
||||
neverCullLandblockId: playerLandblockId,
|
||||
visibleCellIds: null,
|
||||
animatedEntityIds: animatedEntityIds);
|
||||
|
||||
public string DrawPostWorldParticles(
|
||||
LoadedCell? clipRoot,
|
||||
ClipFrameAssembly? clipAssembly,
|
||||
in WorldCameraFrame camera,
|
||||
IReadOnlySet<uint> outdoorOwnerIds,
|
||||
string currentSignature)
|
||||
{
|
||||
if (_particles is null || _particleRenderer is null)
|
||||
return currentSignature;
|
||||
|
||||
if (clipRoot is null)
|
||||
{
|
||||
if (clipAssembly is not null)
|
||||
{
|
||||
_particleRenderer.DrawForOwners(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
ParticleRenderPass.Scene,
|
||||
_visibleParticleOwners,
|
||||
includeUnattached: true,
|
||||
excludedAttachedOwnerIds: _noExcludedParticleOwners);
|
||||
return AppendSignature(currentSignature, "filtered");
|
||||
}
|
||||
|
||||
_particleRenderer.Draw(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
ParticleRenderPass.Scene);
|
||||
return AppendSignature(currentSignature, "global");
|
||||
}
|
||||
|
||||
if (clipRoot.IsOutdoorNode)
|
||||
{
|
||||
_particleRenderer.DrawForOwners(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
ParticleRenderPass.Scene,
|
||||
outdoorOwnerIds,
|
||||
includeUnattached: true);
|
||||
return AppendSignature(currentSignature, "unattached");
|
||||
}
|
||||
|
||||
return currentSignature;
|
||||
}
|
||||
|
||||
public void DrawFlatWeather(
|
||||
in WorldCameraFrame camera,
|
||||
in RenderFrameFoundation foundation,
|
||||
DayGroupData? activeDayGroup,
|
||||
float dayFraction)
|
||||
{
|
||||
_clipFrame.BindTerrainClip(_gl);
|
||||
EnableClipDistances();
|
||||
Exception? drawFailure = null;
|
||||
try
|
||||
{
|
||||
_sky?.RenderWeather(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
dayFraction,
|
||||
activeDayGroup,
|
||||
foundation.Sky,
|
||||
foundation.EnvironOverrideActive);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
drawFailure = error;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
DisableClipDistances();
|
||||
}
|
||||
catch (Exception closeFailure) when (drawFailure is not null)
|
||||
{
|
||||
throw new AggregateException(
|
||||
"Weather drawing failed and its clip-distance bracket could not be closed.",
|
||||
drawFailure,
|
||||
closeFailure);
|
||||
}
|
||||
}
|
||||
|
||||
if (_particles is not null && _particleRenderer is not null)
|
||||
{
|
||||
_particleRenderer.Draw(
|
||||
camera.Camera,
|
||||
camera.Position,
|
||||
ParticleRenderPass.SkyPostScene);
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableClipDistances()
|
||||
{
|
||||
for (int index = 0; index < ClipFrame.MaxPlanes; index++)
|
||||
_gl.Disable(EnableCap.ClipDistance0 + index);
|
||||
}
|
||||
|
||||
public void AbortFrame()
|
||||
{
|
||||
List<Exception>? failures = null;
|
||||
TryAbort(_frameGlState.RestoreFrameDefaults);
|
||||
TryAbort(_clipFrame.Reset);
|
||||
TryAbort(_entities.ClearClipRouting);
|
||||
TryAbort(() => _environmentCells.SetClipRouting(null));
|
||||
_visibleParticleOwners.Clear();
|
||||
if (failures is { Count: > 0 })
|
||||
throw new AggregateException("World scene pass abort failed.", failures);
|
||||
|
||||
void TryAbort(Action operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
operation();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableClipDistances()
|
||||
{
|
||||
for (int index = 0; index < ClipFrame.MaxPlanes; index++)
|
||||
_gl.Enable(EnableCap.ClipDistance0 + index);
|
||||
}
|
||||
|
||||
private static string AppendSignature(string current, string value) =>
|
||||
current == "none" ? value : current + "+" + value;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue