// MobileGL - MobileGL/MG_State/GLState/Core.cpp // Copyright (c) 2025-2026 MobileGL-Dev // Licensed under the GNU Lesser General Public License v3.0: // https://www.gnu.org/licenses/gpl-3.0.txt // https://www.gnu.org/licenses/lgpl-3.0.txt // SPDX-License-Identifier: LGPL-3.0-only // End of Source File Header #include "Core.h" #include "MG_State/GLState/RenderbufferState/RenderbufferObject.h" #include "MG_State/EGLState/Core.h" #include namespace MobileGL::MG_State { void Init() { MGLOG_D("Initializing MobileGL State..."); pGLContext = MakeUnique(); pEGLContext = MakeUnique(); } Bool IsRelaxedSemanticsActive() { return MG_Config::Features.RelaxedSemantics || !(pEGLContext && pEGLContext->IsCurrentContextOpenGLCoreProfile()); } namespace GLState { // Error void GLContext::RecordError(ErrorCode code, UniquePtr info) { m_errorState.RecordError(code, Move(info)); } Bool GLContext::HasGLError() const { return m_errorState.HasGLError(); } Optional GLContext::PeekGLError() const { return m_errorState.PeekGLError(); } Optional> GLContext::PopGLError() { return Move(m_errorState.PopGLError()); } Bool GLContext::HasNonGLError() const { return m_errorState.HasNonGLError(); } Optional GLContext::PeekNonGLError() const { return m_errorState.PeekNonGLError(); } Optional> GLContext::PopNonGLError() { return Move(m_errorState.PopNonGLError()); } void GLContext::ClearErrors() { m_errorState.Clear(); } // Buffer void GLContext::GenBufferNames(Uint number, Vector& buffers) { m_bufferState.GenerateNames(number, buffers); } const SharedPtr& GLContext::GetBufferObject(Uint index) { return m_bufferState.GetBufferObject(index); } BindingSlot& GLContext::GetBufferBindingSlot(BufferTarget target) { if (target == BufferTarget::Index) { const auto& vao = m_vertexArrayState.GetBoundVertexArray(); MOBILEGL_ASSERT(vao != nullptr, "No VAO is currently bound when accessing index buffer binding slot."); return vao->GetIndexBufferBindingSlot(); } return m_bufferState.GetBindingSlot(target); } BindingSlotRange1D& GLContext::GetBufferBindingPoint(BufferTarget target, Uint index) { return m_bufferState.GetBindingPoint(target, index); } const SharedPtr& GLContext::CreateBufferObject(Uint index) { return m_bufferState.CreateBufferObject(index); } void GLContext::MarkBufferObjectForDeletion(Uint index) { if (ValidateBufferObject(index)) { // GL semantics: deleting a buffer detaches it only from the CURRENT // context's bindings, including the currently bound VAO's attachment // points; attachments in other VAOs must survive (the shared_ptr keeps // the data object alive, matching the spec's deferred deletion). The // previous every-VAO scan was wrong per spec and O(VAOs) per delete — // with one VAO per chunk section, vanilla's steady buffer churn made it // dominate the render thread and FPS decay over session time. auto bufferObject = m_bufferState.GetBufferObject(index); const auto& vao = m_vertexArrayState.GetBoundVertexArray(); if (vao != nullptr) { if (vao->GetIndexBufferBindingSlot().GetBoundObject() == bufferObject) { vao->GetIndexBufferBindingSlot().Bind(nullptr); } for (SizeT j = 0; j < VertexArrayObject::MAX_VERTEX_ATTRIBS; ++j) { if (vao->GetAttribute(j).Buffer == bufferObject) { vao->BindAttributeBuffer(j, nullptr); } } } } m_bufferState.MarkBufferObjectForDeletion(index); } Bool GLContext::ValidateBufferName(Uint index) const { return m_bufferState.ValidateName(index); } Bool GLContext::ValidateBufferObject(Uint index) const { return m_bufferState.ValidateBufferObject(index); } // VertexArray void GLContext::GenVertexArrayNames(Uint number, Vector& vertexArrays) { m_vertexArrayState.GenerateNames(number, vertexArrays); } const SharedPtr& GLContext::GetVertexArrayObject(Uint index) { return m_vertexArrayState.GetVertexArrayObject(index); } void GLContext::BindVertexArray(Uint index) { m_vertexArrayState.Bind(index); } const SharedPtr& GLContext::CreateVertexArrayObject(Uint index) { return m_vertexArrayState.CreateVertexArrayObject(index); } void GLContext::MarkVertexArrayForDeletion(Uint index) { m_vertexArrayState.MarkVertexArrayForDeletion(index); } Bool GLContext::ValidateVertexArrayName(Uint index) const { return m_vertexArrayState.ValidateName(index); } Bool GLContext::ValidateVertexArrayObject(Uint index) const { return m_vertexArrayState.ValidateVertexArrayObject(index); } const SharedPtr& GLContext::GetBoundVertexArray() { return m_vertexArrayState.GetBoundVertexArray(); } VertexAttribTypeInfo ClassifyVertexAttribType(GLenum glType) { switch (glType) { case GL_FLOAT: return {VertexAttribBaseType::Float, 1}; case GL_FLOAT_VEC2: return {VertexAttribBaseType::Float, 2}; case GL_FLOAT_VEC3: return {VertexAttribBaseType::Float, 3}; case GL_FLOAT_VEC4: return {VertexAttribBaseType::Float, 4}; case GL_INT: return {VertexAttribBaseType::Int, 1}; case GL_INT_VEC2: return {VertexAttribBaseType::Int, 2}; case GL_INT_VEC3: return {VertexAttribBaseType::Int, 3}; case GL_INT_VEC4: return {VertexAttribBaseType::Int, 4}; case GL_UNSIGNED_INT: return {VertexAttribBaseType::Uint, 1}; case GL_UNSIGNED_INT_VEC2: return {VertexAttribBaseType::Uint, 2}; case GL_UNSIGNED_INT_VEC3: return {VertexAttribBaseType::Uint, 3}; case GL_UNSIGNED_INT_VEC4: return {VertexAttribBaseType::Uint, 4}; default: return {}; } } // The three accessors below are reachable from backend draw paths with a location taken from // shader reflection, so the bound must be enforced at runtime rather than by MOBILEGL_ASSERT // (which expands to nothing outside debug builds). void GLContext::SetCurrentVertexAttributeFloat(Uint index, const Array& value) { if (index >= m_currentVertexAttributes.size()) { MGLOG_E("SetCurrentVertexAttributeFloat: index %u is out of range", index); return; } auto& current = m_currentVertexAttributes[index]; current.floatValue = value; for (SizeT component = 0; component < value.size(); ++component) { current.intValue[component] = static_cast(value[component]); current.uintValue[component] = static_cast(value[component]); } } void GLContext::SetCurrentVertexAttributeInt(Uint index, const Array& value) { if (index >= m_currentVertexAttributes.size()) { MGLOG_E("SetCurrentVertexAttributeInt: index %u is out of range", index); return; } auto& current = m_currentVertexAttributes[index]; current.intValue = value; for (SizeT component = 0; component < value.size(); ++component) { current.floatValue[component] = static_cast(value[component]); current.uintValue[component] = static_cast(value[component]); } } void GLContext::SetCurrentVertexAttributeUint(Uint index, const Array& value) { if (index >= m_currentVertexAttributes.size()) { MGLOG_E("SetCurrentVertexAttributeUint: index %u is out of range", index); return; } auto& current = m_currentVertexAttributes[index]; current.uintValue = value; for (SizeT component = 0; component < value.size(); ++component) { current.floatValue[component] = static_cast(value[component]); current.intValue[component] = static_cast(value[component]); } } const CurrentVertexAttributeValue& GLContext::GetCurrentVertexAttribute(Uint index) const { static const CurrentVertexAttributeValue defaultValue{}; if (index >= m_currentVertexAttributes.size()) { MGLOG_E("GetCurrentVertexAttribute: index %u is out of range", index); return defaultValue; } return m_currentVertexAttributes[index]; } // Texture void GLContext::GenTextureNames(Uint number, Vector& textures) { m_textureState.GenerateNames(number, textures); } const SharedPtr& GLContext::GetTextureObject(Uint index) { return m_textureState.GetTextureObject(index); } const SharedPtr& GLContext::GetDefaultTextureObject(TextureTarget target) const { return m_textureState.GetDefaultTextureObject(target); } const SharedPtr& GLContext::CreateTextureObject(Uint index, TextureTarget target) { return m_textureState.CreateTextureObject(index, target); } void GLContext::MarkTextureObjectForDeletion(Uint index) { m_textureState.MarkTextureObjectForDeletion(index, IsRelaxedSemanticsActive()); } TextureUnit& GLContext::GetTextureUnitObject(Int unit) { return m_textureState.GetUnitObject(unit); } ImageTextureBinding& GLContext::GetImageTextureBinding(Int unit) { return m_textureState.GetImageTextureBinding(unit); } const ImageTextureBinding& GLContext::GetImageTextureBinding(Int unit) const { return m_textureState.GetImageTextureBinding(unit); } Bool GLContext::ValidateTextureName(Uint index) const { return m_textureState.ValidateName(index); } Bool GLContext::ValidateTextureObject(Uint index) const { return m_textureState.ValidateTextureObject(index); } Int GLContext::GetActiveTextureUnit() const { return m_textureState.GetActiveTextureUnit(); } void GLContext::SetActiveTextureUnit(Int unit) { m_textureState.SetActiveTextureUnit(unit); } // Program Uint GLContext::CreateProgram() { return m_programState.CreateProgram(); } Uint GLContext::CreateShader(const ShaderStage stage) { return m_programState.CreateShader(stage); } void GLContext::MarkProgramForDeletion(const Uint index) { return m_programState.MarkProgramObjectForDeletion(index); } void GLContext::MarkShaderForDeletion(const Uint index) { return m_programState.MarkShaderObjectForDeletion(index); } void GLContext::ReleaseShaderNameIfOrphaned(const Uint index) { return m_programState.ReleaseShaderNameIfOrphaned(index); } Bool GLContext::ValidateProgramName(const Uint index) const { return m_programState.ValidateProgramObject(index); } Bool GLContext::ValidateShaderName(const Uint index) const { return m_programState.ValidateShaderObject(index); } const SharedPtr& GLContext::GetProgramObject(const Uint index) { return m_programState.GetProgramObject(index); } const SharedPtr& GLContext::GetShaderObject(const Uint index) { return m_programState.GetShaderObject(index); } void GLContext::UseProgram(Uint program) { return m_programState.UseProgram(program); } const SharedPtr& GLContext::GetCurrentProgram() { return m_programState.GetCurrentProgram(); } // RenderState Uint GLContext::GetRenderStateParametersVersion() const { return m_renderState.GetVersion(); } const RenderStateParameters& GLContext::GetRenderStateParameters() const { return m_renderState.GetAllParameters(); } void GLContext::SetViewport(IntVec4 viewport) { m_renderState.SetViewport(viewport); } const IntVec4& GLContext::GetViewport() const { return m_renderState.GetViewport(); } void GLContext::SetLineWidth(Float width) { m_renderState.SetLineWidth(width); } Float GLContext::GetLineWidth() const { return m_renderState.GetLineWidth(); } void GLContext::SetHint(GLenum target, GLenum mode) { m_renderState.SetHint(target, mode); } GLenum GLContext::GetHint(GLenum target) const { return m_renderState.GetHint(target); } void GLContext::SetPointFadeThresholdSize(Float size) { m_renderState.SetPointFadeThresholdSize(size); } Float GLContext::GetPointFadeThresholdSize() const { return m_renderState.GetPointFadeThresholdSize(); } void GLContext::SetPointSpriteCoordOrigin(GLenum origin) { m_renderState.SetPointSpriteCoordOrigin(origin); } GLenum GLContext::GetPointSpriteCoordOrigin() const { return m_renderState.GetPointSpriteCoordOrigin(); } void GLContext::SetClampReadColor(GLenum clamp) { m_renderState.SetClampReadColor(clamp); } GLenum GLContext::GetClampReadColor() const { return m_renderState.GetClampReadColor(); } void GLContext::SetPolygonMode(GLenum front, GLenum back) { m_renderState.SetPolygonMode(front, back); } GLenum GLContext::GetPolygonModeFront() const { return m_renderState.GetPolygonModeFront(); } GLenum GLContext::GetPolygonModeBack() const { return m_renderState.GetPolygonModeBack(); } void GLContext::SetPrimitiveRestartIndex(Uint32 index) { m_renderState.SetPrimitiveRestartIndex(index); } Uint32 GLContext::GetPrimitiveRestartIndex() const { return m_renderState.GetPrimitiveRestartIndex(); } void GLContext::SetPointSize(Float size) { m_renderState.SetPointSize(size); } Float GLContext::GetPointSize() const { return m_renderState.GetPointSize(); } void GLContext::SetPolygonOffset(Float factor, Float units) { m_renderState.SetPolygonOffset(factor, units); } Float GLContext::GetPolygonOffsetFactor() const { return m_renderState.GetPolygonOffsetFactor(); } Float GLContext::GetPolygonOffsetUnits() const { return m_renderState.GetPolygonOffsetUnits(); } void GLContext::SetCapability(CapabilityInput cap, Bool enabled) { m_renderState.SetCapability(cap, enabled); } Bool GLContext::IsCapabilityEnabled(CapabilityInput cap) const { return m_renderState.IsCapabilityEnabled(cap); } void GLContext::SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled) { m_renderState.SetCapabilityIndexed(cap, index, enabled); } Bool GLContext::IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const { return m_renderState.IsCapabilityEnabledIndexed(cap, index); } void GLContext::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha) { m_renderState.SetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); } void GLContext::GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { m_renderState.GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); } void GLContext::SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha) { m_renderState.SetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha); } void GLContext::GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { m_renderState.GetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha); } void GLContext::SetBlendEquation(BlendEquation color, BlendEquation alpha) { m_renderState.SetBlendEquation(color, alpha); } void GLContext::GetBlendEquation(BlendEquation& color, BlendEquation& alpha) const { m_renderState.GetBlendEquation(color, alpha); } void GLContext::SetBlendEquationIndexed(Uint index, BlendEquation color, BlendEquation alpha) { m_renderState.SetBlendEquationIndexed(index, color, alpha); } void GLContext::GetBlendEquationIndexed(Uint index, BlendEquation& color, BlendEquation& alpha) const { m_renderState.GetBlendEquationIndexed(index, color, alpha); } void GLContext::SetLogicOp(LogicOperation logicOp) { m_renderState.SetLogicOp(logicOp); } LogicOperation GLContext::GetLogicOp() const { return m_renderState.GetLogicOp(); } void GLContext::SetDepthFunc(DepthTestFunc func) { m_renderState.SetDepthFunc(func); } DepthTestFunc GLContext::GetDepthFunc() const { return m_renderState.GetDepthFunc(); } void GLContext::SetDepthMask(Bool flag) { m_renderState.SetDepthMask(flag); } Bool GLContext::GetDepthMask() const { return m_renderState.GetDepthMask(); } void GLContext::SetStencilFunc(StencilFace face, DepthTestFunc func, Int ref, Uint32 mask) { m_renderState.SetStencilFunc(face, func, ref, mask); } void GLContext::SetStencilMask(StencilFace face, Uint32 mask) { m_renderState.SetStencilMask(face, mask); } void GLContext::SetStencilOp(StencilFace face, StencilOperation fail, StencilOperation depthFail, StencilOperation depthPass) { m_renderState.SetStencilOp(face, fail, depthFail, depthPass); } const StencilFaceState& GLContext::GetStencilState(StencilFace face) const { return m_renderState.GetStencilState(face); } void GLContext::SetColorMask(BoolVec4 mask) { m_renderState.SetColorMask(mask); } BoolVec4 GLContext::GetColorMask() const { return m_renderState.GetColorMask(); } void GLContext::SetColorMaskIndexed(Uint index, BoolVec4 mask) { m_renderState.SetColorMaskIndexed(index, mask); } BoolVec4 GLContext::GetColorMaskIndexed(Uint index) const { return m_renderState.GetColorMaskIndexed(index); } void GLContext::SetClearColor(FloatVec4 color) { m_renderState.SetClearColor(color); } const FloatVec4& GLContext::GetClearColor() const { return m_renderState.GetClearColor(); } void GLContext::SetClearDepth(Float depth) { m_renderState.SetClearDepth(depth); } Float GLContext::GetClearDepth() const { return m_renderState.GetClearDepth(); } void GLContext::SetClearStencil(Int stencil) { m_renderState.SetClearStencil(stencil); } Uint32 GLContext::GetClearStencil() const { return m_renderState.GetClearStencil(); } void GLContext::SetBlendColor(FloatVec4 color) { m_renderState.SetBlendColor(color); } const FloatVec4& GLContext::GetBlendColor() const { return m_renderState.GetBlendColor(); } void GLContext::SetDepthRange(FloatVec2 range) { m_renderState.SetDepthRange(range); } const FloatVec2& GLContext::GetDepthRange() const { return m_renderState.GetDepthRange(); } void GLContext::SetSampleCoverage(Float value, Bool invert) { m_renderState.SetSampleCoverage(value, invert); } Float GLContext::GetSampleCoverageValue() const { return m_renderState.GetSampleCoverageValue(); } Bool GLContext::GetSampleCoverageInvert() const { return m_renderState.GetSampleCoverageInvert(); } void GLContext::SetSampleMaskValue(Uint32 mask) { m_renderState.SetSampleMaskValue(mask); } Uint32 GLContext::GetSampleMaskValue() const { return m_renderState.GetSampleMaskValue(); } void GLContext::SetPixelStoreParam(PixelStoreParam param, Int value) { m_renderState.SetPixelStoreParam(param, value); } Int GLContext::GetPixelStoreParam(PixelStoreParam param) const { return m_renderState.GetPixelStoreParam(param); } PixelStoreParameters GLContext::GetPixelStoreParameters(Bool isUnpack) const { return m_renderState.GetPixelStoreParameters(isUnpack); } void GLContext::SetCullFaceMode(CullFaceMode mode) { m_renderState.SetCullFaceMode(mode); } CullFaceMode GLContext::GetCullFaceMode() const { return m_renderState.GetCullFaceMode(); } void GLContext::SetFrontFaceMode(FrontFaceMode mode) { m_renderState.SetFrontFaceMode(mode); } FrontFaceMode GLContext::GetFrontFaceMode() const { return m_renderState.GetFrontFaceMode(); } void GLContext::SetProvokingVertexMode(ProvokingVertexMode mode) { m_renderState.SetProvokingVertexMode(mode); } ProvokingVertexMode GLContext::GetProvokingVertexMode() const { return m_renderState.GetProvokingVertexMode(); } void GLContext::SetScissorBox(IntVec4 box) { m_renderState.SetScissorBox(box); } const IntVec4& GLContext::GetScissorBox() const { return m_renderState.GetScissorBox(); } // Framebuffer void GLContext::GenFramebufferNames(Uint number, Vector& framebuffers) { m_framebufferState.GenerateNames(number, framebuffers); } const SharedPtr& GLContext::GetFramebufferObject(Uint index) { return m_framebufferState.GetFramebufferObject(index); } BindingSlot& GLContext::GetFramebufferBindingSlot(FramebufferTarget target) { return m_framebufferState.GetBindingSlot(target); } const SharedPtr& GLContext::CreateFramebufferObject(Uint index) { return m_framebufferState.CreateFramebufferObject(index); } void GLContext::MarkFramebufferObjectForDeletion(Uint index) { m_framebufferState.MarkFramebufferObjectForDeletion(index); } Bool GLContext::ValidateFramebufferName(Uint index) const { return m_framebufferState.ValidateName(index); } Bool GLContext::ValidateFramebufferObject(Uint index) const { return m_framebufferState.ValidateFramebufferObject(index); } // Sampler void GLContext::GenSamplerNames(Uint number, Vector& samplers) { m_samplerState.GenerateNames(number, samplers); } const SharedPtr& GLContext::GetSamplerObject(Uint index) { return m_samplerState.GetSamplerObject(index); } const SharedPtr& GLContext::CreateSamplerObject(Uint index) { return m_samplerState.CreateSamplerObject(index); } void GLContext::MarkSamplerObjectForDeletion(Uint index) { // Unbind the sampler from all texture units if (ValidateSamplerObject(index)) { auto sampler = m_samplerState.GetSamplerObject(index); for (Int unit = 0; unit < TextureState::MAX_TEXTURE_IMAGE_UNITS; ++unit) { auto& textureUnit = m_textureState.GetUnitObject(unit); if (textureUnit.GetSamplerObject() == sampler) { textureUnit.SetSamplerObject(nullptr); } } } m_samplerState.MarkSamplerObjectForDeletion(index); } Bool GLContext::ValidateSamplerName(Uint index) const { return m_samplerState.ValidateName(index); } Bool GLContext::ValidateSamplerObject(Uint index) const { return m_samplerState.ValidateSamplerObject(index); } // Renderbuffer void GLContext::GenRenderbufferNames(Uint number, Vector& renderbuffers) { m_renderbufferState.GenerateNames(number, renderbuffers); } const SharedPtr& GLContext::GetRenderbufferObject(Uint index) { return m_renderbufferState.GetRenderbufferObject(index); } BindingSlot& GLContext::GetRenderbufferBindingSlot(RenderbufferTarget target) { return m_renderbufferState.GetBindingSlot(target); } const SharedPtr& GLContext::CreateRenderbufferObject(Uint index) { return m_renderbufferState.CreateRenderbufferObject(index); } void GLContext::MarkRenderbufferObjectForDeletion(Uint index) { m_renderbufferState.MarkRenderbufferObjectForDeletion(index); } Bool GLContext::ValidateRenderbufferName(Uint index) const { return m_renderbufferState.ValidateName(index); } Bool GLContext::ValidateRenderbufferObject(Uint index) const { return m_renderbufferState.ValidateRenderbufferObject(index); } } // namespace GLState UniquePtr pGLContext; } // namespace MobileGL::MG_State