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>
106 lines
3.6 KiB
C#
106 lines
3.6 KiB
C#
using AcDream.App.Rendering;
|
|
using Silk.NET.OpenGL;
|
|
|
|
namespace AcDream.App.Tests.Rendering;
|
|
|
|
public sealed class RenderFrameGlStateControllerTests
|
|
{
|
|
[Fact]
|
|
public void RestoreFrameDefaults_ReestablishesTheCompleteFrameGlobalContract()
|
|
{
|
|
var api = new RecordingApi();
|
|
var state = new RenderFrameGlStateController(api);
|
|
|
|
state.RestoreFrameDefaults();
|
|
|
|
Assert.Equal(
|
|
[
|
|
"cap:ScissorTest:False",
|
|
"cap:StencilTest:False",
|
|
"cap:Blend:False",
|
|
"cap:SampleAlphaToMaskSgis:False",
|
|
"cap:ClipDistance0:False",
|
|
"cap:ClipDistance1:False",
|
|
"cap:ClipDistance2:False",
|
|
"cap:ClipDistance3:False",
|
|
"cap:ClipDistance4:False",
|
|
"cap:ClipDistance5:False",
|
|
"cap:ClipDistance6:False",
|
|
"cap:ClipDistance7:False",
|
|
"color-mask:True:True:True:True",
|
|
"stencil-mask:255",
|
|
"depth-mask:True",
|
|
"depth-func:Less",
|
|
"cap:DepthTest:True",
|
|
"cap:CullFace:False",
|
|
"cull:Back",
|
|
"front:CW",
|
|
"program:0",
|
|
"vao:0",
|
|
"buffer:ArrayBuffer:0",
|
|
],
|
|
api.Calls);
|
|
}
|
|
|
|
[Fact]
|
|
public void PortalDepthMask_SuccessExitRestoresTheSameCullOffConvention()
|
|
{
|
|
string source = File.ReadAllText(Path.Combine(
|
|
FindRepoRoot(),
|
|
"src",
|
|
"AcDream.App",
|
|
"Rendering",
|
|
"PortalDepthMaskRenderer.cs"));
|
|
int restore = source.IndexOf(
|
|
"// ---- restore the frame-global convention ----",
|
|
StringComparison.Ordinal);
|
|
|
|
Assert.True(restore >= 0, "Missing portal-depth state restore block.");
|
|
string restoreBlock = source[restore..];
|
|
Assert.Contains("_gl.Disable(EnableCap.CullFace);", restoreBlock);
|
|
Assert.DoesNotContain("_gl.Enable(EnableCap.CullFace);", restoreBlock);
|
|
}
|
|
|
|
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 RecordingApi : IRenderFrameGlStateApi
|
|
{
|
|
public List<string> Calls { get; } = [];
|
|
|
|
public void SetCapability(EnableCap capability, bool enabled) =>
|
|
Calls.Add($"cap:{capability}:{enabled}");
|
|
|
|
public void ColorMask(bool red, bool green, bool blue, bool alpha) =>
|
|
Calls.Add($"color-mask:{red}:{green}:{blue}:{alpha}");
|
|
|
|
public void StencilMask(uint mask) => Calls.Add($"stencil-mask:{mask}");
|
|
|
|
public void DepthMask(bool enabled) => Calls.Add($"depth-mask:{enabled}");
|
|
|
|
public void DepthFunc(DepthFunction function) =>
|
|
Calls.Add($"depth-func:{function}");
|
|
|
|
public void CullFace(TriangleFace face) => Calls.Add($"cull:{face}");
|
|
|
|
public void FrontFace(FrontFaceDirection direction) =>
|
|
Calls.Add($"front:{direction}");
|
|
|
|
public void UseProgram(uint program) => Calls.Add($"program:{program}");
|
|
|
|
public void BindVertexArray(uint vertexArray) => Calls.Add($"vao:{vertexArray}");
|
|
|
|
public void BindBuffer(BufferTargetARB target, uint buffer) =>
|
|
Calls.Add($"buffer:{target}:{buffer}");
|
|
}
|
|
}
|