test: replace frame orchestration source freezes
This commit is contained in:
parent
9bd5d47c47
commit
9b94050229
3 changed files with 501 additions and 466 deletions
|
|
@ -1,4 +1,13 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using AcDream.App.Diagnostics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.App.Tests.Architecture;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Tests.Rendering;
|
||||
|
||||
|
|
@ -47,36 +56,40 @@ public sealed class RenderFrameResourceControllerTests
|
|||
[Fact]
|
||||
public void Production_begin_resources_preserve_the_exact_frame_order()
|
||||
{
|
||||
string source = ResourceSource();
|
||||
|
||||
// Campaign V slice V4a: the world-HUD and retained-UI TextRenderers no
|
||||
// longer take a per-slot begin call — they now read the shared
|
||||
// IGpuFrame ring reset once per frame by IGpuDevice.BeginFrame() (see
|
||||
// GpuDeviceFrameLifetime), so _worldText/_uiText are gone from this
|
||||
// sequence.
|
||||
AssertAppearsInOrder(
|
||||
source,
|
||||
"_textures?.BeginCompositeTextureFrame();",
|
||||
"_textures?.TickCompositeTextureCache();",
|
||||
"_dispatcher?.BeginFrame(gpuSlot);",
|
||||
"_environmentCells?.BeginFrame(gpuSlot);",
|
||||
"_portalDepth?.BeginFrame(gpuSlot);",
|
||||
"_clip?.BeginFrame(gpuSlot);",
|
||||
"_terrain?.BeginFrame(gpuSlot);",
|
||||
"_lighting?.BeginFrame(gpuSlot);");
|
||||
Assert.DoesNotContain("_profiler.FrameBoundary(", source);
|
||||
MethodInfo begin = RequiredMethod(
|
||||
typeof(RuntimeRenderFrameBeginResources),
|
||||
nameof(RuntimeRenderFrameBeginResources.Begin));
|
||||
AssertCallOrder(
|
||||
begin,
|
||||
(typeof(TextureCache), nameof(TextureCache.BeginCompositeTextureFrame)),
|
||||
(typeof(TextureCache), nameof(TextureCache.TickCompositeTextureCache)),
|
||||
(typeof(WbDrawDispatcher), nameof(WbDrawDispatcher.BeginFrame)),
|
||||
(typeof(EnvCellRenderer), nameof(EnvCellRenderer.BeginFrame)),
|
||||
(typeof(PortalDepthMaskRenderer), nameof(PortalDepthMaskRenderer.BeginFrame)),
|
||||
(typeof(ClipFrame), nameof(ClipFrame.BeginFrame)),
|
||||
(typeof(TerrainModernRenderer), nameof(TerrainModernRenderer.BeginFrame)),
|
||||
(typeof(SceneLightingUboBinding), nameof(SceneLightingUboBinding.BeginFrame)));
|
||||
Assert.DoesNotContain(
|
||||
CompiledCallGraph.Read(begin),
|
||||
call => call.Target.DeclaringType == typeof(FrameProfiler)
|
||||
&& call.Target.Name == "FrameBoundary");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Production_live_preparation_publishes_meshes_before_reveal_and_particles()
|
||||
{
|
||||
string source = ResourceSource();
|
||||
|
||||
AssertAppearsInOrder(
|
||||
source,
|
||||
"_meshes?.Tick();",
|
||||
"_worldReveal?.PrepareAndEvaluate(revealCell);",
|
||||
"_particles?.BeginFrame(gpuSlot);");
|
||||
AssertCallOrder(
|
||||
RequiredMethod(
|
||||
typeof(RuntimeRenderFrameLivePreparation),
|
||||
nameof(RuntimeRenderFrameLivePreparation.Prepare)),
|
||||
(typeof(WbMeshAdapter), nameof(WbMeshAdapter.Tick)),
|
||||
(typeof(WorldRevealCoordinator), nameof(WorldRevealCoordinator.PrepareAndEvaluate)),
|
||||
(typeof(ParticleRenderer), nameof(ParticleRenderer.BeginFrame)));
|
||||
}
|
||||
|
||||
// Campaign V slice V11 deleted RuntimeRenderFrameClearPhase (the raw-GL
|
||||
|
|
@ -101,11 +114,18 @@ public sealed class RenderFrameResourceControllerTests
|
|||
controller.Tick(0.5d);
|
||||
|
||||
Assert.Equal(0.75d, controller.ElapsedSeconds);
|
||||
AssertAppearsInOrder(
|
||||
ResourceSource(),
|
||||
"_weather.Tick(",
|
||||
"nowSeconds: _elapsedSeconds,",
|
||||
"_elapsedSeconds += deltaSeconds;");
|
||||
MethodInfo tick = RequiredMethod(
|
||||
typeof(RenderWeatherFrameController),
|
||||
nameof(RenderWeatherFrameController.Tick));
|
||||
CompiledCall weather = Assert.Single(
|
||||
CompiledCallGraph.Read(tick),
|
||||
call => call.Target.DeclaringType == typeof(WeatherSystem)
|
||||
&& call.Target.Name == nameof(WeatherSystem.Tick));
|
||||
CompiledFieldReference elapsedStore = Assert.Single(
|
||||
CompiledCallGraph.ReadFieldReferences(tick),
|
||||
reference => reference.Field.Name == "_elapsedSeconds"
|
||||
&& reference.OpCode == OpCodes.Stfld);
|
||||
Assert.True(weather.Offset < elapsedStore.Offset);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -127,38 +147,30 @@ public sealed class RenderFrameResourceControllerTests
|
|||
Assert.False(live.IsWaitingForLogin);
|
||||
}
|
||||
|
||||
private static string ResourceSource() => File.ReadAllText(Path.Combine(
|
||||
FindRepoRoot(),
|
||||
"src",
|
||||
"AcDream.App",
|
||||
"Rendering",
|
||||
"RenderFrameResourceController.cs"));
|
||||
private static MethodInfo RequiredMethod(Type owner, string name) =>
|
||||
owner.GetMethod(
|
||||
name,
|
||||
BindingFlags.Instance | BindingFlags.Static
|
||||
| BindingFlags.Public | BindingFlags.NonPublic)
|
||||
?? throw new MissingMethodException(owner.FullName, name);
|
||||
|
||||
private static void AssertAppearsInOrder(string source, params string[] needles)
|
||||
private static void AssertCallOrder(
|
||||
MethodBase method,
|
||||
params (Type Type, string Method)[] expected)
|
||||
{
|
||||
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(method);
|
||||
int cursor = -1;
|
||||
foreach (string needle in needles)
|
||||
foreach ((Type type, string name) in expected)
|
||||
{
|
||||
int next = source.IndexOf(needle, cursor + 1, StringComparison.Ordinal);
|
||||
Assert.True(next >= 0, $"Missing expected source fragment: {needle}");
|
||||
Assert.True(next > cursor, $"Out-of-order source fragment: {needle}");
|
||||
cursor = next;
|
||||
int found = Enumerable.Range(cursor + 1, calls.Count - cursor - 1)
|
||||
.FirstOrDefault(index => calls[index].Target.DeclaringType == type
|
||||
&& calls[index].Target.Name == name, -1);
|
||||
Assert.True(found > cursor,
|
||||
$"Missing compiled edge after {cursor}: {type.FullName}.{name}.");
|
||||
cursor = found;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindRepoRoot()
|
||||
{
|
||||
DirectoryInfo? directory = new(AppContext.BaseDirectory);
|
||||
while (directory is not null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
|
||||
return directory.FullName;
|
||||
directory = directory.Parent;
|
||||
}
|
||||
|
||||
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
|
||||
}
|
||||
|
||||
private sealed class AdvancingSlotSource(int firstSlot) : IRenderFrameSlotSource
|
||||
{
|
||||
public int ReadCount { get; private set; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue