perf(rendering): draw retained frame product

Make the incremental render scene the production entity source at the existing retail PView stages while retaining the accepted dispatcher upload and draw executor. Keep diagnostics consumer-gated, retain ordered indices across unchanged frames, refresh only dirty records, and preserve exact mesh-load, selection, alpha, lighting, and route lifecycle semantics.
This commit is contained in:
Erik 2026-07-25 04:12:23 +02:00
parent c7d7848dd2
commit ef1d263337
13 changed files with 1333 additions and 267 deletions

View file

@ -1,4 +1,5 @@
using System.Numerics;
using AcDream.App.Rendering.Scene;
using AcDream.App.Rendering.Sky;
using AcDream.App.Rendering.Wb;
using AcDream.Core.Rendering;
@ -79,8 +80,33 @@ internal interface IOutdoorSceneParticleOwnerSource
IReadOnlySet<uint> OutdoorSceneParticleEntityIds { get; }
}
internal readonly record struct RenderFrameEntityDrawRequest(
RenderFrameView View,
RenderFrameCandidateRoute Route,
int RouteIndex,
uint CellId,
uint TupleLandblockId);
internal interface IRenderFrameEntityPassExecutor
{
void BeginEntityFrame(in RenderFrameView view);
bool DrawEntityRoute(
ICamera camera,
in RenderFrameView view,
RenderFrameCandidateRoute route,
int routeIndex,
uint cellId,
uint tupleLandblockId);
void CompleteEntityFrame(in RenderFrameView view);
void AbortEntityFrame();
}
internal sealed class RetailPViewPassExecutor :
IRetailPViewPassExecutor,
IRenderFrameEntityPassExecutor,
IOutdoorSceneParticleOwnerSource
{
private readonly GL _gl;
@ -147,6 +173,30 @@ internal sealed class RetailPViewPassExecutor :
_particleClassifications.BeginFrame();
}
public void BeginEntityFrame(in RenderFrameView view) =>
_entities.BeginPackedProductionFrame(in view);
public bool DrawEntityRoute(
ICamera camera,
in RenderFrameView view,
RenderFrameCandidateRoute route,
int routeIndex,
uint cellId,
uint tupleLandblockId) =>
_entities.DrawPackedProductionRoute(
camera,
in view,
route,
routeIndex,
cellId,
tupleLandblockId);
public void CompleteEntityFrame(in RenderFrameView view) =>
_entities.CompletePackedProductionFrame(in view);
public void AbortEntityFrame() =>
_entities.AbortPackedProductionFrame();
public void AbortFrame()
{
List<Exception>? failures = null;
@ -332,7 +382,18 @@ internal sealed class RetailPViewPassExecutor :
_terrainDiagnostics.Complete();
DisableClipDistances();
if (context.OutdoorEntities.Count > 0)
if (context.EntityDraw is RenderFrameEntityDrawRequest request)
{
RenderFrameView drawView = request.View;
_entities.DrawPackedProductionRoute(
frame.Camera,
in drawView,
request.Route,
request.RouteIndex,
request.CellId,
request.TupleLandblockId);
}
else if (context.OutdoorEntities.Count > 0)
{
var sceneryEntry = (
frame.PlayerLandblockId ?? 0u,
@ -363,7 +424,18 @@ internal sealed class RetailPViewPassExecutor :
_clipFrame.BindTerrainClip(_gl);
DisableClipDistances();
if (context.Dynamics.Count > 0)
if (context.EntityDraw is RenderFrameEntityDrawRequest request)
{
RenderFrameView drawView = request.View;
_entities.DrawPackedProductionRoute(
frame.Camera,
in drawView,
request.Route,
request.RouteIndex,
request.CellId,
request.TupleLandblockId);
}
else if (context.Dynamics.Count > 0)
{
var dynamicsEntry = (
frame.PlayerLandblockId ?? 0u,