acdream/src/AcDream.App/Rendering/TextRenderGlStateScope.cs
Erik 9d7df1bfc5 refactor(render): compose render frame orchestrator
Move the accepted draw transaction and failure recovery behind typed frame-phase owners so GameWindow only supplies immutable frame input. Preserve retail draw order, make ImGui/bootstrap shutdown ownership explicit, and restore exact text-render GL state on failures.\n\nCo-authored-by: Codex <codex@openai.com>
2026-07-22 08:03:49 +02:00

156 lines
5.3 KiB
C#

using Silk.NET.OpenGL;
namespace AcDream.App.Rendering;
internal interface ITextRenderGlStateApi
{
bool IsEnabled(EnableCap capability);
int GetInteger(GetPName parameter);
bool GetBoolean(GetPName parameter);
void SetCapability(EnableCap capability, bool enabled);
void DepthMask(bool enabled);
void BlendFuncSeparate(
BlendingFactor sourceRgb,
BlendingFactor destinationRgb,
BlendingFactor sourceAlpha,
BlendingFactor destinationAlpha);
void UseProgram(uint program);
void BindVertexArray(uint vertexArray);
void BindBuffer(BufferTargetARB target, uint buffer);
void ActiveTexture(TextureUnit unit);
void BindTexture(TextureTarget target, uint texture);
}
internal sealed class SilkTextRenderGlStateApi : ITextRenderGlStateApi
{
private readonly GL _gl;
public SilkTextRenderGlStateApi(GL gl) =>
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
public bool IsEnabled(EnableCap capability) => _gl.IsEnabled(capability);
public int GetInteger(GetPName parameter) => _gl.GetInteger(parameter);
public bool GetBoolean(GetPName parameter) => _gl.GetBoolean(parameter);
public void SetCapability(EnableCap capability, bool enabled)
{
if (enabled)
_gl.Enable(capability);
else
_gl.Disable(capability);
}
public void DepthMask(bool enabled) => _gl.DepthMask(enabled);
public void BlendFuncSeparate(
BlendingFactor sourceRgb,
BlendingFactor destinationRgb,
BlendingFactor sourceAlpha,
BlendingFactor destinationAlpha) =>
_gl.BlendFuncSeparate(
sourceRgb,
destinationRgb,
sourceAlpha,
destinationAlpha);
public void UseProgram(uint program) => _gl.UseProgram(program);
public void BindVertexArray(uint vertexArray) => _gl.BindVertexArray(vertexArray);
public void BindBuffer(BufferTargetARB target, uint buffer) =>
_gl.BindBuffer(target, buffer);
public void ActiveTexture(TextureUnit unit) => _gl.ActiveTexture(unit);
public void BindTexture(TextureTarget target, uint texture) =>
_gl.BindTexture(target, texture);
}
/// <summary>
/// Exact, focused state transaction for <see cref="TextRenderer.Flush"/>. It
/// captures every GL value that Flush or DrawLayer mutates, while avoiding the
/// dozens of unrelated synchronous reads made by the broad diagnostic scope.
/// </summary>
internal readonly struct TextRenderGlStateScope : IDisposable
{
private readonly ITextRenderGlStateApi _gl;
private readonly bool _depthTest;
private readonly bool _blend;
private readonly bool _cullFace;
private readonly bool _alphaToCoverage;
private readonly bool _multisample;
private readonly bool _depthWrite;
private readonly int _blendSourceRgb;
private readonly int _blendDestinationRgb;
private readonly int _blendSourceAlpha;
private readonly int _blendDestinationAlpha;
private readonly int _program;
private readonly int _vertexArray;
private readonly int _arrayBuffer;
private readonly int _activeTexture;
private readonly int _texture0Binding2D;
public TextRenderGlStateScope(ITextRenderGlStateApi gl)
{
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
_depthTest = gl.IsEnabled(EnableCap.DepthTest);
_blend = gl.IsEnabled(EnableCap.Blend);
_cullFace = gl.IsEnabled(EnableCap.CullFace);
_alphaToCoverage = gl.IsEnabled(EnableCap.SampleAlphaToCoverage);
_multisample = gl.IsEnabled(EnableCap.Multisample);
_depthWrite = gl.GetBoolean(GetPName.DepthWritemask);
_blendSourceRgb = gl.GetInteger(GetPName.BlendSrcRgb);
_blendDestinationRgb = gl.GetInteger(GetPName.BlendDstRgb);
_blendSourceAlpha = gl.GetInteger(GetPName.BlendSrcAlpha);
_blendDestinationAlpha = gl.GetInteger(GetPName.BlendDstAlpha);
_program = gl.GetInteger(GetPName.CurrentProgram);
_vertexArray = gl.GetInteger(GetPName.VertexArrayBinding);
_arrayBuffer = gl.GetInteger(GetPName.ArrayBufferBinding);
_activeTexture = gl.GetInteger(GetPName.ActiveTexture);
try
{
gl.ActiveTexture(TextureUnit.Texture0);
_texture0Binding2D = gl.GetInteger(GetPName.TextureBinding2D);
}
finally
{
gl.ActiveTexture((TextureUnit)_activeTexture);
}
}
public void Dispose()
{
_gl.UseProgram((uint)_program);
_gl.BindVertexArray((uint)_vertexArray);
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, (uint)_arrayBuffer);
_gl.ActiveTexture(TextureUnit.Texture0);
_gl.BindTexture(TextureTarget.Texture2D, (uint)_texture0Binding2D);
_gl.ActiveTexture((TextureUnit)_activeTexture);
_gl.DepthMask(_depthWrite);
_gl.BlendFuncSeparate(
(BlendingFactor)_blendSourceRgb,
(BlendingFactor)_blendDestinationRgb,
(BlendingFactor)_blendSourceAlpha,
(BlendingFactor)_blendDestinationAlpha);
_gl.SetCapability(EnableCap.DepthTest, _depthTest);
_gl.SetCapability(EnableCap.Blend, _blend);
_gl.SetCapability(EnableCap.CullFace, _cullFace);
_gl.SetCapability(EnableCap.SampleAlphaToCoverage, _alphaToCoverage);
_gl.SetCapability(EnableCap.Multisample, _multisample);
}
}