using Silk.NET.OpenGL;
using System;
namespace AcDream.App.Rendering.Wb {
///
/// A RAII scope for saving and restoring OpenGL state.
///
public unsafe struct GLStateScope : IDisposable {
private readonly GL _gl;
private fixed int _viewport[4];
private bool _scissorTest;
private fixed int _scissorBox[4];
private bool _depthTest;
private int _depthFunc;
private bool _depthMask;
private bool _cullFace;
private int _cullFaceMode;
private int _frontFace;
private bool _blend;
private int _blendSrc;
private int _blendDst;
private int _blendEquation;
// Extended state
private int _blendSrcAlpha;
private int _blendDstAlpha;
private int _blendEquationAlpha;
private fixed byte _colorMask[4];
private fixed float _clearColor[4];
private float _clearDepth;
private int _currentProgram;
private int _vertexArrayBinding;
private int _arrayBufferBinding;
private int _elementArrayBufferBinding;
private int _activeTexture;
private int _textureBinding2D;
private bool _stencilTest;
private int _stencilFunc;
private int _stencilRef;
private int _stencilValueMask;
private int _stencilFail;
private int _stencilPassDepthFail;
private int _stencilPassDepthPass;
private int _stencilWritemask;
private int _unpackAlignment;
private int _packAlignment;
private int _drawFramebufferBinding;
// Skia / Avalonia extra state protections
private fixed float _blendColor[4];
private int _polygonMode;
private bool _sampleAlphaToCoverage;
private bool _multisample;
private bool _primitiveRestart;
private int _readFramebufferBinding;
private int _uniformBufferBinding0;
private float _lineWidth;
private bool _programPointSize;
private int _samplerBinding0;
private int _samplerBinding1;
private int _samplerBinding2;
private int _unpackRowLength;
private int _unpackSkipRows;
private int _unpackSkipPixels;
private bool _sampleAlphaToOne;
private bool _isDisposed;
///
/// Captures the current OpenGL state.
///
///
public GLStateScope(GL gl) {
_gl = gl;
_isDisposed = false;
fixed (int* v = _viewport) _gl.GetInteger(GetPName.Viewport, v);
_scissorTest = _gl.IsEnabled(EnableCap.ScissorTest);
fixed (int* s = _scissorBox) _gl.GetInteger(GetPName.ScissorBox, s);
_depthTest = _gl.IsEnabled(EnableCap.DepthTest);
_gl.GetInteger(GetPName.DepthFunc, out _depthFunc);
byte depthMask = 0;
_gl.GetBoolean((GetPName)GLEnum.DepthWritemask, (bool*)&depthMask);
_depthMask = depthMask != 0;
_cullFace = _gl.IsEnabled(EnableCap.CullFace);
_gl.GetInteger(GetPName.CullFaceMode, out _cullFaceMode);
_gl.GetInteger(GetPName.FrontFace, out _frontFace);
_blend = _gl.IsEnabled(EnableCap.Blend);
_gl.GetInteger(GetPName.BlendSrcRgb, out _blendSrc);
_gl.GetInteger(GetPName.BlendDstRgb, out _blendDst);
_gl.GetInteger(GetPName.BlendSrcAlpha, out _blendSrcAlpha);
_gl.GetInteger(GetPName.BlendDstAlpha, out _blendDstAlpha);
_gl.GetInteger(GetPName.BlendEquationRgb, out _blendEquation);
_gl.GetInteger(GetPName.BlendEquationAlpha, out _blendEquationAlpha);
fixed (byte* c = _colorMask) _gl.GetBoolean((GetPName)GLEnum.ColorWritemask, (bool*)c);
fixed (float* cc = _clearColor) _gl.GetFloat(GetPName.ColorClearValue, cc);
_gl.GetFloat(GetPName.DepthClearValue, out _clearDepth);
_gl.GetInteger(GetPName.CurrentProgram, out _currentProgram);
_gl.GetInteger(GetPName.VertexArrayBinding, out _vertexArrayBinding);
_gl.GetInteger(GetPName.ArrayBufferBinding, out _arrayBufferBinding);
_gl.GetInteger(GetPName.ElementArrayBufferBinding, out _elementArrayBufferBinding);
_gl.GetInteger(GetPName.ActiveTexture, out _activeTexture);
_gl.GetInteger(GetPName.TextureBinding2D, out _textureBinding2D);
_stencilTest = _gl.IsEnabled(EnableCap.StencilTest);
_gl.GetInteger(GetPName.StencilFunc, out _stencilFunc);
_gl.GetInteger(GetPName.StencilRef, out _stencilRef);
_gl.GetInteger(GetPName.StencilValueMask, out _stencilValueMask);
_gl.GetInteger(GetPName.StencilFail, out _stencilFail);
_gl.GetInteger(GetPName.StencilPassDepthFail, out _stencilPassDepthFail);
_gl.GetInteger(GetPName.StencilPassDepthPass, out _stencilPassDepthPass);
_gl.GetInteger(GetPName.StencilWritemask, out _stencilWritemask);
_gl.GetInteger(GetPName.UnpackAlignment, out _unpackAlignment);
_gl.GetInteger(GetPName.PackAlignment, out _packAlignment);
_gl.GetInteger(GetPName.DrawFramebufferBinding, out _drawFramebufferBinding);
fixed (float* bc = _blendColor) _gl.GetFloat(GetPName.BlendColor, bc);
_gl.GetInteger(GetPName.PolygonMode, out _polygonMode);
_sampleAlphaToCoverage = _gl.IsEnabled(EnableCap.SampleAlphaToCoverage);
_multisample = _gl.IsEnabled(EnableCap.Multisample);
_primitiveRestart = _gl.IsEnabled((EnableCap)GLEnum.PrimitiveRestart);
_gl.GetInteger(GetPName.ReadFramebufferBinding, out _readFramebufferBinding);
_gl.GetInteger(GetPName.UniformBufferBinding, out _uniformBufferBinding0);
_gl.GetFloat(GetPName.LineWidth, out _lineWidth);
_programPointSize = _gl.IsEnabled((EnableCap)GLEnum.ProgramPointSize);
_gl.ActiveTexture(TextureUnit.Texture0);
_gl.GetInteger((GetPName)GLEnum.SamplerBinding, out _samplerBinding0);
_gl.ActiveTexture(TextureUnit.Texture1);
_gl.GetInteger((GetPName)GLEnum.SamplerBinding, out _samplerBinding1);
_gl.ActiveTexture(TextureUnit.Texture2);
_gl.GetInteger((GetPName)GLEnum.SamplerBinding, out _samplerBinding2);
_gl.ActiveTexture((TextureUnit)_activeTexture);
_gl.GetInteger((GetPName)GLEnum.UnpackRowLength, out _unpackRowLength);
_gl.GetInteger((GetPName)GLEnum.UnpackSkipRows, out _unpackSkipRows);
_gl.GetInteger((GetPName)GLEnum.UnpackSkipPixels, out _unpackSkipPixels);
_sampleAlphaToOne = _gl.IsEnabled(EnableCap.SampleAlphaToOne);
}
///
/// Restores only the scissor state from the scope.
///
public void RestoreScissor() {
if (_scissorTest) _gl.Enable(EnableCap.ScissorTest);
else _gl.Disable(EnableCap.ScissorTest);
_gl.Scissor(_scissorBox[0], _scissorBox[1], (uint)_scissorBox[2], (uint)_scissorBox[3]);
}
///
/// Restores the captured OpenGL state.
///
public void Dispose() {
if (_isDisposed) return;
// Restoring state
if (_currentProgram != 0) _gl.UseProgram((uint)_currentProgram); else _gl.UseProgram(0);
_gl.BindVertexArray((uint)_vertexArrayBinding);
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, (uint)_arrayBufferBinding);
_gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, (uint)_elementArrayBufferBinding);
_gl.BindBuffer(GLEnum.UniformBuffer, (uint)_uniformBufferBinding0);
_gl.ActiveTexture((TextureUnit)_activeTexture);
_gl.BindTexture(TextureTarget.Texture2D, (uint)_textureBinding2D);
if (_stencilTest) _gl.Enable(EnableCap.StencilTest); else _gl.Disable(EnableCap.StencilTest);
_gl.StencilFunc((StencilFunction)_stencilFunc, _stencilRef, (uint)_stencilValueMask);
_gl.StencilOp((StencilOp)_stencilFail, (StencilOp)_stencilPassDepthFail, (StencilOp)_stencilPassDepthPass);
_gl.StencilMask((uint)_stencilWritemask);
_gl.PixelStore(PixelStoreParameter.UnpackAlignment, _unpackAlignment);
_gl.PixelStore(PixelStoreParameter.PackAlignment, _packAlignment);
_gl.PixelStore(PixelStoreParameter.UnpackRowLength, _unpackRowLength);
_gl.PixelStore(PixelStoreParameter.UnpackSkipRows, _unpackSkipRows);
_gl.PixelStore(PixelStoreParameter.UnpackSkipPixels, _unpackSkipPixels);
_gl.ClearColor(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]);
_gl.ClearDepth(_clearDepth);
_gl.Viewport(_viewport[0], _viewport[1], (uint)_viewport[2], (uint)_viewport[3]);
RestoreScissor();
if (_depthTest) _gl.Enable(EnableCap.DepthTest); else _gl.Disable(EnableCap.DepthTest);
_gl.DepthFunc((DepthFunction)_depthFunc);
_gl.DepthMask(_depthMask);
if (_cullFace) _gl.Enable(EnableCap.CullFace); else _gl.Disable(EnableCap.CullFace);
_gl.CullFace((TriangleFace)_cullFaceMode);
_gl.FrontFace((FrontFaceDirection)_frontFace);
if (_blend) _gl.Enable(EnableCap.Blend); else _gl.Disable(EnableCap.Blend);
_gl.BlendFuncSeparate((BlendingFactor)_blendSrc, (BlendingFactor)_blendDst, (BlendingFactor)_blendSrcAlpha, (BlendingFactor)_blendDstAlpha);
_gl.BlendEquationSeparate((BlendEquationModeEXT)_blendEquation, (BlendEquationModeEXT)_blendEquationAlpha);
_gl.BlendColor(_blendColor[0], _blendColor[1], _blendColor[2], _blendColor[3]);
_gl.ColorMask(_colorMask[0] != 0, _colorMask[1] != 0, _colorMask[2] != 0, _colorMask[3] != 0);
_gl.PolygonMode(GLEnum.FrontAndBack, (PolygonMode)_polygonMode);
if (_sampleAlphaToCoverage) _gl.Enable(EnableCap.SampleAlphaToCoverage); else _gl.Disable(EnableCap.SampleAlphaToCoverage);
if (_sampleAlphaToOne) _gl.Enable(EnableCap.SampleAlphaToOne); else _gl.Disable(EnableCap.SampleAlphaToOne);
if (_multisample) _gl.Enable(EnableCap.Multisample); else _gl.Disable(EnableCap.Multisample);
if (_primitiveRestart) _gl.Enable((EnableCap)GLEnum.PrimitiveRestart); else _gl.Disable((EnableCap)GLEnum.PrimitiveRestart);
if (_programPointSize) _gl.Enable((EnableCap)GLEnum.ProgramPointSize); else _gl.Disable((EnableCap)GLEnum.ProgramPointSize);
_gl.LineWidth(_lineWidth);
_gl.BindSampler(0, (uint)_samplerBinding0);
_gl.BindSampler(1, (uint)_samplerBinding1);
_gl.BindSampler(2, (uint)_samplerBinding2);
_gl.BindFramebuffer(FramebufferTarget.DrawFramebuffer, (uint)_drawFramebufferBinding);
_gl.BindFramebuffer(FramebufferTarget.ReadFramebuffer, (uint)_readFramebufferBinding);
_isDisposed = true;
}
}
}