using Silk.NET.OpenGL; namespace AcDream.App.Rendering; internal interface IRenderFrameGlState { void RestoreFrameDefaults(); } internal interface IRenderFrameGlStateApi { void SetCapability(EnableCap capability, bool enabled); void ColorMask(bool red, bool green, bool blue, bool alpha); void StencilMask(uint mask); void DepthMask(bool enabled); void DepthFunc(DepthFunction function); void CullFace(TriangleFace face); void FrontFace(FrontFaceDirection direction); void UseProgram(uint program); void BindVertexArray(uint vertexArray); void BindBuffer(BufferTargetARB target, uint buffer); } internal sealed class SilkRenderFrameGlStateApi : IRenderFrameGlStateApi { private readonly GL _gl; public SilkRenderFrameGlStateApi(GL gl) { _gl = gl ?? throw new ArgumentNullException(nameof(gl)); } public void SetCapability(EnableCap capability, bool enabled) { if (enabled) _gl.Enable(capability); else _gl.Disable(capability); } public void ColorMask(bool red, bool green, bool blue, bool alpha) => _gl.ColorMask(red, green, blue, alpha); public void StencilMask(uint mask) => _gl.StencilMask(mask); public void DepthMask(bool enabled) => _gl.DepthMask(enabled); public void DepthFunc(DepthFunction function) => _gl.DepthFunc(function); public void CullFace(TriangleFace face) => _gl.CullFace(face); public void FrontFace(FrontFaceDirection direction) => _gl.FrontFace(direction); 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); } /// /// Restores the frame-global OpenGL convention shared by frame clear and /// exceptional world-pass rollback. This prevents a failed doorway scissor or /// portal depth mask from clipping/color-masking the next frame's clear. /// internal sealed class RenderFrameGlStateController : IRenderFrameGlState { private readonly IRenderFrameGlStateApi _gl; public RenderFrameGlStateController(IRenderFrameGlStateApi gl) { _gl = gl ?? throw new ArgumentNullException(nameof(gl)); } public void RestoreFrameDefaults() { _gl.SetCapability(EnableCap.ScissorTest, enabled: false); _gl.SetCapability(EnableCap.StencilTest, enabled: false); _gl.SetCapability(EnableCap.Blend, enabled: false); _gl.SetCapability(EnableCap.SampleAlphaToCoverage, enabled: false); for (int index = 0; index < ClipFrame.MaxPlanes; index++) { _gl.SetCapability( EnableCap.ClipDistance0 + index, enabled: false); } _gl.ColorMask(true, true, true, true); _gl.StencilMask(0xFF); _gl.DepthMask(true); _gl.DepthFunc(DepthFunction.Less); _gl.SetCapability(EnableCap.DepthTest, enabled: true); // Renderers that need face culling establish it locally. The shared // frame convention is culling off; SkyRenderer restores the state it // observed, so enabling it here would leak culling into later passes. _gl.SetCapability(EnableCap.CullFace, enabled: false); _gl.CullFace(TriangleFace.Back); _gl.FrontFace(FrontFaceDirection.CW); _gl.UseProgram(0); _gl.BindVertexArray(0); _gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0); } }