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>
This commit is contained in:
parent
28e1cf8029
commit
9d7df1bfc5
25 changed files with 1675 additions and 279 deletions
|
|
@ -24,6 +24,7 @@ public sealed unsafe class TextRenderer : IDisposable
|
|||
private const int FloatsPerVertex = 8; // pos(2) + uv(2) + color(4)
|
||||
|
||||
private readonly GL _gl;
|
||||
private readonly ITextRenderGlStateApi _glState;
|
||||
private readonly Shader _shader;
|
||||
private uint _vao;
|
||||
private uint _vbo;
|
||||
|
|
@ -53,6 +54,7 @@ public sealed unsafe class TextRenderer : IDisposable
|
|||
// at its FIRST-insertion point, so later bar sprites covered glyphs emitted
|
||||
// earlier via the shared dat-font atlas — the stamina/mana numbers vanished.)
|
||||
private sealed class SpriteSeg { public uint Texture; public readonly List<float> Verts = new(256); }
|
||||
|
||||
private readonly List<SpriteSeg> _spriteSegs = new();
|
||||
private int _segUsed;
|
||||
private int _textVerts;
|
||||
|
|
@ -77,6 +79,7 @@ public sealed unsafe class TextRenderer : IDisposable
|
|||
public TextRenderer(GL gl, string shaderDir)
|
||||
{
|
||||
_gl = gl;
|
||||
_glState = new SilkTextRenderGlStateApi(gl);
|
||||
_shader = new Shader(gl,
|
||||
Path.Combine(shaderDir, "ui_text.vert"),
|
||||
Path.Combine(shaderDir, "ui_text.frag"));
|
||||
|
|
@ -350,23 +353,24 @@ public sealed unsafe class TextRenderer : IDisposable
|
|||
bool anyOverlay = _overlaySegUsed > 0 || _overlayTextVerts > 0 || _overlayRectVerts > 0;
|
||||
if (!anyNormal && !anyOverlay) return;
|
||||
|
||||
// Retained UI is a private render pass: an upload or draw failure must
|
||||
// not leak its depth/cull/blend/MSAA state into a later recoverable
|
||||
// frame. The focused scope restores from Flush's generated finally,
|
||||
// including when either DrawLayer call throws.
|
||||
using var stateScope = new TextRenderGlStateScope(_glState);
|
||||
|
||||
_shader.Use();
|
||||
_shader.SetVec2("uScreenSize", _screenSize);
|
||||
|
||||
_gl.BindVertexArray(_vao);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
|
||||
|
||||
// Save GL state.
|
||||
bool wasDepth = _gl.IsEnabled(EnableCap.DepthTest);
|
||||
bool wasBlend = _gl.IsEnabled(EnableCap.Blend);
|
||||
bool wasCull = _gl.IsEnabled(EnableCap.CullFace);
|
||||
// Establish the self-contained UI pass state.
|
||||
// The world pass leaves alpha-to-coverage + multisample enabled (WbDrawDispatcher,
|
||||
// QualitySettings MSAA). If they bleed into the UI pass, each glyph's soft alpha
|
||||
// EDGE is converted to dithered MSAA coverage instead of a clean alpha blend —
|
||||
// the "text not sharp / fuzzy" artifact. The UI composites with straight alpha
|
||||
// blending and must own this state (feedback_render_self_contained_gl_state).
|
||||
bool wasA2C = _gl.IsEnabled(EnableCap.SampleAlphaToCoverage);
|
||||
bool wasMsaa = _gl.IsEnabled(EnableCap.Multisample);
|
||||
_gl.Disable(EnableCap.SampleAlphaToCoverage);
|
||||
_gl.Disable(EnableCap.Multisample);
|
||||
_gl.Disable(EnableCap.DepthTest);
|
||||
|
|
@ -386,15 +390,6 @@ public sealed unsafe class TextRenderer : IDisposable
|
|||
DrawLayer(_spriteSegs, _segUsed, _rectBuf, _rectVerts, _textBuf, _textVerts, font);
|
||||
DrawLayer(_overlaySpriteSegs, _overlaySegUsed, _overlayRectBuf, _overlayRectVerts, _overlayTextBuf, _overlayTextVerts, font);
|
||||
|
||||
// Restore GL state.
|
||||
_gl.DepthMask(true);
|
||||
if (!wasBlend) _gl.Disable(EnableCap.Blend);
|
||||
if (wasCull) _gl.Enable(EnableCap.CullFace);
|
||||
if (wasDepth) _gl.Enable(EnableCap.DepthTest);
|
||||
if (wasA2C) _gl.Enable(EnableCap.SampleAlphaToCoverage);
|
||||
if (wasMsaa) _gl.Enable(EnableCap.Multisample);
|
||||
|
||||
_gl.BindVertexArray(0);
|
||||
}
|
||||
|
||||
/// <summary>Draw one compositing layer: sprites (submission order, one call per
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue