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>
156 lines
5.3 KiB
C#
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);
|
|
}
|
|
}
|