refactor(render): extract typed retail pview passes
This commit is contained in:
parent
6d6e5b5fa5
commit
85239fb373
13 changed files with 1888 additions and 841 deletions
|
|
@ -2,6 +2,7 @@ using System.Numerics;
|
|||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using AcDream.Core.Vfx;
|
||||
using AcDream.Core.World;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
|
@ -123,6 +124,12 @@ internal sealed class WorldRenderDiagnostics
|
|||
private string? _lastScissorSignature;
|
||||
private long _scissorSequence;
|
||||
private string? _lastOutStageSignature;
|
||||
private string? _lastOutStageRoutingSignature;
|
||||
private readonly Dictionary<uint, bool> _outStageOwnerVerdicts = [];
|
||||
private readonly Dictionary<uint, int> _phantomObjectSignatures = [];
|
||||
private string? _lastClipRouteSignature;
|
||||
private long _clipRouteSequence;
|
||||
private readonly List<uint> _clipRouteCellKeys = [];
|
||||
|
||||
public WorldRenderDiagnostics(
|
||||
IRenderGlStateReader gl,
|
||||
|
|
@ -192,6 +199,177 @@ internal sealed class WorldRenderDiagnostics
|
|||
_log.WriteLine($"[clip-route-scis] n={_scissorSequence} {signature}");
|
||||
}
|
||||
|
||||
public void EmitClipRouteProbe(
|
||||
bool enabled,
|
||||
ClipFrame clipFrame,
|
||||
ClipFrameAssembly clipAssembly,
|
||||
ClipViewSlice slice,
|
||||
int sliceIndex)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
var text = new StringBuilder(256);
|
||||
text.Append(FormattableString.Invariant(
|
||||
$"slice={sliceIndex}/{clipAssembly.OutsideViewSlices.Length} slot={slice.Slot}"));
|
||||
text.Append(FormattableString.Invariant(
|
||||
$" ndc=({slice.NdcAabb.X:F3},{slice.NdcAabb.Y:F3},{slice.NdcAabb.Z:F3},{slice.NdcAabb.W:F3})"));
|
||||
text.Append(FormattableString.Invariant($" planes={slice.Planes.Length}["));
|
||||
for (int i = 0; i < slice.Planes.Length; i++)
|
||||
{
|
||||
Vector4 plane = slice.Planes[i];
|
||||
if (i > 0)
|
||||
text.Append(' ');
|
||||
text.Append(FormattableString.Invariant(
|
||||
$"({plane.X:F3},{plane.Y:F3},{plane.Z:F3},{plane.W:F3})"));
|
||||
}
|
||||
|
||||
text.Append("] cells={");
|
||||
_clipRouteCellKeys.Clear();
|
||||
foreach (uint key in clipAssembly.CellIdToSlot.Keys)
|
||||
_clipRouteCellKeys.Add(key);
|
||||
_clipRouteCellKeys.Sort();
|
||||
for (int i = 0; i < _clipRouteCellKeys.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
text.Append(',');
|
||||
text.Append(FormattableString.Invariant(
|
||||
$"0x{_clipRouteCellKeys[i]:X8}:{clipAssembly.CellIdToSlot[_clipRouteCellKeys[i]]}"));
|
||||
}
|
||||
text.Append('}');
|
||||
|
||||
ReadOnlySpan<byte> regionBytes = clipFrame.RegionBytesForTest;
|
||||
int offset = slice.Slot * ClipFrame.CellClipStrideBytes;
|
||||
if (offset >= 0 && offset + ClipFrame.CellClipStrideBytes <= regionBytes.Length)
|
||||
{
|
||||
uint count = BitConverter.ToUInt32(regionBytes.Slice(offset, 4));
|
||||
text.Append(FormattableString.Invariant($" ssbo[{slice.Slot}]: n={count}"));
|
||||
int planeCount = (int)Math.Min(count, (uint)ClipFrame.MaxPlanes);
|
||||
for (int i = 0; i < planeCount; i++)
|
||||
{
|
||||
int planeOffset = offset + ClipFrame.CellClipPlanesOffset + i * 16;
|
||||
float x = BitConverter.ToSingle(regionBytes.Slice(planeOffset, 4));
|
||||
float y = BitConverter.ToSingle(regionBytes.Slice(planeOffset + 4, 4));
|
||||
float z = BitConverter.ToSingle(regionBytes.Slice(planeOffset + 8, 4));
|
||||
float w = BitConverter.ToSingle(regionBytes.Slice(planeOffset + 12, 4));
|
||||
text.Append(FormattableString.Invariant($" ({x:F3},{y:F3},{z:F3},{w:F3})"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text.Append(FormattableString.Invariant(
|
||||
$" ssbo[{slice.Slot}]: OUT-OF-RANGE len={regionBytes.Length}"));
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> terrainBytes = clipFrame.TerrainBytesForTest;
|
||||
int terrainCount = BitConverter.ToInt32(terrainBytes[..4]);
|
||||
float p0 = BitConverter.ToSingle(terrainBytes.Slice(16, 4));
|
||||
float p1 = BitConverter.ToSingle(terrainBytes.Slice(20, 4));
|
||||
float p2 = BitConverter.ToSingle(terrainBytes.Slice(24, 4));
|
||||
float p3 = BitConverter.ToSingle(terrainBytes.Slice(28, 4));
|
||||
text.Append(FormattableString.Invariant(
|
||||
$" ubo: n={terrainCount} p0=({p0:F3},{p1:F3},{p2:F3},{p3:F3})"));
|
||||
|
||||
string signature = text.ToString();
|
||||
_clipRouteSequence++;
|
||||
if (signature == _lastClipRouteSignature)
|
||||
return;
|
||||
_lastClipRouteSignature = signature;
|
||||
_log.WriteLine($"[clip-route] n={_clipRouteSequence} {signature}");
|
||||
}
|
||||
|
||||
public void EmitOutStageOwner(
|
||||
bool enabled,
|
||||
IReadOnlySet<uint> watchedEntityIds,
|
||||
WorldEntity entity,
|
||||
Vector3 sphereCenter,
|
||||
float sphereRadius,
|
||||
int sliceIndex,
|
||||
bool passed)
|
||||
{
|
||||
if (!enabled
|
||||
|| !watchedEntityIds.Contains(entity.Id)
|
||||
|| (_outStageOwnerVerdicts.TryGetValue(entity.Id, out bool previous)
|
||||
&& previous == passed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_outStageOwnerVerdicts[entity.Id] = passed;
|
||||
_log.WriteLine(FormattableString.Invariant(
|
||||
$"[outstage-own] id=0x{entity.Id:X8} src=0x{entity.SourceGfxObjOrSetupId:X8} pos=({entity.Position.X:F1},{entity.Position.Y:F1},{entity.Position.Z:F1}) c=({sphereCenter.X:F1},{sphereCenter.Y:F1},{sphereCenter.Z:F1}) r={sphereRadius:F1} slice={sliceIndex} {(passed ? "PASS" : "CULL")}"));
|
||||
}
|
||||
|
||||
public void EmitOutStageRouting(
|
||||
bool enabled,
|
||||
int sliceIndex,
|
||||
IReadOnlyList<WorldEntity> entities,
|
||||
ViewconeCuller viewcone)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
var text = new StringBuilder(192);
|
||||
text.Append("slice=").Append(sliceIndex)
|
||||
.Append(" outStage=").Append(entities.Count).Append(" [");
|
||||
for (int i = 0; i < entities.Count; i++)
|
||||
{
|
||||
WorldEntity entity = entities[i];
|
||||
EntitySphere(entity, out Vector3 center, out float radius);
|
||||
bool passed = viewcone.SphereVisibleInOutsideSlice(
|
||||
sliceIndex,
|
||||
center,
|
||||
radius);
|
||||
if (i > 0)
|
||||
text.Append(' ');
|
||||
text.Append(FormattableString.Invariant(
|
||||
$"0x{(entity.ServerGuid != 0 ? entity.ServerGuid : entity.Id):X8}(s{entity.SourceGfxObjOrSetupId:X8}):{(passed ? "PASS" : "CULL")}:r={radius:F1}"));
|
||||
}
|
||||
text.Append(']');
|
||||
string signature = text.ToString();
|
||||
if (signature == _lastOutStageRoutingSignature)
|
||||
return;
|
||||
_lastOutStageRoutingSignature = signature;
|
||||
_log.WriteLine("[outstage] " + signature);
|
||||
}
|
||||
|
||||
public void EmitPhantomObjects(bool enabled, uint cellId, int survivorCount)
|
||||
{
|
||||
if (!enabled
|
||||
|| (_phantomObjectSignatures.TryGetValue(cellId, out int previous)
|
||||
&& previous == survivorCount))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_phantomObjectSignatures[cellId] = survivorCount;
|
||||
_log.WriteLine(
|
||||
$"[phantom-objs] cell=0x{cellId:X8} entities={survivorCount} (drawn unclipped, no viewcone)");
|
||||
}
|
||||
|
||||
public void EmitSeamMask(
|
||||
bool enabled,
|
||||
IReadOnlySet<uint> targetCells,
|
||||
uint cellId,
|
||||
int portalIndex,
|
||||
bool forceFarZ,
|
||||
ReadOnlySpan<Vector3> vertices)
|
||||
{
|
||||
if (!enabled || !targetCells.Contains(cellId))
|
||||
return;
|
||||
|
||||
float minimumZ = float.MaxValue;
|
||||
float maximumZ = float.MinValue;
|
||||
foreach (Vector3 vertex in vertices)
|
||||
{
|
||||
minimumZ = Math.Min(minimumZ, vertex.Z);
|
||||
maximumZ = Math.Max(maximumZ, vertex.Z);
|
||||
}
|
||||
|
||||
_log.WriteLine(FormattableString.Invariant(
|
||||
$"[seam-mask] t={Environment.TickCount64} cell=0x{cellId:X8} portal={portalIndex} far={forceFarZ} n={vertices.Length} z=[{minimumZ:F3},{maximumZ:F3}]"));
|
||||
}
|
||||
|
||||
public void EmitPViewInput(
|
||||
bool enabled,
|
||||
PortalVisibilityFrame portalFrame,
|
||||
|
|
@ -590,4 +768,15 @@ internal sealed class WorldRenderDiagnostics
|
|||
.Append(" live=").Append(partition.Dynamics.Count)
|
||||
.ToString();
|
||||
}
|
||||
|
||||
private static void EntitySphere(
|
||||
WorldEntity entity,
|
||||
out Vector3 center,
|
||||
out float radius)
|
||||
{
|
||||
if (entity.AabbDirty)
|
||||
entity.RefreshAabb();
|
||||
center = (entity.AabbMin + entity.AabbMax) * 0.5f;
|
||||
radius = (entity.AabbMax - entity.AabbMin).Length() * 0.5f;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue