refactor(render): delete legacy pview execution machinery
This commit is contained in:
parent
1be4889c1c
commit
69e69408e8
9 changed files with 58 additions and 3627 deletions
|
|
@ -1,140 +1,17 @@
|
|||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using AcDream.App.Composition;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Tests.Architecture;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Tests.Rendering;
|
||||
|
||||
public sealed class RetailPViewPassExecutorTests
|
||||
{
|
||||
[Fact]
|
||||
public void DrawInside_outdoor_executes_the_real_typed_sequence_without_interior_clear()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
LoadedCell root = OutdoorCellNode.Build(0xA9B40000u);
|
||||
|
||||
renderer.DrawInside(Frame(root), executor);
|
||||
|
||||
Assert.Equal(
|
||||
[
|
||||
"begin",
|
||||
"assemble",
|
||||
"append-look-in-clips",
|
||||
// FW4 slice 1: the clip-region publication moved below the
|
||||
// walk block (a walk-rooted interior frame re-derives the
|
||||
// outside-view slices from the walk's own views first, and
|
||||
// the appended slots must join the same single publication).
|
||||
"indoor-routing",
|
||||
"prepare-clip:3",
|
||||
"prepare-cells",
|
||||
"diagnostics",
|
||||
"terrain-clip",
|
||||
"clear-routing",
|
||||
"landscape-early",
|
||||
"terrain-clip",
|
||||
"clear-routing",
|
||||
"landscape-late",
|
||||
"unattached-particles-outdoor",
|
||||
// #132: an OUTDOOR root does NOT drain at the stage boundary.
|
||||
// The far prefix drains at the pre-punch barrier (retail
|
||||
// DrawBuilding @0x0059F2A0 flushes before its portal-only
|
||||
// far-Z pass), and the full drain runs after the dynamics
|
||||
// pass, where the frame's opaque world depth is complete.
|
||||
"indoor-routing",
|
||||
"indoor-routing",
|
||||
"landscape-alpha-farther",
|
||||
"exit-mask",
|
||||
"indoor-routing",
|
||||
"opaque-shells",
|
||||
"landscape-alpha",
|
||||
"unattached-particles-interior",
|
||||
],
|
||||
executor.Operations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_interior_exit_flushes_before_clear_and_resets_borrowed_result()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
LoadedCell outdoor = OutdoorCellNode.Build(0xA9B40000u);
|
||||
RetailPViewFrameResult borrowed = renderer.DrawInside(Frame(outdoor), executor);
|
||||
Assert.Contains(outdoor.CellId, borrowed.DrawableCells);
|
||||
|
||||
executor.Operations.Clear();
|
||||
LoadedCell interior = InteriorWithExit(0xA9B40100u);
|
||||
RetailPViewFrameResult reused = renderer.DrawInside(Frame(interior), executor);
|
||||
|
||||
Assert.Same(borrowed, reused);
|
||||
Assert.DoesNotContain(outdoor.CellId, reused.DrawableCells);
|
||||
Assert.Contains(interior.CellId, reused.DrawableCells);
|
||||
AssertAppearsInOrder(
|
||||
string.Join('|', executor.Operations),
|
||||
"landscape-early",
|
||||
"landscape-late",
|
||||
"unattached-particles-outdoor",
|
||||
"landscape-alpha",
|
||||
"interior-depth-clear",
|
||||
"indoor-routing",
|
||||
"exit-mask");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_interior_without_an_outside_slice_skips_landscape_and_depth_clear()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
var root = new LoadedCell
|
||||
{
|
||||
CellId = 0xA9B40100u,
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
};
|
||||
|
||||
renderer.DrawInside(Frame(root), executor);
|
||||
|
||||
Assert.DoesNotContain("terrain-clip", executor.Operations);
|
||||
Assert.DoesNotContain("landscape-early", executor.Operations);
|
||||
Assert.DoesNotContain("landscape-late", executor.Operations);
|
||||
Assert.DoesNotContain("landscape-alpha", executor.Operations);
|
||||
Assert.DoesNotContain("interior-depth-clear", executor.Operations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_interior_without_an_outside_slice_still_draws_interior_unattached_particles()
|
||||
{
|
||||
// Repro (Sanctuary middle cell, looking north): spell ground effects
|
||||
// vanished whenever no exit portal was in view, because unattached
|
||||
// emitters submitted once PER outside slice under that slice's
|
||||
// hardware clip slot — zero slices meant zero submissions. Retail
|
||||
// draws such an emitter during its owner cell's walk turn
|
||||
// (ShouldDrawParticles @0x0050FE60) and never clips it to a view.
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
var root = new LoadedCell
|
||||
{
|
||||
CellId = 0xA9B40100u,
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
};
|
||||
|
||||
renderer.DrawInside(Frame(root), executor);
|
||||
|
||||
Assert.Contains("unattached-particles-interior", executor.Operations);
|
||||
Assert.DoesNotContain(
|
||||
"unattached-particles-outdoor",
|
||||
executor.Operations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Particle_classifications_reset_before_an_empty_following_frame()
|
||||
{
|
||||
var classifications = new RetailPViewParticleClassifications();
|
||||
classifications.ReplaceOutdoor([Entity(7u)]);
|
||||
classifications.ReplaceOutdoor(new HashSet<uint> { 7u });
|
||||
classifications.Visible.Add(8u);
|
||||
classifications.Dynamics.Add(9u);
|
||||
|
||||
|
|
@ -145,159 +22,6 @@ public sealed class RetailPViewPassExecutorTests
|
|||
Assert.Empty(classifications.Dynamics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_real_fixtures_reach_entity_particle_and_transparent_shell_operations()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor { HasTransparentShells = true };
|
||||
LoadedCell interior = InteriorWithExit(0xA9B40100u);
|
||||
WorldEntity cellStatic = Entity(10u, parentCellId: interior.CellId);
|
||||
WorldEntity dynamic = Entity(
|
||||
11u,
|
||||
serverGuid: 0x80000011u,
|
||||
parentCellId: interior.CellId);
|
||||
|
||||
renderer.DrawInside(Frame(interior, [cellStatic, dynamic]), executor);
|
||||
|
||||
Assert.Contains("opaque-shells", executor.Operations);
|
||||
Assert.Contains("transparent-shells-ordered", executor.Operations);
|
||||
Assert.Contains("entity-bucket", executor.Operations);
|
||||
Assert.Contains("cell-particles", executor.Operations);
|
||||
Assert.Contains("dynamics-particles", executor.Operations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_referee_records_the_exact_routed_entity_buckets()
|
||||
{
|
||||
var oracle = new CurrentRenderSceneOracle();
|
||||
var renderer = new RetailPViewRenderer(oracle);
|
||||
using var executor = new RecordingExecutor();
|
||||
LoadedCell interior = InteriorWithExit(0xA9B40100u);
|
||||
WorldEntity cellStatic = Entity(20u, parentCellId: interior.CellId);
|
||||
WorldEntity dynamic = Entity(
|
||||
21u,
|
||||
serverGuid: 0x80000021u,
|
||||
parentCellId: interior.CellId);
|
||||
|
||||
renderer.DrawInside(Frame(interior, [cellStatic, dynamic]), executor);
|
||||
|
||||
Assert.Equal(1uL, oracle.Snapshot.CompletedPViewFrameSequence);
|
||||
Assert.Equal(2, oracle.Snapshot.PViewCandidateCount);
|
||||
Assert.Collection(
|
||||
oracle.PViewCandidates,
|
||||
candidate =>
|
||||
{
|
||||
Assert.Equal(CurrentRenderPViewRoute.CellStatic, candidate.Route);
|
||||
Assert.Equal(cellStatic.Id, candidate.Projection.EntityId);
|
||||
},
|
||||
candidate =>
|
||||
{
|
||||
Assert.Equal(CurrentRenderPViewRoute.DynamicLast, candidate.Route);
|
||||
Assert.Equal(dynamic.Id, candidate.Projection.EntityId);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_interior_root_executes_the_nearby_building_look_in_punch()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
LoadedCell root = InteriorWithExit(0xA9B40100u);
|
||||
root.BuildingId = 1u;
|
||||
LoadedCell[] building = NearbyTwoCellBuilding();
|
||||
|
||||
renderer.DrawInside(
|
||||
Frame(
|
||||
root,
|
||||
nearbyBuildingCells: building,
|
||||
additionalCells: building),
|
||||
executor);
|
||||
|
||||
Assert.Contains("look-in-punch", executor.Operations);
|
||||
AssertAppearsInOrder(
|
||||
string.Join('|', executor.Operations),
|
||||
"landscape-early",
|
||||
"unattached-particles-outdoor",
|
||||
"landscape-static-particles",
|
||||
"landscape-alpha-farther",
|
||||
"look-in-punch",
|
||||
"landscape-late",
|
||||
"landscape-alpha",
|
||||
"interior-depth-clear");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_repaints_the_exterior_building_shell_after_its_look_in()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
LoadedCell root = InteriorWithExit(0xA9B40100u);
|
||||
root.BuildingId = 1u;
|
||||
LoadedCell[] building = NearbyTwoCellBuilding();
|
||||
WorldEntity exteriorShell = Entity(
|
||||
0x700u,
|
||||
isBuildingShell: true,
|
||||
buildingShellAnchorCellId: building[0].CellId);
|
||||
|
||||
renderer.DrawInside(
|
||||
Frame(
|
||||
root,
|
||||
[exteriorShell],
|
||||
nearbyBuildingCells: building,
|
||||
additionalCells: building),
|
||||
executor);
|
||||
|
||||
AssertAppearsInOrder(
|
||||
string.Join('|', executor.Operations),
|
||||
"landscape-early",
|
||||
"look-in-punch",
|
||||
"landscape-building-shell",
|
||||
"landscape-late");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DrawInside_pairs_each_look_in_with_only_its_own_shell_and_alpha_barrier()
|
||||
{
|
||||
var renderer = new RetailPViewRenderer();
|
||||
using var executor = new RecordingExecutor();
|
||||
LoadedCell root = InteriorWithExit(0xA9B40100u);
|
||||
root.BuildingId = 1u;
|
||||
LoadedCell[] first = NearbyTwoCellBuilding();
|
||||
LoadedCell[] second = NearbyTwoCellBuilding(
|
||||
0xA9B40172u,
|
||||
0xA9B40173u,
|
||||
buildingId: 3u);
|
||||
LoadedCell[] buildings = [.. first, .. second];
|
||||
WorldEntity[] shells =
|
||||
[
|
||||
Entity(
|
||||
0x700u,
|
||||
isBuildingShell: true,
|
||||
buildingShellAnchorCellId: first[0].CellId),
|
||||
Entity(
|
||||
0x701u,
|
||||
isBuildingShell: true,
|
||||
buildingShellAnchorCellId: second[0].CellId),
|
||||
];
|
||||
|
||||
renderer.DrawInside(
|
||||
Frame(
|
||||
root,
|
||||
shells,
|
||||
nearbyBuildingCells: buildings,
|
||||
additionalCells: buildings),
|
||||
executor);
|
||||
|
||||
AssertAppearsInOrder(
|
||||
string.Join('|', executor.Operations),
|
||||
"look-in-punch",
|
||||
"landscape-building-shell",
|
||||
"landscape-static-particles",
|
||||
"landscape-alpha-farther",
|
||||
"look-in-punch",
|
||||
"landscape-building-shell");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extracted_contracts_retain_no_window_callbacks_or_visibility_owner()
|
||||
{
|
||||
|
|
@ -330,7 +54,8 @@ public sealed class RetailPViewPassExecutorTests
|
|||
nameof(RetailPViewParticleClassifications.BeginFrame)) >= 0);
|
||||
|
||||
MethodInfo landscape = typeof(RetailPViewPassExecutor).GetMethod(
|
||||
nameof(RetailPViewPassExecutor.DrawLandscapeSlice))!;
|
||||
"DrawWalkTerrainSlice",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||
IReadOnlyList<CompiledCall> landscapeCalls = CompiledCallGraph.Read(landscape);
|
||||
int diagnosticsBegin = RequiredCallIndex(
|
||||
landscapeCalls,
|
||||
|
|
@ -350,7 +75,7 @@ public sealed class RetailPViewPassExecutorTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GameWindow_composes_one_executor_instead_of_a_draw_callback_bag()
|
||||
public void Frame_composition_constructs_one_walk_executor()
|
||||
{
|
||||
MethodInfo compose = typeof(FrameRootCompositionPhase).GetMethod(
|
||||
"ComposeCore",
|
||||
|
|
@ -377,368 +102,15 @@ public sealed class RetailPViewPassExecutorTests
|
|||
&& call.Target.Name == ".ctor");
|
||||
}
|
||||
|
||||
private static RetailPViewFrameInput Frame(
|
||||
LoadedCell root,
|
||||
IReadOnlyList<WorldEntity>? entities = null,
|
||||
IReadOnlyList<LoadedCell>? nearbyBuildingCells = null,
|
||||
IReadOnlyList<LoadedCell>? additionalCells = null)
|
||||
{
|
||||
var cells = new Dictionary<uint, LoadedCell> { [root.CellId] = root };
|
||||
if (additionalCells is not null)
|
||||
{
|
||||
foreach (LoadedCell cell in additionalCells)
|
||||
cells[cell.CellId] = cell;
|
||||
}
|
||||
|
||||
var entries = new List<(
|
||||
uint LandblockId,
|
||||
Vector3 AabbMin,
|
||||
Vector3 AabbMax,
|
||||
IReadOnlyList<WorldEntity> Entities,
|
||||
IReadOnlyDictionary<uint, WorldEntity>? AnimatedById)>();
|
||||
if (entities is not null)
|
||||
{
|
||||
entries.Add((
|
||||
root.CellId & 0xFFFF0000u,
|
||||
Vector3.Zero,
|
||||
Vector3.Zero,
|
||||
entities,
|
||||
null));
|
||||
}
|
||||
|
||||
return new RetailPViewFrameInput().Reset(
|
||||
root,
|
||||
nearbyBuildingCells,
|
||||
Vector3.Zero,
|
||||
TestCamera.ViewProjection,
|
||||
new DictionaryCellSource(cells),
|
||||
new TestCamera(),
|
||||
Vector3.Zero,
|
||||
frustum: null,
|
||||
playerLandblockId: root.CellId & 0xFFFF0000u,
|
||||
animatedEntityIds: null,
|
||||
renderCenterLbX: 0,
|
||||
renderCenterLbY: 0,
|
||||
renderRadius: 1,
|
||||
landblockEntries: entries,
|
||||
renderSky: true,
|
||||
renderWeather: true,
|
||||
dayFraction: 0f,
|
||||
activeDayGroup: null,
|
||||
skyKeyframe: default,
|
||||
environOverrideActive: false,
|
||||
viewerCellId: root.CellId,
|
||||
playerCellId: root.CellId,
|
||||
playerViewPosition: Vector3.Zero,
|
||||
cameraView: TestCamera.ViewMatrix,
|
||||
cameraCellResolution: CameraCellResolution.None);
|
||||
}
|
||||
|
||||
private static LoadedCell InteriorWithExit(uint cellId)
|
||||
{
|
||||
var cell = new LoadedCell
|
||||
{
|
||||
CellId = cellId,
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
Portals = [new CellPortalInfo(0xFFFF, 0, 0, 0)],
|
||||
};
|
||||
cell.PortalPolygons.Add(
|
||||
[
|
||||
new Vector3(-1f, -1f, -2f),
|
||||
new Vector3(1f, -1f, -2f),
|
||||
new Vector3(1f, 1f, -2f),
|
||||
new Vector3(-1f, 1f, -2f),
|
||||
]);
|
||||
return cell;
|
||||
}
|
||||
|
||||
private static LoadedCell[] NearbyTwoCellBuilding(
|
||||
uint vestibuleId = 0xA9B40170u,
|
||||
uint roomId = 0xA9B40171u,
|
||||
uint buildingId = 2u)
|
||||
{
|
||||
var vestibule = new LoadedCell
|
||||
{
|
||||
CellId = vestibuleId,
|
||||
BuildingId = buildingId,
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
Portals =
|
||||
[
|
||||
new CellPortalInfo(0xFFFF, 0, 0, 0),
|
||||
new CellPortalInfo((ushort)(roomId & 0xFFFFu), 1, 0, 0),
|
||||
],
|
||||
ClipPlanes =
|
||||
[
|
||||
new PortalClipPlane
|
||||
{
|
||||
Normal = new Vector3(0, 0, 1),
|
||||
D = 3f,
|
||||
InsideSide = 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
vestibule.PortalPolygons.Add(
|
||||
[
|
||||
new Vector3(-0.5f, -0.5f, -2f),
|
||||
new Vector3(0.5f, -0.5f, -2f),
|
||||
new Vector3(0.5f, 0.5f, -2f),
|
||||
new Vector3(-0.5f, 0.5f, -2f),
|
||||
]);
|
||||
vestibule.PortalPolygons.Add(
|
||||
[
|
||||
new Vector3(-0.6f, -0.6f, -4f),
|
||||
new Vector3(0.6f, -0.6f, -4f),
|
||||
new Vector3(0.6f, 0.6f, -4f),
|
||||
new Vector3(-0.6f, 0.6f, -4f),
|
||||
]);
|
||||
|
||||
var room = new LoadedCell
|
||||
{
|
||||
CellId = roomId,
|
||||
BuildingId = buildingId,
|
||||
WorldTransform = Matrix4x4.Identity,
|
||||
InverseWorldTransform = Matrix4x4.Identity,
|
||||
Portals =
|
||||
[
|
||||
new CellPortalInfo(
|
||||
(ushort)(vestibuleId & 0xFFFFu),
|
||||
0,
|
||||
0,
|
||||
1),
|
||||
],
|
||||
};
|
||||
room.PortalPolygons.Add(
|
||||
[
|
||||
new Vector3(-0.6f, -0.6f, -4f),
|
||||
new Vector3(0.6f, -0.6f, -4f),
|
||||
new Vector3(0.6f, 0.6f, -4f),
|
||||
new Vector3(-0.6f, 0.6f, -4f),
|
||||
]);
|
||||
return [vestibule, room];
|
||||
}
|
||||
|
||||
private static WorldEntity Entity(
|
||||
uint id,
|
||||
uint serverGuid = 0,
|
||||
uint? parentCellId = null,
|
||||
bool isBuildingShell = false,
|
||||
uint? buildingShellAnchorCellId = null) => new()
|
||||
{
|
||||
Id = id,
|
||||
ServerGuid = serverGuid,
|
||||
SourceGfxObjOrSetupId = 0,
|
||||
Position = Vector3.Zero,
|
||||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = [new MeshRef(1u, Matrix4x4.Identity)],
|
||||
ParentCellId = parentCellId,
|
||||
IsBuildingShell = isBuildingShell,
|
||||
BuildingShellAnchorCellId = buildingShellAnchorCellId,
|
||||
};
|
||||
|
||||
private static void AssertAppearsInOrder(string source, params string[] needles)
|
||||
{
|
||||
int cursor = -1;
|
||||
foreach (string needle in needles)
|
||||
{
|
||||
int next = source.IndexOf(needle, cursor + 1, StringComparison.Ordinal);
|
||||
Assert.True(next > cursor, $"Missing or out-of-order fragment: {needle}");
|
||||
cursor = next;
|
||||
}
|
||||
}
|
||||
|
||||
private static int RequiredCallIndex(
|
||||
IReadOnlyList<CompiledCall> calls,
|
||||
Type declaringType,
|
||||
string methodName)
|
||||
{
|
||||
int index = CompiledCallGraph.IndexOf(calls, declaringType, methodName);
|
||||
Assert.True(index >= 0, $"Missing compiled call: {declaringType.Name}.{methodName}");
|
||||
Assert.True(
|
||||
index >= 0,
|
||||
$"Expected call to {declaringType.Name}.{methodName}.");
|
||||
return index;
|
||||
}
|
||||
|
||||
private sealed class DictionaryCellSource(
|
||||
IReadOnlyDictionary<uint, LoadedCell> cells) : IRetailPViewCellSource
|
||||
{
|
||||
public LoadedCell? Find(uint cellId) =>
|
||||
cells.TryGetValue(cellId, out LoadedCell? cell) ? cell : null;
|
||||
}
|
||||
|
||||
private sealed class TestCamera : ICamera
|
||||
{
|
||||
public static Matrix4x4 ViewMatrix { get; } = Matrix4x4.CreateLookAt(
|
||||
Vector3.Zero,
|
||||
new Vector3(0, 0, -1),
|
||||
Vector3.UnitY);
|
||||
|
||||
public static Matrix4x4 ViewProjection { get; } = ViewMatrix
|
||||
* Matrix4x4.CreatePerspectiveFieldOfView(1.2f, 1f, 0.1f, 1000f);
|
||||
|
||||
public Matrix4x4 View => ViewMatrix;
|
||||
public Matrix4x4 Projection { get; } =
|
||||
Matrix4x4.CreatePerspectiveFieldOfView(1.2f, 1f, 0.1f, 1000f);
|
||||
public float Aspect { get; set; } = 1f;
|
||||
}
|
||||
|
||||
private sealed class RecordingExecutor :
|
||||
IRetailPViewPassExecutor,
|
||||
IRenderFrameEntityPassExecutor,
|
||||
IDisposable
|
||||
{
|
||||
public void AbortFrame() => Operations.Add("abort");
|
||||
|
||||
private readonly ClipFrame _clipFrame = ClipFrame.NoClip();
|
||||
|
||||
public List<string> Operations { get; } = [];
|
||||
public bool HasTransparentShells { get; init; }
|
||||
|
||||
public void BeginFrame() => Operations.Add("begin");
|
||||
public ClipFrameAssembly AssembleClipFrame(
|
||||
PortalVisibilityFrame portalFrame,
|
||||
ClipFrameAssembly reuseAssembly)
|
||||
{
|
||||
Operations.Add("assemble");
|
||||
return ClipFrameAssembler.Assemble(_clipFrame, portalFrame, reuseAssembly);
|
||||
}
|
||||
|
||||
public void AppendLookInClipFrames(
|
||||
IReadOnlyList<PortalVisibilityFrame> lookInFrames,
|
||||
ClipFrameAssembly assembly)
|
||||
{
|
||||
Operations.Add("append-look-in-clips");
|
||||
ClipFrameAssembler.AppendLookInFrames(
|
||||
_clipFrame,
|
||||
lookInFrames,
|
||||
assembly);
|
||||
}
|
||||
|
||||
public void PrepareClipFrame(int terrainUploadCount) =>
|
||||
Operations.Add($"prepare-clip:{terrainUploadCount}");
|
||||
|
||||
public void SetTerrainClip(ReadOnlySpan<Vector4> planes) =>
|
||||
Operations.Add("terrain-clip");
|
||||
|
||||
public void ClearClipRouting() => Operations.Add("clear-routing");
|
||||
public void UseIndoorMembershipOnlyRouting() => Operations.Add("indoor-routing");
|
||||
public void UseCellPortalViewRouting(uint cellId, ClipViewSlice slice) =>
|
||||
Operations.Add($"cell-portal-routing:{cellId:X8}:{slice.Slot}");
|
||||
public void PrepareCellBatches(RetailPViewFrameInput frame, HashSet<uint> visibleCellIds) =>
|
||||
Operations.Add("prepare-cells");
|
||||
public void DrawOpaqueCellShells(HashSet<uint> cellIds) => Operations.Add("opaque-shells");
|
||||
public bool CellHasTransparentShell(uint cellId) => HasTransparentShells;
|
||||
public void DrawTransparentCellShells(HashSet<uint> cellIds) => Operations.Add("transparent-shells");
|
||||
public void DrawTransparentCellShellsOrdered(IReadOnlyList<uint> cellIds) =>
|
||||
Operations.Add("transparent-shells-ordered");
|
||||
public void DrawEntityBucket(
|
||||
RetailPViewFrameInput frame,
|
||||
IReadOnlyList<WorldEntity> entities,
|
||||
HashSet<uint>? visibleCellIds) => Operations.Add("entity-bucket");
|
||||
public void EmitClipRouteProbe(
|
||||
ClipFrameAssembly clipAssembly,
|
||||
ClipViewSlice slice,
|
||||
int sliceIndex) => Operations.Add("clip-probe");
|
||||
public void DrawLandscapeSlice(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewLandscapeSliceContext context)
|
||||
{
|
||||
Operations.Add("landscape-early");
|
||||
if (context.EntityDraw is RenderFrameEntityDrawRequest request)
|
||||
{
|
||||
RenderFrameView view = request.View;
|
||||
DrawEntityRoute(
|
||||
frame.Camera,
|
||||
in view,
|
||||
request.Route,
|
||||
request.RouteIndex,
|
||||
request.CellId,
|
||||
request.TupleLandblockId);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawLandscapeSliceLate(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewLandscapeLateSliceContext context)
|
||||
{
|
||||
Operations.Add("landscape-late");
|
||||
if (context.EntityDraw is RenderFrameEntityDrawRequest request)
|
||||
{
|
||||
RenderFrameView view = request.View;
|
||||
DrawEntityRoute(
|
||||
frame.Camera,
|
||||
in view,
|
||||
request.Route,
|
||||
request.RouteIndex,
|
||||
request.CellId,
|
||||
request.TupleLandblockId);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawLandscapeStaticParticles(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewLandscapeStaticParticleContext context) =>
|
||||
Operations.Add("landscape-static-particles");
|
||||
public void DrawLandscapeBuildingShellSlice(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewLandscapeBuildingShellSliceContext context)
|
||||
{
|
||||
Operations.Add("landscape-building-shell");
|
||||
if (context.EntityDraw is RenderFrameEntityDrawRequest request)
|
||||
{
|
||||
RenderFrameView view = request.View;
|
||||
DrawEntityRoute(
|
||||
frame.Camera,
|
||||
in view,
|
||||
request.Route,
|
||||
request.RouteIndex,
|
||||
request.CellId,
|
||||
request.TupleLandblockId);
|
||||
}
|
||||
}
|
||||
public void ClearInteriorDepth() => Operations.Add("interior-depth-clear");
|
||||
public void DrawExitPortalMask(RetailPViewFrameInput frame, RetailPViewCellSliceContext context) => Operations.Add("exit-mask");
|
||||
public void DrawLookInPortalPunch(
|
||||
RetailPViewFrameInput frame,
|
||||
RetailPViewCellSliceContext context,
|
||||
int portalIndex) => Operations.Add("look-in-punch");
|
||||
public void DrawUnattachedSceneParticles(
|
||||
RetailPViewFrameInput frame,
|
||||
bool outdoorCells) => Operations.Add(
|
||||
outdoorCells
|
||||
? "unattached-particles-outdoor"
|
||||
: "unattached-particles-interior");
|
||||
public void FlushLandscapeAlpha() => Operations.Add("landscape-alpha");
|
||||
public void FlushLandscapeAlphaFartherThan(float minViewerDistance) =>
|
||||
Operations.Add("landscape-alpha-farther");
|
||||
public void DrawCellParticles(RetailPViewFrameInput frame, RetailPViewCellSliceContext context) => Operations.Add("cell-particles");
|
||||
public void DrawDynamicsParticles(
|
||||
RetailPViewFrameInput frame,
|
||||
IReadOnlySet<uint> ownerIds) =>
|
||||
Operations.Add("dynamics-particles");
|
||||
public void EmitDiagnostics(RetailPViewFrameInput frame, RetailPViewFrameResult result) => Operations.Add("diagnostics");
|
||||
|
||||
public void BeginEntityFrame(in RenderFrameView view) =>
|
||||
Operations.Add("entity-frame-begin");
|
||||
|
||||
public bool DrawEntityRoute(
|
||||
ICamera camera,
|
||||
in RenderFrameView view,
|
||||
RenderFrameCandidateRoute route,
|
||||
int routeIndex,
|
||||
uint cellId,
|
||||
uint tupleLandblockId)
|
||||
{
|
||||
Operations.Add(
|
||||
$"entity-route:{route}:{routeIndex}:{cellId:X8}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CompleteEntityFrame(in RenderFrameView view) =>
|
||||
Operations.Add("entity-frame-complete");
|
||||
|
||||
public void AbortEntityFrame() =>
|
||||
Operations.Add("entity-frame-abort");
|
||||
|
||||
public void Dispose() => _clipFrame.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue