From 727939af5bd5f1e9fa65c53e44a58298fa4a3fc9 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 9 Jun 2026 15:32:22 +0800 Subject: [PATCH] [Fix] (MG_Impl, MG_State, MG_Backend, MG_Util): Do source audit by Codex. --- MobileGL/Init.cpp | 17 + MobileGL/MG_Backend/BackendObject.h | 48 + .../DirectGLES/BackendObject_DirectGLES.cpp | 64 + .../DirectGLES/BackendObject_DirectGLES.h | 1 + MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 80 ++ MobileGL/MG_Backend/DirectGLES/Managers.cpp | 46 +- MobileGL/MG_Backend/DirectGLES/Managers.h | 1 + MobileGL/MG_Backend/DirectGLES/Utils.cpp | 2 +- .../BackendObject_DirectVulkan.cpp | 48 + .../DirectVulkan/Renderer/FrameContext.cpp | 12 +- .../DirectVulkan/Renderer/FrameContext.h | 4 +- .../DirectVulkan/Renderer/PipelineFactory.cpp | 48 +- .../DirectVulkan/Renderer/PipelineFactory.h | 13 + .../DirectVulkan/Renderer/SwapchainObject.cpp | 18 +- .../DirectVulkan/Renderer/VkClearManager.cpp | 192 ++- .../DirectVulkan/Renderer/VkClearManager.h | 48 +- .../Renderer/VkRenderPassManager.cpp | 104 +- .../Renderer/VkRenderPassManager.h | 2 +- .../Renderer/VkTextureManager.cpp | 83 +- .../DirectVulkan/Renderer/VkTextureManager.h | 37 + .../DirectVulkan/Renderer/VulkanRenderer.cpp | 390 ++++-- .../DirectVulkan/Renderer/VulkanRenderer.h | 1 + MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp | 3 + MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp | 131 +- MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h | 2 + .../MG_Impl/GLImpl/Drawing/GL_Drawing.cpp | 92 ++ .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 95 +- .../GLImpl/Framebuffer/GL_Framebuffer.cpp | 393 +++++- MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp | 1142 ++++++++++++++--- MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h | 3 + .../MG_Impl/GLImpl/Program/GL_Program.cpp | 357 ++++-- MobileGL/MG_Impl/GLImpl/Program/GL_Program.h | 13 + .../GLImpl/RenderState/GL_RenderState.cpp | 192 ++- .../GLImpl/RenderState/GL_RenderState.h | 1 + .../MG_Impl/GLImpl/Sampler/GL_Sampler.cpp | 26 + MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp | 222 +++- MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h | 3 + .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 164 ++- .../MG_Impl/GLImpl/Texture/ProxyTexture.cpp | 38 +- .../GLImpl/VertexArray/GL_VertexArray.cpp | 337 +++++ .../GLImpl/VertexArray/GL_VertexArray.h | 20 + MobileGL/MG_Impl/Init.cpp | 1 + MobileGL/MG_State/EGLState/Core.cpp | 15 + MobileGL/MG_State/EGLState/Core.h | 1 + .../GLState/BufferState/BufferObject.cpp | 32 +- .../GLState/BufferState/BufferObject.h | 1 + .../GLState/BufferState/BufferState.cpp | 8 + MobileGL/MG_State/GLState/Core.cpp | 95 +- MobileGL/MG_State/GLState/Core.h | 23 + .../FramebufferState/FramebufferObject.cpp | 34 +- .../FramebufferState/FramebufferObject.h | 10 +- .../FramebufferState/FramebufferState.cpp | 2 +- .../GLState/ProgramState/ProgramObject.cpp | 26 +- .../GLState/ProgramState/ProgramObject.h | 49 + .../GLState/ProgramState/ShaderObject.cpp | 8 + .../GLState/RenderState/RenderState.cpp | 186 +++ .../GLState/RenderState/RenderState.h | 104 ++ .../RenderbufferState/RenderbufferObject.cpp | 4 + .../RenderbufferState/RenderbufferObject.h | 3 +- .../GLState/SamplerState/SamplerObject.cpp | 4 - .../TextureState/TextureObject2DCube.cpp | 2 +- .../VertexArrayState/VertexArrayState.cpp | 4 +- .../Backend/DirectVulkan/SanityTest.cpp | 7 +- .../MG_Test/Backend/DirectVulkan/TestExec.cpp | 7 +- .../MG_Util/BackendLoaders/OpenGL/Loader.cpp | 186 ++- .../MG_Util/BackendLoaders/OpenGL/Loader.h | 51 + .../MG_Util/BackendLoaders/Vulkan/Loader.cpp | 122 ++ .../MG_Util/BackendLoaders/Vulkan/Loader.h | 48 + .../Converters/GLToMG/DataTypeConverter.cpp | 4 +- .../GLToMG/RenderStateEnumConverter.cpp | 62 + .../GLToMG/RenderStateEnumConverter.h | 2 + .../GLToMG/TextureEnumConverter.cpp | 10 + .../Converters/MGToGL/DataTypeConverter.cpp | 4 +- .../MGToGL/FramebufferEnumConverter.cpp | 13 +- .../MGToGL/RenderStateEnumConverter.cpp | 62 + .../MGToGL/RenderStateEnumConverter.h | 2 + .../MGToGL/TextureEnumConverter.cpp | 4 + .../MGToStr/BufferEnumConverter.cpp | 8 +- .../MGToStr/FramebufferEnumConverter.cpp | 12 +- .../MGToStr/RenderStateEnumConverter.cpp | 62 + .../MGToStr/RenderStateEnumConverter.h | 2 + .../MGToStr/TextureEnumConverter.cpp | 2 + .../MGToVk/RenderStateEnumConverter.cpp | 62 + .../MGToVk/RenderStateEnumConverter.h | 2 + MobileGL/MG_Util/Debug/Log.cpp | 4 +- MobileGL/MG_Util/Metrics/TextureMetrics.cpp | 27 +- .../ShaderSourceProcessor.cpp | 4 + .../EliminateFloatEqualsZeroPass.cpp | 26 +- .../MG_Util/ShaderTranspiler/SpvcSession.h | 6 +- .../MG_Util/Texture/PixelStoreProcessor.cpp | 4 +- .../Texture/TextureFormatProcessor.cpp | 12 +- MobileGL/MG_Util/Types.h | 2 +- SOURCE_AUDIT.md | 826 ++++++++++++ 93 files changed, 6019 insertions(+), 731 deletions(-) create mode 100644 SOURCE_AUDIT.md diff --git a/MobileGL/Init.cpp b/MobileGL/Init.cpp index 75a086ea..4907ca4a 100644 --- a/MobileGL/Init.cpp +++ b/MobileGL/Init.cpp @@ -15,7 +15,16 @@ #include namespace MobileGL { + namespace { + Bool g_isInitialized = false; + } + void Initialize() { + if (g_isInitialized) { + MGLOG_D("MobileGL already initialized; skipping duplicate Initialize()"); + return; + } + MG_Util::Debug::InitFile(); MGLOG_I("Initializing MobileGL..."); MG_ConfigLoader::Init(); @@ -28,16 +37,24 @@ namespace MobileGL { MGLOG_D("MG_Impl initialized"); glslang::InitializeProcess(); MGLOG_D("glslang initialized"); + g_isInitialized = true; MGLOG_I("MobileGL initialized"); } void Destroy() { + if (!g_isInitialized) { + return; + } + MGLOG_I("MobileGL closing..."); glslang::FinalizeProcess(); + MG_Backend::pActiveBackendObject.reset(); MG_State::pGLContext.reset(); MG_State::pEGLContext.reset(); MG_Impl::GLImpl::TextureImpl::pProxyTextureManager.reset(); MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo.reset(); + MG_Backend::gBackendFunctionsTable = {}; + g_isInitialized = false; MG_Util::Debug::Close(); // TODO: add and use Destroy functions for other subsystems diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 10f80e2c..cb285176 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -90,6 +90,54 @@ namespace MobileGL { struct DynamicBackendParameters { SizeT UniformBufferOffsetAlignment = 256; + Float AliasedLineWidthRangeMin = 1.0f; + Float AliasedLineWidthRangeMax = 1.0f; + Float SmoothLineWidthRangeMin = 1.0f; + Float SmoothLineWidthRangeMax = 1.0f; + Float SmoothLineWidthGranularity = 1.0f; + Float PointSizeRangeMin = 1.0f; + Float PointSizeRangeMax = 1.0f; + Float PointSizeGranularity = 1.0f; + Int Max3DTextureSize = 16384; + Int MaxArrayTextureLayers = 2048; + Int MaxCubeMapTextureSize = 16384; + Int MaxFramebufferWidth = 16384; + Int MaxFramebufferHeight = 16384; + Int MaxFramebufferLayers = 2048; + Int MaxRenderbufferSize = 16384; + Int MaxTextureSize = 16384; + Int MaxColorTextureSamples = 1; + Int MaxDepthTextureSamples = 1; + Int MaxFramebufferSamples = 1; + Int MaxIntegerSamples = 1; + Int MaxSamples = 1; + Int MaxSampleMaskWords = 1; + Int MaxTextureImageUnits = 32; + Int MaxVertexTextureImageUnits = 32; + Int MaxComputeTextureImageUnits = 32; + Int MaxCombinedTextureImageUnits = 192; + Int MaxVertexAttribs = 16; + Int MaxComputeShaderStorageBlocks = 8; + Int MaxCombinedShaderStorageBlocks = 32; + Int MaxComputeUniformBlocks = 12; + Int MaxComputeWorkGroupInvocations = 128; + Int MaxShaderStorageBufferBindings = 8; + Int MaxTextureBufferSize = 65536; + Int MaxUniformBufferBindings = 24; + Int MaxUniformBlockSize = 16384; + Int MaxImageUnits = 8; + Int MaxCombinedImageUniforms = 8; + Int MaxComputeImageUniforms = 8; + Int MaxDrawBuffers = 8; + Int MaxColorAttachments = 8; + Int MaxClipDistances = 8; + Int MaxViewports = 16; + Int MaxViewportWidth = 16384; + Int MaxViewportHeight = 16384; + Float ViewportBoundsRangeMin = 0.0f; + Float ViewportBoundsRangeMax = 0.0f; + Int ViewportSubpixelBits = 0; + Bool SupportsWideLines = false; }; enum class WindowBackend { diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 119ba42b..5516c61a 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -97,6 +97,21 @@ namespace MobileGL::MG_Backend::DirectGLES { return BackendObject::CreateEGLWindowSurface(handle); } + Bool BackendObject_DirectGLES::CreateEGLPbufferSurface(EGLint width, EGLint height) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_initialized) { + MGLOG_E("DirectGLES backend not initialized"); + return false; + } + + if (m_eglSurfaceInitialized) { + DestroyEGLContext(); + ResetEGLRuntimeState(); + } + + return BackendObject::CreateEGLPbufferSurface(width, height); + } + Bool BackendObject_DirectGLES::InitPbufferSurface(EGLint width, EGLint height) { return DirectGLES::InitPbufferSurface(width, height); } @@ -214,6 +229,55 @@ namespace MobileGL::MG_Backend::DirectGLES { void BackendObject_DirectGLES::UpdateDynamicBackendParameters() { m_dynamicParameters.UniformBufferOffsetAlignment = m_GLESCapabilities.UniformBufferOffsetAlignment; + m_dynamicParameters.AliasedLineWidthRangeMin = m_GLESCapabilities.AliasedLineWidthRangeMin; + m_dynamicParameters.AliasedLineWidthRangeMax = m_GLESCapabilities.AliasedLineWidthRangeMax; + m_dynamicParameters.SmoothLineWidthRangeMin = m_GLESCapabilities.SmoothLineWidthRangeMin; + m_dynamicParameters.SmoothLineWidthRangeMax = m_GLESCapabilities.SmoothLineWidthRangeMax; + m_dynamicParameters.SmoothLineWidthGranularity = m_GLESCapabilities.SmoothLineWidthGranularity; + m_dynamicParameters.PointSizeRangeMin = m_GLESCapabilities.PointSizeRangeMin; + m_dynamicParameters.PointSizeRangeMax = m_GLESCapabilities.PointSizeRangeMax; + m_dynamicParameters.PointSizeGranularity = m_GLESCapabilities.PointSizeGranularity; + m_dynamicParameters.Max3DTextureSize = m_GLESCapabilities.Max3DTextureSize; + m_dynamicParameters.MaxArrayTextureLayers = m_GLESCapabilities.MaxArrayTextureLayers; + m_dynamicParameters.MaxCubeMapTextureSize = m_GLESCapabilities.MaxCubeMapTextureSize; + m_dynamicParameters.MaxFramebufferWidth = m_GLESCapabilities.MaxFramebufferWidth; + m_dynamicParameters.MaxFramebufferHeight = m_GLESCapabilities.MaxFramebufferHeight; + m_dynamicParameters.MaxFramebufferLayers = m_GLESCapabilities.MaxFramebufferLayers; + m_dynamicParameters.MaxRenderbufferSize = m_GLESCapabilities.MaxRenderbufferSize; + m_dynamicParameters.MaxTextureSize = m_GLESCapabilities.MaxTextureSize; + m_dynamicParameters.MaxColorTextureSamples = m_GLESCapabilities.MaxColorTextureSamples; + m_dynamicParameters.MaxDepthTextureSamples = m_GLESCapabilities.MaxDepthTextureSamples; + m_dynamicParameters.MaxFramebufferSamples = m_GLESCapabilities.MaxFramebufferSamples; + m_dynamicParameters.MaxIntegerSamples = m_GLESCapabilities.MaxIntegerSamples; + m_dynamicParameters.MaxSamples = m_GLESCapabilities.MaxSamples; + m_dynamicParameters.MaxSampleMaskWords = m_GLESCapabilities.MaxSampleMaskWords; + m_dynamicParameters.MaxTextureImageUnits = m_GLESCapabilities.MaxTextureImageUnits; + m_dynamicParameters.MaxVertexTextureImageUnits = m_GLESCapabilities.MaxVertexTextureImageUnits; + m_dynamicParameters.MaxComputeTextureImageUnits = m_GLESCapabilities.MaxComputeTextureImageUnits; + m_dynamicParameters.MaxCombinedTextureImageUnits = m_GLESCapabilities.MaxCombinedTextureImageUnits; + m_dynamicParameters.MaxVertexAttribs = m_GLESCapabilities.MaxVertexAttribs; + m_dynamicParameters.MaxComputeShaderStorageBlocks = m_GLESCapabilities.MaxComputeShaderStorageBlocks; + m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_GLESCapabilities.MaxCombinedShaderStorageBlocks; + m_dynamicParameters.MaxComputeUniformBlocks = m_GLESCapabilities.MaxComputeUniformBlocks; + m_dynamicParameters.MaxComputeWorkGroupInvocations = m_GLESCapabilities.MaxComputeWorkGroupInvocations; + m_dynamicParameters.MaxShaderStorageBufferBindings = m_GLESCapabilities.MaxShaderStorageBufferBindings; + m_dynamicParameters.MaxTextureBufferSize = m_GLESCapabilities.MaxTextureBufferSize; + m_dynamicParameters.MaxUniformBufferBindings = m_GLESCapabilities.MaxUniformBufferBindings; + m_dynamicParameters.MaxUniformBlockSize = m_GLESCapabilities.MaxUniformBlockSize; + m_dynamicParameters.MaxImageUnits = m_GLESCapabilities.MaxImageUnits; + m_dynamicParameters.MaxCombinedImageUniforms = m_GLESCapabilities.MaxCombinedImageUniforms; + m_dynamicParameters.MaxComputeImageUniforms = m_GLESCapabilities.MaxComputeImageUniforms; + m_dynamicParameters.MaxDrawBuffers = m_GLESCapabilities.MaxDrawBuffers; + m_dynamicParameters.MaxColorAttachments = m_GLESCapabilities.MaxColorAttachments; + m_dynamicParameters.MaxClipDistances = m_GLESCapabilities.MaxClipDistances; + m_dynamicParameters.MaxViewports = m_GLESCapabilities.MaxViewports; + m_dynamicParameters.MaxViewportWidth = m_GLESCapabilities.MaxViewportWidth; + m_dynamicParameters.MaxViewportHeight = m_GLESCapabilities.MaxViewportHeight; + m_dynamicParameters.ViewportBoundsRangeMin = m_GLESCapabilities.ViewportBoundsRangeMin; + m_dynamicParameters.ViewportBoundsRangeMax = m_GLESCapabilities.ViewportBoundsRangeMax; + m_dynamicParameters.ViewportSubpixelBits = m_GLESCapabilities.ViewportSubpixelBits; + m_dynamicParameters.SupportsWideLines = + m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f; } const MG_External::GLESFunctionsTable& BackendObject_DirectGLES::GetGLESFunctions() const { diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h index c228233b..94e07d2b 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h @@ -21,6 +21,7 @@ namespace MobileGL::MG_Backend::DirectGLES { Bool InitWindowSurface() override; Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override; Bool CreateEGLWindowSurface(const WindowHandle& handle) override; + Bool CreateEGLPbufferSurface(EGLint width, EGLint height) override; Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override; Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override; void ReleaseEGLResources() override; diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 05447c79..aaa0e469 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -415,7 +415,16 @@ namespace MobileGL::MG_Backend::DirectGLES { } \ } SYNC_CAPABILITY(DepthTest, GL_DEPTH_TEST); + SYNC_CAPABILITY(ColorLogicOp, GL_COLOR_LOGIC_OP); + SYNC_CAPABILITY(Dither, GL_DITHER); + SYNC_CAPABILITY(Multisample, GL_MULTISAMPLE); + SYNC_CAPABILITY(SampleAlphaToCoverage, GL_SAMPLE_ALPHA_TO_COVERAGE); + SYNC_CAPABILITY(SampleCoverage, GL_SAMPLE_COVERAGE); + SYNC_CAPABILITY(SampleMask, GL_SAMPLE_MASK); + SYNC_CAPABILITY(PolygonOffsetFill, GL_POLYGON_OFFSET_FILL); + SYNC_CAPABILITY(RasterizerDiscard, GL_RASTERIZER_DISCARD); SYNC_CAPABILITY(ScissorTest, GL_SCISSOR_TEST); + SYNC_CAPABILITY(StencilTest, GL_STENCIL_TEST); SYNC_CAPABILITY(CullFace, GL_CULL_FACE); #undef SYNC_CAPABILITY @@ -528,6 +537,34 @@ namespace MobileGL::MG_Backend::DirectGLES { if (parameters.DepthMask != g_syncedRenderStateParameters.DepthMask) { g_GLESFuncs.glDepthMask(parameters.DepthMask ? GL_TRUE : GL_FALSE); } + if (parameters.DepthRange != g_syncedRenderStateParameters.DepthRange) { + g_GLESFuncs.glDepthRangef(parameters.DepthRange.x(), parameters.DepthRange.y()); + } + } + + { // Stencil state + for (SizeT faceIndex = 0; faceIndex < parameters.StencilStates.size(); ++faceIndex) { + const StencilFaceState& current = parameters.StencilStates[faceIndex]; + const StencilFaceState& synced = g_syncedRenderStateParameters.StencilStates[faceIndex]; + const GLenum glFace = faceIndex == 0 ? GL_FRONT : GL_BACK; + + if (current.Func != synced.Func || current.Ref != synced.Ref || + current.ValueMask != synced.ValueMask) { + g_GLESFuncs.glStencilFuncSeparate( + glFace, MG_Util::ConvertDepthTestFuncToGLEnum(current.Func), current.Ref, + current.ValueMask); + } + if (current.WriteMask != synced.WriteMask) { + g_GLESFuncs.glStencilMaskSeparate(glFace, current.WriteMask); + } + if (current.FailOp != synced.FailOp || current.PassDepthFailOp != synced.PassDepthFailOp || + current.PassDepthPassOp != synced.PassDepthPassOp) { + g_GLESFuncs.glStencilOpSeparate( + glFace, MG_Util::ConvertStencilOperationToGLEnum(current.FailOp), + MG_Util::ConvertStencilOperationToGLEnum(current.PassDepthFailOp), + MG_Util::ConvertStencilOperationToGLEnum(current.PassDepthPassOp)); + } + } } { // Color mask @@ -546,6 +583,10 @@ namespace MobileGL::MG_Backend::DirectGLES { if (parameters.ClearDepth != g_syncedRenderStateParameters.ClearDepth) { g_GLESFuncs.glClearDepthf(parameters.ClearDepth); } + if (parameters.BlendColor != g_syncedRenderStateParameters.BlendColor) { + const FloatVec4& blendColor = parameters.BlendColor; + g_GLESFuncs.glBlendColor(blendColor.x(), blendColor.y(), blendColor.z(), blendColor.w()); + } } { // Cull face mode @@ -566,6 +607,45 @@ namespace MobileGL::MG_Backend::DirectGLES { } } + { // Logic op + if (parameters.LogicOp != g_syncedRenderStateParameters.LogicOp) { + g_GLESFuncs.glLogicOp(MG_Util::ConvertLogicOperationToGLEnum(parameters.LogicOp)); + } + } + + { // Polygon offset + if (parameters.PolygonOffsetFactor != g_syncedRenderStateParameters.PolygonOffsetFactor || + parameters.PolygonOffsetUnits != g_syncedRenderStateParameters.PolygonOffsetUnits) { + g_GLESFuncs.glPolygonOffset(parameters.PolygonOffsetFactor, parameters.PolygonOffsetUnits); + } + } + + { // Line width + if (parameters.LineWidth != g_syncedRenderStateParameters.LineWidth) { + g_GLESFuncs.glLineWidth(parameters.LineWidth); + } + } + + { // Point size + if (parameters.PointSize != g_syncedRenderStateParameters.PointSize) { + g_GLESFuncs.glPointSize(parameters.PointSize); + } + } + + { // Sample coverage + if (parameters.SampleCoverageValue != g_syncedRenderStateParameters.SampleCoverageValue || + parameters.SampleCoverageInvert != g_syncedRenderStateParameters.SampleCoverageInvert) { + g_GLESFuncs.glSampleCoverage(parameters.SampleCoverageValue, + ToGLBoolean(parameters.SampleCoverageInvert)); + } + } + + { // Sample mask + if (g_GLESFuncs.glSampleMaski && parameters.SampleMaskValue != g_syncedRenderStateParameters.SampleMaskValue) { + g_GLESFuncs.glSampleMaski(0, parameters.SampleMaskValue); + } + } + g_syncedRenderStateVersion = currentRenderStateVersion; g_syncedRenderStateParameters = parameters; g_hasSyncedRenderState = true; diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 144aca17..44e5aae6 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -614,10 +614,27 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertGLEnumToString(err).c_str()); }); auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); - g_GLESFuncs.glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, - static_cast(texelSize.x()), - static_cast(texelSize.y()), glFormat, glType, - textureMipmapObject->MapMipmapData(uploadTarget, level)); + const void* mipData = textureMipmapObject->MapMipmapData(uploadTarget, level); + switch (stateTextureObject->GetTarget()) { + case TextureTarget::Texture2D: + case TextureTarget::TextureCubeMap: + g_GLESFuncs.glTexSubImage2D(glUploadTarget, static_cast(level), 0, 0, + static_cast(texelSize.x()), + static_cast(texelSize.y()), glFormat, glType, + mipData); + break; + case TextureTarget::Texture3D: + g_GLESFuncs.glTexSubImage3D(glUploadTarget, static_cast(level), 0, 0, 0, + static_cast(texelSize.x()), + static_cast(texelSize.y()), + static_cast(texelSize.z()), glFormat, glType, + mipData); + break; + default: + MGLOG_E("Unhandled texture target %s", + MG_Util::ConvertTextureTargetToString(stateTextureObject->GetTarget()).c_str()); + break; + } textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); } } @@ -912,7 +929,11 @@ namespace MobileGL::MG_Backend::DirectGLES { return false; } const auto& backendTextureObject = backendTextureIt->second; - auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); + auto glTextureTarget = + MG_Util::ConvertTextureUploadTargetToGLEnum(attachmentObject.GetTextureUploadTarget()); + if (glTextureTarget == GL_UNKNOWN_MGL) { + glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); + } backendTextureObject->Bind(glTextureTarget); g_GLESFuncs.glFramebufferTexture2D(glFBOTarget, glBackendAttachment, glTextureTarget, backendTextureObject->GetBackendTextureId(), @@ -1428,7 +1449,8 @@ namespace MobileGL::MG_Backend::DirectGLES { stateRBOObject->GetExternalIndex()); if (m_isInitialized && m_cacheInternalFormat == stateRBOObject->GetInternalFormat() && - m_cacheWidth == stateRBOObject->GetWidth() && m_cacheHeight == stateRBOObject->GetHeight()) { + m_cacheWidth == stateRBOObject->GetWidth() && m_cacheHeight == stateRBOObject->GetHeight() && + m_cacheSamples == stateRBOObject->GetSamples()) { MGLOG_D("RBO %u already initialized with matching parameters, skipping re-allocation.", stateRBOObject->GetExternalIndex()); return; @@ -1440,15 +1462,23 @@ namespace MobileGL::MG_Backend::DirectGLES { TextureInternalFormat internalFormat = stateRBOObject->GetInternalFormat(); Int width = static_cast(stateRBOObject->GetWidth()); Int height = static_cast(stateRBOObject->GetHeight()); + Int samples = static_cast(stateRBOObject->GetSamples()); GLenum glInternalFormat, glType, glFormat; TextureImpl::GenerateTextureFormatInfo(internalFormat, &glInternalFormat, &glFormat, &glType); - g_GLESFuncs.glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, static_cast(width), - static_cast(height)); + if (samples > 0) { + g_GLESFuncs.glRenderbufferStorageMultisample( + GL_RENDERBUFFER, static_cast(samples), glInternalFormat, static_cast(width), + static_cast(height)); + } else { + g_GLESFuncs.glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, static_cast(width), + static_cast(height)); + } m_cacheInternalFormat = internalFormat; m_cacheWidth = width; m_cacheHeight = height; + m_cacheSamples = samples; m_isInitialized = true; MGLOG_D("RBO %u sync completed. backend ID %u", stateRBOObject->GetExternalIndex(), m_backendRBOId); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index fccdc41a..9171aef6 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -313,6 +313,7 @@ namespace MobileGL::MG_Backend::DirectGLES { TextureInternalFormat m_cacheInternalFormat = TextureInternalFormat::Unknown; Int m_cacheWidth = 0; Int m_cacheHeight = 0; + Int m_cacheSamples = 0; }; extern StateBackendObjectRegistry diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index bf88bc30..622a276d 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -149,7 +149,7 @@ namespace MobileGL::MG_Backend::DirectGLES { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); #endif - while (GLenum err = g_GLESFuncs.glGetError() != GL_NO_ERROR) { + for (GLenum err = g_GLESFuncs.glGetError(); err != GL_NO_ERROR; err = g_GLESFuncs.glGetError()) { MGLOG_E("-> GLES Error: %s", MG_Util::ConvertGLEnumToString(err).c_str()); } } diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 21e1279d..1e3016cc 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -207,5 +207,53 @@ namespace MobileGL::MG_Backend::DirectVulkan { void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() { m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment; + m_dynamicParameters.AliasedLineWidthRangeMin = m_vulkanCaps.AliasedLineWidthRangeMin; + m_dynamicParameters.AliasedLineWidthRangeMax = m_vulkanCaps.AliasedLineWidthRangeMax; + m_dynamicParameters.SmoothLineWidthRangeMin = m_vulkanCaps.SmoothLineWidthRangeMin; + m_dynamicParameters.SmoothLineWidthRangeMax = m_vulkanCaps.SmoothLineWidthRangeMax; + m_dynamicParameters.SmoothLineWidthGranularity = m_vulkanCaps.SmoothLineWidthGranularity; + m_dynamicParameters.PointSizeRangeMin = m_vulkanCaps.PointSizeRangeMin; + m_dynamicParameters.PointSizeRangeMax = m_vulkanCaps.PointSizeRangeMax; + m_dynamicParameters.PointSizeGranularity = m_vulkanCaps.PointSizeGranularity; + m_dynamicParameters.Max3DTextureSize = m_vulkanCaps.Max3DTextureSize; + m_dynamicParameters.MaxArrayTextureLayers = m_vulkanCaps.MaxArrayTextureLayers; + m_dynamicParameters.MaxCubeMapTextureSize = m_vulkanCaps.MaxCubeMapTextureSize; + m_dynamicParameters.MaxFramebufferWidth = m_vulkanCaps.MaxFramebufferWidth; + m_dynamicParameters.MaxFramebufferHeight = m_vulkanCaps.MaxFramebufferHeight; + m_dynamicParameters.MaxFramebufferLayers = m_vulkanCaps.MaxFramebufferLayers; + m_dynamicParameters.MaxRenderbufferSize = m_vulkanCaps.MaxRenderbufferSize; + m_dynamicParameters.MaxTextureSize = m_vulkanCaps.MaxTextureSize; + m_dynamicParameters.MaxColorTextureSamples = m_vulkanCaps.MaxColorTextureSamples; + m_dynamicParameters.MaxDepthTextureSamples = m_vulkanCaps.MaxDepthTextureSamples; + m_dynamicParameters.MaxFramebufferSamples = m_vulkanCaps.MaxFramebufferSamples; + m_dynamicParameters.MaxIntegerSamples = m_vulkanCaps.MaxIntegerSamples; + m_dynamicParameters.MaxSamples = m_vulkanCaps.MaxSamples; + m_dynamicParameters.MaxSampleMaskWords = m_vulkanCaps.MaxSampleMaskWords; + m_dynamicParameters.MaxTextureImageUnits = m_vulkanCaps.MaxTextureImageUnits; + m_dynamicParameters.MaxVertexTextureImageUnits = m_vulkanCaps.MaxVertexTextureImageUnits; + m_dynamicParameters.MaxComputeTextureImageUnits = m_vulkanCaps.MaxComputeTextureImageUnits; + m_dynamicParameters.MaxCombinedTextureImageUnits = m_vulkanCaps.MaxCombinedTextureImageUnits; + m_dynamicParameters.MaxVertexAttribs = m_vulkanCaps.MaxVertexAttribs; + m_dynamicParameters.MaxComputeShaderStorageBlocks = m_vulkanCaps.MaxComputeShaderStorageBlocks; + m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_vulkanCaps.MaxCombinedShaderStorageBlocks; + m_dynamicParameters.MaxComputeUniformBlocks = m_vulkanCaps.MaxComputeUniformBlocks; + m_dynamicParameters.MaxComputeWorkGroupInvocations = m_vulkanCaps.MaxComputeWorkGroupInvocations; + m_dynamicParameters.MaxShaderStorageBufferBindings = m_vulkanCaps.MaxShaderStorageBufferBindings; + m_dynamicParameters.MaxTextureBufferSize = m_vulkanCaps.MaxTextureBufferSize; + m_dynamicParameters.MaxUniformBufferBindings = m_vulkanCaps.MaxUniformBufferBindings; + m_dynamicParameters.MaxUniformBlockSize = m_vulkanCaps.MaxUniformBlockSize; + m_dynamicParameters.MaxImageUnits = m_vulkanCaps.MaxImageUnits; + m_dynamicParameters.MaxCombinedImageUniforms = m_vulkanCaps.MaxCombinedImageUniforms; + m_dynamicParameters.MaxComputeImageUniforms = m_vulkanCaps.MaxComputeImageUniforms; + m_dynamicParameters.MaxDrawBuffers = m_vulkanCaps.MaxDrawBuffers; + m_dynamicParameters.MaxColorAttachments = m_vulkanCaps.MaxColorAttachments; + m_dynamicParameters.MaxClipDistances = m_vulkanCaps.MaxClipDistances; + m_dynamicParameters.MaxViewports = m_vulkanCaps.MaxViewports; + m_dynamicParameters.MaxViewportWidth = m_vulkanCaps.MaxViewportWidth; + m_dynamicParameters.MaxViewportHeight = m_vulkanCaps.MaxViewportHeight; + m_dynamicParameters.ViewportBoundsRangeMin = m_vulkanCaps.ViewportBoundsRangeMin; + m_dynamicParameters.ViewportBoundsRangeMax = m_vulkanCaps.ViewportBoundsRangeMax; + m_dynamicParameters.ViewportSubpixelBits = m_vulkanCaps.ViewportSubpixelBits; + m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines; } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp index 0a7c9211..7d6bd004 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp @@ -188,18 +188,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { return packet; } - FrameContext::PresentInfoPacket FrameContext::GetPresentInfo(VkSwapchainKHR swapchain, const Uint32& imageIndex) const { + FrameContext::PresentInfoPacket FrameContext::GetPresentInfo(VkSwapchainKHR swapchain, Uint32 imageIndex) const { AssertValidSwapchainImageIndex(imageIndex); PresentInfoPacket packet{}; packet.waitSemaphore = m_swapchainImageRenderFinishedSemaphores[imageIndex]; packet.swapchain = swapchain; - packet.imageIndex = &imageIndex; + packet.imageIndex = imageIndex; packet.presentInfo.waitSemaphoreCount = 1; packet.presentInfo.pWaitSemaphores = &packet.waitSemaphore; packet.presentInfo.swapchainCount = 1; packet.presentInfo.pSwapchains = &packet.swapchain; - packet.presentInfo.pImageIndices = packet.imageIndex; + packet.presentInfo.pImageIndices = &packet.imageIndex; packet.presentInfo.pResults = nullptr; return packet; } @@ -212,13 +212,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { return result; } - result = vkResetFences(device, 1, &frame.imageInFlightFence); + result = vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence, + &outImageIndex); if (result != VK_SUCCESS) { return result; } - return vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence, - &outImageIndex); + return vkResetFences(device, 1, &frame.imageInFlightFence); } Uint32 FrameContext::GetCurrentFrameIndex() const { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h index 83815445..ed99d556 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h @@ -25,7 +25,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { struct PresentInfoPacket { VkSemaphore waitSemaphore = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE; - const Uint32* imageIndex = nullptr; + Uint32 imageIndex = 0; VkPresentInfoKHR presentInfo{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR}; }; @@ -53,7 +53,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool TransitionToPresent(VkImage image, VkImageLayout oldLayout, VkImageLayout presentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); SubmitInfoPacket GetSubmitInfo(Bool shouldSubmitCommandBuffer, Uint32 swapchainImageIndex) const; - PresentInfoPacket GetPresentInfo(VkSwapchainKHR swapchain, const Uint32& imageIndex) const; + PresentInfoPacket GetPresentInfo(VkSwapchainKHR swapchain, Uint32 imageIndex) const; VkResult WaitAndAcquireNextImage(VkDevice device, VkSwapchainKHR swapchain, Uint32& outImageIndex, Uint64 timeout = UINT64_MAX, VkFence acquireFence = VK_NULL_HANDLE); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp index e5fd2fbd..052642bb 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp @@ -39,7 +39,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { XXHASH_VERIFY(XXH64_update(m_hashState, &payload.frontFace, sizeof(payload.frontFace))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthTestEnable, sizeof(payload.depthTestEnable))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthWriteEnable, sizeof(payload.depthWriteEnable))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthBiasEnable, sizeof(payload.depthBiasEnable))); + XXHASH_VERIFY( + XXH64_update(m_hashState, &payload.rasterizerDiscardEnable, sizeof(payload.rasterizerDiscardEnable))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.logicOpEnable, sizeof(payload.logicOpEnable))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.stencilTestEnable, sizeof(payload.stencilTestEnable))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthCompareOp, sizeof(payload.depthCompareOp))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.logicOp, sizeof(payload.logicOp))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.frontStencilFailOp, sizeof(payload.frontStencilFailOp))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.frontStencilPassOp, sizeof(payload.frontStencilPassOp))); + XXHASH_VERIFY( + XXH64_update(m_hashState, &payload.frontStencilDepthFailOp, sizeof(payload.frontStencilDepthFailOp))); + XXHASH_VERIFY( + XXH64_update(m_hashState, &payload.frontStencilCompareOp, sizeof(payload.frontStencilCompareOp))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.backStencilFailOp, sizeof(payload.backStencilFailOp))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.backStencilPassOp, sizeof(payload.backStencilPassOp))); + XXHASH_VERIFY( + XXH64_update(m_hashState, &payload.backStencilDepthFailOp, sizeof(payload.backStencilDepthFailOp))); + XXHASH_VERIFY( + XXH64_update(m_hashState, &payload.backStencilCompareOp, sizeof(payload.backStencilCompareOp))); if (payload.colorAttachmentCount > 0) { XXHASH_VERIFY(XXH64_update( m_hashState, @@ -86,7 +104,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { static constexpr VkDynamicState kDynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_BLEND_CONSTANTS, + VK_DYNAMIC_STATE_DEPTH_BIAS, + VK_DYNAMIC_STATE_LINE_WIDTH, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, + VK_DYNAMIC_STATE_STENCIL_REFERENCE }; VkPipelineDynamicStateCreateInfo dynamicState{}; @@ -105,6 +129,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { raster.polygonMode = VK_POLYGON_MODE_FILL; raster.cullMode = payload.cullMode; raster.frontFace = payload.frontFace; + raster.depthBiasEnable = payload.depthBiasEnable ? VK_TRUE : VK_FALSE; + raster.rasterizerDiscardEnable = payload.rasterizerDiscardEnable ? VK_TRUE : VK_FALSE; raster.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; @@ -115,13 +141,31 @@ namespace MobileGL::MG_Backend::DirectVulkan { depthStencil.depthWriteEnable = payload.depthWriteEnable ? VK_TRUE : VK_FALSE; depthStencil.depthCompareOp = payload.depthCompareOp; depthStencil.depthBoundsTestEnable = VK_FALSE; - depthStencil.stencilTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = payload.stencilTestEnable ? VK_TRUE : VK_FALSE; + if (payload.stencilTestEnable) { + depthStencil.front.failOp = payload.frontStencilFailOp; + depthStencil.front.passOp = payload.frontStencilPassOp; + depthStencil.front.depthFailOp = payload.frontStencilDepthFailOp; + depthStencil.front.compareOp = payload.frontStencilCompareOp; + depthStencil.front.compareMask = 0xffffffffu; + depthStencil.front.writeMask = 0xffffffffu; + depthStencil.front.reference = 0; + depthStencil.back.failOp = payload.backStencilFailOp; + depthStencil.back.passOp = payload.backStencilPassOp; + depthStencil.back.depthFailOp = payload.backStencilDepthFailOp; + depthStencil.back.compareOp = payload.backStencilCompareOp; + depthStencil.back.compareMask = 0xffffffffu; + depthStencil.back.writeMask = 0xffffffffu; + depthStencil.back.reference = 0; + } Vector colorAttachments(payload.colorAttachmentCount); for (Uint32 i = 0; i < payload.colorAttachmentCount; ++i) { colorAttachments[i] = payload.colorBlendAttachments[i]; } VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; + blend.logicOpEnable = payload.logicOpEnable ? VK_TRUE : VK_FALSE; + blend.logicOp = payload.logicOp; blend.attachmentCount = payload.colorAttachmentCount; blend.pAttachments = colorAttachments.empty() ? nullptr : colorAttachments.data(); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h index ae9faea6..474cc938 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h @@ -32,7 +32,20 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkFrontFace frontFace = VK_FRONT_FACE_CLOCKWISE; Bool depthTestEnable = false; Bool depthWriteEnable = false; + Bool depthBiasEnable = false; + Bool rasterizerDiscardEnable = false; + Bool logicOpEnable = false; + Bool stencilTestEnable = false; VkCompareOp depthCompareOp = VK_COMPARE_OP_ALWAYS; + VkLogicOp logicOp = VK_LOGIC_OP_COPY; + VkStencilOp frontStencilFailOp = VK_STENCIL_OP_KEEP; + VkStencilOp frontStencilPassOp = VK_STENCIL_OP_KEEP; + VkStencilOp frontStencilDepthFailOp = VK_STENCIL_OP_KEEP; + VkCompareOp frontStencilCompareOp = VK_COMPARE_OP_ALWAYS; + VkStencilOp backStencilFailOp = VK_STENCIL_OP_KEEP; + VkStencilOp backStencilPassOp = VK_STENCIL_OP_KEEP; + VkStencilOp backStencilDepthFailOp = VK_STENCIL_OP_KEEP; + VkCompareOp backStencilCompareOp = VK_COMPARE_OP_ALWAYS; Array colorBlendAttachments{}; const Vector* stages = nullptr; const VkPipelineVertexInputStateCreateInfo* vertexInputState = nullptr; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp index 11915422..7e4fd215 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp @@ -153,7 +153,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { MGLOG_I("Picked present mode: %s", string_VkPresentModeKHR(presentMode)); const auto& swapchainCaps = swapchainCapabilities.capabilities; - const auto targetImageCount = std::max(minImageCountHint, swapchainCaps.minImageCount); + Uint32 targetImageCount = std::max(minImageCountHint, swapchainCaps.minImageCount); + if (swapchainCaps.maxImageCount != 0) { + targetImageCount = std::min(targetImageCount, swapchainCaps.maxImageCount); + } MGLOG_I("Set minImageCount = %u", targetImageCount); MGLOG_I("Swapchain currentTransform = %s", string_VkSurfaceTransformFlagBitsKHR(swapchainCaps.currentTransform)); @@ -234,11 +237,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { // Properly initialize Default FBO here auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; + const Int extentWidth = static_cast(createInfo.imageExtent.width); + const Int extentHeight = static_cast(createInfo.imageExtent.height); + const SizeT defaultAttachmentByteSize = + static_cast(createInfo.imageExtent.width) * static_cast(createInfo.imageExtent.height) * 4; + auto* colorTex = static_cast(defaultFBOInfo->colorAttachment.get()); colorTex->AllocateStorage( TextureUploadTarget::Texture2D, 0, { - {(Int)createInfo.imageExtent.width, (Int)createInfo.imageExtent.height, 1}, - createInfo.imageExtent.width * (Int)createInfo.imageExtent.height * 4}); // TODO: 4 is format size + {extentWidth, extentHeight, 1}, + defaultAttachmentByteSize}); // TODO: 4 is format size TextureInternalFormat depthFormat = TextureInternalFormat::Depth24Stencil8; switch (m_depthStencilFormat) { case VK_FORMAT_D24_UNORM_S8_UINT: @@ -257,8 +265,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { auto* depthTex = static_cast(defaultFBOInfo->depthAttachment.get()); depthTex->SetInternalFormat(depthFormat); depthTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, { - {(Int)createInfo.imageExtent.width, (Int)createInfo.imageExtent.height, 1}, - createInfo.imageExtent.width * createInfo.imageExtent.width * 4}); // TODO: 4 is format size + {extentWidth, extentHeight, 1}, + defaultAttachmentByteSize}); // TODO: 4 is format size } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp index 1192b4bb..d6ee545a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp @@ -12,7 +12,19 @@ #include "MG_Util/Converters/MGToStr/TextureEnumConverter.h" namespace MobileGL::MG_Backend::DirectVulkan { - static SharedPtr GetClearableAttachmentTexture( + static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) { + return target >= TextureUploadTarget::CubeMapPositiveX && + target <= TextureUploadTarget::CubeMapNegativeZ; + } + + static Uint32 ResolveAttachmentBaseArrayLayer(TextureUploadTarget target) { + if (!IsCubeMapFaceUploadTarget(target)) { + return 0; + } + return static_cast(target) - static_cast(TextureUploadTarget::CubeMapPositiveX); + } + + static const MG_State::GLState::FramebufferAttachmentObject* GetClearableAttachment( const MG_State::GLState::FramebufferObject& drawFbo, FramebufferAttachmentType attachmentType) { if (attachmentType == FramebufferAttachmentType::None) { return nullptr; @@ -23,7 +35,30 @@ namespace MobileGL::MG_Backend::DirectVulkan { return nullptr; } - return attachment.GetTexture(); + return &attachment; + } + + PendingClearKey VkClearManager::MakePendingClearKey(MG_State::GLState::ITextureObject* texture, Uint32 mipLevel, + Uint32 baseArrayLayer, Uint32 layerCount) { + return PendingClearKey { + .texture = texture, + .mipLevel = mipLevel, + .baseArrayLayer = baseArrayLayer, + .layerCount = layerCount, + }; + } + + PendingClearKey VkClearManager::MakePendingClearKey( + const MG_State::GLState::FramebufferAttachmentObject& attachment) { + MOBILEGL_ASSERT(attachment.IsTexture() && !attachment.IsRenderbuffer(), + "MakePendingClearKey requires a texture framebuffer attachment"); + auto* texture = attachment.GetTexture().get(); + MOBILEGL_ASSERT(texture != nullptr, "MakePendingClearKey: texture attachment resolved to null"); + const Uint32 mipLevel = static_cast(std::max(attachment.GetTextureLevel(), 0)); + const TextureUploadTarget uploadTarget = attachment.GetTextureUploadTarget(); + const Uint32 baseArrayLayer = ResolveAttachmentBaseArrayLayer(uploadTarget); + const Uint32 layerCount = IsCubeMapFaceUploadTarget(uploadTarget) ? 1u : 1u; + return MakePendingClearKey(texture, mipLevel, baseArrayLayer, layerCount); } Bool VkClearManager::Initialize() { @@ -40,46 +75,46 @@ namespace MobileGL::MG_Backend::DirectVulkan { auto& drawbufs = drawFbo.GetDrawBuffers(); // This should automatically work on default & offscreen FBO for (auto drawbuf: drawbufs) { - auto texture = GetClearableAttachmentTexture(drawFbo, drawbuf); - if (!texture) { + const auto* attachment = GetClearableAttachment(drawFbo, drawbuf); + if (!attachment) { continue; } QueueClear({ .mask = GL_COLOR_BUFFER_BIT, .color = clearPayload.color - }, texture); + }, *attachment); MGLOG_D("%s: %s (texture %d) - color = (%.2f, %.2f, %.2f, %.2f)", __func__, MG_Util::ConvertFramebufferAttachmentTypeToString(drawbuf).c_str(), - texture->GetExternalIndex(), + attachment->GetTexture()->GetExternalIndex(), clearPayload.color[0], clearPayload.color[1], clearPayload.color[2], clearPayload.color[3]); } } if (mask & GL_DEPTH_BUFFER_BIT) { - auto texture = GetClearableAttachmentTexture(drawFbo, FramebufferAttachmentType::Depth); - if (texture) { + const auto* attachment = GetClearableAttachment(drawFbo, FramebufferAttachmentType::Depth); + if (attachment) { QueueClear({ .mask = GL_DEPTH_BUFFER_BIT, .depth = clearPayload.depth, - }, texture); + }, *attachment); MGLOG_D("%s: Depth (texture %d) - depth = (%.2f)", __func__, - texture->GetExternalIndex(), clearPayload.depth); + attachment->GetTexture()->GetExternalIndex(), clearPayload.depth); } } if (mask & GL_STENCIL_BUFFER_BIT) { - auto texture = GetClearableAttachmentTexture(drawFbo, FramebufferAttachmentType::Stencil); - if (texture) { + const auto* attachment = GetClearableAttachment(drawFbo, FramebufferAttachmentType::Stencil); + if (attachment) { QueueClear({ .mask = GL_STENCIL_BUFFER_BIT, .stencil = clearPayload.stencil, - }, texture); + }, *attachment); MGLOG_D("%s: Stencil (texture %d) - stencil = (%u)", __func__, - texture->GetExternalIndex(), clearPayload.stencil); + attachment->GetTexture()->GetExternalIndex(), clearPayload.stencil); } } } @@ -94,7 +129,35 @@ namespace MobileGL::MG_Backend::DirectVulkan { return; auto* pTexture = weakTexturePtr.lock().get(); m_aliveObjects[pTexture] = weakTexturePtr; - auto& pending = m_pendingClears[pTexture]; + auto& pending = m_pendingClears[MakePendingClearKey(pTexture)]; + pending.mask |= clearPayload.mask; + if (clearPayload.mask & GL_COLOR_BUFFER_BIT) { + pending.color = clearPayload.color; + } + if (clearPayload.mask & GL_DEPTH_BUFFER_BIT) { + pending.depth = clearPayload.depth; + } + if (clearPayload.mask & GL_STENCIL_BUFFER_BIT) { + pending.stencil = clearPayload.stencil; + } + } + + void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload, + const MG_State::GLState::FramebufferAttachmentObject& attachment) { + if (clearPayload.mask == 0 || !attachment.IsTexture() || attachment.IsRenderbuffer()) { + return; + } + const auto texture = attachment.GetTexture(); + if (!texture) { + return; + } + WeakPtr weakTexturePtr = texture; + if (weakTexturePtr.expired()) { + return; + } + auto* pTexture = weakTexturePtr.lock().get(); + m_aliveObjects[pTexture] = weakTexturePtr; + auto& pending = m_pendingClears[MakePendingClearKey(attachment)]; pending.mask |= clearPayload.mask; if (clearPayload.mask & GL_COLOR_BUFFER_BIT) { pending.color = clearPayload.color; @@ -108,20 +171,40 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) { - return m_pendingClears.find(texture) != m_pendingClears.end(); + if (texture == nullptr) { + return false; + } + return std::any_of(m_pendingClears.begin(), m_pendingClears.end(), + [texture](const auto& item) { return item.first.texture == texture; }); } - Bool VkClearManager::GetPendingClear(MG_State::GLState::ITextureObject* texture, ClearAttachmentPayload& outPayload) { - if (m_aliveObjects.find(texture) == m_aliveObjects.end() || - m_pendingClears.find(texture) == m_pendingClears.end()) { - MGLOG_D("%s: Failed getting pending clear for texture %d", __func__, texture->GetExternalIndex()); + Bool VkClearManager::HasPendingClear(const PendingClearKey& key) { + return key.texture != nullptr && m_pendingClears.find(key) != m_pendingClears.end(); + } + + Bool VkClearManager::HasPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment) { + if (!attachment.IsTexture() || attachment.IsRenderbuffer() || !attachment.GetTexture()) { + return false; + } + return HasPendingClear(MakePendingClearKey(attachment)); + } + + Bool VkClearManager::GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload) { + if (key.texture == nullptr || m_aliveObjects.find(key.texture) == m_aliveObjects.end()) { + return false; + } + auto it = m_pendingClears.find(key); + if (it == m_pendingClears.end()) { + MGLOG_D("%s: Failed getting pending clear for texture %d mip=%u layer=%u count=%u", __func__, + key.texture ? key.texture->GetExternalIndex() : 0, key.mipLevel, key.baseArrayLayer, key.layerCount); return false; } - outPayload = m_pendingClears[texture]; - MGLOG_D("%s: Got pending clear for texture %d (%s), mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__, - texture->GetExternalIndex(), - MG_Util::ConvertTextureInternalFormatToString(texture->GetFormat()).c_str(), + outPayload = it->second; + MGLOG_D("%s: Got pending clear for texture %d (%s), mip=%u layer=%u count=%u mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__, + key.texture->GetExternalIndex(), + MG_Util::ConvertTextureInternalFormatToString(key.texture->GetFormat()).c_str(), + key.mipLevel, key.baseArrayLayer, key.layerCount, static_cast(outPayload.mask), outPayload.color[0], outPayload.color[1], outPayload.color[2], outPayload.color[3], outPayload.depth, @@ -129,10 +212,59 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } + Bool VkClearManager::GetPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment, + ClearAttachmentPayload& outPayload) { + if (!attachment.IsTexture() || attachment.IsRenderbuffer() || !attachment.GetTexture()) { + MGLOG_D("%s: Failed getting pending clear for non-texture framebuffer attachment", __func__); + return false; + } + return GetPendingClear(MakePendingClearKey(attachment), outPayload); + } + + Bool VkClearManager::GetPendingClears(MG_State::GLState::ITextureObject* texture, + Vector& outEntries) { + outEntries.clear(); + if (texture == nullptr || m_aliveObjects.find(texture) == m_aliveObjects.end()) { + return false; + } + for (const auto& [key, payload] : m_pendingClears) { + if (key.texture != texture) { + continue; + } + outEntries.emplace_back(PendingClearEntry{.key = key, .payload = payload}); + } + return !outEntries.empty(); + } + void VkClearManager::PopPendingClear(MG_State::GLState::ITextureObject* texture) { - MGLOG_D("%s: Pop pending clear for texture %d", __func__, texture->GetExternalIndex()); + if (texture == nullptr) { + return; + } + MGLOG_D("%s: Pop all pending clears for texture %d", __func__, texture->GetExternalIndex()); m_aliveObjects.erase(texture); - m_pendingClears.erase(texture); + for (auto it = m_pendingClears.begin(); it != m_pendingClears.end();) { + if (it->first.texture == texture) { + it = m_pendingClears.erase(it); + } else { + ++it; + } + } + } + + void VkClearManager::PopPendingClear(const PendingClearKey& key) { + if (key.texture == nullptr) { + return; + } + MGLOG_D("%s: Pop pending clear for texture %d mip=%u layer=%u count=%u", __func__, + key.texture->GetExternalIndex(), key.mipLevel, key.baseArrayLayer, key.layerCount); + m_pendingClears.erase(key); + } + + void VkClearManager::PopPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment) { + if (!attachment.IsTexture() || attachment.IsRenderbuffer() || !attachment.GetTexture()) { + return; + } + PopPendingClear(MakePendingClearKey(attachment)); } SizeT VkClearManager::CollectGarbage() { @@ -145,7 +277,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) { auto current = it++; if (current->second.expired()) { - m_pendingClears.erase(current->first); + for (auto clearIt = m_pendingClears.begin(); clearIt != m_pendingClears.end();) { + if (clearIt->first.texture == current->first) { + clearIt = m_pendingClears.erase(clearIt); + } else { + ++clearIt; + } + } m_aliveObjects.erase(current); ++count; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h index 9bfb1d75..817e5429 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h @@ -29,8 +29,43 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 stencil = 0; }; + struct PendingClearKey { + MG_State::GLState::ITextureObject* texture = nullptr; + Uint32 mipLevel = 0; + Uint32 baseArrayLayer = 0; + Uint32 layerCount = 1; + + Bool operator==(const PendingClearKey& other) const { + return texture == other.texture && mipLevel == other.mipLevel && + baseArrayLayer == other.baseArrayLayer && layerCount == other.layerCount; + } + }; + + struct PendingClearEntry { + PendingClearKey key{}; + ClearAttachmentPayload payload{}; + }; + + struct PendingClearKeyHash { + SizeT operator()(const PendingClearKey& key) const { + const SizeT textureHash = std::hash{}(key.texture); + const SizeT mipHash = std::hash{}(key.mipLevel); + const SizeT layerHash = std::hash{}(key.baseArrayLayer); + const SizeT layerCountHash = std::hash{}(key.layerCount); + SizeT hash = textureHash; + hash ^= mipHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= layerHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= layerCountHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + return hash; + } + }; + class VkClearManager { public: + static PendingClearKey MakePendingClearKey(const MG_State::GLState::FramebufferAttachmentObject& attachment); + static PendingClearKey MakePendingClearKey(MG_State::GLState::ITextureObject* texture, Uint32 mipLevel = 0, + Uint32 baseArrayLayer = 0, Uint32 layerCount = 1); + Bool Initialize(); void Shutdown(); @@ -38,13 +73,22 @@ namespace MobileGL::MG_Backend::DirectVulkan { void QueueClear( const ClearAttachmentPayload& clearPayload, const SharedPtr& texture); + void QueueClear(const ClearAttachmentPayload& clearPayload, + const MG_State::GLState::FramebufferAttachmentObject& attachment); Bool HasPendingClear(MG_State::GLState::ITextureObject* texture); - Bool GetPendingClear(MG_State::GLState::ITextureObject* texture, ClearAttachmentPayload& outPayload); + Bool HasPendingClear(const PendingClearKey& key); + Bool HasPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment); + Bool GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload); + Bool GetPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment, + ClearAttachmentPayload& outPayload); + Bool GetPendingClears(MG_State::GLState::ITextureObject* texture, Vector& outEntries); void PopPendingClear(MG_State::GLState::ITextureObject* texture); + void PopPendingClear(const PendingClearKey& key); + void PopPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment); SizeT CollectGarbage(); private: Uint8 m_gcCounter = 0; - UnorderedMap m_pendingClears; + UnorderedMap m_pendingClears; UnorderedMap> m_aliveObjects; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp index 6773ff60..0d46b75e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp @@ -22,6 +22,32 @@ namespace MobileGL::MG_Backend::DirectVulkan { return requestedAlpha; } + static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) { + return target >= TextureUploadTarget::CubeMapPositiveX && + target <= TextureUploadTarget::CubeMapNegativeZ; + } + + static Uint32 ResolveAttachmentBaseArrayLayer(const MG_State::GLState::FramebufferAttachmentObject& attachment) { + const TextureUploadTarget uploadTarget = attachment.GetTextureUploadTarget(); + if (!IsCubeMapFaceUploadTarget(uploadTarget)) { + return 0; + } + return static_cast(uploadTarget) - static_cast(TextureUploadTarget::CubeMapPositiveX); + } + + static Uint32 ResolveAttachmentLayerCount(const MG_State::GLState::FramebufferAttachmentObject& attachment) { + static_cast(attachment); + return 1u; + } + + static VkImageViewType ResolveAttachmentViewType( + const MG_State::GLState::FramebufferAttachmentObject& attachment, + const VkTextureManager::TextureResource& resource) { + return IsCubeMapFaceUploadTarget(attachment.GetTextureUploadTarget()) ? + VK_IMAGE_VIEW_TYPE_2D : + resource.viewType; + } + static MG_State::GLState::ITextureObject* ResolveCompleteColorAttachmentTexture( const MG_State::GLState::FramebufferObject& fbo, FramebufferAttachmentType attachmentType, @@ -79,6 +105,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void VkRenderPassManager::Shutdown() { + m_renderPasses.clear(); + RenderPassEntry::s_textureResourcesScratch.clear(); + s_activeRenderPass = {}; + s_hasActiveRenderPass = false; } VkRenderPassManager::HashType VkRenderPassManager::ComputeHash( @@ -117,6 +147,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (att.IsTexture()) { const Int textureLevel = att.GetTextureLevel(); XXHASH_VERIFY(XXH64_update(m_hashState, &textureLevel, sizeof(textureLevel))); + const TextureUploadTarget textureUploadTarget = att.GetTextureUploadTarget(); + XXHASH_VERIFY(XXH64_update(m_hashState, &textureUploadTarget, sizeof(textureUploadTarget))); Uint64 imageIdentity = 0; auto* texture = att.GetTexture().get(); @@ -129,11 +161,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (includePendingClear && att.IsTexture()) { auto* texture = att.GetTexture().get(); - auto hasClear = m_clearManager.HasPendingClear(texture); + const auto pendingClearKey = VkClearManager::MakePendingClearKey(att); + auto hasClear = m_clearManager.HasPendingClear(pendingClearKey); XXHASH_VERIFY(XXH64_update(m_hashState, &hasClear, sizeof(hasClear))); if (hasClear) { ClearAttachmentPayload clearPayload{}; - Bool hasPayload = m_clearManager.GetPendingClear(texture, clearPayload); + Bool hasPayload = m_clearManager.GetPendingClear(pendingClearKey, clearPayload); XXHASH_VERIFY(XXH64_update(m_hashState, &hasPayload, sizeof(hasPayload))); if (hasPayload) { XXHASH_VERIFY(XXH64_update(m_hashState, &clearPayload.mask, sizeof(clearPayload.mask))); @@ -180,18 +213,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { } const auto& att = fbo.GetAttachment(attachment); - if (att.IsTexture() && m_clearManager.HasPendingClear(att.GetTexture().get())) { + if (att.IsTexture() && m_clearManager.HasPendingClear(att)) { return true; } } const auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth); - if (depthAtt.IsTexture() && m_clearManager.HasPendingClear(depthAtt.GetTexture().get())) { + if (depthAtt.IsTexture() && m_clearManager.HasPendingClear(depthAtt)) { return true; } const auto& stencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil); - if (stencilAtt.IsTexture() && m_clearManager.HasPendingClear(stencilAtt.GetTexture().get())) { + if (stencilAtt.IsTexture() && m_clearManager.HasPendingClear(stencilAtt)) { return true; } @@ -266,7 +299,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { texture2d->GetFormat()); desc.samples = VK_SAMPLE_COUNT_1_BIT; ClearAttachmentPayload clearPayload{}; - Bool hasClear = m_clearManager.GetPendingClear(texture, clearPayload); + Bool hasClear = m_clearManager.GetPendingClear(att, clearPayload); VkImageLayout trackedColorLayout = VK_IMAGE_LAYOUT_UNDEFINED; desc.loadOp = hasClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : @@ -280,7 +313,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (hasClear) { pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { .attachmentIndex = attachmentIndex, - .texture = texture + .key = VkClearManager::MakePendingClearKey(att) }); } if (width == 0) @@ -313,7 +346,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { .textureMipLevel = attachmentMipLevel, .finalLayout = desc.finalLayout, }); - attachmentViews.emplace_back(m_textureManager.GetOrCreateViewAtMipLevel(*texture, attachmentMipLevel)); + const Uint32 baseArrayLayer = ResolveAttachmentBaseArrayLayer(att); + const Uint32 layerCount = ResolveAttachmentLayerCount(att); + const VkImageViewType attachmentViewType = ResolveAttachmentViewType(att, *textureResource); + attachmentViews.emplace_back( + m_textureManager.GetOrCreateAttachmentViewAtMipLevel( + *texture, attachmentMipLevel, baseArrayLayer, layerCount, attachmentViewType)); MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE, "GetOrCreateRenderPass: GetOrCreateAttachmentView failed at color attachment %d", i); } @@ -340,19 +378,35 @@ namespace MobileGL::MG_Backend::DirectVulkan { attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } - // Depth attachment description + // Depth/stencil attachment description auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth); + auto& stencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil); VkAttachmentDescription depthAttachmentDescription; VkAttachmentReference depthAttachmentRef; depthAttachmentRef.attachment = VK_ATTACHMENT_UNUSED; depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkTextureManager::TextureResource* depthTextureResource = nullptr; - if (depthAtt.IsComplete() && depthAtt.IsTexture()) { - auto& texture = *depthAtt.GetTexture(); - const Uint32 attachmentMipLevel = static_cast(std::max(depthAtt.GetTextureLevel(), 0)); + const auto isUsableDepthStencilAttachment = [](const auto& attachment) { + return attachment.IsComplete() && attachment.IsTexture(); + }; + const auto* selectedDepthStencilAttachment = isUsableDepthStencilAttachment(depthAtt) ? &depthAtt : + (isUsableDepthStencilAttachment(stencilAtt) ? &stencilAtt : nullptr); + const Bool hasDistinctDepthAndStencilAttachments = + isUsableDepthStencilAttachment(depthAtt) && isUsableDepthStencilAttachment(stencilAtt) && + (depthAtt.GetTexture().get() != stencilAtt.GetTexture().get() || + depthAtt.GetTextureUploadTarget() != stencilAtt.GetTextureUploadTarget() || + depthAtt.GetTextureLevel() != stencilAtt.GetTextureLevel()); + if (hasDistinctDepthAndStencilAttachments) { + MGLOG_E("GetOrCreateRenderPass: separate depth/stencil attachments are not supported yet; using the depth attachment and ignoring the standalone stencil attachment for framebuffer %u", + fbo.GetExternalIndex()); + } + if (selectedDepthStencilAttachment != nullptr) { + auto& texture = *selectedDepthStencilAttachment->GetTexture(); + const Uint32 attachmentMipLevel = + static_cast(std::max(selectedDepthStencilAttachment->GetTextureLevel(), 0)); const Uint32 depthAttachmentIndex = static_cast(attachmentDescriptions.size()); ClearAttachmentPayload clearPayload{}; - Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload); + Bool hasClear = m_clearManager.GetPendingClear(*selectedDepthStencilAttachment, clearPayload); Bool clearDepth = hasClear && (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0; Bool clearStencil = hasClear && (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0; VkImageLayout trackedDepthLayout = isDefaultFbo ? @@ -396,7 +450,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (hasClear) { pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { .attachmentIndex = depthAttachmentIndex, - .texture = &texture + .key = VkClearManager::MakePendingClearKey(*selectedDepthStencilAttachment) }); } if (isDefaultFbo) { @@ -417,12 +471,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { .finalLayout = depthAttachmentDescription.finalLayout, }); textureResources.emplace_back(depthTextureResource); - attachmentViews.emplace_back(m_textureManager.GetOrCreateViewAtMipLevel(texture, attachmentMipLevel)); + const Uint32 baseArrayLayer = ResolveAttachmentBaseArrayLayer(*selectedDepthStencilAttachment); + const Uint32 layerCount = ResolveAttachmentLayerCount(*selectedDepthStencilAttachment); + const VkImageViewType attachmentViewType = + ResolveAttachmentViewType(*selectedDepthStencilAttachment, *depthTextureResource); + attachmentViews.emplace_back( + m_textureManager.GetOrCreateAttachmentViewAtMipLevel( + texture, attachmentMipLevel, baseArrayLayer, layerCount, attachmentViewType)); MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE, "GetOrCreateRenderPass: GetOrCreateAttachmentView failed at depth attachment"); if (width == 0 || height == 0) { - width = depthAtt.GetSize().x(); - height = depthAtt.GetSize().y(); + width = selectedDepthStencilAttachment->GetSize().x(); + height = selectedDepthStencilAttachment->GetSize().y(); } } attachmentDescriptions.emplace_back(depthAttachmentDescription); @@ -439,7 +499,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { subpassDesc.colorAttachmentCount = colorAttachmentRefs.size(); subpassDesc.pColorAttachments = colorAttachmentRefs.data(); subpassDesc.pResolveAttachments = nullptr; - subpassDesc.pDepthStencilAttachment = depthAtt.IsComplete() ? &depthAttachmentRef : VK_NULL_HANDLE; + subpassDesc.pDepthStencilAttachment = hasDepthStencilAttachment ? &depthAttachmentRef : VK_NULL_HANDLE; subpassDesc.preserveAttachmentCount = 0; subpassDesc.pPreserveAttachments = nullptr; @@ -512,11 +572,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { clearValue.depthStencil = {1.0f, 0}; } for (const auto& pending: renderPassEntry.pendingClearAttachments) { - if (!pending.texture || pending.attachmentIndex >= clearValues.size()) { + if (pending.key.texture == nullptr || pending.attachmentIndex >= clearValues.size()) { continue; } ClearAttachmentPayload clearPayload{}; - if (!s_clearManager->GetPendingClear(pending.texture, clearPayload)) { + if (!s_clearManager->GetPendingClear(pending.key, clearPayload)) { continue; } if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) { @@ -524,7 +584,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { clearPayload.color.x(), clearPayload.color.y(), clearPayload.color.z(), - ResolveColorClearAlpha(pending.texture, clearPayload.color.w()) + ResolveColorClearAlpha(pending.key.texture, clearPayload.color.w()) }; } if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { @@ -540,7 +600,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); for (const auto& pending: renderPassEntry.pendingClearAttachments) { - s_clearManager->PopPendingClear(pending.texture); + s_clearManager->PopPendingClear(pending.key); } s_activeRenderPass.hash = renderPassEntry.hash; s_activeRenderPass.compatibilityHash = renderPassEntry.compatibilityHash; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h index 190bf60a..3f60782b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h @@ -26,7 +26,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { struct PendingClearAttachmentInfo { Uint32 attachmentIndex = 0; - MG_State::GLState::ITextureObject* texture = nullptr; + PendingClearKey key{}; }; struct TrackedAttachmentLayoutInfo { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index d48ba0fe..2d365326 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -71,6 +71,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } + static VkImageLayout ResolveSampledReadOnlyLayout(VkImageAspectFlags aspectMask) { + return (aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0 + ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL + : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + static void GetImageTransitionSourceState(VkImageLayout oldLayout, VkPipelineStageFlags& outSrcStageMask, VkAccessFlags& outSrcAccessMask) { @@ -594,7 +600,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } perMipView = CreateImageView(resource->image, resource->format, resource->aspect, resource->viewType, - mipLevel, 1, resource->arrayLayers); + mipLevel, 1, 0, resource->arrayLayers); if (perMipView == VK_NULL_HANDLE) { MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(), mipLevel); return VK_NULL_HANDLE; @@ -603,6 +609,53 @@ namespace MobileGL::MG_Backend::DirectVulkan { return perMipView; } + VkImageView VkTextureManager::GetOrCreateAttachmentViewAtMipLevel(MG_State::GLState::ITextureObject& texture, + Uint32 mipLevel, Uint32 baseArrayLayer, + Uint32 layerCount, + VkImageViewType viewType) { + TextureResource* resource = SyncTextureAndGetDescriptor(texture); + if (resource == nullptr || resource->image == VK_NULL_HANDLE || mipLevel >= resource->mipLevels) { + return VK_NULL_HANDLE; + } + if (layerCount == 0 || baseArrayLayer >= resource->arrayLayers || + baseArrayLayer + layerCount > resource->arrayLayers) { + MGLOG_D("%s: invalid layer span [%u, %u) for textureId=%d arrayLayers=%u", + __func__, baseArrayLayer, baseArrayLayer + layerCount, texture.GetExternalIndex(), + resource->arrayLayers); + return VK_NULL_HANDLE; + } + + if (baseArrayLayer == 0 && layerCount == resource->arrayLayers && viewType == resource->viewType) { + return GetOrCreateViewAtMipLevel(texture, mipLevel); + } + + const TextureResource::AttachmentViewKey key{ + .mipLevel = mipLevel, + .baseArrayLayer = baseArrayLayer, + .layerCount = layerCount, + .viewType = viewType, + }; + auto it = resource->attachmentViews.find(key); + if (it == resource->attachmentViews.end()) { + it = resource->attachmentViews.emplace(key, VK_NULL_HANDLE).first; + } + VkImageView& attachmentView = it->second; + if (attachmentView != VK_NULL_HANDLE) { + return attachmentView; + } + + attachmentView = CreateImageView(resource->image, resource->format, resource->aspect, viewType, + mipLevel, 1, baseArrayLayer, layerCount); + if (attachmentView == VK_NULL_HANDLE) { + MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u baseArrayLayer=%u layerCount=%u viewType=%d", + __func__, texture.GetExternalIndex(), mipLevel, baseArrayLayer, layerCount, static_cast(viewType)); + resource->attachmentViews.erase(it); + return VK_NULL_HANDLE; + } + + return attachmentView; + } + VkImageView VkTextureManager::GetOrCreateSampledViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel) { TextureResource* resource = SyncTextureAndGetDescriptor(texture); @@ -622,7 +675,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat()); const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo); perMipSampledView = CreateImageView(resource->image, resource->format, resource->aspect, resource->viewType, - mipLevel, 1, resource->arrayLayers, &sampledComponents); + mipLevel, 1, 0, resource->arrayLayers, &sampledComponents); if (perMipSampledView == VK_NULL_HANDLE) { MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(), mipLevel); @@ -1109,7 +1162,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat()); const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo); resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, resource.viewType, - baseMipLevel, levelCount, resource.arrayLayers, &sampledComponents); + baseMipLevel, levelCount, 0, resource.arrayLayers, &sampledComponents); if (resource.fullView == VK_NULL_HANDLE) { return false; } @@ -1122,6 +1175,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkImageView VkTextureManager::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect, VkImageViewType viewType, Uint32 baseMipLevel, Uint32 levelCount, + Uint32 baseArrayLayer, Uint32 layerCount, const VkComponentMapping* components) const { VkImageViewCreateInfo viewInfo{}; @@ -1136,7 +1190,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { viewInfo.subresourceRange.aspectMask = aspect; viewInfo.subresourceRange.baseMipLevel = baseMipLevel; viewInfo.subresourceRange.levelCount = levelCount; - viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.baseArrayLayer = baseArrayLayer; viewInfo.subresourceRange.layerCount = layerCount; VkImageView view = VK_NULL_HANDLE; @@ -1293,17 +1347,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { 1, ©); } + const VkImageLayout finalLayout = ResolveSampledReadOnlyLayout(aspectMask); VkImageLayout uploadLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; ok = TransitionImageLayout(commandBuffer, outResource.image, - uploadLayout, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - kGraphicsSampledReadStages, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - aspectMask, 0, outResource.mipLevels, outResource.arrayLayers); - MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL failed"); - outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + uploadLayout, + finalLayout, + VK_PIPELINE_STAGE_TRANSFER_BIT, + kGraphicsSampledReadStages, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + aspectMask, 0, outResource.mipLevels, outResource.arrayLayers); + MOBILEGL_ASSERT(ok, "TransitionImageLayout to sampled read-only layout failed"); + outResource.layout = finalLayout; VK_VERIFY(vkEndCommandBuffer(commandBuffer), "vkEndCommandBuffer(texture)"); @@ -1331,7 +1386,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { for (const auto& item : uploadItems) { mipmapTexture.MarkStorageDirty(item.target, item.level, false); } - outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + outResource.layout = finalLayout; return true; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index 64bd6d89..159ad852 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -30,11 +30,37 @@ public: }; struct TextureResource { + struct AttachmentViewKey { + Uint32 mipLevel = 0; + Uint32 baseArrayLayer = 0; + Uint32 layerCount = 1; + VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D; + + Bool operator==(const AttachmentViewKey& other) const { + return mipLevel == other.mipLevel && + baseArrayLayer == other.baseArrayLayer && + layerCount == other.layerCount && + viewType == other.viewType; + } + }; + + struct AttachmentViewKeyHash { + SizeT operator()(const AttachmentViewKey& key) const { + SizeT hash = std::hash{}(key.mipLevel); + hash ^= std::hash{}(key.baseArrayLayer) + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= std::hash{}(key.layerCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= std::hash{}(static_cast(key.viewType)) + + 0x9e3779b9u + (hash << 6) + (hash >> 2); + return hash; + } + }; + VkImage image = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; VkImageView fullView = VK_NULL_HANDLE; Vector perMipViews; Vector perMipSampledViews; + UnorderedMap attachmentViews; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; VkExtent2D extent = {0, 0}; Uint32 depth = 1; @@ -55,6 +81,7 @@ public: std::swap(this->fullView, that.fullView); std::swap(this->perMipViews, that.perMipViews); std::swap(this->perMipSampledViews, that.perMipSampledViews); + std::swap(this->attachmentViews, that.attachmentViews); std::swap(this->layout, that.layout); std::swap(this->extent, that.extent); std::swap(this->depth, that.depth); @@ -82,12 +109,18 @@ public: vkDestroyImageView(s_device, sampledView, nullptr); } } + for (const auto& [_, attachmentView] : attachmentViews) { + if (attachmentView != VK_NULL_HANDLE) { + vkDestroyImageView(s_device, attachmentView, nullptr); + } + } if (image != VK_NULL_HANDLE && allocation != nullptr) { vmaDestroyImage(s_allocator, image, allocation); } fullView = VK_NULL_HANDLE; perMipViews.clear(); perMipSampledViews.clear(); + attachmentViews.clear(); image = VK_NULL_HANDLE; allocation = nullptr; layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -118,6 +151,9 @@ public: TextureResource* SyncTextureAndGetDescriptor( MG_State::GLState::ITextureObject& texture); VkImageView GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel); + VkImageView GetOrCreateAttachmentViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel, + Uint32 baseArrayLayer, Uint32 layerCount, + VkImageViewType viewType); VkImageView GetOrCreateSampledViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel); void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout); void UpdateTrackedImageLayoutAfterAttachmentWrite(VkCommandBuffer commandBuffer, @@ -146,6 +182,7 @@ private: Bool SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource); VkImageView CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect, VkImageViewType viewType, Uint32 baseMipLevel, Uint32 levelCount, + Uint32 baseArrayLayer, Uint32 layerCount, const VkComponentMapping* components = nullptr) const; Bool UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 29455b7c..6d82349f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -90,6 +90,78 @@ namespace MobileGL::MG_Backend::DirectVulkan { return requestedAlpha; } + static void ApplyGLViewportState(VkCommandBuffer commandBuffer, const IntVec2& fallbackExtent) { + const IntVec4& viewportState = MG_State::pGLContext->GetViewport(); + const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRange(); + + VkViewport viewport{}; + viewport.x = static_cast(viewportState.x()); + viewport.y = static_cast(viewportState.y()); + viewport.width = + static_cast(viewportState.z() > 0 ? viewportState.z() : fallbackExtent.x()); + viewport.height = + static_cast(viewportState.w() > 0 ? viewportState.w() : fallbackExtent.y()); + viewport.minDepth = depthRange.x(); + viewport.maxDepth = depthRange.y(); + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + } + + static void ApplyBlendConstants(VkCommandBuffer commandBuffer) { + const FloatVec4& blendColor = MG_State::pGLContext->GetBlendColor(); + const float blendConstants[4] = { + blendColor.x(), + blendColor.y(), + blendColor.z(), + blendColor.w(), + }; + vkCmdSetBlendConstants(commandBuffer, blendConstants); + } + + static Bool DrawModeUsesPolygonFill(GLenum mode) { + switch (mode) { + case GL_TRIANGLES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + return true; + default: + return false; + } + } + + static void ApplyPolygonOffsetState(VkCommandBuffer commandBuffer) { + vkCmdSetDepthBias(commandBuffer, MG_State::pGLContext->GetPolygonOffsetUnits(), 0.0f, + MG_State::pGLContext->GetPolygonOffsetFactor()); + } + + static void ApplyLineWidthState(VkCommandBuffer commandBuffer) { + Float lineWidth = MG_State::pGLContext->GetLineWidth(); + if (MG_Backend::pActiveBackendObject != nullptr) { + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + const Float minLineWidth = dynamicParameters.AliasedLineWidthRangeMin; + const Float maxLineWidth = dynamicParameters.AliasedLineWidthRangeMax; + if (lineWidth < minLineWidth) { + lineWidth = minLineWidth; + } else if (lineWidth > maxLineWidth) { + lineWidth = maxLineWidth; + } + } + vkCmdSetLineWidth(commandBuffer, lineWidth); + } + + static void ApplyStencilState(VkCommandBuffer commandBuffer) { + const StencilFaceState& frontStencil = MG_State::pGLContext->GetStencilState(StencilFace::Front); + const StencilFaceState& backStencil = MG_State::pGLContext->GetStencilState(StencilFace::Back); + + vkCmdSetStencilCompareMask(commandBuffer, VK_STENCIL_FACE_FRONT_BIT, frontStencil.ValueMask); + vkCmdSetStencilCompareMask(commandBuffer, VK_STENCIL_FACE_BACK_BIT, backStencil.ValueMask); + vkCmdSetStencilWriteMask(commandBuffer, VK_STENCIL_FACE_FRONT_BIT, frontStencil.WriteMask); + vkCmdSetStencilWriteMask(commandBuffer, VK_STENCIL_FACE_BACK_BIT, backStencil.WriteMask); + vkCmdSetStencilReference(commandBuffer, VK_STENCIL_FACE_FRONT_BIT, + static_cast(std::max(frontStencil.Ref, 0))); + vkCmdSetStencilReference(commandBuffer, VK_STENCIL_FACE_BACK_BIT, + static_cast(std::max(backStencil.Ref, 0))); + } + enum class NumericDomain { Unknown, FloatLike, @@ -632,6 +704,51 @@ namespace MobileGL::MG_Backend::DirectVulkan { MG_State::pGLContext->RecordError(code, MakeUnique("DirectVulkan", func, message)); } + static Bool HasDistinctCompleteDepthStencilTextureAttachments( + const MG_State::GLState::FramebufferObject& framebufferObject) { + if (framebufferObject.GetExternalIndex() == 0) { + return false; + } + + const auto& depthAttachment = framebufferObject.GetAttachment(FramebufferAttachmentType::Depth); + const auto& stencilAttachment = framebufferObject.GetAttachment(FramebufferAttachmentType::Stencil); + if (!depthAttachment.IsComplete() || !stencilAttachment.IsComplete() || + !depthAttachment.IsTexture() || !stencilAttachment.IsTexture()) { + return false; + } + + return depthAttachment.GetTexture().get() != stencilAttachment.GetTexture().get() || + depthAttachment.GetTextureUploadTarget() != stencilAttachment.GetTextureUploadTarget() || + depthAttachment.GetTextureLevel() != stencilAttachment.GetTextureLevel(); + } + + static Bool HasCompleteRenderbufferAttachment(const MG_State::GLState::FramebufferObject& framebufferObject) { + if (framebufferObject.GetExternalIndex() == 0) { + return false; + } + + for (const auto& attachment : framebufferObject.GetAllAttachmentObjects()) { + if (attachment.IsRenderbuffer() && attachment.IsComplete()) { + return true; + } + } + return false; + } + + static Bool IsUnsupportedFramebufferForDirectVulkan( + const MG_State::GLState::FramebufferObject& framebufferObject) { + return HasDistinctCompleteDepthStencilTextureAttachments(framebufferObject) || + HasCompleteRenderbufferAttachment(framebufferObject); + } + + static void RecordUnsupportedFramebufferError(const char* func) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidFramebufferOperation, + MakeUnique( + "DirectVulkan", func, + "DirectVulkan does not support this non-default framebuffer configuration.")); + } + static Bool IsValidSampledImageLayout(VkImageLayout layout) { switch (layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: @@ -778,6 +895,19 @@ void main() { : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } + static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) { + return target >= TextureUploadTarget::CubeMapPositiveX && + target <= TextureUploadTarget::CubeMapNegativeZ; + } + + static Uint32 ResolveAttachmentBaseArrayLayer(const MG_State::GLState::FramebufferAttachmentObject& attachment) { + const TextureUploadTarget uploadTarget = attachment.GetTextureUploadTarget(); + if (!IsCubeMapFaceUploadTarget(uploadTarget)) { + return 0; + } + return static_cast(uploadTarget) - static_cast(TextureUploadTarget::CubeMapPositiveX); + } + enum class BlitSurfaceTransform : Uint32 { Identity = 0, Rotate90 = 1, @@ -792,6 +922,8 @@ void main() { IntVec2 extent = {0, 0}; Uint32 mipLevel = 0; Uint32 mipLevelCount = 1; + Uint32 baseArrayLayer = 0; + Uint32 layerCount = 1; const char* label = nullptr; }; @@ -968,6 +1100,8 @@ void main() { outBinding.extent = {static_cast(extent.width), static_cast(extent.height)}; outBinding.mipLevel = 0; outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; return true; } @@ -990,6 +1124,8 @@ void main() { outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()}; outBinding.mipLevel = static_cast(std::max(attachment.GetTextureLevel(), 0)); outBinding.mipLevelCount = resource->mipLevels; + outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment); + outBinding.layerCount = 1; return true; } @@ -1013,6 +1149,8 @@ void main() { outBinding.extent = {static_cast(extent.width), static_cast(extent.height)}; outBinding.mipLevel = 0; outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; outBinding.trackedLayout = nullptr; if ((requiredAspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { outBinding.image = swapchainObject.GetImage(swapchainImageIndex); @@ -1067,6 +1205,8 @@ void main() { outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()}; outBinding.mipLevel = static_cast(std::max(attachment.GetTextureLevel(), 0)); outBinding.mipLevelCount = resource->mipLevels; + outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment); + outBinding.layerCount = 1; return true; } @@ -1099,6 +1239,8 @@ void main() { static_cast(std::max(1u, resource->extent.height >> mipLevel))}; outBinding.mipLevel = mipLevel; outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; outBinding.label = "destination texture"; return true; } @@ -1123,6 +1265,8 @@ void main() { outBinding.extent = {static_cast(extent.width), static_cast(extent.height)}; outBinding.mipLevel = 0; outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; outBinding.trackedLayout = nullptr; if ((requiredAspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { outBinding.image = swapchainObject.GetImage(swapchainImageIndex); @@ -1179,6 +1323,8 @@ void main() { outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()}; outBinding.mipLevel = static_cast(std::max(attachment.GetTextureLevel(), 0)); outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment); + outBinding.layerCount = 1; return true; } @@ -2166,14 +2312,7 @@ void main() { vkCmdBeginRenderPass(frame.commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(dstTexelSize.x()); - viewport.height = static_cast(dstTexelSize.y()); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(frame.commandBuffer, 0, 1, &viewport); + ApplyGLViewportState(frame.commandBuffer, dstTexelSize.xy()); VkRect2D scissor{}; scissor.offset = {0, 0}; @@ -2345,6 +2484,16 @@ void main() { } auto cullFaceEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace); auto depthTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest); + auto polygonOffsetFillEnabled = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PolygonOffsetFill) && + DrawModeUsesPolygonFill(mode); + auto rasterizerDiscardEnabled = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard); + auto colorLogicOpEnabled = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ColorLogicOp) && m_logicOpFeatureEnabled; + auto stencilTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest); + const StencilFaceState& frontStencil = MG_State::pGLContext->GetStencilState(StencilFace::Front); + const StencilFaceState& backStencil = MG_State::pGLContext->GetStencilState(StencilFace::Back); auto mask = MG_State::pGLContext->GetColorMask(); const auto colorWriteMask = static_cast( (mask.r() ? VK_COLOR_COMPONENT_R_BIT : 0u) | @@ -2366,19 +2515,52 @@ void main() { .frontFace = VK_FRONT_FACE_CLOCKWISE, .depthTestEnable = depthTestEnabled, .depthWriteEnable = depthTestEnabled && MG_State::pGLContext->GetDepthMask(), + .depthBiasEnable = polygonOffsetFillEnabled, + .rasterizerDiscardEnable = rasterizerDiscardEnabled, + .logicOpEnable = colorLogicOpEnabled, + .stencilTestEnable = stencilTestEnabled, .depthCompareOp = MG_Util::ConvertDepthTestFuncToVkEnum(MG_State::pGLContext->GetDepthFunc()), + .logicOp = MG_Util::ConvertLogicOperationToVkEnum(MG_State::pGLContext->GetLogicOp()), + .frontStencilFailOp = MG_Util::ConvertStencilOperationToVkEnum(frontStencil.FailOp), + .frontStencilPassOp = MG_Util::ConvertStencilOperationToVkEnum(frontStencil.PassDepthPassOp), + .frontStencilDepthFailOp = MG_Util::ConvertStencilOperationToVkEnum(frontStencil.PassDepthFailOp), + .frontStencilCompareOp = MG_Util::ConvertDepthTestFuncToVkEnum(frontStencil.Func), + .backStencilFailOp = MG_Util::ConvertStencilOperationToVkEnum(backStencil.FailOp), + .backStencilPassOp = MG_Util::ConvertStencilOperationToVkEnum(backStencil.PassDepthPassOp), + .backStencilDepthFailOp = MG_Util::ConvertStencilOperationToVkEnum(backStencil.PassDepthFailOp), + .backStencilCompareOp = MG_Util::ConvertDepthTestFuncToVkEnum(backStencil.Func), .stages = &programObj.stages, .vertexInputState = pipelineVertexInputState }; + if (!payload.stencilTestEnable) { + payload.frontStencilFailOp = VK_STENCIL_OP_KEEP; + payload.frontStencilPassOp = VK_STENCIL_OP_KEEP; + payload.frontStencilDepthFailOp = VK_STENCIL_OP_KEEP; + payload.frontStencilCompareOp = VK_COMPARE_OP_ALWAYS; + payload.backStencilFailOp = VK_STENCIL_OP_KEEP; + payload.backStencilPassOp = VK_STENCIL_OP_KEEP; + payload.backStencilDepthFailOp = VK_STENCIL_OP_KEEP; + payload.backStencilCompareOp = VK_COMPARE_OP_ALWAYS; + } const Bool hasDepthStencilAttachment = renderPassEntry.hasDepthStencilAttachment; - if (!hasDepthStencilAttachment && (payload.depthTestEnable || payload.depthWriteEnable)) { - MGLOG_D("GetOrCreatePipeline: disabling depth test/write for program=%u because render pass has no depth attachment (attachmentCount=%u colorAttachmentCount=%u)", + if (!hasDepthStencilAttachment && + (payload.depthTestEnable || payload.depthWriteEnable || payload.stencilTestEnable)) { + MGLOG_D("GetOrCreatePipeline: disabling depth/stencil tests for program=%u because render pass has no depth attachment (attachmentCount=%u colorAttachmentCount=%u)", program.GetExternalIndex(), renderPassEntry.attachmentCount, renderPassEntry.colorAttachmentCount); payload.depthTestEnable = false; payload.depthWriteEnable = false; + payload.stencilTestEnable = false; payload.depthCompareOp = VK_COMPARE_OP_ALWAYS; + payload.frontStencilFailOp = VK_STENCIL_OP_KEEP; + payload.frontStencilPassOp = VK_STENCIL_OP_KEEP; + payload.frontStencilDepthFailOp = VK_STENCIL_OP_KEEP; + payload.frontStencilCompareOp = VK_COMPARE_OP_ALWAYS; + payload.backStencilFailOp = VK_STENCIL_OP_KEEP; + payload.backStencilPassOp = VK_STENCIL_OP_KEEP; + payload.backStencilDepthFailOp = VK_STENCIL_OP_KEEP; + payload.backStencilCompareOp = VK_COMPARE_OP_ALWAYS; } const Uint32 fragmentOutputMask = programObj.activeFragmentOutputLocationMask; MOBILEGL_ASSERT( @@ -2555,6 +2737,10 @@ void main() { m_textureManager->CollectGarbage(); const auto& drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + if (drawFbo != nullptr && IsUnsupportedFramebufferForDirectVulkan(*drawFbo)) { + RecordUnsupportedFramebufferError(__func__); + return false; + } const auto& vao = *MG_State::pGLContext->GetBoundVertexArray(); const auto& program = *MG_State::pGLContext->GetCurrentProgram(); ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); @@ -2686,14 +2872,11 @@ void main() { MOBILEGL_ASSERT(idxUploadOk, "SetupDraw skipped: failed to upload index buffer"); } - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(renderPassEntry->extent.x()); - viewport.height = static_cast(renderPassEntry->extent.y()); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(frame.commandBuffer, 0, 1, &viewport); + ApplyGLViewportState(frame.commandBuffer, renderPassEntry->extent); + ApplyBlendConstants(frame.commandBuffer); + ApplyPolygonOffsetState(frame.commandBuffer); + ApplyLineWidthState(frame.commandBuffer); + ApplyStencilState(frame.commandBuffer); Bool scissorEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest); VkRect2D scissor{}; @@ -2780,6 +2963,10 @@ void main() { m_clearManager->CollectGarbage(); auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get(); MOBILEGL_ASSERT(fbo, "VulkanRenderer::Clear: draw framebuffer not found (fbo == nullptr)"); + if (IsUnsupportedFramebufferForDirectVulkan(*fbo)) { + RecordUnsupportedFramebufferError(__func__); + return; + } ClearFramebufferPayload payload { .color = MG_State::pGLContext->GetClearColor(), @@ -2794,6 +2981,10 @@ void main() { m_clearManager->CollectGarbage(); auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get(); MOBILEGL_ASSERT(fbo, "VulkanRenderer::QueueClearBufferPayload: draw framebuffer not found"); + if (IsUnsupportedFramebufferForDirectVulkan(*fbo)) { + RecordUnsupportedFramebufferError(__func__); + return; + } auto queueAttachmentClear = [&](FramebufferAttachmentType attachmentType) { if (attachmentType == FramebufferAttachmentType::None) { @@ -2803,11 +2994,7 @@ void main() { if (!attachment.IsTexture() || attachment.IsRenderbuffer()) { return; } - auto texture = attachment.GetTexture(); - if (!texture) { - return; - } - m_clearManager->QueueClear(clearPayload, texture); + m_clearManager->QueueClear(clearPayload, attachment); }; switch (buffer) { @@ -2920,8 +3107,8 @@ void main() { Bool VulkanRenderer::MaterializePendingClearForTexture(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture) { - ClearAttachmentPayload clearPayload{}; - if (!m_clearManager->GetPendingClear(&texture, clearPayload)) { + Vector pendingClears; + if (!m_clearManager->GetPendingClears(&texture, pendingClears)) { return true; } MOBILEGL_ASSERT(VkRenderPassManager::GetActiveRenderPass() == nullptr, @@ -2940,53 +3127,65 @@ void main() { Bool ok = VkTextureManager::TransitionImageLayout( commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, - resource->aspect, 0, resource->mipLevels); + resource->aspect, 0, resource->mipLevels, resource->arrayLayers); MOBILEGL_ASSERT(ok, "MaterializePendingClearForTexture: failed to transition textureId=%d to TRANSFER_DST", texture.GetExternalIndex()); - VkImageSubresourceRange subresourceRange{}; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = 1; - subresourceRange.baseArrayLayer = 0; - subresourceRange.layerCount = 1; - VkImageLayout sampledLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - VkClearColorValue clearValue{}; - clearValue.float32[0] = clearPayload.color.x(); - clearValue.float32[1] = clearPayload.color.y(); - clearValue.float32[2] = clearPayload.color.z(); - clearValue.float32[3] = ResolveColorClearAlpha(&texture, clearPayload.color.w()); - vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &clearValue, 1, &subresourceRange); - } else { - VkImageAspectFlags clearAspectMask = 0; - if ((resource->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 && - (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { - clearAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + for (const auto& pendingClear : pendingClears) { + MOBILEGL_ASSERT(pendingClear.key.mipLevel < resource->mipLevels, + "MaterializePendingClearForTexture: textureId=%d pending clear mip=%u out of range %u", + texture.GetExternalIndex(), pendingClear.key.mipLevel, resource->mipLevels); + MOBILEGL_ASSERT(pendingClear.key.baseArrayLayer + pendingClear.key.layerCount <= resource->arrayLayers, + "MaterializePendingClearForTexture: textureId=%d pending clear layer span [%u, %u) exceeds arrayLayers=%u", + texture.GetExternalIndex(), pendingClear.key.baseArrayLayer, + pendingClear.key.baseArrayLayer + pendingClear.key.layerCount, resource->arrayLayers); + + VkImageSubresourceRange subresourceRange{}; + subresourceRange.baseMipLevel = pendingClear.key.mipLevel; + subresourceRange.levelCount = 1; + subresourceRange.baseArrayLayer = pendingClear.key.baseArrayLayer; + subresourceRange.layerCount = pendingClear.key.layerCount; + + const auto& clearPayload = pendingClear.payload; + if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + VkClearColorValue clearValue{}; + clearValue.float32[0] = clearPayload.color.x(); + clearValue.float32[1] = clearPayload.color.y(); + clearValue.float32[2] = clearPayload.color.z(); + clearValue.float32[3] = ResolveColorClearAlpha(&texture, clearPayload.color.w()); + vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &clearValue, 1, &subresourceRange); + } else { + VkImageAspectFlags clearAspectMask = 0; + if ((resource->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 && + (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { + clearAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if ((resource->aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) { + clearAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + MOBILEGL_ASSERT(clearAspectMask != 0, + "MaterializePendingClearForTexture: textureId=%d has no matching depth/stencil clear mask", + texture.GetExternalIndex()); + subresourceRange.aspectMask = clearAspectMask; + VkClearDepthStencilValue clearValue{}; + clearValue.depth = clearPayload.depth; + clearValue.stencil = clearPayload.stencil; + vkCmdClearDepthStencilImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &clearValue, 1, &subresourceRange); + sampledLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; } - if ((resource->aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && - (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) { - clearAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - MOBILEGL_ASSERT(clearAspectMask != 0, - "MaterializePendingClearForTexture: textureId=%d has no matching depth/stencil clear mask", - texture.GetExternalIndex()); - subresourceRange.aspectMask = clearAspectMask; - VkClearDepthStencilValue clearValue{}; - clearValue.depth = clearPayload.depth; - clearValue.stencil = clearPayload.stencil; - vkCmdClearDepthStencilImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &clearValue, 1, &subresourceRange); - sampledLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; } ok = VkTextureManager::TransitionImageLayout( commandBuffer, resource->image, clearLayout, sampledLayout, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels); + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels, + resource->arrayLayers); MOBILEGL_ASSERT(ok, "MaterializePendingClearForTexture: failed to transition textureId=%d to sampled layout", texture.GetExternalIndex()); @@ -3054,14 +3253,7 @@ void main() { const Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry); MOBILEGL_ASSERT(ok, "%s: BeginRenderPass failed", __func__); - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(renderPassEntry.extent.x()); - viewport.height = static_cast(renderPassEntry.extent.y()); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(frame.commandBuffer, 0, 1, &viewport); + ApplyGLViewportState(frame.commandBuffer, renderPassEntry.extent); VkRect2D scissor{}; scissor.offset = {0, 0}; @@ -3163,6 +3355,11 @@ void main() { auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); MOBILEGL_ASSERT(readFbo != nullptr, "VulkanRenderer::BlitFramebuffer: read framebuffer is null"); MOBILEGL_ASSERT(drawFbo != nullptr, "VulkanRenderer::BlitFramebuffer: draw framebuffer is null"); + if (IsUnsupportedFramebufferForDirectVulkan(*readFbo) || + IsUnsupportedFramebufferForDirectVulkan(*drawFbo)) { + RecordUnsupportedFramebufferError(__func__); + return; + } auto& frame = m_frameContext.GetCurrent(); if (!frame.isCommandRecording) { @@ -3285,13 +3482,13 @@ void main() { VkImageCopy copyRegion{}; copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask; copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel; - copyRegion.srcSubresource.baseArrayLayer = 0; - copyRegion.srcSubresource.layerCount = 1; + copyRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer; + copyRegion.srcSubresource.layerCount = srcBinding.layerCount; copyRegion.srcOffset = {srcX0, srcY0, 0}; copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask; copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel; - copyRegion.dstSubresource.baseArrayLayer = 0; - copyRegion.dstSubresource.layerCount = 1; + copyRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer; + copyRegion.dstSubresource.layerCount = dstBinding.layerCount; copyRegion.dstOffset = {dstX0, dstY0, 0}; copyRegion.extent = {static_cast(srcWidth), static_cast(srcHeight), 1}; @@ -3421,14 +3618,14 @@ void main() { VkImageBlit blitRegion{}; blitRegion.srcSubresource.aspectMask = srcBinding.aspectMask; blitRegion.srcSubresource.mipLevel = srcBinding.mipLevel; - blitRegion.srcSubresource.baseArrayLayer = 0; - blitRegion.srcSubresource.layerCount = 1; + blitRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer; + blitRegion.srcSubresource.layerCount = srcBinding.layerCount; blitRegion.srcOffsets[0] = {srcX0, srcY0, 0}; blitRegion.srcOffsets[1] = {srcX1, srcY1, 1}; blitRegion.dstSubresource.aspectMask = dstBinding.aspectMask; blitRegion.dstSubresource.mipLevel = dstBinding.mipLevel; - blitRegion.dstSubresource.baseArrayLayer = 0; - blitRegion.dstSubresource.layerCount = 1; + blitRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer; + blitRegion.dstSubresource.layerCount = dstBinding.layerCount; blitRegion.dstOffsets[0] = {dstX0, dstY0, 0}; blitRegion.dstOffsets[1] = {dstX1, dstY1, 1}; if (drawIsDefaultFbo) { @@ -3503,6 +3700,11 @@ void main() { "CopyTexSubImage2D requires a framebuffer bound to GL_READ_FRAMEBUFFER."); return; } + if (IsUnsupportedFramebufferForDirectVulkan(*readFbo)) { + RecordTextureCopyError(__func__, ErrorCode::InvalidFramebufferOperation, + "CopyTexSubImage2D does not support the current non-default read framebuffer configuration on DirectVulkan."); + return; + } auto& frame = m_frameContext.GetCurrent(); if (!frame.isCommandRecording) { @@ -3601,13 +3803,13 @@ void main() { VkImageCopy copyRegion{}; copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask; copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel; - copyRegion.srcSubresource.baseArrayLayer = 0; - copyRegion.srcSubresource.layerCount = 1; + copyRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer; + copyRegion.srcSubresource.layerCount = srcBinding.layerCount; copyRegion.srcOffset = {x, y, 0}; copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask; copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel; - copyRegion.dstSubresource.baseArrayLayer = 0; - copyRegion.dstSubresource.layerCount = 1; + copyRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer; + copyRegion.dstSubresource.layerCount = dstBinding.layerCount; copyRegion.dstOffset = {xoffset, yoffset, 0}; copyRegion.extent = {static_cast(width), static_cast(height), 1}; vkCmdCopyImage(frame.commandBuffer, @@ -4064,7 +4266,8 @@ void main() { if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result); RecreateSwapchain(); - result = VK_SUCCESS; + result = + m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired); } VK_VERIFY(result, "Present, vkAcquireNextImageKHR"); CollectDeferredDepthMipmapCleanup(m_frameContext.GetCurrentFrameIndex()); @@ -4350,8 +4553,11 @@ void main() { VkPhysicalDeviceFeatures deviceFeatures{}; deviceFeatures.geometryShader = supportedDeviceFeatures.geometryShader; deviceFeatures.independentBlend = supportedDeviceFeatures.independentBlend; + deviceFeatures.logicOp = supportedDeviceFeatures.logicOp; deviceFeatures.shaderClipDistance = supportedDeviceFeatures.shaderClipDistance; deviceFeatures.shaderCullDistance = supportedDeviceFeatures.shaderCullDistance; + deviceFeatures.wideLines = supportedDeviceFeatures.wideLines; + m_logicOpFeatureEnabled = deviceFeatures.logicOp == VK_TRUE; VkDeviceCreateInfo deviceCreateInfo{}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -4416,16 +4622,20 @@ void main() { deviceCreateInfo.enabledExtensionCount = static_cast(enabledDeviceExtensions.size()); deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data(); - MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s", + MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s logicOp=%s shaderClipDistance=%s shaderCullDistance=%s wideLines=%s", supportedDeviceFeatures.geometryShader ? "true" : "false", supportedDeviceFeatures.independentBlend ? "true" : "false", + supportedDeviceFeatures.logicOp ? "true" : "false", supportedDeviceFeatures.shaderClipDistance ? "true" : "false", - supportedDeviceFeatures.shaderCullDistance ? "true" : "false"); - MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s", + supportedDeviceFeatures.shaderCullDistance ? "true" : "false", + supportedDeviceFeatures.wideLines ? "true" : "false"); + MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s logicOp=%s shaderClipDistance=%s shaderCullDistance=%s wideLines=%s", deviceFeatures.geometryShader ? "true" : "false", deviceFeatures.independentBlend ? "true" : "false", + deviceFeatures.logicOp ? "true" : "false", deviceFeatures.shaderClipDistance ? "true" : "false", - deviceFeatures.shaderCullDistance ? "true" : "false"); + deviceFeatures.shaderCullDistance ? "true" : "false", + deviceFeatures.wideLines ? "true" : "false"); VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice"); s_vkCmdDrawIndexedIndirectCount = reinterpret_cast( @@ -4726,12 +4936,12 @@ void main() { clearRect.layerCount = 1; for (const auto& pending : compatibleRenderPassEntry.pendingClearAttachments) { - if (!pending.texture) { + if (pending.key.texture == nullptr) { continue; } ClearAttachmentPayload clearPayload{}; - if (!m_clearManager->GetPendingClear(pending.texture, clearPayload)) { + if (!m_clearManager->GetPendingClear(pending.key, clearPayload)) { continue; } @@ -4744,7 +4954,7 @@ void main() { clearPayload.color.x(), clearPayload.color.y(), clearPayload.color.z(), - ResolveColorClearAlpha(pending.texture, clearPayload.color.w()) + ResolveColorClearAlpha(pending.key.texture, clearPayload.color.w()) }; } else { if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { @@ -4761,7 +4971,7 @@ void main() { } vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect); - m_clearManager->PopPendingClear(pending.texture); + m_clearManager->PopPendingClear(pending.key); } } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 39627a3b..19fa62ae 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -195,6 +195,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkQueue m_presentQueue = VK_NULL_HANDLE; Bool m_drawIndirectCountExtensionEnabled = false; Bool m_indexTypeUint8ExtensionEnabled = false; + Bool m_logicOpFeatureEnabled = false; using PFNDrawIndexedIndirectCountFunc = void(VKAPI_PTR*)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, Uint32 maxDrawCount, diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index 4f689eda..d1a12543 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -258,6 +258,9 @@ namespace MobileGL::MG_Impl::EGLImpl { if (!state) { return EGL_FALSE; } + if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) { + (void)backendObject->MakeEGLCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } state->ReleaseThread(); return EGL_TRUE; } diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp index 7f4fd073..8f439114 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -80,6 +80,93 @@ namespace MobileGL::MG_Impl::GLImpl { } } + void GetBufferPointerv_State(GLenum target, GLenum pname, void** params) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", "GetBufferPointerv_State", + "Params pointer cannot be null.")); + return; + } + + BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); + if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return; + + auto& bindingSlot = GetBufferBindingSlot(bufferTarget); + auto& bufferObject = bindingSlot.GetBoundObject(); + if (!bufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "GetBufferPointerv_State", + "Buffer target is bound to no buffer object.")); + return; + } + + if (pname != GL_BUFFER_MAP_POINTER) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "GetBufferPointerv_State", + std::format("Invalid pname enum: 0x{:X}", pname))); + return; + } + + *params = bufferObject->GetMappedPointer(); + } + + void GetBufferParameteri64v_State(GLenum target, GLenum pname, GLint64* params) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", "GetBufferParameteri64v_State", + "Params pointer cannot be null.")); + return; + } + + BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); + if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return; + + auto& bindingSlot = GetBufferBindingSlot(bufferTarget); + auto& bufferObject = bindingSlot.GetBoundObject(); + if (!bufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "GetBufferParameteri64v_State", + "Buffer target is bound to no buffer object.")); + return; + } + + switch (pname) { + case GL_BUFFER_SIZE: + *params = static_cast(bufferObject->GetSize()); + break; + case GL_BUFFER_USAGE: + *params = static_cast(MG_Util::ConvertBufferUsageToGLEnum(bufferObject->GetUsage())); + break; + case GL_BUFFER_ACCESS: + if (bufferObject->IsMapped()) { + auto access = bufferObject->GetMappingAccess(); + if (access & BufferMappingAccessBit::Read && access & BufferMappingAccessBit::Write) { + *params = GL_READ_WRITE; + } else if (access & BufferMappingAccessBit::Read) { + *params = GL_READ_ONLY; + } else if (access & BufferMappingAccessBit::Write) { + *params = GL_WRITE_ONLY; + } else { + *params = 0; + } + } else { + *params = 0; + } + break; + case GL_BUFFER_MAPPED: + *params = bufferObject->IsMapped() ? GL_TRUE : GL_FALSE; + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", "GetBufferParameteri64v_State", + std::format("Invalid pname enum: 0x{:X}", pname))); + break; + } + } + void DeleteBuffers_State(GLsizei n, const GLuint* buffers) { if (n < 0) { MG_State::pGLContext->RecordError( @@ -524,16 +611,23 @@ namespace MobileGL::MG_Impl::GLImpl { BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; - Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer); - if (!doesBufferObjectCreated) { - MG_State::pGLContext->CreateBufferObject(buffer); + SharedPtr bufferObject; + if (buffer != 0) { + Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer); + if (!doesBufferObjectCreated) { + MG_State::pGLContext->CreateBufferObject(buffer); + } + bufferObject = MG_State::pGLContext->GetBufferObject(buffer); } - auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, pointIndex); point.Bind(bufferObject); - point.SetRange(Range1D(0, bufferObject->GetSize())); - MGLOG_D("%s: set range (0, %d)", __func__, bufferObject->GetSize()); + if (bufferObject) { + point.SetRange(Range1D(0, bufferObject->GetSize())); + MGLOG_D("%s: set range (0, %d)", __func__, bufferObject->GetSize()); + } else { + point.ClearRange(); + } } void BindBufferRange_State(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { @@ -542,15 +636,22 @@ namespace MobileGL::MG_Impl::GLImpl { BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; - Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer); - if (!doesBufferObjectCreated) { - MG_State::pGLContext->CreateBufferObject(buffer); + SharedPtr bufferObject; + if (buffer != 0) { + Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer); + if (!doesBufferObjectCreated) { + MG_State::pGLContext->CreateBufferObject(buffer); + } + bufferObject = MG_State::pGLContext->GetBufferObject(buffer); } - auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); point.Bind(bufferObject); - point.SetRange(Range1D(offset, offset + size)); + if (bufferObject) { + point.SetRange(Range1D(offset, offset + size)); + } else { + point.ClearRange(); + } } /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ @@ -558,6 +659,14 @@ namespace MobileGL::MG_Impl::GLImpl { GetBufferParameteriv_State(target, pname, params); } + void GetBufferPointerv(GLenum target, GLenum pname, void** params) { + GetBufferPointerv_State(target, pname, params); + } + + void GetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params) { + GetBufferParameteri64v_State(target, pname, params); + } + GLboolean IsBuffer(GLuint buffer) { return IsBuffer_State(buffer); } diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h index c24f9eea..e7618ae3 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h @@ -12,6 +12,8 @@ namespace MobileGL::MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params); + void GetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params); + void GetBufferPointerv(GLenum target, GLenum pname, void** params); GLboolean IsBuffer(GLuint buffer); void DeleteBuffers(GLsizei n, const GLuint* buffers); void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length); diff --git a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp index 155a8e03..cbdaf69f 100644 --- a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp +++ b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp @@ -12,6 +12,62 @@ #include namespace MobileGL::MG_Impl::GLImpl { + static Bool ValidateCurrentProgramForExecution(const char* functionName) { + const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram(); + if (!currentProgram) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, "There is no current program object.")); + return false; + } + + if (!currentProgram->GetLinkStatus()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, + "The current program object is not linked.")); + return false; + } + + return true; + } + + static Bool ValidateCurrentProgramForCompute(const char* functionName) { + if (!ValidateCurrentProgramForExecution(functionName)) return false; + + const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram(); + if (currentProgram->GetShaderIndexByStage(ShaderStage::Compute) < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, + "The current program object has no compute shader stage.")); + return false; + } + + return true; + } + + static Bool ValidatePrimitiveModeForBackend(const char* functionName, GLenum mode) { + const auto& activeBackendObject = MG_Backend::pActiveBackendObject; + if (!activeBackendObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, "No active backend object.")); + return false; + } + + if (activeBackendObject->GetBackendType() == BackendType::DirectVulkan && mode == GL_LINE_LOOP) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", functionName, + "Primitive mode GL_LINE_LOOP is not supported by the DirectVulkan backend.")); + return false; + } + + return true; + } + void Clear_Backend(GLbitfield mask) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -164,6 +220,7 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", __func__, "Backend does not support compute dispatch.")); return; } + if (!ValidateCurrentProgramForCompute(__func__)) return; dispatchCompute(numGroupsX, numGroupsY, numGroupsZ); } @@ -176,6 +233,7 @@ namespace MobileGL::MG_Impl::GLImpl { "Backend does not support indirect compute dispatch.")); return; } + if (!ValidateCurrentProgramForCompute(__func__)) return; dispatchComputeIndirect(indirect); } @@ -203,74 +261,106 @@ namespace MobileGL::MG_Impl::GLImpl { } void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; MultiDrawElementsIndirect_Backend(mode, type, indirect, drawcount, stride); } void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; MultiDrawArraysIndirect_Backend(mode, indirect, drawcount, stride); } void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawRangeElementsBaseVertex_Backend(mode, start, end, count, type, indices, basevertex); } void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawRangeElements_Backend(mode, start, end, count, type, indices); } void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElementsInstancedBaseVertexBaseInstance_Backend(mode, count, type, indices, instancecount, basevertex, baseinstance); } void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElementsInstancedBaseVertex_Backend(mode, count, type, indices, instancecount, basevertex); } void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLuint baseinstance) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElementsInstancedBaseInstance_Backend(mode, count, type, indices, instancecount, baseinstance); } void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElementsInstanced_Backend(mode, count, type, indices, instancecount); } void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElementsIndirect_Backend(mode, type, indirect); } void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawArraysInstancedBaseInstance_Backend(mode, first, count, instancecount, baseinstance); } void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawArraysInstanced_Backend(mode, first, count, instancecount); } void DrawArraysIndirect(GLenum mode, const void* indirect) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawArraysIndirect_Backend(mode, indirect); } void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElementsBaseVertex_Backend(mode, count, type, indices, basevertex); } void DrawArrays(GLenum mode, GLint first, GLsizei count) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawArrays_Backend(mode, first, count); } void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; MultiDrawElements_Backend(mode, count, type, indices, drawcount); } void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; MultiDrawElementsBaseVertex_Backend(mode, count, type, indices, drawcount, basevertex); } @@ -279,6 +369,8 @@ namespace MobileGL::MG_Impl::GLImpl { } void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { + if (!ValidateCurrentProgramForExecution(__func__)) return; + if (!ValidatePrimitiveModeForBackend(__func__, mode)) return; DrawElements_Backend(mode, count, type, indices); } diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 76a896a6..0f7584da 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -16,6 +16,7 @@ #include "../RenderState/GL_RenderState.h" #include "../Framebuffer/GL_Framebuffer.h" #include "../VertexArray/GL_VertexArray.h" +#include "../Sync/GL_Sync.h" #define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) { @@ -75,7 +76,7 @@ DECLARE_GL_FUNCTION_HEAD(void, BufferSubData, GLenum target, GLintptr offset, GL DECLARE_GL_FUNCTION_HEAD(GLenum, CheckFramebufferStatus, GLenum target) DECLARE_GL_FUNCTION_END(GLenum, CheckFramebufferStatus, target) DECLARE_GL_FUNCTION_HEAD(void, Clear, GLbitfield mask) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Clear, mask) DECLARE_GL_FUNCTION_HEAD(void, ClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearColor, red, green, blue, alpha) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearDepthf, GLfloat d) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearDepthf, d) +DECLARE_GL_FUNCTION_HEAD(void, ClearDepthf, GLfloat d) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearDepth, static_cast(d)) DECLARE_GL_FUNCTION_HEAD(void, ClearStencil, GLint s) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearStencil, s) DECLARE_GL_FUNCTION_HEAD(void, ColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ColorMask, red, green, blue, alpha) DECLARE_GL_FUNCTION_HEAD(void, CompileShader, GLuint shader) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompileShader, shader) @@ -94,7 +95,7 @@ DECLARE_GL_FUNCTION_HEAD(void, DeleteShader, GLuint shader) DECLARE_GL_FUNCTION_ DECLARE_GL_FUNCTION_HEAD(void, DeleteTextures, GLsizei n, const GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteTextures, n, textures) DECLARE_GL_FUNCTION_HEAD(void, DepthFunc, GLenum func) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DepthFunc, func) DECLARE_GL_FUNCTION_HEAD(void, DepthMask, GLboolean flag) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DepthMask, flag) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DepthRangef, GLfloat n, GLfloat f) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DepthRangef, n, f) +DECLARE_GL_FUNCTION_HEAD(void, DepthRangef, GLfloat n, GLfloat f) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DepthRange, static_cast(n), static_cast(f)) DECLARE_GL_FUNCTION_HEAD(void, DetachShader, GLuint program, GLuint shader) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DetachShader, program, shader) DECLARE_GL_FUNCTION_HEAD(void, Disable, GLenum cap) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Disable, cap) DECLARE_GL_FUNCTION_HEAD(void, DisableVertexAttribArray, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DisableVertexAttribArray, index) @@ -116,10 +117,10 @@ DECLARE_GL_FUNCTION_HEAD(void, GetActiveAttrib, GLuint program, GLuint index, GL DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniform, program, index, bufSize, length, size, type, name) DECLARE_GL_FUNCTION_HEAD(void, GetAttachedShaders, GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetAttachedShaders, program, maxCount, count, shaders) DECLARE_GL_FUNCTION_HEAD(GLint, GetAttribLocation, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetAttribLocation, program, name) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBooleanv, GLenum pname, GLboolean* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBooleanv, pname, data) +DECLARE_GL_FUNCTION_HEAD(void, GetBooleanv, GLenum pname, GLboolean* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBooleanv, pname, data) DECLARE_GL_FUNCTION_HEAD(void, GetBufferParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferParameteriv, target, pname, params) DECLARE_GL_FUNCTION_HEAD(GLenum, GetError) DECLARE_GL_FUNCTION_END(GLenum, GetError) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetFloatv, GLenum pname, GLfloat* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetFloatv, pname, data) +DECLARE_GL_FUNCTION_HEAD(void, GetFloatv, GLenum pname, GLfloat* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetFloatv, pname, data) DECLARE_GL_FUNCTION_HEAD(void, GetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetFramebufferAttachmentParameteriv, target, attachment, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetIntegerv, GLenum pname, GLint* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetIntegerv, pname, data) DECLARE_GL_FUNCTION_HEAD(void, GetProgramiv, GLuint program, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramiv, program, pname, params) @@ -134,9 +135,9 @@ DECLARE_GL_FUNCTION_HEAD(void, GetTexParameteriv, GLenum target, GLenum pname, G DECLARE_GL_FUNCTION_HEAD(void, GetUniformfv, GLuint program, GLint location, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetUniformfv, program, location, params) DECLARE_GL_FUNCTION_HEAD(void, GetUniformiv, GLuint program, GLint location, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetUniformiv, program, location, params) DECLARE_GL_FUNCTION_HEAD(GLint, GetUniformLocation, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetUniformLocation, program, name) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexAttribfv, GLuint index, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexAttribfv, index, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexAttribiv, GLuint index, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexAttribiv, index, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexAttribPointerv, GLuint index, GLenum pname, void** pointer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexAttribPointerv, index, pname, pointer) +DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribfv, GLuint index, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribfv, index, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribiv, GLuint index, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribiv, index, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribPointerv, GLuint index, GLenum pname, void** pointer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribPointerv, index, pname, pointer) DECLARE_GL_FUNCTION_HEAD(void, Hint, GLenum target, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Hint, target, mode) DECLARE_GL_FUNCTION_HEAD(GLboolean, IsBuffer, GLuint buffer) DECLARE_GL_FUNCTION_END(GLboolean, IsBuffer, buffer) DECLARE_GL_FUNCTION_HEAD(GLboolean, IsEnabled, GLenum cap) DECLARE_GL_FUNCTION_END(GLboolean, IsEnabled, cap) @@ -189,14 +190,14 @@ DECLARE_GL_FUNCTION_HEAD(void, UniformMatrix3fv, GLint location, GLsizei count, DECLARE_GL_FUNCTION_HEAD(void, UniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformMatrix4fv, location, count, transpose, value) DECLARE_GL_FUNCTION_HEAD(void, UseProgram, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UseProgram, program) DECLARE_GL_FUNCTION_HEAD(void, ValidateProgram, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ValidateProgram, program) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib1f, GLuint index, GLfloat x) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib1f, index, x) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib1fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib1fv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib2f, GLuint index, GLfloat x, GLfloat y) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib2f, index, x, y) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib2fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib2fv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib3f, GLuint index, GLfloat x, GLfloat y, GLfloat z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib3f, index, x, y, z) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib3fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib3fv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4f, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4f, index, x, y, z, w) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4fv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib1f, GLuint index, GLfloat x) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib1f, index, x) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib1fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib1fv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib2f, GLuint index, GLfloat x, GLfloat y) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib2f, index, x, y) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib2fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib2fv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib3f, GLuint index, GLfloat x, GLfloat y, GLfloat z) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib3f, index, x, y, z) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib3fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib3fv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib4f, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib4f, index, x, y, z, w) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib4fv, GLuint index, const GLfloat* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib4fv, index, v) DECLARE_GL_FUNCTION_HEAD(void, VertexAttribPointer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribPointer, index, size, type, normalized, stride, pointer) DECLARE_GL_FUNCTION_HEAD(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Viewport, x, y, width, height) DECLARE_GL_FUNCTION_HEAD(void, ReadBuffer, GLenum src) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ReadBuffer, src) @@ -214,7 +215,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, EndQuery, GLenum target) DECLARE_GL_FUNCTION DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryiv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryiv, target, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryObjectuiv, GLuint id, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryObjectuiv, id, pname, params) DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapBuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLboolean, UnmapBuffer, target) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferPointerv, target, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferPointerv, target, pname, params) DECLARE_GL_FUNCTION_HEAD(void, DrawBuffers, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawBuffers, n, bufs) DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix2x3fv, location, count, transpose, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix3x2fv, location, count, transpose, value) @@ -239,28 +240,28 @@ DECLARE_GL_FUNCTION_HEAD(void, BindBufferBase, GLenum target, GLuint index, GLui DECLARE_GL_FUNCTION_STUB_HEAD(void, TransformFeedbackVaryings, GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TransformFeedbackVaryings, program, count, varyings, bufferMode) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbackVarying, GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbackVarying, program, index, bufSize, length, size, type, name) DECLARE_GL_FUNCTION_HEAD(void, VertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribIPointer, index, size, type, stride, pointer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexAttribIiv, GLuint index, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexAttribIiv, index, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexAttribIuiv, GLuint index, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexAttribIuiv, index, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4i, GLuint index, GLint x, GLint y, GLint z, GLint w) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribI4i, index, x, y, z, w) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4ui, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribI4ui, index, x, y, z, w) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4iv, GLuint index, const GLint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribI4iv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4uiv, GLuint index, const GLuint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribI4uiv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetUniformuiv, GLuint program, GLint location, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetUniformuiv, program, location, params) +DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribIiv, GLuint index, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribIiv, index, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribIuiv, GLuint index, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribIuiv, index, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttribI4i, GLuint index, GLint x, GLint y, GLint z, GLint w) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribI4i, index, x, y, z, w) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttribI4ui, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribI4ui, index, x, y, z, w) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttribI4iv, GLuint index, const GLint* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribI4iv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttribI4uiv, GLuint index, const GLuint* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribI4uiv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, GetUniformuiv, GLuint program, GLint location, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetUniformuiv, program, location, params) DECLARE_GL_FUNCTION_HEAD(GLint, GetFragDataLocation, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetFragDataLocation, program, name) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1ui, location, v0) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform2ui, GLint location, GLuint v0, GLuint v1) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform2ui, location, v0, v1) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform3ui, location, v0, v1, v2) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform4ui, location, v0, v1, v2, v3) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1uiv, location, count, value) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform2uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform2uiv, location, count, value) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform3uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform3uiv, location, count, value) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform4uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform4uiv, location, count, value) +DECLARE_GL_FUNCTION_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform1ui, location, v0) +DECLARE_GL_FUNCTION_HEAD(void, Uniform2ui, GLint location, GLuint v0, GLuint v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform2ui, location, v0, v1) +DECLARE_GL_FUNCTION_HEAD(void, Uniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform3ui, location, v0, v1, v2) +DECLARE_GL_FUNCTION_HEAD(void, Uniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform4ui, location, v0, v1, v2, v3) +DECLARE_GL_FUNCTION_HEAD(void, Uniform1uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform1uiv, location, count, value) +DECLARE_GL_FUNCTION_HEAD(void, Uniform2uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform2uiv, location, count, value) +DECLARE_GL_FUNCTION_HEAD(void, Uniform3uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform3uiv, location, count, value) +DECLARE_GL_FUNCTION_HEAD(void, Uniform4uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform4uiv, location, count, value) DECLARE_GL_FUNCTION_HEAD(void, ClearBufferiv, GLenum buffer, GLint drawbuffer, const GLint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferiv, buffer, drawbuffer, value) DECLARE_GL_FUNCTION_HEAD(void, ClearBufferuiv, GLenum buffer, GLint drawbuffer, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferuiv, buffer, drawbuffer, value) DECLARE_GL_FUNCTION_HEAD(void, ClearBufferfv, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferfv, buffer, drawbuffer, value) DECLARE_GL_FUNCTION_HEAD(void, ClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferfi, buffer, drawbuffer, depth, stencil) DECLARE_GL_FUNCTION_HEAD(void, CopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, GLuint* uniformIndices) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetUniformIndices, program, uniformCount, uniformNames, uniformIndices) +DECLARE_GL_FUNCTION_HEAD(void, GetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, GLuint* uniformIndices) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetUniformIndices, program, uniformCount, uniformNames, uniformIndices) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params) DECLARE_GL_FUNCTION_HEAD(GLuint, GetUniformBlockIndex, GLuint program, const GLchar* uniformBlockName) DECLARE_GL_FUNCTION_END(GLuint, GetUniformBlockIndex, program, uniformBlockName) DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformBlockiv, program, uniformBlockIndex, pname, params) @@ -268,15 +269,15 @@ DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockName, GLuint program, GLuint DECLARE_GL_FUNCTION_HEAD(void, UniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding) DECLARE_GL_FUNCTION_HEAD(void, DrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysInstanced, mode, first, count, instancecount) DECLARE_GL_FUNCTION_HEAD(void, DrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsInstanced, mode, count, type, indices, instancecount) -DECLARE_GL_FUNCTION_STUB_HEAD(GLsync, FenceSync, GLenum condition, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END(GLsync, FenceSync, condition, flags) -DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsSync, sync) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteSync, GLsync sync) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteSync, sync) -DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, ClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_STUB_END(GLenum, ClientWaitSync, sync, flags, timeout) -DECLARE_GL_FUNCTION_STUB_HEAD(void, WaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WaitSync, sync, flags, timeout) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInteger64v, GLenum pname, GLint64* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInteger64v, pname, data) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetSynciv, sync, pname, bufSize, length, values) +DECLARE_GL_FUNCTION_HEAD(GLsync, FenceSync, GLenum condition, GLbitfield flags) DECLARE_GL_FUNCTION_END(GLsync, FenceSync, condition, flags) +DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_END(GLboolean, IsSync, sync) +DECLARE_GL_FUNCTION_HEAD(void, DeleteSync, GLsync sync) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteSync, sync) +DECLARE_GL_FUNCTION_HEAD(GLenum, ClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_END(GLenum, ClientWaitSync, sync, flags, timeout) +DECLARE_GL_FUNCTION_HEAD(void, WaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_END_NO_RETURN(void, WaitSync, sync, flags, timeout) +DECLARE_GL_FUNCTION_HEAD(void, GetInteger64v, GLenum pname, GLint64* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInteger64v, pname, data) +DECLARE_GL_FUNCTION_HEAD(void, GetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSynciv, sync, pname, bufSize, length, values) DECLARE_GL_FUNCTION_HEAD(void, GetInteger64i_v, GLenum target, GLuint index, GLint64* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInteger64i_v, target, index, data) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GenSamplers, GLsizei count, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenSamplers, count, samplers) DECLARE_GL_FUNCTION_HEAD(void, DeleteSamplers, GLsizei count, const GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteSamplers, count, samplers) DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSampler, GLuint sampler) DECLARE_GL_FUNCTION_END(GLboolean, IsSampler, sampler) @@ -357,7 +358,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix4x3fv, GLuint program, G DECLARE_GL_FUNCTION_STUB_HEAD(void, ValidateProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ValidateProgramPipeline, pipeline) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog) DECLARE_GL_FUNCTION_HEAD(void, BindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindImageTexture, unit, texture, level, layered, layer, access, format) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBooleani_v, GLenum target, GLuint index, GLboolean* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBooleani_v, target, index, data) +DECLARE_GL_FUNCTION_HEAD(void, GetBooleani_v, GLenum target, GLuint index, GLboolean* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBooleani_v, target, index, data) DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrier, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrier, barriers) DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrierByRegion, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrierByRegion, barriers) DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations) @@ -829,8 +830,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib3sv, GLuint index, const GLshort DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Nbv, GLuint index, const GLbyte* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Nbv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Niv, GLuint index, const GLint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Niv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Nsv, GLuint index, const GLshort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Nsv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Nub, GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Nub, index, x, y, z, w) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Nubv, GLuint index, const GLubyte* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Nubv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib4Nub, GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib4Nub, index, x, y, z, w) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib4Nubv, GLuint index, const GLubyte* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib4Nubv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Nuiv, GLuint index, const GLuint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Nuiv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4Nusv, GLuint index, const GLushort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4Nusv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4bv, GLuint index, const GLbyte* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4bv, index, v) @@ -839,11 +840,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4dv, GLuint index, const GLdoubl DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4iv, GLuint index, const GLint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4iv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4s, GLuint index, GLshort x, GLshort y, GLshort z, GLshort w) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4s, index, x, y, z, w) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4sv, GLuint index, const GLshort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4sv, index, v) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4ubv, GLuint index, const GLubyte* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4ubv, index, v) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib4ubv, GLuint index, const GLubyte* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib4ubv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4uiv, GLuint index, const GLuint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4uiv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4usv, GLuint index, const GLushort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4usv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveRestartIndex, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PrimitiveRestartIndex, index) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformName, GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformName) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformName, program, uniformIndex, bufSize, length, uniformName) +DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformName, GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformName) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetActiveUniformName, program, uniformIndex, bufSize, length, uniformName) DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsBaseVertex, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsBaseVertex, mode, count, type, indices, drawcount, basevertex) DECLARE_GL_FUNCTION_STUB_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProvokingVertex, mode) DECLARE_GL_FUNCTION_HEAD(void, TexImage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations) @@ -1489,9 +1490,9 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, Vertex3xvOES, const GLfixed* coords) DECLARE DECLARE_GL_FUNCTION_STUB_HEAD(void, Vertex4xOES, GLfixed x, GLfixed y, GLfixed z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Vertex4xOES, x, y, z) DECLARE_GL_FUNCTION_STUB_HEAD(void, Vertex4xvOES, const GLfixed* coords) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Vertex4xvOES, coords) DECLARE_GL_FUNCTION_STUB_HEAD(GLbitfield, QueryMatrixxOES, GLfixed* mantissa, GLint* exponent) DECLARE_GL_FUNCTION_STUB_END(GLbitfield, QueryMatrixxOES, mantissa, exponent) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearDepthfOES, GLclampf depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearDepthfOES, depth) +DECLARE_GL_FUNCTION_HEAD(void, ClearDepthfOES, GLclampf depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearDepth, static_cast(depth)) DECLARE_GL_FUNCTION_STUB_HEAD(void, ClipPlanefOES, GLenum plane, const GLfloat* equation) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClipPlanefOES, plane, equation) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DepthRangefOES, GLclampf n, GLclampf f) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DepthRangefOES, n, f) +DECLARE_GL_FUNCTION_HEAD(void, DepthRangefOES, GLclampf n, GLclampf f) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DepthRange, static_cast(n), static_cast(f)) DECLARE_GL_FUNCTION_STUB_HEAD(void, FrustumfOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FrustumfOES, l, r, b, t, n, f) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetClipPlanefOES, GLenum plane, GLfloat* equation) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetClipPlanefOES, plane, equation) DECLARE_GL_FUNCTION_STUB_HEAD(void, OrthofOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, OrthofOES, l, r, b, t, n, f) diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index 73941e5a..bc9ff58c 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -15,10 +15,144 @@ #include #include #include +#include #include +#include #include namespace MobileGL::MG_Impl::GLImpl { + namespace { + Bool IsActiveBackendDirectVulkan() { + auto* activeBackend = MG_Backend::pActiveBackendObject.get(); + return activeBackend != nullptr && activeBackend->GetBackendType() == BackendType::DirectVulkan; + } + + Bool HasDistinctCompleteDepthStencilTextureAttachments( + const MG_State::GLState::FramebufferObject& framebufferObject) { + if (framebufferObject.GetExternalIndex() == 0) { + return false; + } + + const auto& depthAttachment = framebufferObject.GetAttachment(FramebufferAttachmentType::Depth); + const auto& stencilAttachment = framebufferObject.GetAttachment(FramebufferAttachmentType::Stencil); + if (!depthAttachment.IsComplete() || !stencilAttachment.IsComplete() || + !depthAttachment.IsTexture() || !stencilAttachment.IsTexture()) { + return false; + } + + return depthAttachment.GetTexture().get() != stencilAttachment.GetTexture().get() || + depthAttachment.GetTextureUploadTarget() != stencilAttachment.GetTextureUploadTarget() || + depthAttachment.GetTextureLevel() != stencilAttachment.GetTextureLevel(); + } + + Bool HasCompleteRenderbufferAttachment(const MG_State::GLState::FramebufferObject& framebufferObject) { + if (framebufferObject.GetExternalIndex() == 0) { + return false; + } + + for (const auto& attachment : framebufferObject.GetAllAttachmentObjects()) { + if (attachment.IsRenderbuffer() && attachment.IsComplete()) { + return true; + } + } + return false; + } + + Bool IsUnsupportedFramebufferForDirectVulkan( + const MG_State::GLState::FramebufferObject& framebufferObject) { + return HasDistinctCompleteDepthStencilTextureAttachments(framebufferObject) || + HasCompleteRenderbufferAttachment(framebufferObject); + } + + void RecordUnsupportedFramebufferTextureAttachmentError(const char* functionName, const char* detail) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, detail)); + } + + Bool ResolveRepresentableFramebufferTextureUploadTarget(const MG_State::GLState::ITextureObject& textureObject, + TextureUploadTarget& outUploadTarget) { + switch (textureObject.GetTarget()) { + case TextureTarget::Texture1D: + outUploadTarget = TextureUploadTarget::Texture1D; + return true; + case TextureTarget::Texture2D: + outUploadTarget = TextureUploadTarget::Texture2D; + return true; + case TextureTarget::TextureRectangle: + outUploadTarget = TextureUploadTarget::TextureRectangle; + return true; + case TextureTarget::Texture2DMultisample: + outUploadTarget = TextureUploadTarget::Texture2DMultisample; + return true; + default: + outUploadTarget = TextureUploadTarget::Unknown; + return false; + } + } + + void AttachFramebufferTextureWithUploadTarget(const char* functionName, GLenum target, GLenum attachment, + GLuint texture, GLint level, + TextureUploadTarget textureUploadTarget) { + if (target == GL_FRAMEBUFFER) { + target = GL_DRAW_FRAMEBUFFER; + } + + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + AttachFramebufferTextureWithUploadTarget(functionName, target, GL_DEPTH_ATTACHMENT, texture, level, + textureUploadTarget); + AttachFramebufferTextureWithUploadTarget(functionName, target, GL_STENCIL_ATTACHMENT, texture, level, + textureUploadTarget); + return; + } + + const FramebufferAttachmentType attachmentType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment); + const FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target); + if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return; + if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return; + if (!TextureImpl::ValidateTextureName(texture, true)) return; + + auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); + auto& framebufferObject = bindingSlot.GetBoundObject(); + if (!framebufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, + "Framebuffer target is bound to no framebuffer object.")); + return; + } + + if (texture == 0) { + framebufferObject->Detach(attachmentType); + return; + } + + auto& textureObject = MG_State::pGLContext->GetTextureObject(texture); + if (!textureObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, + std::format("Texture object {} is not valid.", texture))); + return; + } + + const auto expectedTextureTarget = MG_Util::ConvertTextureUploadTargetToTextureTarget(textureUploadTarget); + if (expectedTextureTarget == TextureTarget::Unknown || + textureObject->GetTarget() != expectedTextureTarget) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", functionName, + std::format("Attachment target {} does not match texture {} target {}.", + MG_Util::ConvertTextureUploadTargetToString(textureUploadTarget), texture, + MG_Util::ConvertTextureTargetToString(textureObject->GetTarget())))); + return; + } + + framebufferObject->AttachTexture(attachmentType, textureObject, textureUploadTarget, level); + } + } // namespace + void BlitFramebuffer_Backend(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { MG_Backend::gBackendFunctionsTable.GL.BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, @@ -26,12 +160,53 @@ namespace MobileGL::MG_Impl::GLImpl { } void SampleMaski_State(GLuint maskNumber, GLbitfield mask) { - // TODO: implement + if (maskNumber != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "SampleMaski_State", + "Only sample mask word 0 is currently supported.")); + return; + } + + MG_State::pGLContext->SetSampleMaskValue(static_cast(mask)); } void RenderbufferStorageMultisample_State(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { - // TODO: implement + RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); + if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return; + + auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(rbTarget); + auto& renderbufferObject = bindingSlot.GetBoundObject(); + if (!renderbufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "RenderbufferStorageMultisample_State", + "Renderbuffer target is bound to no renderbuffer object.")); + return; + } + + TextureInternalFormat format = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + if (!TextureImpl::ValidateTextureInternalFormat(format)) return; + + if (samples < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "RenderbufferStorageMultisample_State", + "Sample count must be non-negative.")); + return; + } + if (width < 0 || height < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "RenderbufferStorageMultisample_State", + "Width and height must be non-negative.")); + return; + } + + renderbufferObject->AllocateStorage({width, height}); + renderbufferObject->SetInternalFormat(format); + renderbufferObject->SetSamples(samples); } void RenderbufferStorage_State(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { @@ -56,14 +231,15 @@ namespace MobileGL::MG_Impl::GLImpl { } renderbufferObject->AllocateStorage({width, height}); renderbufferObject->SetInternalFormat(format); + renderbufferObject->SetSamples(0); } GLboolean IsRenderbuffer_State(GLuint renderbuffer) { - return MG_State::pGLContext->ValidateRenderbufferName(renderbuffer); + return MG_State::pGLContext->ValidateRenderbufferObject(renderbuffer); } GLboolean IsFramebuffer_State(GLuint framebuffer) { - return MG_State::pGLContext->ValidateFramebufferName(framebuffer); + return MG_State::pGLContext->ValidateFramebufferObject(framebuffer); } void GetFramebufferAttachmentParameteriv_State(GLenum target, GLenum attachment, GLenum pname, GLint* params) { @@ -136,6 +312,26 @@ namespace MobileGL::MG_Impl::GLImpl { : 0; break; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: + if (attachmentObject != nullptr && attachmentObject->IsTexture() && attachmentObject->IsValid()) { + const GLenum glUploadTarget = + MG_Util::ConvertTextureUploadTargetToGLEnum(attachmentObject->GetTextureUploadTarget()); + switch (glUploadTarget) { + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + *params = static_cast(glUploadTarget); + break; + default: + *params = 0; + break; + } + } else { + *params = 0; + } + break; case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: *params = 0; break; @@ -174,12 +370,31 @@ namespace MobileGL::MG_Impl::GLImpl { } void FramebufferTextureLayer_State(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { - // TODO: implement + if (texture == 0) { + const TextureUploadTarget detachTarget = TextureUploadTarget::Texture2D; + AttachFramebufferTextureWithUploadTarget(__func__, target, attachment, texture, level, detachTarget); + return; + } + + static_cast(layer); + RecordUnsupportedFramebufferTextureAttachmentError( + __func__, + "Layered framebuffer texture attachments are not represented by the current framebuffer attachment model."); } void FramebufferTexture3D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) { - // TODO: implement + if (texture == 0) { + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(textarget); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + AttachFramebufferTextureWithUploadTarget(__func__, target, attachment, texture, level, textureUploadTarget); + return; + } + + static_cast(zoffset); + RecordUnsupportedFramebufferTextureAttachmentError( + __func__, + "3D framebuffer texture slice attachments are not represented by the current framebuffer attachment model."); } void FramebufferTexture2D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { @@ -199,6 +414,11 @@ namespace MobileGL::MG_Impl::GLImpl { if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return; if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return; if (!TextureImpl::ValidateTextureName(texture, true)) return; + TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown; + if (texture != 0) { + textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(textarget); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + } auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); auto& framebufferObject = bindingSlot.GetBoundObject(); @@ -224,15 +444,59 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - framebufferObject->AttachTexture(attachmentType, textureObject, textureObject ? level : 0); + const auto expectedTextureTarget = MG_Util::ConvertTextureUploadTargetToTextureTarget(textureUploadTarget); + if (expectedTextureTarget == TextureTarget::Unknown || + textureObject->GetTarget() != expectedTextureTarget) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", "FramebufferTexture2D_State", + std::format("Attachment target {} does not match texture {} target {}.", + MG_Util::ConvertGLEnumToString(textarget), + texture, + MG_Util::ConvertTextureTargetToString(textureObject->GetTarget())))); + return; + } + + framebufferObject->AttachTexture(attachmentType, textureObject, textureUploadTarget, textureObject ? level : 0); } void FramebufferTexture1D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - // TODO: implement + TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown; + if (texture != 0) { + textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(textarget); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + } else { + textureUploadTarget = TextureUploadTarget::Texture1D; + } + AttachFramebufferTextureWithUploadTarget(__func__, target, attachment, texture, level, textureUploadTarget); } void FramebufferTexture_State(GLenum target, GLenum attachment, GLuint texture, GLint level) { - // TODO: implement + if (texture == 0) { + AttachFramebufferTextureWithUploadTarget(__func__, target, attachment, texture, level, + TextureUploadTarget::Texture2D); + return; + } + + auto& textureObject = MG_State::pGLContext->GetTextureObject(texture); + if (!textureObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + std::format("Texture object {} is not valid.", texture))); + return; + } + + TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown; + if (!ResolveRepresentableFramebufferTextureUploadTarget(*textureObject, textureUploadTarget)) { + RecordUnsupportedFramebufferTextureAttachmentError( + __func__, + "Layered or multi-image framebuffer texture targets are not fully represented by the current framebuffer attachment model."); + return; + } + + AttachFramebufferTextureWithUploadTarget(__func__, target, attachment, texture, level, textureUploadTarget); } void FramebufferRenderbuffer_State(GLenum target, GLenum attachment, GLenum renderbuffertarget, @@ -246,7 +510,8 @@ namespace MobileGL::MG_Impl::GLImpl { RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(renderbuffertarget); if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return; if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return; - if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return; + if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return; + if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer, true)) return; auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); auto& framebufferObject = bindingSlot.GetBoundObject(); if (!framebufferObject) { @@ -290,6 +555,12 @@ namespace MobileGL::MG_Impl::GLImpl { // Get bound framebuffer auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw); auto& fbo = bindingSlot.GetBoundObject(); + if (!fbo) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "No framebuffer bound to draw target.")); + return; + } bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); static int existenceMap[(SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount] = {-1}; @@ -308,20 +579,20 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (isDefaultFBO && attType >= FramebufferAttachmentType::Color0 && - attType <= FramebufferAttachmentType::Color31) { + if (isDefaultFBO && attType != FramebufferAttachmentType::None && + (attType < FramebufferAttachmentType::FrontLeft || attType > FramebufferAttachmentType::BackRight)) { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique( "MG_Impl/GLImpl", __func__, - std::format( - "FBO is default FBO, but bufs[{}] = {} is one of the `GL_COLOR_ATTACHMENTn` tokens.", i, - MG_Util::ConvertGLEnumToString(bufs[i])))); + std::format("FBO is default FBO, but bufs[{}] = {} is not `GL_NONE` or one of the default " + "framebuffer color buffer tokens.", + i, MG_Util::ConvertGLEnumToString(bufs[i])))); return; } - if (!isDefaultFBO && attType >= FramebufferAttachmentType::FrontLeft && - attType <= FramebufferAttachmentType::BackRight) { + if (!isDefaultFBO && attType != FramebufferAttachmentType::None && + (attType < FramebufferAttachmentType::Color0 || attType > FramebufferAttachmentType::Color31)) { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique( @@ -388,6 +659,43 @@ namespace MobileGL::MG_Impl::GLImpl { // Get bound framebuffer auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read); auto& fbo = bindingSlot.GetBoundObject(); + if (!fbo) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "No framebuffer bound to read target.")); + return; + } + const Bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + if (isDefaultFBO && attType != FramebufferAttachmentType::None && + (attType < FramebufferAttachmentType::FrontLeft || attType > FramebufferAttachmentType::BackRight)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", __func__, + std::format("Default framebuffer read buffer {} is not valid.", MG_Util::ConvertGLEnumToString(mode)))); + return; + } + if (!isDefaultFBO && attType != FramebufferAttachmentType::None && + (attType < FramebufferAttachmentType::Color0 || attType > FramebufferAttachmentType::Color31)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + std::format("Framebuffer object read buffer {} is not valid.", + MG_Util::ConvertGLEnumToString(mode)))); + return; + } + if (!isDefaultFBO && + static_cast(attType) > + static_cast(FramebufferAttachmentType::Color0) + + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + std::format("Read buffer {} indicates a color buffer that does not exist " + "in the current GL context.", + MG_Util::ConvertGLEnumToString(mode)))); + return; + } fbo->SetReadBuffer(attType); } @@ -453,24 +761,35 @@ namespace MobileGL::MG_Impl::GLImpl { // TODO: distinguish GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT and GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT // TODO: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER, - // GL_FRAMEBUFFER_UNSUPPORTED, GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, + // additional GL_FRAMEBUFFER_UNSUPPORTED cases, GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, // GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS - return framebufferObject->CheckCompleteness() ? GL_FRAMEBUFFER_COMPLETE - : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + if (!framebufferObject->CheckCompleteness()) { + return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + if (IsActiveBackendDirectVulkan() && + IsUnsupportedFramebufferForDirectVulkan(*framebufferObject)) { + return GL_FRAMEBUFFER_UNSUPPORTED; + } + return GL_FRAMEBUFFER_COMPLETE; } void BindRenderbuffer_State(GLenum target, GLuint renderbuffer) { - if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return; + if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer, true)) return; RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return; + auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget); + if (renderbuffer == 0) { + bindingSlot.Bind(nullptr); + return; + } + Bool doesRenderbufferCreated = MG_State::pGLContext->ValidateRenderbufferObject(renderbuffer); if (!doesRenderbufferCreated) { MG_State::pGLContext->CreateRenderbufferObject(renderbuffer); } auto& renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer); - auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget); bindingSlot.Bind(renderbufferObject); } @@ -567,7 +886,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_Backend::gBackendFunctionsTable.GL.ClearBufferiv(buffer, drawbuffer, value); } - void ReadPixels_State(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { + Bool ReadPixels_State(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); @@ -576,7 +895,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError(ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Width and height must be non-negative")); - return; + return false; } // Validate format @@ -584,7 +903,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Invalid format")); - return; + return false; } // Validate type @@ -592,7 +911,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Invalid pixel data type")); - return; + return false; } // Get bound framebuffer @@ -603,7 +922,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "No framebuffer bound to read target")); - return; + return false; } // Check framebuffer completeness @@ -611,7 +930,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidFramebufferOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Framebuffer is incomplete")); - return; + return false; } // Check for required buffers @@ -621,7 +940,7 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "No stencil buffer for stencil index format")); - return; + return false; } } else if (textureInputFormat == TextureInputFormat::DepthComponent) { if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid()) { @@ -629,7 +948,7 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "No depth buffer for depth component format")); - return; + return false; } } else if (textureInputFormat == TextureInputFormat::DepthStencil) { if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid() || @@ -638,7 +957,7 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "No depth/stencil buffer for depth-stencil format")); - return; + return false; } // Validate type for depth/stencil @@ -647,7 +966,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Invalid type for depth-stencil format")); - return; + return false; } } @@ -661,7 +980,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Pixel pack buffer is currently mapped")); - return; + return false; } // Check alignment @@ -671,7 +990,7 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Pixel data not aligned for pixel pack buffer")); - return; + return false; } } @@ -683,9 +1002,11 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "ReadPixels not supported for multisampled framebuffers")); - return; + return false; } } + + return true; } void ReadPixels_Backend(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { @@ -694,7 +1015,7 @@ namespace MobileGL::MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { - ReadPixels_State(x, y, width, height, format, type, pixels); + if (!ReadPixels_State(x, y, width, height, format, type, pixels)) return; ReadPixels_Backend(x, y, width, height, format, type, pixels); } diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index ebb96907..f9a66287 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -9,16 +9,238 @@ #include "GL_Getter.h" #include #include +#include #include #include #include +#include +#include #include +#include #include #include #include +#include #include namespace MobileGL::MG_Impl::GLImpl { + namespace { + enum class IndexedBufferQueryKind { + Binding, + Start, + Size, + }; + + void CopyFloatsToInts(const GLfloat* src, SizeT count, GLint* dst) { + for (SizeT i = 0; i < count; ++i) { + dst[i] = static_cast(src[i]); + } + } + + constexpr GLint kFrontendMaxComputeUniformComponents = 1024; + constexpr GLint kFrontendMaxComputeAtomicCounters = 8; + constexpr GLint kFrontendMaxComputeAtomicCounterBuffers = 1; + constexpr GLint kFrontendMaxCombinedAtomicCounters = 8; + constexpr GLint kFrontendMaxFragmentAtomicCounters = 8; + constexpr GLint kFrontendMaxGeometryAtomicCounters = 0; + constexpr GLint kFrontendMaxTessControlAtomicCounters = 0; + constexpr GLint kFrontendMaxTessEvaluationAtomicCounters = 0; + constexpr GLint kFrontendMaxVertexAtomicCounters = 0; + constexpr GLint kFrontendMaxVertexUniformComponents = 4096; + constexpr GLint kFrontendMaxVertexUniformVectors = 128; + constexpr GLint kFrontendMaxVertexUniformBlocks = 14; + constexpr GLint kFrontendMaxVertexOutputComponents = 64; + constexpr GLint kFrontendMaxFragmentInputComponents = 128; + constexpr GLint kFrontendMaxFragmentUniformComponents = 4096; + constexpr GLint kFrontendMaxFragmentUniformVectors = 256; + constexpr GLint kFrontendMaxFragmentUniformBlocks = 14; + constexpr GLint kFrontendMaxGeometryInputComponents = 64; + constexpr GLint kFrontendMaxGeometryOutputComponents = 128; + constexpr GLint kFrontendMaxGeometryTextureImageUnits = 16; + constexpr GLint kFrontendMaxGeometryUniformComponents = 1024; + constexpr GLint kFrontendMaxGeometryUniformBlocks = 14; + constexpr GLint kFrontendMaxCombinedUniformBlocks = kFrontendMaxVertexUniformBlocks + + kFrontendMaxGeometryUniformBlocks + + kFrontendMaxFragmentUniformBlocks; + constexpr GLint kFrontendMaxVaryingComponents = 60; + constexpr GLint kFrontendMaxVaryingVectors = 8; + constexpr GLint kFrontendMaxProgramTexelOffset = 7; + constexpr GLint kFrontendMinProgramTexelOffset = -8; + + GLint GetMaxCombinedUniformComponents(GLint maxDefaultUniformComponents, GLint maxUniformBlocks, + GLint maxUniformBlockSizeBytes) { + return maxDefaultUniformComponents + maxUniformBlocks * (maxUniformBlockSizeBytes / 4); + } + + bool TryDecodeIndexedBufferQuery(GLenum pname, BufferTarget& bufferTarget, IndexedBufferQueryKind& queryKind) { + switch (pname) { + case GL_UNIFORM_BUFFER_BINDING: + bufferTarget = BufferTarget::Uniform; + queryKind = IndexedBufferQueryKind::Binding; + return true; + case GL_UNIFORM_BUFFER_START: + bufferTarget = BufferTarget::Uniform; + queryKind = IndexedBufferQueryKind::Start; + return true; + case GL_UNIFORM_BUFFER_SIZE: + bufferTarget = BufferTarget::Uniform; + queryKind = IndexedBufferQueryKind::Size; + return true; + case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: + bufferTarget = BufferTarget::TransformFeedback; + queryKind = IndexedBufferQueryKind::Binding; + return true; + case GL_TRANSFORM_FEEDBACK_BUFFER_START: + bufferTarget = BufferTarget::TransformFeedback; + queryKind = IndexedBufferQueryKind::Start; + return true; + case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: + bufferTarget = BufferTarget::TransformFeedback; + queryKind = IndexedBufferQueryKind::Size; + return true; + case GL_ATOMIC_COUNTER_BUFFER_BINDING: + bufferTarget = BufferTarget::AtomicCounter; + queryKind = IndexedBufferQueryKind::Binding; + return true; + case GL_ATOMIC_COUNTER_BUFFER_START: + bufferTarget = BufferTarget::AtomicCounter; + queryKind = IndexedBufferQueryKind::Start; + return true; + case GL_ATOMIC_COUNTER_BUFFER_SIZE: + bufferTarget = BufferTarget::AtomicCounter; + queryKind = IndexedBufferQueryKind::Size; + return true; + case GL_SHADER_STORAGE_BUFFER_BINDING: + bufferTarget = BufferTarget::ShaderStorage; + queryKind = IndexedBufferQueryKind::Binding; + return true; + case GL_SHADER_STORAGE_BUFFER_START: + bufferTarget = BufferTarget::ShaderStorage; + queryKind = IndexedBufferQueryKind::Start; + return true; + case GL_SHADER_STORAGE_BUFFER_SIZE: + bufferTarget = BufferTarget::ShaderStorage; + queryKind = IndexedBufferQueryKind::Size; + return true; + default: + return false; + } + } + + bool IsIndexedBufferBindingQueryKind(IndexedBufferQueryKind queryKind) { + return queryKind == IndexedBufferQueryKind::Binding; + } + + bool IsIndexedBufferRangeQueryKind(IndexedBufferQueryKind queryKind) { + return queryKind == IndexedBufferQueryKind::Start || queryKind == IndexedBufferQueryKind::Size; + } + + bool TryDecodeDrawBufferQuery(GLenum pname, SizeT& drawBufferIndex) { + if (pname == GL_DRAW_BUFFER) { + drawBufferIndex = 0; + return true; + } + if (pname >= GL_DRAW_BUFFER0 && pname <= GL_DRAW_BUFFER15) { + drawBufferIndex = static_cast(pname - GL_DRAW_BUFFER0); + return true; + } + return false; + } + + bool TryResolveImplementationColorReadParams(GLint& outFormat, GLint& outType) { + const auto& readFbo = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + if (!readFbo) return false; + + const auto attachmentType = readFbo->GetReadBuffer(); + if (attachmentType == FramebufferAttachmentType::None) return false; + + const auto& attachment = readFbo->GetAttachment(attachmentType); + TextureInternalFormat internalFormat = TextureInternalFormat::Unknown; + if (attachment.IsTexture() && attachment.GetTexture()) { + internalFormat = attachment.GetTexture()->GetFormat(); + } else if (attachment.IsRenderbuffer() && attachment.GetRenderbuffer()) { + internalFormat = attachment.GetRenderbuffer()->GetInternalFormat(); + } + + if (internalFormat == TextureInternalFormat::Unknown) return false; + + const GLenum glInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat); + GLenum normalizedInternalFormat = glInternalFormat; + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; + MG_Util::TextureFormatProcessor::NormalizePixelFormat(glInternalFormat, PixelFormatNormalizeOptionBit::None, + &normalizedInternalFormat, &format, &type); + outFormat = static_cast(format); + outType = static_cast(type); + return true; + } + + GLint ResolveDrawFramebufferSampleCount() { + const auto& drawFbo = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + if (!drawFbo) return 0; + + GLint maxSamples = 0; + for (const auto& attachment : drawFbo->GetAllAttachmentObjects()) { + if (!attachment.IsRenderbuffer() || !attachment.GetRenderbuffer()) continue; + maxSamples = std::max(maxSamples, static_cast(attachment.GetRenderbuffer()->GetSamples())); + } + return maxSamples; + } + + void RecordIndexedOnlyGetterError(const char* functionName, GLenum pname) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", functionName, + "pname " + MG_Util::ConvertGLEnumToString(pname) + + " is only valid with indexed getter entrypoints.")); + } + + bool ValidateIndexedBufferQueryIndex(GLenum pname, GLuint index, const char* functionName, + BufferTarget bufferTarget) { + switch (bufferTarget) { + case BufferTarget::Uniform: + case BufferTarget::TransformFeedback: + case BufferTarget::AtomicCounter: + case BufferTarget::ShaderStorage: + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", functionName, + "pname " + std::to_string(pname) + + " is not a supported indexed buffer query.")); + return false; + } + + if (index >= MG_State::pGLContext->GetBufferBindingPointCount(bufferTarget)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique( + "MG_Impl/GLImpl", functionName, + "index " + std::to_string(index) + " is out of range for indexed buffer query " + + std::to_string(pname) + ".")); + return false; + } + + return true; + } + + void CopyIntsToBooleans(const GLint* src, SizeT count, GLboolean* dst) { + for (SizeT i = 0; i < count; ++i) { + dst[i] = src[i] ? GL_TRUE : GL_FALSE; + } + } + + void CopyIntsToFloats(const GLint* src, SizeT count, GLfloat* dst) { + for (SizeT i = 0; i < count; ++i) { + dst[i] = static_cast(src[i]); + } + } + } // namespace + /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ const GLubyte* GetString(GLenum name) { static String vendorString; @@ -38,50 +260,42 @@ namespace MobileGL::MG_Impl::GLImpl { switch (name) { case GL_VENDOR: - if (vendorString.empty()) { - if (rendererInfo.ExtraVendor.has_value()) { - vendorString = std::format("{}{}", MG_Config::CoreVendor, rendererInfo.ExtraVendor.value()); - } else { - vendorString = MG_Config::CoreVendor; - } + if (rendererInfo.ExtraVendor.has_value()) { + vendorString = std::format("{}{}", MG_Config::CoreVendor, rendererInfo.ExtraVendor.value()); + } else { + vendorString = MG_Config::CoreVendor; } MGLOG_D("vendorString: %s", vendorString.c_str()); return (const GLubyte*)vendorString.c_str(); case GL_VERSION: { - if (versionStr.empty()) { - versionStr = - std::format("{} {} {}, {} Backend, GIT@" GIT_COMMIT_HASH_SHORT, - rendererInfo.RendererGLInfo.TargetGLVersion.toString(), MG_Config::ProjectName, - MG_Config::CoreVersion.toFormattedString(MG_Config::DefaultVersionStringFormatAttrib), - rendererInfo.BackendName); - } + versionStr = + std::format("{} {} {}, {} Backend, GIT@" GIT_COMMIT_HASH_SHORT, + rendererInfo.RendererGLInfo.TargetGLVersion.toString(), MG_Config::ProjectName, + MG_Config::CoreVersion.toFormattedString(MG_Config::DefaultVersionStringFormatAttrib), + rendererInfo.BackendName); MGLOG_D("versionStr: %s", versionStr.c_str()); return (const GLubyte*)versionStr.c_str(); } case GL_RENDERER: { - if (rendererString.empty()) { - String backendVersionStr = activeBackendObject->GetBackendAPIVersionString(); - rendererString = - std::format("{} ({}) ({})", rendererInfo.RendererName, MG_Config::CoreName, backendVersionStr); - } + String backendVersionStr = activeBackendObject->GetBackendAPIVersionString(); + rendererString = + std::format("{} ({}) ({})", rendererInfo.RendererName, MG_Config::CoreName, backendVersionStr); MGLOG_D("rendererString: %s", rendererString.c_str()); return (const GLubyte*)rendererString.c_str(); } case GL_SHADING_LANGUAGE_VERSION: - if (shadingLanguageVersion.empty()) { - shadingLanguageVersion = - std::format("{} {}", rendererInfo.RendererGLInfo.TargetGLSLVersion.toString({true, false}), - MG_Config::ProjectName); - } + shadingLanguageVersion = + std::format("{} {}", rendererInfo.RendererGLInfo.TargetGLSLVersion.toString({true, false}), + MG_Config::ProjectName); MGLOG_D("shadingLanguageVersion: %s", shadingLanguageVersion.c_str()); return (const GLubyte*)shadingLanguageVersion.c_str(); case GL_EXTENSIONS: - if (extensionsString.empty()) { - for (auto& ext : rendererInfo.RendererGLInfo.Extensions) { - extensionsString += MG_Util::ConvertGLExtToString(ext); + extensionsString.clear(); + for (const auto& ext : rendererInfo.RendererGLInfo.Extensions) { + if (!extensionsString.empty()) { extensionsString += " "; } - extensionsString.pop_back(); + extensionsString += MG_Util::ConvertGLExtToString(ext); } return (const GLubyte*)extensionsString.c_str(); default: @@ -108,18 +322,241 @@ namespace MobileGL::MG_Impl::GLImpl { } static Vector extStrings; - static Bool initialized = false; - if (!initialized) { - extStrings.reserve(exts.size()); - for (const auto& ext : exts) { - extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext)); - } - initialized = true; + extStrings.clear(); + extStrings.reserve(exts.size()); + for (const auto& ext : exts) { + extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext)); } return (const GLubyte*)extStrings[index].c_str(); } + void GetBooleanv(GLenum pname, GLboolean* params) { + MGLOG_D("glGetBooleanv, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null")); + return; + } + + switch (pname) { + case GL_BLEND: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Blend) ? GL_TRUE : GL_FALSE; + return; + case GL_BLEND_COLOR: { + const FloatVec4& blendColor = MG_State::pGLContext->GetBlendColor(); + params[0] = blendColor.x() != 0.0f ? GL_TRUE : GL_FALSE; + params[1] = blendColor.y() != 0.0f ? GL_TRUE : GL_FALSE; + params[2] = blendColor.z() != 0.0f ? GL_TRUE : GL_FALSE; + params[3] = blendColor.w() != 0.0f ? GL_TRUE : GL_FALSE; + return; + } + case GL_COLOR_CLEAR_VALUE: { + const FloatVec4& clearColor = MG_State::pGLContext->GetClearColor(); + params[0] = clearColor.x() != 0.0f ? GL_TRUE : GL_FALSE; + params[1] = clearColor.y() != 0.0f ? GL_TRUE : GL_FALSE; + params[2] = clearColor.z() != 0.0f ? GL_TRUE : GL_FALSE; + params[3] = clearColor.w() != 0.0f ? GL_TRUE : GL_FALSE; + return; + } + case GL_COLOR_WRITEMASK: { + BoolVec4 mask = MG_State::pGLContext->GetColorMask(); + params[0] = mask.x() ? GL_TRUE : GL_FALSE; + params[1] = mask.y() ? GL_TRUE : GL_FALSE; + params[2] = mask.z() ? GL_TRUE : GL_FALSE; + params[3] = mask.w() ? GL_TRUE : GL_FALSE; + return; + } + case GL_DEPTH_CLEAR_VALUE: + *params = MG_State::pGLContext->GetClearDepth() != 0.0f ? GL_TRUE : GL_FALSE; + return; + case GL_DEPTH_RANGE: { + const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRange(); + params[0] = depthRange.x() != 0.0f ? GL_TRUE : GL_FALSE; + params[1] = depthRange.y() != 0.0f ? GL_TRUE : GL_FALSE; + return; + } + case GL_LINE_WIDTH: + *params = MG_State::pGLContext->GetLineWidth() != 0.0f ? GL_TRUE : GL_FALSE; + return; + case GL_POINT_SIZE: + *params = MG_State::pGLContext->GetPointSize() != 0.0f ? GL_TRUE : GL_FALSE; + return; + case GL_CULL_FACE: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace) ? GL_TRUE : GL_FALSE; + return; + case GL_DEPTH_TEST: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest) ? GL_TRUE : GL_FALSE; + return; + case GL_DEPTH_WRITEMASK: + *params = MG_State::pGLContext->GetDepthMask() ? GL_TRUE : GL_FALSE; + return; + case GL_DITHER: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Dither) ? GL_TRUE : GL_FALSE; + return; + case GL_MULTISAMPLE: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Multisample) ? GL_TRUE : GL_FALSE; + return; + case GL_POLYGON_OFFSET_FILL: + *params = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PolygonOffsetFill) ? GL_TRUE : GL_FALSE; + return; + case GL_PRIMITIVE_RESTART_FIXED_INDEX: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex) + ? GL_TRUE + : GL_FALSE; + return; + case GL_RASTERIZER_DISCARD: + *params = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard) ? GL_TRUE : GL_FALSE; + return; + case GL_SAMPLE_ALPHA_TO_COVERAGE: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleAlphaToCoverage) + ? GL_TRUE + : GL_FALSE; + return; + case GL_SAMPLE_ALPHA_TO_ONE: + *params = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleAlphaToOne) ? GL_TRUE : GL_FALSE; + return; + case GL_SAMPLE_COVERAGE: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleCoverage) + ? GL_TRUE + : GL_FALSE; + return; + case GL_SAMPLE_MASK: + *params = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleMask) ? GL_TRUE : GL_FALSE; + return; + case GL_SCISSOR_TEST: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest) ? GL_TRUE : GL_FALSE; + return; + case GL_STENCIL_TEST: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest) ? GL_TRUE : GL_FALSE; + return; + default: + break; + } + + GLint ints[4] = {}; + GetIntegerv(pname, ints); + switch (pname) { + case GL_COLOR_WRITEMASK: + case GL_SCISSOR_BOX: + CopyIntsToBooleans(ints, 4, params); + return; + default: + *params = ints[0] ? GL_TRUE : GL_FALSE; + return; + } + } + + void GetFloatv(GLenum pname, GLfloat* params) { + MGLOG_D("glGetFloatv, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null")); + return; + } + + switch (pname) { + case GL_BLEND_COLOR: { + const FloatVec4& blendColor = MG_State::pGLContext->GetBlendColor(); + params[0] = blendColor.x(); + params[1] = blendColor.y(); + params[2] = blendColor.z(); + params[3] = blendColor.w(); + return; + } + case GL_COLOR_CLEAR_VALUE: { + const FloatVec4& clearColor = MG_State::pGLContext->GetClearColor(); + params[0] = clearColor.x(); + params[1] = clearColor.y(); + params[2] = clearColor.z(); + params[3] = clearColor.w(); + return; + } + case GL_DEPTH_RANGE: { + const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRange(); + params[0] = depthRange.x(); + params[1] = depthRange.y(); + return; + } + case GL_VIEWPORT_BOUNDS_RANGE: { + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + params[0] = dynamicParameters.ViewportBoundsRangeMin; + params[1] = dynamicParameters.ViewportBoundsRangeMax; + return; + } + case GL_DEPTH_CLEAR_VALUE: + params[0] = MG_State::pGLContext->GetClearDepth(); + return; + case GL_ALIASED_LINE_WIDTH_RANGE: { + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + params[0] = dynamicParameters.AliasedLineWidthRangeMin; + params[1] = dynamicParameters.AliasedLineWidthRangeMax; + return; + } + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_POINT_SIZE_RANGE: { + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + params[0] = dynamicParameters.PointSizeRangeMin; + params[1] = dynamicParameters.PointSizeRangeMax; + return; + } + case GL_LINE_WIDTH: + params[0] = MG_State::pGLContext->GetLineWidth(); + return; + case GL_POINT_SIZE: + params[0] = MG_State::pGLContext->GetPointSize(); + return; + case GL_POLYGON_OFFSET_FACTOR: + params[0] = MG_State::pGLContext->GetPolygonOffsetFactor(); + return; + case GL_POLYGON_OFFSET_UNITS: + params[0] = MG_State::pGLContext->GetPolygonOffsetUnits(); + return; + case GL_SMOOTH_LINE_WIDTH_RANGE: { + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + params[0] = dynamicParameters.SmoothLineWidthRangeMin; + params[1] = dynamicParameters.SmoothLineWidthRangeMax; + return; + } + case GL_SMOOTH_LINE_WIDTH_GRANULARITY: + params[0] = MG_Backend::pActiveBackendObject->GetDynamicParameters().SmoothLineWidthGranularity; + return; + case GL_POINT_SIZE_GRANULARITY: + params[0] = MG_Backend::pActiveBackendObject->GetDynamicParameters().PointSizeGranularity; + return; + case GL_SAMPLE_COVERAGE_VALUE: + params[0] = MG_State::pGLContext->GetSampleCoverageValue(); + return; + default: + break; + } + + GLint ints[4] = {}; + GetIntegerv(pname, ints); + switch (pname) { + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_SMOOTH_LINE_WIDTH_RANGE: + case GL_POINT_SIZE_RANGE: + case GL_MAX_VIEWPORT_DIMS: + CopyIntsToFloats(ints, 2, params); + return; + case GL_SCISSOR_BOX: + case GL_VIEWPORT: + CopyIntsToFloats(ints, 4, params); + return; + default: + params[0] = static_cast(ints[0]); + return; + } + } + void GetIntegeri_v(GLenum target, GLuint index, GLint* data) { if (!data) { MG_State::pGLContext->RecordError( @@ -127,6 +564,37 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", __func__, "data pointer cannot be null")); return; } + + BufferTarget bufferTarget = BufferTarget::Unknown; + IndexedBufferQueryKind queryKind = IndexedBufferQueryKind::Binding; + if (TryDecodeIndexedBufferQuery(target, bufferTarget, queryKind)) { + if (!ValidateIndexedBufferQueryIndex(target, index, __func__, bufferTarget)) return; + const auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); + const auto& bufferObject = bindingPoint.GetBoundObject(); + if (!bufferObject) { + *data = 0; + return; + } + + switch (queryKind) { + case IndexedBufferQueryKind::Binding: + *data = static_cast(bufferObject->GetExternalIndex()); + return; + case IndexedBufferQueryKind::Start: + *data = static_cast(bindingPoint.GetRange().start); + return; + case IndexedBufferQueryKind::Size: { + const Range1D range = bindingPoint.GetRange(); + const auto start = std::min(range.start, bufferObject->GetSize()); + const auto end = std::min(range.end, bufferObject->GetSize()); + *data = static_cast(end - start); + return; + } + default: + break; + } + } + auto getIntegeri = MG_Backend::gBackendFunctionsTable.GL.GetIntegeri_v; if (!getIntegeri) { *data = 0; @@ -145,6 +613,37 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", __func__, "data pointer cannot be null")); return; } + + BufferTarget bufferTarget = BufferTarget::Unknown; + IndexedBufferQueryKind queryKind = IndexedBufferQueryKind::Binding; + if (TryDecodeIndexedBufferQuery(target, bufferTarget, queryKind)) { + if (!ValidateIndexedBufferQueryIndex(target, index, __func__, bufferTarget)) return; + const auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); + const auto& bufferObject = bindingPoint.GetBoundObject(); + if (!bufferObject) { + *data = 0; + return; + } + + const Range1D range = bindingPoint.GetRange(); + switch (queryKind) { + case IndexedBufferQueryKind::Binding: + *data = static_cast(bufferObject->GetExternalIndex()); + return; + case IndexedBufferQueryKind::Start: + *data = static_cast(range.start); + return; + case IndexedBufferQueryKind::Size: { + const auto start = std::min(range.start, bufferObject->GetSize()); + const auto end = std::min(range.end, bufferObject->GetSize()); + *data = static_cast(end - start); + return; + } + default: + break; + } + } + auto getInteger64i = MG_Backend::gBackendFunctionsTable.GL.GetInteger64i_v; if (!getInteger64i) { *data = 0; @@ -156,6 +655,42 @@ namespace MobileGL::MG_Impl::GLImpl { getInteger64i(target, index, data); } + void GetInteger64v(GLenum pname, GLint64* params) { + MGLOG_D("glGetInteger64v, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null")); + return; + } + + GLint ints[4] = {}; + GetIntegerv(pname, ints); + + switch (pname) { + case GL_BLEND_COLOR: + case GL_COLOR_CLEAR_VALUE: + case GL_COLOR_WRITEMASK: + case GL_SCISSOR_BOX: + case GL_VIEWPORT: + for (int i = 0; i < 4; ++i) { + params[i] = static_cast(ints[i]); + } + return; + case GL_DEPTH_RANGE: + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_MAX_VIEWPORT_DIMS: + case GL_POINT_SIZE_RANGE: + case GL_VIEWPORT_BOUNDS_RANGE: + params[0] = static_cast(ints[0]); + params[1] = static_cast(ints[1]); + return; + default: + params[0] = static_cast(ints[0]); + return; + } + } + void GetIntegerv(GLenum pname, GLint* params) { MGLOG_D("glGetIntegerv, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); if (!params) { @@ -176,9 +711,19 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_ACTIVE_TEXTURE: *params = MG_State::pGLContext->GetActiveTextureUnit() + GL_TEXTURE0; break; - case GL_ALIASED_LINE_WIDTH_RANGE: - *params = 0; // TODO + case GL_ALIASED_LINE_WIDTH_RANGE: { + const auto& dynamicParameters = activeBackendObject->GetDynamicParameters(); + params[0] = static_cast(dynamicParameters.AliasedLineWidthRangeMin); + params[1] = static_cast(dynamicParameters.AliasedLineWidthRangeMax); break; + } + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_POINT_SIZE_RANGE: { + const auto& dynamicParameters = activeBackendObject->GetDynamicParameters(); + params[0] = static_cast(dynamicParameters.PointSizeRangeMin); + params[1] = static_cast(dynamicParameters.PointSizeRangeMax); + break; + } case GL_ARRAY_BUFFER_BINDING: { auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex).GetBoundObject(); if (obj) @@ -190,9 +735,14 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_BLEND: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Blend) ? GL_TRUE : GL_FALSE; break; - case GL_BLEND_COLOR: - *params = 0; // TODO + case GL_BLEND_COLOR: { + const FloatVec4& blendColor = MG_State::pGLContext->GetBlendColor(); + params[0] = static_cast(blendColor.x()); + params[1] = static_cast(blendColor.y()); + params[2] = static_cast(blendColor.z()); + params[3] = static_cast(blendColor.w()); break; + } case GL_BLEND_DST_ALPHA: { BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha; MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); @@ -231,11 +781,16 @@ namespace MobileGL::MG_Impl::GLImpl { *params = static_cast(MG_Util::ConvertBlendFactorToGLEnum(srcRGB)); break; } - case GL_COLOR_CLEAR_VALUE: - *params = 0; // TODO + case GL_COLOR_CLEAR_VALUE: { + const FloatVec4& clearColor = MG_State::pGLContext->GetClearColor(); + params[0] = static_cast(clearColor.x()); + params[1] = static_cast(clearColor.y()); + params[2] = static_cast(clearColor.z()); + params[3] = static_cast(clearColor.w()); break; + } case GL_COLOR_LOGIC_OP: - *params = 0; // TODO + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ColorLogicOp) ? GL_TRUE : GL_FALSE; break; case GL_COLOR_WRITEMASK: { BoolVec4 mask = MG_State::pGLContext->GetColorMask(); @@ -246,34 +801,36 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_COMPRESSED_TEXTURE_FORMATS: - *params = 0; // TODO + *params = 0; // compressed texture upload entrypoints are still unimplemented break; case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: - *params = 16; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxComputeShaderStorageBlocks; break; case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: - *params = 96; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxCombinedShaderStorageBlocks; break; case GL_MAX_COMPUTE_UNIFORM_BLOCKS: - *params = 14; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxComputeUniformBlocks; break; case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: - *params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxComputeTextureImageUnits; break; case GL_MAX_COMPUTE_UNIFORM_COMPONENTS: - *params = 1024 * 4; // TODO: use backend value + *params = kFrontendMaxComputeUniformComponents; break; case GL_MAX_COMPUTE_ATOMIC_COUNTERS: - *params = 8; // TODO: use backend value + *params = kFrontendMaxComputeAtomicCounters; break; case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS: - *params = 8; // TODO: use backend value + *params = kFrontendMaxComputeAtomicCounterBuffers; break; case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS: - *params = 1024 * 228; // TODO: use backend value + *params = GetMaxCombinedUniformComponents(kFrontendMaxComputeUniformComponents, + activeBackendObject->GetDynamicParameters().MaxComputeUniformBlocks, + activeBackendObject->GetDynamicParameters().MaxUniformBlockSize); break; case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: - *params = 1024; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxComputeWorkGroupInvocations; break; case GL_MAX_COMPUTE_WORK_GROUP_COUNT: GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, ¶ms[0]); @@ -291,13 +848,13 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_MAX_DEBUG_GROUP_STACK_DEPTH: - *params = 0; // TODO + *params = 0; // debug-group entrypoints are stubbed break; case GL_DEBUG_GROUP_STACK_DEPTH: - *params = 0; // TODO + *params = 0; // debug-group entrypoints are stubbed break; case GL_CONTEXT_FLAGS: - *params = 0; // TODO + *params = 0; // contexts are created without debug/robust/forward-compatible flags break; case GL_CULL_FACE: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace) ? GL_TRUE : GL_FALSE; @@ -319,26 +876,65 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_DEPTH_FUNC: *params = (GLint)MG_Util::ConvertDepthTestFuncToGLEnum(MG_State::pGLContext->GetDepthFunc()); break; - case GL_DEPTH_RANGE: - *params = 0; // TODO + case GL_DEPTH_RANGE: { + const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRange(); + params[0] = static_cast(depthRange.x()); + params[1] = static_cast(depthRange.y()); break; + } case GL_DEPTH_TEST: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest) ? GL_TRUE : GL_FALSE; break; case GL_DEPTH_WRITEMASK: *params = MG_State::pGLContext->GetDepthMask() ? GL_TRUE : GL_FALSE; break; + case GL_DEBUG_OUTPUT: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DebugOutput) ? GL_TRUE : GL_FALSE; + break; + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DebugOutputSynchronous) + ? GL_TRUE + : GL_FALSE; + break; case GL_DITHER: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Dither) ? GL_TRUE : GL_FALSE; break; - case GL_DOUBLEBUFFER: - *params = 0; // TODO + case GL_DOUBLEBUFFER: { + const auto currentDrawSurface = MG_State::pEGLContext->GetCurrentSurface(EGL_DRAW); + *params = MG_State::pEGLContext->IsDoubleBufferedSurface(currentDrawSurface) ? GL_TRUE : GL_FALSE; break; + } case GL_DRAW_BUFFER: - *params = 0; // TODO - break; case GL_DRAW_BUFFER0: - *params = 0; // TODO + case GL_DRAW_BUFFER1: + case GL_DRAW_BUFFER2: + case GL_DRAW_BUFFER3: + case GL_DRAW_BUFFER4: + case GL_DRAW_BUFFER5: + case GL_DRAW_BUFFER6: + case GL_DRAW_BUFFER7: + case GL_DRAW_BUFFER8: + case GL_DRAW_BUFFER9: + case GL_DRAW_BUFFER10: + case GL_DRAW_BUFFER11: + case GL_DRAW_BUFFER12: + case GL_DRAW_BUFFER13: + case GL_DRAW_BUFFER14: + case GL_DRAW_BUFFER15: + if (const auto& fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw) + .GetBoundObject()) { + SizeT drawBufferIndex = 0; + const bool decoded = TryDecodeDrawBufferQuery(pname, drawBufferIndex); + MOBILEGL_ASSERT(decoded, "Draw buffer query enum should have been decoded already: 0x%X", pname); + if (drawBufferIndex < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + *params = static_cast( + MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(fbo->GetDrawBuffers()[drawBufferIndex])); + } else { + *params = GL_NONE; + } + } else { + *params = 0; + } break; case GL_DRAW_FRAMEBUFFER_BINDING: { const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); @@ -360,67 +956,79 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: - *params = 0; // TODO + *params = GL_DONT_CARE; break; - case GL_IMPLEMENTATION_COLOR_READ_FORMAT: - *params = 0; // TODO + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: { + GLint format = 0; + GLint type = 0; + *params = TryResolveImplementationColorReadParams(format, type) ? format : 0; break; - case GL_IMPLEMENTATION_COLOR_READ_TYPE: - *params = 0; // TODO + } + case GL_IMPLEMENTATION_COLOR_READ_TYPE: { + GLint format = 0; + GLint type = 0; + *params = TryResolveImplementationColorReadParams(format, type) ? type : 0; break; + } case GL_LINE_SMOOTH: - *params = 0; // TODO + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::LineSmooth) ? GL_TRUE : GL_FALSE; break; case GL_LINE_SMOOTH_HINT: - *params = 0; // TODO + *params = GL_DONT_CARE; break; case GL_LINE_WIDTH: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetLineWidth()); break; case GL_LAYER_PROVOKING_VERTEX: - *params = 0; // TODO + *params = GL_LAST_VERTEX_CONVENTION; break; case GL_LOGIC_OP_MODE: - *params = 0; // TODO + *params = static_cast(MG_Util::ConvertLogicOperationToGLEnum(MG_State::pGLContext->GetLogicOp())); break; case GL_MAJOR_VERSION: *params = rendererInfo.RendererGLInfo.TargetGLVersion.Major; break; case GL_MAX_3D_TEXTURE_SIZE: - *params = 1024 * 16; // TODO + *params = activeBackendObject->GetDynamicParameters().Max3DTextureSize; break; case GL_MAX_ARRAY_TEXTURE_LAYERS: - *params = 2048; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxArrayTextureLayers; break; case GL_MAX_CLIP_DISTANCES: - *params = 8; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxClipDistances; break; case GL_MAX_COLOR_TEXTURE_SAMPLES: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxColorTextureSamples; break; case GL_MAX_COMBINED_ATOMIC_COUNTERS: - *params = 16; // TODO + *params = kFrontendMaxCombinedAtomicCounters; break; case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: - *params = 1024 * 228; // TODO + *params = GetMaxCombinedUniformComponents(kFrontendMaxFragmentUniformComponents, + kFrontendMaxFragmentUniformBlocks, + activeBackendObject->GetDynamicParameters().MaxUniformBlockSize); break; case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS: - *params = 1024 * 228; // TODO + *params = GetMaxCombinedUniformComponents(kFrontendMaxGeometryUniformComponents, + kFrontendMaxGeometryUniformBlocks, + activeBackendObject->GetDynamicParameters().MaxUniformBlockSize); break; case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: - *params = 192; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxCombinedTextureImageUnits; break; case GL_MAX_COMBINED_UNIFORM_BLOCKS: - *params = 70; // TODO + *params = kFrontendMaxCombinedUniformBlocks; break; case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: - *params = 1024 * 228; // TODO + *params = GetMaxCombinedUniformComponents(kFrontendMaxVertexUniformComponents, + kFrontendMaxVertexUniformBlocks, + activeBackendObject->GetDynamicParameters().MaxUniformBlockSize); break; case GL_MAX_CUBE_MAP_TEXTURE_SIZE: - *params = 1024 * 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxCubeMapTextureSize; break; case GL_MAX_DEPTH_TEXTURE_SAMPLES: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxDepthTextureSamples; break; case GL_MAX_DUAL_SOURCE_DRAW_BUFFERS: *params = 1; // TODO @@ -432,74 +1040,77 @@ namespace MobileGL::MG_Impl::GLImpl { *params = 1024 * 1024; // TODO break; case GL_MAX_FRAGMENT_ATOMIC_COUNTERS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxFragmentAtomicCounters; break; case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: *params = 16; // TODO break; case GL_MAX_FRAGMENT_INPUT_COMPONENTS: - *params = 128; // TODO + *params = kFrontendMaxFragmentInputComponents; break; case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxFragmentUniformComponents; break; case GL_MAX_FRAGMENT_UNIFORM_VECTORS: - *params = 256; // TODO + *params = kFrontendMaxFragmentUniformVectors; break; case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: - *params = 14; // TODO + *params = kFrontendMaxFragmentUniformBlocks; break; case GL_MAX_FRAMEBUFFER_WIDTH: - *params = 1024 * 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxFramebufferWidth; break; case GL_MAX_FRAMEBUFFER_HEIGHT: - *params = 1024 * 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxFramebufferHeight; break; case GL_MAX_FRAMEBUFFER_LAYERS: - *params = 1024 * 2; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxFramebufferLayers; break; case GL_MAX_FRAMEBUFFER_SAMPLES: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxFramebufferSamples; break; case GL_MAX_GEOMETRY_ATOMIC_COUNTERS: - *params = 14; // TODO + *params = kFrontendMaxGeometryAtomicCounters; break; case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS: *params = 16; // TODO break; case GL_MAX_GEOMETRY_INPUT_COMPONENTS: - *params = 128; // TODO + *params = kFrontendMaxGeometryInputComponents; break; case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS: - *params = 128; // TODO + *params = kFrontendMaxGeometryOutputComponents; break; case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: - *params = 8; // TODO + *params = kFrontendMaxGeometryTextureImageUnits; break; case GL_MAX_GEOMETRY_UNIFORM_BLOCKS: - *params = 14; // TODO + *params = kFrontendMaxGeometryUniformBlocks; break; case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxGeometryUniformComponents; break; case GL_MAX_IMAGE_UNITS: - *params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxImageUnits; break; case GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS: - *params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS + - MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; + *params = activeBackendObject->GetDynamicParameters().MaxImageUnits + + activeBackendObject->GetDynamicParameters().MaxDrawBuffers; break; case GL_MAX_COMBINED_IMAGE_UNIFORMS: - *params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxCombinedImageUniforms; break; case GL_MAX_IMAGE_SAMPLES: *params = 0; // multisampled image load/store is not exposed by the DirectGLES frontend break; case GL_MAX_COMPUTE_IMAGE_UNIFORMS: - *params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value + *params = activeBackendObject->GetDynamicParameters().MaxComputeImageUniforms; break; case GL_MAX_INTEGER_SAMPLES: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxIntegerSamples; + break; + case GL_MULTISAMPLE: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Multisample) ? GL_TRUE : GL_FALSE; break; case GL_MIN_MAP_BUFFER_ALIGNMENT: *params = 64; // TODO @@ -508,31 +1119,31 @@ namespace MobileGL::MG_Impl::GLImpl { *params = 256; // TODO break; case GL_MAX_PROGRAM_TEXEL_OFFSET: - *params = 7; // TODO + *params = kFrontendMaxProgramTexelOffset; break; case GL_MIN_PROGRAM_TEXEL_OFFSET: - *params = -8; // TODO + *params = kFrontendMinProgramTexelOffset; break; case GL_MAX_RECTANGLE_TEXTURE_SIZE: *params = 16 * 1024; // TODO break; case GL_MAX_RENDERBUFFER_SIZE: - *params = 16 * 1024; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxRenderbufferSize; break; case GL_MAX_SAMPLE_MASK_WORDS: - *params = 1; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxSampleMaskWords; break; case GL_MAX_SERVER_WAIT_TIMEOUT: *params = INT_MAX; // TODO break; case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings; break; case GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxTessControlAtomicCounters; break; case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxTessEvaluationAtomicCounters; break; case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS: *params = 16; // TODO @@ -541,77 +1152,77 @@ namespace MobileGL::MG_Impl::GLImpl { *params = 16; // TODO break; case GL_MAX_TEXTURE_BUFFER_SIZE: - *params = 1024 * 1024 * 128; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxTextureBufferSize; break; case GL_MAX_TEXTURE_IMAGE_UNITS: - *params = 32; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxTextureImageUnits; break; case GL_MAX_TEXTURE_LOD_BIAS: *params = 15; // TODO break; case GL_MAX_TEXTURE_SIZE: - *params = 1024 * 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxTextureSize; break; case GL_MAX_UNIFORM_BUFFER_BINDINGS: - *params = 84; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxUniformBufferBindings; break; case GL_MAX_UNIFORM_BLOCK_SIZE: - *params = 1024 * 24; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxUniformBlockSize; break; case GL_MAX_UNIFORM_LOCATIONS: *params = 1024 * 4; // TODO break; case GL_MAX_VARYING_COMPONENTS: - *params = 16; // TODO + *params = kFrontendMaxVaryingComponents; break; case GL_MAX_VARYING_VECTORS: - *params = 16; // TODO + *params = kFrontendMaxVaryingVectors; break; case GL_MAX_VERTEX_ATOMIC_COUNTERS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxVertexAtomicCounters; break; case GL_MAX_VERTEX_ATTRIBS: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxVertexAttribs; break; case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: *params = 16; // TODO break; case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: - *params = 32; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxVertexTextureImageUnits; break; case GL_MAX_VERTEX_UNIFORM_COMPONENTS: - *params = 1024 * 4; // TODO + *params = kFrontendMaxVertexUniformComponents; break; case GL_MAX_VERTEX_UNIFORM_VECTORS: - *params = 1024; // TODO + *params = kFrontendMaxVertexUniformVectors; break; case GL_MAX_VERTEX_OUTPUT_COMPONENTS: - *params = 128; // TODO + *params = kFrontendMaxVertexOutputComponents; break; case GL_MAX_VERTEX_UNIFORM_BLOCKS: - *params = 14; // TODO + *params = kFrontendMaxVertexUniformBlocks; break; case GL_MAX_VIEWPORT_DIMS: - params[0] = 1024 * 16; // TODO - params[1] = 1024 * 16; // TODO + params[0] = activeBackendObject->GetDynamicParameters().MaxViewportWidth; + params[1] = activeBackendObject->GetDynamicParameters().MaxViewportHeight; break; case GL_MAX_VIEWPORTS: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxViewports; break; case GL_MINOR_VERSION: *params = rendererInfo.RendererGLInfo.TargetGLVersion.Minor; break; case GL_NUM_COMPRESSED_TEXTURE_FORMATS: - *params = 18; // TODO + *params = 0; // compressed texture upload entrypoints are still unimplemented break; case GL_NUM_EXTENSIONS: *params = (Int)rendererInfo.RendererGLInfo.Extensions.size(); break; case GL_NUM_PROGRAM_BINARY_FORMATS: - *params = 2; // TODO + *params = 0; break; case GL_NUM_SHADER_BINARY_FORMATS: - *params = 0; // TODO + *params = 0; // ShaderBinary entrypoints are stubbed break; case GL_PACK_ALIGNMENT: *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::PackAlignment); @@ -638,47 +1249,57 @@ namespace MobileGL::MG_Impl::GLImpl { *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::PackSwapBytes); break; case GL_PIXEL_PACK_BUFFER_BINDING: - *params = 0; // TODO + if (const auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject()) { + *params = static_cast(obj->GetExternalIndex()); + } else { + *params = 0; + } break; case GL_PIXEL_UNPACK_BUFFER_BINDING: - *params = 0; // TODO + if (const auto& obj = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject()) { + *params = static_cast(obj->GetExternalIndex()); + } else { + *params = 0; + } break; case GL_POINT_FADE_THRESHOLD_SIZE: - *params = 0; // TODO + *params = 1; + break; + case GL_PRIMITIVE_RESTART: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) ? GL_TRUE + : GL_FALSE; break; case GL_PRIMITIVE_RESTART_INDEX: - *params = 0; // TODO + *params = 0; // fixed default; PrimitiveRestartIndex entrypoints are stubbed break; case GL_PROGRAM_BINARY_FORMATS: - *params = 0; // TODO + *params = 0; // program-binary entrypoints are stubbed break; case GL_PROGRAM_PIPELINE_BINDING: - *params = 0; // TODO + *params = 0; // program-pipeline entrypoints are stubbed break; case GL_PROGRAM_POINT_SIZE: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ProgramPointSize) ? GL_TRUE : GL_FALSE; break; case GL_PROVOKING_VERTEX: - *params = 0; // TODO + *params = GL_LAST_VERTEX_CONVENTION; break; case GL_POINT_SIZE: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetPointSize()); break; case GL_POLYGON_MODE: params[0] = GL_FILL; params[1] = GL_FILL; break; case GL_POINT_SIZE_GRANULARITY: - *params = 0; // TODO - break; - case GL_POINT_SIZE_RANGE: - *params = 0; // TODO + *params = static_cast(activeBackendObject->GetDynamicParameters().PointSizeGranularity); break; case GL_POLYGON_OFFSET_FACTOR: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetPolygonOffsetFactor()); break; case GL_POLYGON_OFFSET_UNITS: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetPolygonOffsetUnits()); break; case GL_POLYGON_OFFSET_FILL: *params = @@ -696,25 +1317,51 @@ namespace MobileGL::MG_Impl::GLImpl { *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PolygonSmooth) ? GL_TRUE : GL_FALSE; break; case GL_POLYGON_SMOOTH_HINT: - *params = 0; // TODO + *params = GL_DONT_CARE; break; case GL_READ_BUFFER: - *params = 0; // TODO + if (const auto& fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read) + .GetBoundObject()) { + *params = + static_cast(MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(fbo->GetReadBuffer())); + } else { + *params = 0; + } break; case GL_RENDERBUFFER_BINDING: - *params = 0; // TODO + if (const auto& obj = + MG_State::pGLContext->GetRenderbufferBindingSlot(RenderbufferTarget::Renderbuffer).GetBoundObject()) { + *params = static_cast(obj->GetExternalIndex()); + } else { + *params = 0; + } break; case GL_SAMPLE_BUFFERS: - *params = 0; // TODO + *params = ResolveDrawFramebufferSampleCount() > 0 ? 1 : 0; break; case GL_SAMPLE_COVERAGE_VALUE: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetSampleCoverageValue()); break; case GL_SAMPLE_COVERAGE_INVERT: - *params = 0; // TODO + *params = MG_State::pGLContext->GetSampleCoverageInvert() ? GL_TRUE : GL_FALSE; + break; + case GL_SAMPLE_ALPHA_TO_COVERAGE: + *params = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleAlphaToCoverage) ? GL_TRUE : GL_FALSE; + break; + case GL_SAMPLE_ALPHA_TO_ONE: + *params = + MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleAlphaToOne) ? GL_TRUE : GL_FALSE; + break; + case GL_SAMPLE_COVERAGE: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleCoverage) ? GL_TRUE + : GL_FALSE; + break; + case GL_SAMPLE_MASK: + *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleMask) ? GL_TRUE : GL_FALSE; break; case GL_SAMPLE_MASK_VALUE: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetSampleMaskValue()); break; case GL_SAMPLER_BINDING: { Int unit = MG_State::pGLContext->GetActiveTextureUnit(); @@ -724,16 +1371,21 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_SAMPLES: - *params = 0; // TODO + *params = ResolveDrawFramebufferSampleCount(); break; - case GL_SCISSOR_BOX: - *params = 0; // TODO + case GL_SCISSOR_BOX: { + const IntVec4& scissorBox = MG_State::pGLContext->GetScissorBox(); + params[0] = scissorBox.x(); + params[1] = scissorBox.y(); + params[2] = scissorBox.z(); + params[3] = scissorBox.w(); break; + } case GL_SCISSOR_TEST: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest) ? GL_TRUE : GL_FALSE; break; case GL_SHADER_COMPILER: - *params = 0; // TODO + *params = GL_TRUE; break; case GL_SHADER_STORAGE_BUFFER_BINDING: { auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::ShaderStorage).GetBoundObject(); @@ -741,80 +1393,110 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT: - *params = 256; // TODO: use backend value + *params = static_cast(activeBackendObject->GetDynamicParameters().UniformBufferOffsetAlignment); break; case GL_SHADER_STORAGE_BUFFER_START: - *params = 0; // TODO + RecordIndexedOnlyGetterError(__func__, pname); break; case GL_SHADER_STORAGE_BUFFER_SIZE: - *params = 0; // TODO + RecordIndexedOnlyGetterError(__func__, pname); break; - case GL_SMOOTH_LINE_WIDTH_RANGE: - *params = 0; // TODO + case GL_SMOOTH_LINE_WIDTH_RANGE: { + const auto& dynamicParameters = activeBackendObject->GetDynamicParameters(); + params[0] = static_cast(dynamicParameters.SmoothLineWidthRangeMin); + params[1] = static_cast(dynamicParameters.SmoothLineWidthRangeMax); break; + } case GL_SMOOTH_LINE_WIDTH_GRANULARITY: - *params = 0; // TODO + *params = static_cast(activeBackendObject->GetDynamicParameters().SmoothLineWidthGranularity); + break; break; case GL_STENCIL_BACK_FAIL: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertStencilOperationToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Back).FailOp)); break; case GL_STENCIL_BACK_FUNC: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertDepthTestFuncToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Back).Func)); break; case GL_STENCIL_BACK_PASS_DEPTH_FAIL: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertStencilOperationToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Back).PassDepthFailOp)); break; case GL_STENCIL_BACK_PASS_DEPTH_PASS: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertStencilOperationToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Back).PassDepthPassOp)); break; case GL_STENCIL_BACK_REF: - *params = 0; // TODO + *params = MG_State::pGLContext->GetStencilState(StencilFace::Back).Ref; break; case GL_STENCIL_BACK_VALUE_MASK: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetStencilState(StencilFace::Back).ValueMask); break; case GL_STENCIL_BACK_WRITEMASK: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetStencilState(StencilFace::Back).WriteMask); break; case GL_STENCIL_CLEAR_VALUE: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetClearStencil()); break; case GL_STENCIL_FAIL: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertStencilOperationToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Front).FailOp)); break; case GL_STENCIL_FUNC: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertDepthTestFuncToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Front).Func)); break; case GL_STENCIL_PASS_DEPTH_FAIL: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertStencilOperationToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Front).PassDepthFailOp)); break; case GL_STENCIL_PASS_DEPTH_PASS: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertStencilOperationToGLEnum( + MG_State::pGLContext->GetStencilState(StencilFace::Front).PassDepthPassOp)); break; case GL_STENCIL_REF: - *params = 0; // TODO + *params = MG_State::pGLContext->GetStencilState(StencilFace::Front).Ref; break; case GL_STENCIL_TEST: *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest) ? GL_TRUE : GL_FALSE; break; case GL_STENCIL_VALUE_MASK: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetStencilState(StencilFace::Front).ValueMask); break; case GL_STENCIL_WRITEMASK: - *params = 0; // TODO + *params = static_cast(MG_State::pGLContext->GetStencilState(StencilFace::Front).WriteMask); break; case GL_STEREO: - *params = 0; // TODO + *params = 0; // stereo surfaces are not exposed break; case GL_SUBPIXEL_BITS: - *params = 0; // TODO + *params = activeBackendObject->GetDynamicParameters().ViewportSubpixelBits; break; - case GL_TEXTURE_BINDING_1D: - *params = 0; // TODO + case GL_TEXTURE_BINDING_1D: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::Texture1D); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; - case GL_TEXTURE_BINDING_1D_ARRAY: - *params = 0; // TODO + } + case GL_TEXTURE_BINDING_1D_ARRAY: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::Texture1DArray); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; + } case GL_TEXTURE_BINDING_2D: { Int unit = MG_State::pGLContext->GetActiveTextureUnit(); auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); @@ -824,15 +1506,30 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("Get GL_TEXTURE_BINDING_2D: %d", *params); break; } - case GL_TEXTURE_BINDING_2D_ARRAY: - *params = 0; // TODO + case GL_TEXTURE_BINDING_2D_ARRAY: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::Texture2DArray); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; - case GL_TEXTURE_BINDING_2D_MULTISAMPLE: - *params = 0; // TODO + } + case GL_TEXTURE_BINDING_2D_MULTISAMPLE: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::Texture2DMultisample); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; - case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: - *params = 0; // TODO + } + case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::Texture2DMultisampleArray); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; + } case GL_TEXTURE_BINDING_3D: { Int unit = MG_State::pGLContext->GetActiveTextureUnit(); auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); @@ -841,9 +1538,14 @@ namespace MobileGL::MG_Impl::GLImpl { *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; } - case GL_TEXTURE_BINDING_BUFFER: - *params = 0; // TODO + case GL_TEXTURE_BINDING_BUFFER: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::TextureBuffer); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; + } case GL_TEXTURE_BINDING_CUBE_MAP: { Int unit = MG_State::pGLContext->GetActiveTextureUnit(); auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); @@ -852,38 +1554,52 @@ namespace MobileGL::MG_Impl::GLImpl { *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; } - case GL_TEXTURE_BINDING_RECTANGLE: - *params = 0; // TODO + case GL_TEXTURE_BINDING_RECTANGLE: { + Int unit = MG_State::pGLContext->GetActiveTextureUnit(); + auto& tu = MG_State::pGLContext->GetTextureUnitObject(unit); + const auto& slot = tu.GetBindingSlot(TextureTarget::TextureRectangle); + const auto& obj = slot.GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; break; + } case GL_TEXTURE_COMPRESSION_HINT: - *params = 0; // TODO + *params = GL_DONT_CARE; break; case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT: - *params = 0; // TODO + *params = 0; // texture-buffer range entrypoints are stubbed break; case GL_TIMESTAMP: - *params = 0; // TODO + *params = 0; // timer-query entrypoints are stubbed break; case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: - *params = 0; // TODO + if (const auto& obj = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::TransformFeedback).GetBoundObject()) { + *params = static_cast(obj->GetExternalIndex()); + } else { + *params = 0; + } break; case GL_TRANSFORM_FEEDBACK_BUFFER_START: - *params = 0; // TODO + RecordIndexedOnlyGetterError(__func__, pname); break; case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: - *params = 0; // TODO + RecordIndexedOnlyGetterError(__func__, pname); break; case GL_UNIFORM_BUFFER_BINDING: - *params = 0; // TODO + if (const auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Uniform).GetBoundObject()) { + *params = static_cast(obj->GetExternalIndex()); + } else { + *params = 0; + } break; case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: *params = (Int)activeBackendObject->GetDynamicParameters().UniformBufferOffsetAlignment; break; case GL_UNIFORM_BUFFER_SIZE: - *params = 0; // TODO + RecordIndexedOnlyGetterError(__func__, pname); break; case GL_UNIFORM_BUFFER_START: - *params = 0; // TODO + RecordIndexedOnlyGetterError(__func__, pname); break; case GL_UNPACK_ALIGNMENT: *params = MG_State::pGLContext->GetPixelStoreParam(PixelStoreParam::UnpackAlignment); @@ -915,19 +1631,19 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_VERTEX_BINDING_DIVISOR: - *params = 0; // TODO + *params = 0; // vertex-binding entrypoints are stubbed break; case GL_VERTEX_BINDING_OFFSET: - *params = 0; // TODO + *params = 0; // vertex-binding entrypoints are stubbed break; case GL_VERTEX_BINDING_STRIDE: - *params = 0; // TODO + *params = 0; // vertex-binding entrypoints are stubbed break; case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET: - *params = 0; // TODO + *params = 0; // vertex-binding entrypoints are stubbed break; case GL_MAX_VERTEX_ATTRIB_BINDINGS: - *params = 0; // TODO + *params = 0; // vertex-binding entrypoints are stubbed break; case GL_VIEWPORT: { const auto& vp = MG_State::pGLContext->GetViewport(); @@ -938,13 +1654,14 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_VIEWPORT_BOUNDS_RANGE: - *params = 0; // TODO + params[0] = static_cast(activeBackendObject->GetDynamicParameters().ViewportBoundsRangeMin); + params[1] = static_cast(activeBackendObject->GetDynamicParameters().ViewportBoundsRangeMax); break; case GL_VIEWPORT_INDEX_PROVOKING_VERTEX: - *params = 0; // TODO + *params = GL_LAST_VERTEX_CONVENTION; break; case GL_VIEWPORT_SUBPIXEL_BITS: - *params = 0; // TODO + *params = activeBackendObject->GetDynamicParameters().ViewportSubpixelBits; break; case GL_MAX_ELEMENT_INDEX: *params = 1024 * 1024; // TODO @@ -954,10 +1671,11 @@ namespace MobileGL::MG_Impl::GLImpl { break; case GL_MAX_COLOR_ATTACHMENTS: case GL_MAX_DRAW_BUFFERS: - *params = MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; // TODO: use backend value + *params = pname == GL_MAX_COLOR_ATTACHMENTS ? activeBackendObject->GetDynamicParameters().MaxColorAttachments + : activeBackendObject->GetDynamicParameters().MaxDrawBuffers; break; case GL_MAX_SAMPLES: - *params = 16; // TODO + *params = activeBackendObject->GetDynamicParameters().MaxSamples; break; default: MGLOG_E("glGetIntegerv: Invalid enum %s (0x%X)", MG_Util::ConvertGLEnumToString(pname).c_str(), pname); diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h index 4e5b8d97..3700e2c2 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h @@ -13,7 +13,10 @@ namespace MobileGL::MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ const GLubyte* GetString(GLenum name); const GLubyte* GetStringi(GLenum name, GLuint index); + void GetBooleanv(GLenum pname, GLboolean* params); + void GetFloatv(GLenum pname, GLfloat* params); void GetIntegerv(GLenum pname, GLint* params); + void GetInteger64v(GLenum pname, GLint64* params); void GetIntegeri_v(GLenum target, GLuint index, GLint* data); void GetInteger64i_v(GLenum target, GLuint index, GLint64* data); GLenum GetError(); diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 6458f8fa..9633a297 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -16,6 +16,10 @@ #include namespace MobileGL::MG_Impl::GLImpl { + static GLint BoolToGLInt(bool value) { + return value ? GL_TRUE : GL_FALSE; + } + static bool CheckShaderNameValidity(Uint shader) { if (shader == 0 || !MG_State::pGLContext->ValidateShaderName(shader)) { MG_State::pGLContext->RecordError( @@ -80,6 +84,16 @@ namespace MobileGL::MG_Impl::GLImpl { if (length) *length = sz; } + bool RecordInvalidUniformLocationError(const char* functionName, GLint location, const String& targetDescription) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", functionName, + "location " + std::to_string(location) + + " does not correspond to a valid uniform variable location for " + + targetDescription + ".")); + return false; + } + void AttachShader_State(GLuint program, GLuint shader) { auto& programObject = TryToGetProgramObject(program); if (!programObject) return; @@ -188,9 +202,10 @@ namespace MobileGL::MG_Impl::GLImpl { std::to_string(program) + ".")); return; } - if (type != nullptr) *type = programObject->GetAttribType(index); + if (size != nullptr) *size = programObject->GetActiveAttribArraySize(index); + if (type != nullptr) *type = programObject->GetActiveAttribType(index); if (bufSize == 0) return; - auto& attribName = programObject->GetAttribName(index); + auto& attribName = programObject->GetActiveAttribName(index); CopyStr(bufSize, length, name, attribName.c_str(), (GLsizei)attribName.length()); } @@ -216,13 +231,38 @@ namespace MobileGL::MG_Impl::GLImpl { std::to_string(program) + ".")); return; } - - if (type != nullptr) *type = programObject->GetUniformType(index); + if (size != nullptr) *size = programObject->GetActiveUniformArraySize(index); + if (type != nullptr) *type = programObject->GetActiveUniformType(index); if (bufSize == 0) return; - auto& uniformName = programObject->GetUniformName(index); + auto& uniformName = programObject->GetActiveUniformName(index); CopyStr(bufSize, length, name, uniformName.c_str(), (GLsizei)uniformName.length()); } + void GetUniformIndices_State(GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, + GLuint* uniformIndices) { + if (uniformCount < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, + "uniformCount " + std::to_string(uniformCount) + " is less than 0.")); + return; + } + auto& programObject = TryToGetProgramObject(program); + if (!programObject || !programObject->GetLinkStatus()) return; + if (uniformCount == 0 || uniformNames == nullptr || uniformIndices == nullptr) return; + + for (GLsizei i = 0; i < uniformCount; ++i) { + const char* uniformName = uniformNames[i]; + if (uniformName == nullptr) { + uniformIndices[i] = GL_INVALID_INDEX; + continue; + } + + const Int uniformIndex = programObject->GetActiveUniformIndex(uniformName); + uniformIndices[i] = uniformIndex >= 0 ? static_cast(uniformIndex) : GL_INVALID_INDEX; + } + } + void GetAttachedShaders_State(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) { if (maxCount < 0) { MG_State::pGLContext->RecordError( @@ -268,7 +308,7 @@ namespace MobileGL::MG_Impl::GLImpl { break; case GL_INFO_LOG_LENGTH: { const auto& log = programObject->GetInfoLog(); - *params = (GLint)log.length(); + *params = log.empty() ? 0 : static_cast(log.length()) + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; } @@ -287,7 +327,7 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: - *params = programObject->GetActiveAttributesMaxLength(); + *params = programObject->GetActiveAttributesMaxLength() + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORMS: @@ -295,7 +335,7 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORM_MAX_LENGTH: - *params = programObject->GetUniformMaxLength(); + *params = programObject->GetUniformMaxLength() + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORM_BLOCKS: // GL >= 3.1 @@ -303,22 +343,21 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: // ditto. - *params = programObject->GetActiveUniformBlocksMaxNameLength(); + *params = programObject->GetActiveUniformBlocksMaxNameLength() + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_COMPUTE_WORK_GROUP_SIZE: { // GL >= 4.3 - auto getProgramiv = MG_Backend::gBackendFunctionsTable.GL.GetProgramiv; - if (!getProgramiv) { - params[0] = 1; - params[1] = 1; - params[2] = 1; + if (!programObject->GetLinkStatus()) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", __func__, - "Backend does not support program integer queries.")); + std::to_string(program) + + " is not a program object that has been linked.")); return; } - getProgramiv(program, pname, params); + params[0] = static_cast(programObject->GetComputeLocalSize(0)); + params[1] = static_cast(programObject->GetComputeLocalSize(1)); + params[2] = static_cast(programObject->GetComputeLocalSize(2)); MGLOG_D("%s: %s = (%d, %d, %d)", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), params[0], params[1], params[2]); break; @@ -423,25 +462,13 @@ namespace MobileGL::MG_Impl::GLImpl { } // Check if location is valid - if (location < 0 || location > programObject->GetMaxUniformLocation()) { + if (!programObject->IsValidUniformLocation(location)) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " does not correspond to a valid uniform variable location " - "for the specified program object.")); - return; - } - - // Check if the location corresponds to an active uniform - const auto& uniformName = programObject->GetUniformName(location); - if (uniformName.empty()) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " does not correspond to a valid uniform variable location " - "for the specified program object.")); + MakeUnique( + "MG_Impl/GLImpl", __func__, + "location " + std::to_string(location) + + " does not correspond to a valid uniform variable location for the specified program object.")); return; } @@ -468,12 +495,64 @@ namespace MobileGL::MG_Impl::GLImpl { // TODO: handle 1i variant as texture unit } + template + void GetUniformScalar_State(GLuint program, GLint location, T* params) { + auto& programObject = TryToGetProgramObject(program); + if (!programObject) return; + + if (!programObject->GetLinkStatus()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + std::to_string(program) + " has not been successfully linked.")); + return; + } + + if (!programObject->IsValidUniformLocation(location)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", __func__, + "location " + std::to_string(location) + + " does not correspond to a valid uniform variable location for the specified program object.")); + return; + } + + if (programObject->IsUniformOpaqueAtLocation(location)) { + const Int unit = std::max(programObject->GetUniformSamplerOrImageUnitIndex(location), 0); + *params = static_cast(unit); + return; + } + + auto offset = programObject->GetUniformOffset(location); + auto size = programObject->GetUniformSizesInBytes(location); + char* pUBO = static_cast(programObject->MapUBO()); + auto* ttype = programObject->GetUniformTType(location); + + if constexpr (std::is_same_v) { + if (ttype->isMatrix() && ttype->getMatrixCols() == 3) { + auto* pBase = pUBO + offset; + for (int i = 0; i < ttype->getMatrixRows(); i++) { + Memcpy(reinterpret_cast(params) + ttype->getMatrixCols() * sizeof(GLfloat) * i, + pBase + 4 * sizeof(GLfloat) * i, ttype->getMatrixCols() * sizeof(GLfloat)); + } + return; + } + } + + Memcpy(params, pUBO + offset, size); + } + void GetUniformfv_State(GLuint program, GLint location, GLfloat* params) { - GetUniform_State(program, location, params); + GetUniformScalar_State(program, location, params); } void GetUniformiv_State(GLuint program, GLint location, GLint* params) { - GetUniform_State(program, location, params); + GetUniformScalar_State(program, location, params); + } + + void GetUniformuiv_State(GLuint program, GLint location, GLuint* params) { + GetUniformScalar_State(program, location, params); } GLboolean IsProgram_State(GLuint program) { @@ -585,18 +664,11 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for the current program " - "object and location " + - std::to_string(location) + " is not equal to -1.")); - return; - } - for (GLint offset = 0; offset < count; offset++) { + if (!programObject->IsValidUniformLocation(location + offset)) { + RecordInvalidUniformLocationError(__func__, location + offset, "the current program object"); + return; + } Uniform_State(*programObject, location + offset, value + offset * ItemCount); } } @@ -616,17 +688,12 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for program " + - std::to_string(program) + ".")); - return; - } - for (GLint offset = 0; offset < count; offset++) { + if (!programObject->IsValidUniformLocation(location + offset)) { + RecordInvalidUniformLocationError(__func__, location + offset, + "program " + std::to_string(program)); + return; + } Uniform_State(*programObject, location + offset, value + offset * ItemCount); } } @@ -744,19 +811,12 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for the current program " - "object and location " + - std::to_string(location) + " is not equal to -1.")); - return; - } - // For matrix uniforms, we handle each matrix individually for (GLint i = 0; i < count; i++) { + if (!programObject->IsValidUniformLocation(location + i)) { + RecordInvalidUniformLocationError(__func__, location + i, "the current program object"); + return; + } if (transpose == GL_TRUE) { // Transpose the matrix before uploading GLfloat transposedMatrix[4]; @@ -782,20 +842,13 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for the current program " - "object and location " + - std::to_string(location) + " is not equal to -1.")); - return; - } - // For matrix uniforms, we handle each matrix individually // Handle padding in mat3 correctly!! for (GLint i = 0; i < count; i++) { + if (!programObject->IsValidUniformLocation(location + i)) { + RecordInvalidUniformLocationError(__func__, location + i, "the current program object"); + return; + } if (transpose == GL_TRUE) { // Transpose the matrix before uploading GLfloat transposedMatrix[9]; @@ -825,19 +878,12 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for the current program " - "object and location " + - std::to_string(location) + " is not equal to -1.")); - return; - } - // For matrix uniforms, we handle each matrix individually for (GLint i = 0; i < count; i++) { + if (!programObject->IsValidUniformLocation(location + i)) { + RecordInvalidUniformLocationError(__func__, location + i, "the current program object"); + return; + } if (transpose == GL_TRUE) { // Transpose the matrix before uploading GLfloat transposedMatrix[16]; @@ -865,17 +911,11 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for program " + - std::to_string(program) + ".")); - return; - } - for (GLint i = 0; i < count; i++) { + if (!programObject->IsValidUniformLocation(location + i)) { + RecordInvalidUniformLocationError(__func__, location + i, "program " + std::to_string(program)); + return; + } if (transpose == GL_TRUE) { GLfloat transposedMatrix[4]; TransposeMatrix2x2(value + i * 4, transposedMatrix); @@ -901,17 +941,11 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for program " + - std::to_string(program) + ".")); - return; - } - for (GLint i = 0; i < count; i++) { + if (!programObject->IsValidUniformLocation(location + i)) { + RecordInvalidUniformLocationError(__func__, location + i, "program " + std::to_string(program)); + return; + } if (transpose == GL_TRUE) { GLfloat transposedMatrix[9]; TransposeMatrix3x3(value + i * 9, transposedMatrix); @@ -941,17 +975,11 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - if (location > programObject->GetMaxUniformLocation() || location < -1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "location " + std::to_string(location) + - " is an invalid uniform location for program " + - std::to_string(program) + ".")); - return; - } - for (GLint i = 0; i < count; i++) { + if (!programObject->IsValidUniformLocation(location + i)) { + RecordInvalidUniformLocationError(__func__, location + i, "program " + std::to_string(program)); + return; + } if (transpose == GL_TRUE) { GLfloat transposedMatrix[16]; TransposeMatrix4x4(value + i * 16, transposedMatrix); @@ -1033,22 +1061,52 @@ namespace MobileGL::MG_Impl::GLImpl { break; } case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: { - // TODO: deduct global ubo? - *params = programObject->GetActiveUniformBlocksCount(); + *params = programObject->GetUniformBlockActiveUniformCount(uniformBlockIndex); MGLOG_D("%s: GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS = %d", __func__, *params); break; } case GL_UNIFORM_BLOCK_BINDING: { - // TODO - MGLOG_D("%s: GL_UNIFORM_BLOCK_BINDING = ", __func__, *params); + *params = static_cast(programObject->GetUniformBlockBinding(uniformBlockIndex)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_BINDING = %d", __func__, *params); + break; } - case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: + *params = BoolToGLInt(programObject->IsUniformBlockReferencedByStage(uniformBlockIndex, EShLangVertex)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = %d", __func__, *params); + break; case GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER: + *params = + BoolToGLInt(programObject->IsUniformBlockReferencedByStage(uniformBlockIndex, EShLangTessControl)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER = %d", __func__, *params); + break; case GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER: + *params = + BoolToGLInt(programObject->IsUniformBlockReferencedByStage(uniformBlockIndex, EShLangTessEvaluation)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER = %d", __func__, *params); + break; case GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER: + *params = BoolToGLInt(programObject->IsUniformBlockReferencedByStage(uniformBlockIndex, EShLangGeometry)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER = %d", __func__, *params); + break; case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: + *params = BoolToGLInt(programObject->IsUniformBlockReferencedByStage(uniformBlockIndex, EShLangFragment)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = %d", __func__, *params); + break; case GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER: + *params = BoolToGLInt(programObject->IsUniformBlockReferencedByStage(uniformBlockIndex, EShLangCompute)); + MGLOG_D("%s: GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER = %d", __func__, *params); + break; + case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: { + GLint uniformIndexCount = 0; + for (Uint uniformIndex = 0; uniformIndex < programObject->GetUniformCount(); ++uniformIndex) { + if (programObject->GetActiveUniformBlockIndex(uniformIndex) != static_cast(uniformBlockIndex)) { + continue; + } + params[uniformIndexCount++] = static_cast(uniformIndex); + } + MGLOG_D("%s: GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES count = %d", __func__, uniformIndexCount); + break; + } default: MGLOG_E("%s: unknown pname = %p %s", __func__, pname, MG_Util::ConvertGLEnumToString(pname).c_str()); MG_State::pGLContext->RecordError( @@ -1084,7 +1142,7 @@ namespace MobileGL::MG_Impl::GLImpl { const auto& name = programObject->GetUniformBlockName(uniformBlockIndex); CopyStr(bufSize, length, uniformBlockName, name.c_str(), (GLsizei)name.length()); MGLOG_D("%s: \"%s\" at uniformBlockIndex %02d, length = %d", __func__, uniformBlockName, uniformBlockIndex, - *length); + length ? *length : 0); } void BindFragDataLocation_State(GLuint program, GLuint colorNumber, const char* name) { @@ -1166,6 +1224,16 @@ namespace MobileGL::MG_Impl::GLImpl { GetActiveUniform_State(program, index, bufSize, length, size, type, name); } + void GetActiveUniformName(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, + GLchar* uniformName) { + GetActiveUniform_State(program, uniformIndex, bufSize, length, nullptr, nullptr, uniformName); + } + + void GetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, + GLuint* uniformIndices) { + GetUniformIndices_State(program, uniformCount, uniformNames, uniformIndices); + } + void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders) { GetAttachedShaders_State(program, maxCount, count, shaders); } @@ -1206,6 +1274,10 @@ namespace MobileGL::MG_Impl::GLImpl { GetUniformiv_State(program, location, params); } + void GetUniformuiv(GLuint program, GLint location, GLuint* params) { + GetUniformuiv_State(program, location, params); + } + GLboolean IsProgram(GLuint program) { return IsProgram_State(program); } @@ -1263,6 +1335,25 @@ namespace MobileGL::MG_Impl::GLImpl { Uniform4iv(location, 1, v); } + void Uniform1ui(GLint location, GLuint v0) { + Uniform1uiv(location, 1, &v0); + } + + void Uniform2ui(GLint location, GLuint v0, GLuint v1) { + GLuint v[] = {v0, v1}; + Uniform2uiv(location, 1, v); + } + + void Uniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2) { + GLuint v[] = {v0, v1, v2}; + Uniform3uiv(location, 1, v); + } + + void Uniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { + GLuint v[] = {v0, v1, v2, v3}; + Uniform4uiv(location, 1, v); + } + void Uniform1fv(GLint location, GLsizei count, const GLfloat* value) { Uniform1fv_State(location, count, value); } @@ -1295,6 +1386,22 @@ namespace MobileGL::MG_Impl::GLImpl { Uniform4iv_State(location, count, value); } + void Uniform1uiv(GLint location, GLsizei count, const GLuint* value) { + Uniformv_State<1>(location, count, value); + } + + void Uniform2uiv(GLint location, GLsizei count, const GLuint* value) { + Uniformv_State<2>(location, count, value); + } + + void Uniform3uiv(GLint location, GLsizei count, const GLuint* value) { + Uniformv_State<3>(location, count, value); + } + + void Uniform4uiv(GLint location, GLsizei count, const GLuint* value) { + Uniformv_State<4>(location, count, value); + } + void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { UniformMatrix2fv_State(location, count, transpose, value); } diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h index d58e643f..97707865 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h @@ -22,6 +22,10 @@ namespace MobileGL::MG_Impl::GLImpl { GLchar* name); void GetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); + void GetActiveUniformName(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, + GLchar* uniformName); + void GetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, + GLuint* uniformIndices); void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders); GLint GetAttribLocation(GLuint program, const GLchar* name); void GetProgramiv(GLuint program, GLenum pname, GLint* params); @@ -32,6 +36,7 @@ namespace MobileGL::MG_Impl::GLImpl { GLint GetUniformLocation(GLuint program, const GLchar* name); void GetUniformfv(GLuint program, GLint location, GLfloat* params); void GetUniformiv(GLuint program, GLint location, GLint* params); + void GetUniformuiv(GLuint program, GLint location, GLuint* params); GLboolean IsProgram(GLuint program); GLboolean IsShader(GLuint shader); void LinkProgram(GLuint program); @@ -45,6 +50,10 @@ namespace MobileGL::MG_Impl::GLImpl { void Uniform2i(GLint location, GLint v0, GLint v1); void Uniform3i(GLint location, GLint v0, GLint v1, GLint v2); void Uniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + void Uniform1ui(GLint location, GLuint v0); + void Uniform2ui(GLint location, GLuint v0, GLuint v1); + void Uniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2); + void Uniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); void Uniform1fv(GLint location, GLsizei count, const GLfloat* value); void Uniform2fv(GLint location, GLsizei count, const GLfloat* value); void Uniform3fv(GLint location, GLsizei count, const GLfloat* value); @@ -53,6 +62,10 @@ namespace MobileGL::MG_Impl::GLImpl { void Uniform2iv(GLint location, GLsizei count, const GLint* value); void Uniform3iv(GLint location, GLsizei count, const GLint* value); void Uniform4iv(GLint location, GLsizei count, const GLint* value); + void Uniform1uiv(GLint location, GLsizei count, const GLuint* value); + void Uniform2uiv(GLint location, GLsizei count, const GLuint* value); + void Uniform3uiv(GLint location, GLsizei count, const GLuint* value); + void Uniform4uiv(GLint location, GLsizei count, const GLuint* value); void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void UniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp index a96b8bff..a63549af 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp @@ -14,6 +14,32 @@ #include namespace MobileGL::MG_Impl::GLImpl { + static Float ClampUnitFloat(GLfloat value) { + return std::clamp(static_cast(value), 0.0f, 1.0f); + } + + static Bool ValidateIndexedBlendCapability(GLenum target, GLuint index, const char* functionName) { + if (target != GL_BLEND) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", functionName, + "Only GL_BLEND is supported for indexed capability state.")); + return false; + } + + if (index >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique( + "MG_Impl/GLImpl", functionName, + "Buffer index " + std::to_string(index) + " is out of range. Max supported is " + + std::to_string(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS - 1) + ".")); + return false; + } + + return true; + } + static Bool TryConvertBlendEquation(GLenum mode, const char* functionName, ::MobileGL::BlendEquation& outEquation) { outEquation = MG_Util::ConvertGLEnumToBlendEquation(mode); @@ -27,6 +53,43 @@ namespace MobileGL::MG_Impl::GLImpl { return false; } + static Bool TryDecodeStencilFace(GLenum face, const char* functionName, Bool& applyFront, Bool& applyBack) { + switch (face) { + case GL_FRONT: + applyFront = true; + applyBack = false; + return true; + case GL_BACK: + applyFront = false; + applyBack = true; + return true; + case GL_FRONT_AND_BACK: + applyFront = true; + applyBack = true; + return true; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", functionName, + "Stencil face enum " + MG_Util::ConvertGLEnumToString(face) + + " is not supported.")); + return false; + } + } + + static Bool TryConvertStencilOperation(GLenum value, const char* functionName, const char* paramName, + StencilOperation& outOperation) { + outOperation = MG_Util::ConvertGLEnumToStencilOperation(value); + if (outOperation != StencilOperation::Unknown) return true; + + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", functionName, + String(paramName) + " enum " + MG_Util::ConvertGLEnumToString(value) + + " is not supported.")); + return false; + } + void Viewport_State(GLint x, GLint y, GLsizei width, GLsizei height) { if (width < 0 || height < 0) { MG_State::pGLContext->RecordError(ErrorCode::InvalidValue, @@ -39,27 +102,74 @@ namespace MobileGL::MG_Impl::GLImpl { } void StencilOpSeparate_State(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) { - // TODO: implement + Bool applyFront = false; + Bool applyBack = false; + if (!TryDecodeStencilFace(face, "StencilOpSeparate_State", applyFront, applyBack)) return; + + StencilOperation failOp = StencilOperation::Unknown; + StencilOperation depthFailOp = StencilOperation::Unknown; + StencilOperation depthPassOp = StencilOperation::Unknown; + if (!TryConvertStencilOperation(sfail, "StencilOpSeparate_State", "sfail", failOp) || + !TryConvertStencilOperation(dpfail, "StencilOpSeparate_State", "dpfail", depthFailOp) || + !TryConvertStencilOperation(dppass, "StencilOpSeparate_State", "dppass", depthPassOp)) { + return; + } + + if (applyFront) { + MG_State::pGLContext->SetStencilOp(StencilFace::Front, failOp, depthFailOp, depthPassOp); + } + if (applyBack) { + MG_State::pGLContext->SetStencilOp(StencilFace::Back, failOp, depthFailOp, depthPassOp); + } } void StencilOp_State(GLenum fail, GLenum zfail, GLenum zpass) { - // TODO: implement + StencilOpSeparate_State(GL_FRONT_AND_BACK, fail, zfail, zpass); } void StencilMaskSeparate_State(GLenum face, GLuint mask) { - // TODO: implement + Bool applyFront = false; + Bool applyBack = false; + if (!TryDecodeStencilFace(face, "StencilMaskSeparate_State", applyFront, applyBack)) return; + + if (applyFront) { + MG_State::pGLContext->SetStencilMask(StencilFace::Front, mask); + } + if (applyBack) { + MG_State::pGLContext->SetStencilMask(StencilFace::Back, mask); + } } void StencilMask_State(GLuint mask) { - // TODO: implement + StencilMaskSeparate_State(GL_FRONT_AND_BACK, mask); } void StencilFuncSeparate_State(GLenum face, GLenum func, GLint ref, GLuint mask) { - // TODO: implement + Bool applyFront = false; + Bool applyBack = false; + if (!TryDecodeStencilFace(face, "StencilFuncSeparate_State", applyFront, applyBack)) return; + + DepthTestFunc depthFunc = MG_Util::ConvertGLEnumToDepthTestFunc(func); + if (depthFunc == DepthTestFunc::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", "StencilFuncSeparate_State", + "Stencil func enum " + MG_Util::ConvertGLEnumToString(func) + + " is not supported.")); + return; + } + + const Int clampedRef = std::max(ref, 0); + if (applyFront) { + MG_State::pGLContext->SetStencilFunc(StencilFace::Front, depthFunc, clampedRef, mask); + } + if (applyBack) { + MG_State::pGLContext->SetStencilFunc(StencilFace::Back, depthFunc, clampedRef, mask); + } } void StencilFunc_State(GLenum func, GLint ref, GLuint mask) { - // TODO: implement + StencilFuncSeparate_State(GL_FRONT_AND_BACK, func, ref, mask); } void Scissor_State(GLint x, GLint y, GLsizei width, GLsizei height) { @@ -74,11 +184,11 @@ namespace MobileGL::MG_Impl::GLImpl { } void SampleCoverage_State(GLfloat value, GLboolean invert) { - // TODO: implement + MG_State::pGLContext->SetSampleCoverage(std::clamp(static_cast(value), 0.0f, 1.0f), invert == GL_TRUE); } void PolygonOffset_State(GLfloat factor, GLfloat units) { - // TODO: implement + MG_State::pGLContext->SetPolygonOffset(static_cast(factor), static_cast(units)); } void PolygonMode_State(GLenum face, GLenum mode) { @@ -86,7 +196,15 @@ namespace MobileGL::MG_Impl::GLImpl { } void PointSize_State(GLfloat size) { - // TODO: implement + if (size <= 0.0f) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "PointSize_State", + "Point size must be greater than zero.")); + return; + } + + MG_State::pGLContext->SetPointSize(static_cast(size)); } void PointParameterf_State(GLenum pname, GLfloat param) { @@ -114,14 +232,36 @@ namespace MobileGL::MG_Impl::GLImpl { } void LogicOp_State(GLenum opcode) { - // TODO: implement + LogicOperation logicOp = MG_Util::ConvertGLEnumToLogicOperation(opcode); + if (logicOp == LogicOperation::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", "LogicOp_State", + "Logic op enum " + MG_Util::ConvertGLEnumToString(opcode) + + " is not supported.")); + return; + } + + MG_State::pGLContext->SetLogicOp(logicOp); } void LineWidth_State(GLfloat width) { - // TODO: implement + if (width <= 0.0f) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "LineWidth_State", + "Line width must be greater than zero.")); + return; + } + + MG_State::pGLContext->SetLineWidth(static_cast(width)); } GLboolean IsEnabledi_State(GLenum target, GLuint index) { + if (!ValidateIndexedBlendCapability(target, index, "IsEnabledi_State")) { + return GL_FALSE; + } + CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); if (capInput == CapabilityInput::Unknown) { MG_State::pGLContext->RecordError( @@ -135,6 +275,18 @@ namespace MobileGL::MG_Impl::GLImpl { return MG_State::pGLContext->IsCapabilityEnabledIndexed(capInput, index) ? GL_TRUE : GL_FALSE; } + void GetBooleani_v_State(GLenum target, GLuint index, GLboolean* data) { + if (!data) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "GetBooleani_v_State", + "data pointer cannot be null.")); + return; + } + + *data = IsEnabledi_State(target, index); + } + GLboolean IsEnabled_State(GLenum cap) { CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap); if (capInput == CapabilityInput::Unknown) { @@ -197,7 +349,8 @@ namespace MobileGL::MG_Impl::GLImpl { } void DepthRange_State(GLclampd near_val, GLclampd far_val) { - // TODO: implement + MG_State::pGLContext->SetDepthRange( + FloatVec2(ClampUnitFloat(static_cast(near_val)), ClampUnitFloat(static_cast(far_val)))); } void DepthMask_State(GLboolean flag) { @@ -289,7 +442,8 @@ namespace MobileGL::MG_Impl::GLImpl { } void BlendColor_State(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { - // TODO: implement + MG_State::pGLContext->SetBlendColor( + FloatVec4(ClampUnitFloat(red), ClampUnitFloat(green), ClampUnitFloat(blue), ClampUnitFloat(alpha))); } void ClearStencil_State(GLint s) { @@ -359,6 +513,10 @@ namespace MobileGL::MG_Impl::GLImpl { } void Disablei_State(GLenum target, GLuint index) { + if (!ValidateIndexedBlendCapability(target, index, "Disablei_State")) { + return; + } + auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); if (capInput == CapabilityInput::Unknown) { MG_State::pGLContext->RecordError( @@ -373,6 +531,10 @@ namespace MobileGL::MG_Impl::GLImpl { } void Enablei_State(GLenum target, GLuint index) { + if (!ValidateIndexedBlendCapability(target, index, "Enablei_State")) { + return; + } + auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); if (capInput == CapabilityInput::Unknown) { MG_State::pGLContext->RecordError( @@ -403,6 +565,10 @@ namespace MobileGL::MG_Impl::GLImpl { BlendFuncSeparatei_State(buf, srcRGB, dstRGB, srcAlpha, dstAlpha); } + void GetBooleani_v(GLenum target, GLuint index, GLboolean* data) { + GetBooleani_v_State(target, index, data); + } + void Disablei(GLenum target, GLuint index) { Disablei_State(target, index); } diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h index 400909dc..b9c25122 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h @@ -15,6 +15,7 @@ namespace MobileGL::MG_Impl::GLImpl { void BlendEquationSeparatei(GLuint buf, GLenum modeRGB, GLenum modeAlpha); void BlendFunci(GLuint buf, GLenum src, GLenum dst); void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + void GetBooleani_v(GLenum target, GLuint index, GLboolean* data); void Disablei(GLenum target, GLuint index); void Enablei(GLenum target, GLuint index); void BlendFunc(GLenum sfactor, GLenum dfactor); diff --git a/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp b/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp index 2a46e49d..aab9d528 100644 --- a/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp @@ -13,7 +13,31 @@ #include namespace MobileGL::MG_Impl::GLImpl { + namespace { + Bool ValidateSamplerParameterValue(GLenum pname, const void* param, Bool isFloat, Bool isInteger) { + if (param == nullptr) return false; + + switch (pname) { + case GL_TEXTURE_MIN_LOD: + case GL_TEXTURE_MAX_LOD: + case GL_TEXTURE_LOD_BIAS: + return true; + default: + break; + } + + if (isFloat) { + return SamplerImpl::ValidateSamplerFloatParam(pname, *(const GLfloat*)param); + } + if (isInteger) { + return SamplerImpl::ValidateSamplerIntParam(pname, static_cast(*(const GLuint*)param)); + } + return SamplerImpl::ValidateSamplerIntParam(pname, *(const GLint*)param); + } + } // namespace + void SetSamplerParam_State(GLuint sampler, GLenum pname, const void* param, bool isFloat, bool isInteger) { + if (param == nullptr) return; if (!SamplerImpl::ValidateSamplerName(sampler)) return; Bool doesSamplerObjectCreated = MG_State::pGLContext->ValidateSamplerObject(sampler); @@ -23,6 +47,7 @@ namespace MobileGL::MG_Impl::GLImpl { } auto& samplerObj = MG_State::pGLContext->GetSamplerObject(sampler); if (!SamplerImpl::ValidateSamplerObject(sampler)) return; + if (!ValidateSamplerParameterValue(pname, param, isFloat, isInteger)) return; using namespace MG_Util; switch (pname) { @@ -65,6 +90,7 @@ namespace MobileGL::MG_Impl::GLImpl { } void GetSamplerParam_State(GLuint sampler, GLenum pname, void* params, bool isFloat, bool isInteger) { + if (params == nullptr) return; if (!SamplerImpl::ValidateSamplerName(sampler)) return; Bool doesSamplerObjectCreated = MG_State::pGLContext->ValidateSamplerObject(sampler); diff --git a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp index 4bff1afc..d9fd5033 100644 --- a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp @@ -9,35 +9,239 @@ #include "GL_Sync.h" #include "MG_State/GLState/Core.h" +#include +#include +#include +#include namespace MobileGL::MG_Impl::GLImpl { + namespace { + const MG_External::GLESFunctionsTable* TryGetDirectGLESFunctions(const char* funcName) { + auto* activeBackend = MG_Backend::pActiveBackendObject.get(); + if (!activeBackend) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", funcName, "No active backend object.")); + return nullptr; + } + + auto* directGLESBackend = + dynamic_cast(activeBackend); + if (!directGLESBackend) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", funcName, + "Sync objects are only implemented for the DirectGLES backend.")); + return nullptr; + } + + return &directGLESBackend->GetGLESFunctions(); + } + + Bool ValidateFenceSyncArgs(const char* funcName, GLenum condition, GLbitfield flags) { + if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", funcName, + "condition must be GL_SYNC_GPU_COMMANDS_COMPLETE.")); + return false; + } + if (flags != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", funcName, "flags must be zero.")); + return false; + } + return true; + } + + Bool ValidateSyncHandle(const char* funcName, GLsync sync) { + if (sync != nullptr) { + return true; + } + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", funcName, "sync must be a valid non-null handle.")); + return false; + } + } + GLsync FenceSync_Backend(GLenum condition, GLbitfield flags) { - return 0; + const auto* glesFuncs = TryGetDirectGLESFunctions(__func__); + if (!glesFuncs || !glesFuncs->glFenceSync) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not expose glFenceSync.")); + return nullptr; + } + return glesFuncs->glFenceSync(condition, flags); + } + + GLboolean IsSync_Backend(GLsync sync) { + const auto* glesFuncs = TryGetDirectGLESFunctions(__func__); + if (!glesFuncs || !glesFuncs->glIsSync) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not expose glIsSync.")); + return GL_FALSE; + } + return glesFuncs->glIsSync(sync); } GLenum ClientWaitSync_Backend(GLsync sync, GLbitfield flags, GLuint64 timeout) { - return 0; + const auto* glesFuncs = TryGetDirectGLESFunctions(__func__); + if (!glesFuncs || !glesFuncs->glClientWaitSync) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not expose glClientWaitSync.")); + return GL_WAIT_FAILED; + } + return glesFuncs->glClientWaitSync(sync, flags, timeout); } - void DeleteSync_Backend(GLsync sync) {} + void WaitSync_Backend(GLsync sync, GLbitfield flags, GLuint64 timeout) { + const auto* glesFuncs = TryGetDirectGLESFunctions(__func__); + if (!glesFuncs || !glesFuncs->glWaitSync) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not expose glWaitSync.")); + return; + } + glesFuncs->glWaitSync(sync, flags, timeout); + } + + void GetSynciv_Backend(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) { + const auto* glesFuncs = TryGetDirectGLESFunctions(__func__); + if (!glesFuncs || !glesFuncs->glGetSynciv) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not expose glGetSynciv.")); + return; + } + glesFuncs->glGetSynciv(sync, pname, bufSize, length, values); + } + + void DeleteSync_Backend(GLsync sync) { + const auto* glesFuncs = TryGetDirectGLESFunctions(__func__); + if (!glesFuncs || !glesFuncs->glDeleteSync) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not expose glDeleteSync.")); + return; + } + glesFuncs->glDeleteSync(sync); + } GLsync FenceSync_State(GLenum condition, GLbitfield flags) { - return 0; + if (!ValidateFenceSyncArgs(__func__, condition, flags)) { + return nullptr; + } + return FenceSync_Backend(condition, flags); + } + + GLboolean IsSync_State(GLsync sync) { + if (sync == nullptr) return GL_FALSE; + return IsSync_Backend(sync); } GLenum ClientWaitSync_State(GLsync sync, GLbitfield flags, GLuint64 timeout) { - return 0; + (void)timeout; + if (!ValidateSyncHandle(__func__, sync)) { + return GL_WAIT_FAILED; + } + if ((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, + "flags contains unsupported bits.")); + return GL_WAIT_FAILED; + } + return ClientWaitSync_Backend(sync, flags, timeout); } - void DeleteSync_State(GLsync sync) {} + void WaitSync_State(GLsync sync, GLbitfield flags, GLuint64 timeout) { + if (!ValidateSyncHandle(__func__, sync)) { + return; + } + if (flags != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "flags must be zero.")); + return; + } + if (timeout != GL_TIMEOUT_IGNORED) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "timeout must be GL_TIMEOUT_IGNORED.")); + return; + } + WaitSync_Backend(sync, flags, timeout); + } + + void GetSynciv_State(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) { + if (!ValidateSyncHandle(__func__, sync)) { + return; + } + if (bufSize < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "bufSize must be non-negative.")); + return; + } + if (bufSize > 0 && values == nullptr) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, + "values must not be null when bufSize is positive.")); + return; + } + switch (pname) { + case GL_OBJECT_TYPE: + case GL_SYNC_CONDITION: + case GL_SYNC_STATUS: + case GL_SYNC_FLAGS: + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, "Unsupported sync pname.")); + return; + } + GetSynciv_Backend(sync, pname, bufSize, length, values); + } + + void DeleteSync_State(GLsync sync) { + if (sync == nullptr) return; + DeleteSync_Backend(sync); + } GLsync FenceSync(GLenum condition, GLbitfield flags) { - return 0; + return FenceSync_State(condition, flags); + } + + GLboolean IsSync(GLsync sync) { + return IsSync_State(sync); } GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) { - return 0; + return ClientWaitSync_State(sync, flags, timeout); } - void DeleteSync(GLsync sync) {} + void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) { + WaitSync_State(sync, flags, timeout); + } + + void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) { + GetSynciv_State(sync, pname, bufSize, length, values); + } + + void DeleteSync(GLsync sync) { + DeleteSync_State(sync); + } } // namespace MobileGL::MG_Impl::GLImpl diff --git a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h index 356967da..39629563 100644 --- a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h +++ b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h @@ -11,6 +11,9 @@ namespace MobileGL::MG_Impl::GLImpl { GLsync FenceSync(GLenum condition, GLbitfield flags); + GLboolean IsSync(GLsync sync); + void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout); GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout); + void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values); void DeleteSync(GLsync sync); } // namespace MobileGL::MG_Impl::GLImpl diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 1970a1f2..6e31d51c 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -31,6 +31,21 @@ namespace MobileGL::MG_Impl::GLImpl { static SharedPtr nullTextureObject; static UnorderedMap g_autoGenerateMipmapByTextureId; + void GetTexParameteriv_State(GLenum target, GLenum pname, GLint* params); + + namespace { + void SetTextureBorderColorFromFloats(const SharedPtr& textureObject, + const GLfloat* params) { + textureObject->SetBorderColor(FloatVec4(params[0], params[1], params[2], params[3])); + } + + void SetTextureBorderColorFromInts(const SharedPtr& textureObject, + const GLint* params) { + textureObject->SetBorderColor(FloatVec4(static_cast(params[0]), static_cast(params[1]), + static_cast(params[2]), static_cast(params[3]))); + } + } // namespace + const SharedPtr& GetTextureObjectByTarget( TextureUploadTarget textureUploadTarget, TextureTarget textureTarget) { if (TextureImpl::IsProxyTextureTarget(textureUploadTarget)) { @@ -360,7 +375,7 @@ namespace MobileGL::MG_Impl::GLImpl { // ======================= Processing ================================ auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); if (!textureObject) return; - THROW_UNIMPL_EXCEPTION; + SetTextureBorderColorFromFloats(textureObject, params); break; } case GL_TEXTURE_SWIZZLE_RGBA: { @@ -401,8 +416,7 @@ namespace MobileGL::MG_Impl::GLImpl { // ======================= Processing ================================ auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); if (!textureObject) return; - - THROW_UNIMPL_EXCEPTION; + SetTextureBorderColorFromInts(textureObject, params); break; } case GL_TEXTURE_SWIZZLE_RGBA: { @@ -436,7 +450,11 @@ namespace MobileGL::MG_Impl::GLImpl { void TexParameterIiv_State(GLenum target, GLenum pname, const GLint* params) { switch (pname) { case GL_TEXTURE_BORDER_COLOR: { - THROW_UNIMPL_EXCEPTION; + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); + if (!textureObject) return; + SetTextureBorderColorFromInts(textureObject, params); break; } case GL_TEXTURE_SWIZZLE_RGBA: { @@ -470,7 +488,12 @@ namespace MobileGL::MG_Impl::GLImpl { void TexParameterIuiv_State(GLenum target, GLenum pname, const GLuint* params) { switch (pname) { case GL_TEXTURE_BORDER_COLOR: { - THROW_UNIMPL_EXCEPTION; + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); + if (!textureObject) return; + textureObject->SetBorderColor(FloatVec4(static_cast(params[0]), static_cast(params[1]), + static_cast(params[2]), static_cast(params[3]))); break; } case GL_TEXTURE_SWIZZLE_RGBA: { @@ -806,13 +829,17 @@ namespace MobileGL::MG_Impl::GLImpl { } void GetTexParameterIuiv_State(GLenum target, GLenum pname, GLuint* params) { - // TODO: implement - THROW_UNIMPL_EXCEPTION; + if (params == nullptr) return; + + GLint signedParams[4] = {0, 0, 0, 0}; + GetTexParameteriv_State(target, pname, signedParams); + for (int i = 0; i < 4; ++i) { + params[i] = static_cast(signedParams[i]); + } } void GetTexParameterIiv_State(GLenum target, GLenum pname, GLint* params) { - // TODO: implement - THROW_UNIMPL_EXCEPTION; + GetTexParameteriv_State(target, pname, params); } void GetTexParameteriv_State(GLenum target, GLenum pname, GLint* params) { @@ -855,9 +882,48 @@ namespace MobileGL::MG_Impl::GLImpl { } break; case GL_TEXTURE_BASE_LEVEL: + if (params) { + *params = static_cast(textureObject->GetLevelRange().x()); + } + break; case GL_TEXTURE_MAX_LEVEL: + if (params) { + *params = static_cast(textureObject->GetLevelRange().y()); + } + break; case GL_TEXTURE_BORDER_COLOR: - break; // TODO + if (params) { + const auto& borderColor = textureObject->GetBorderColor(); + params[0] = static_cast(borderColor.x()); + params[1] = static_cast(borderColor.y()); + params[2] = static_cast(borderColor.z()); + params[3] = static_cast(borderColor.w()); + } + break; + case GL_TEXTURE_SWIZZLE_R: + if (params) { + *params = static_cast( + MG_Util::ConvertTextureSwizzleParamToGLEnum(textureObject->GetSwizzleParam(TextureSwizzleParam::Red))); + } + break; + case GL_TEXTURE_SWIZZLE_G: + if (params) { + *params = static_cast(MG_Util::ConvertTextureSwizzleParamToGLEnum( + textureObject->GetSwizzleParam(TextureSwizzleParam::Green))); + } + break; + case GL_TEXTURE_SWIZZLE_B: + if (params) { + *params = static_cast( + MG_Util::ConvertTextureSwizzleParamToGLEnum(textureObject->GetSwizzleParam(TextureSwizzleParam::Blue))); + } + break; + case GL_TEXTURE_SWIZZLE_A: + if (params) { + *params = static_cast(MG_Util::ConvertTextureSwizzleParamToGLEnum( + textureObject->GetSwizzleParam(TextureSwizzleParam::Alpha))); + } + break; case GL_TEXTURE_WRAP_S: if (params) { *params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapS()); @@ -933,14 +999,50 @@ namespace MobileGL::MG_Impl::GLImpl { } break; case GL_TEXTURE_BASE_LEVEL: + if (params) { + *params = static_cast(textureObject->GetLevelRange().x()); + } + break; case GL_TEXTURE_MAX_LEVEL: + if (params) { + *params = static_cast(textureObject->GetLevelRange().y()); + } + break; case GL_TEXTURE_SWIZZLE_R: + if (params) { + *params = static_cast( + MG_Util::ConvertTextureSwizzleParamToGLEnum(textureObject->GetSwizzleParam(TextureSwizzleParam::Red))); + } + break; case GL_TEXTURE_SWIZZLE_G: + if (params) { + *params = static_cast(MG_Util::ConvertTextureSwizzleParamToGLEnum( + textureObject->GetSwizzleParam(TextureSwizzleParam::Green))); + } + break; case GL_TEXTURE_SWIZZLE_B: + if (params) { + *params = static_cast(MG_Util::ConvertTextureSwizzleParamToGLEnum( + textureObject->GetSwizzleParam(TextureSwizzleParam::Blue))); + } + break; case GL_TEXTURE_SWIZZLE_A: + if (params) { + *params = static_cast(MG_Util::ConvertTextureSwizzleParamToGLEnum( + textureObject->GetSwizzleParam(TextureSwizzleParam::Alpha))); + } + break; case GL_TEXTURE_SWIZZLE_RGBA: - case GL_TEXTURE_BORDER_COLOR: break; // TODO + case GL_TEXTURE_BORDER_COLOR: + if (params) { + const auto& borderColor = textureObject->GetBorderColor(); + params[0] = borderColor.x(); + params[1] = borderColor.y(); + params[2] = borderColor.z(); + params[3] = borderColor.w(); + } + break; case GL_TEXTURE_WRAP_S: if (params) { *params = @@ -1386,7 +1488,7 @@ namespace MobileGL::MG_Impl::GLImpl { } // Add to GL_Texture.cpp - void GetTexImage_State(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { + Bool GetTexImage_State(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { // ======================= Converting ================================ TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); @@ -1399,7 +1501,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Invalid texture target")); - return; + return false; } // Validate level @@ -1407,7 +1509,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Level must be non-negative")); - return; + return false; } // Validate format @@ -1415,7 +1517,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Invalid format")); - return; + return false; } // Validate type @@ -1423,7 +1525,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Invalid pixel data type")); - return; + return false; } // Get texture object @@ -1438,7 +1540,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "No valid texture bound to target")); - return; + return false; } // Check texture completeness @@ -1446,7 +1548,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Texture is incomplete")); - return; + return false; } // Check PBO state @@ -1459,7 +1561,7 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Pixel pack buffer is currently mapped")); - return; + return false; } // Check alignment @@ -1469,7 +1571,7 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "Pixel data not aligned for pixel pack buffer")); - return; + return false; } } @@ -1482,21 +1584,11 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "No stencil buffer for stencil index format")); - return; + return false; } } - // Check for multisampling - if (textureObject->GetStorageType() == TextureStorageType::Mipmap) { - auto mipmapObject = static_cast(textureObject.get()); - if (mipmapObject->GetMipmapLevelCount() > 1) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", - "Multisampled textures not supported for GetTexImage")); - return; - } - } + return true; } /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ @@ -1538,9 +1630,6 @@ namespace MobileGL::MG_Impl::GLImpl { textureObject = MG_State::pGLContext->GetTextureObject(texture); } - MG_State::pGLContext->GetImageTextureBinding(static_cast(unit)) - .Bind(textureObject, level, layered, layer, access, format); - auto bindImageTexture = MG_Backend::gBackendFunctionsTable.GL.BindImageTexture; if (!bindImageTexture) { MG_State::pGLContext->RecordError( @@ -1549,6 +1638,9 @@ namespace MobileGL::MG_Impl::GLImpl { "Backend does not support image texture binding.")); return; } + + MG_State::pGLContext->GetImageTextureBinding(static_cast(unit)) + .Bind(textureObject, level, layered, layer, access, format); bindImageTexture(unit, texture, level, layered, layer, access, format); } @@ -1557,7 +1649,7 @@ namespace MobileGL::MG_Impl::GLImpl { } void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { - GetTexImage_State(target, level, format, type, pixels); + if (!GetTexImage_State(target, level, format, type, pixels)) return; GetTexImage_Backend(target, level, format, type, pixels); } diff --git a/MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp index 5b7a8c71..c16df634 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp @@ -7,7 +7,11 @@ // End of Source File Header #include "ProxyTexture.h" +#include #include +#include +#include +#include namespace MobileGL::MG_Impl::GLImpl::TextureImpl { UniquePtr pProxyTextureManager; @@ -37,7 +41,39 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl { m_proxyTexturesMap.erase(it); } auto& obj = m_proxyTexturesMap[target]; - obj = MakeShared(0); + switch (target) { + case TextureUploadTarget::ProxyTexture1D: + case TextureUploadTarget::ProxyTexture1DArray: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyTexture2D: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyTexture3D: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyCubeMap: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyTextureRectangle: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyTexture2DArray: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyCubeMapArray: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyTexture2DMultisample: + obj = MakeShared(0); + break; + case TextureUploadTarget::ProxyTexture2DMultisampleArray: + obj = MakeShared(0); + break; + default: + obj = MakeShared(0); + break; + } return obj; } diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp index 1218237a..4b3970fc 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp @@ -11,8 +11,64 @@ #include #include #include +#include namespace MobileGL::MG_Impl::GLImpl { + namespace { + static bool ValidateCurrentVertexAttribIndex(GLuint index, const char* funcName) { + if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return false; + if (index == 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", funcName, + "Generic vertex attribute 0 current value cannot be modified.")); + return false; + } + return true; + } + + static bool TryGetVertexAttribute(GLuint index, const MG_State::GLState::VertexAttribute** outAttr) { + if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return false; + + auto& vao = MG_State::pGLContext->GetBoundVertexArray(); + if (!vao) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "No vertex array object is bound.")); + return false; + } + + *outAttr = &vao->GetAttribute(index); + return true; + } + + static bool IsCurrentVertexAttribQuery(GLenum pname) { + return pname == GL_CURRENT_VERTEX_ATTRIB; + } + + static bool ValidateVertexAttribPname(GLenum pname) { + switch (pname) { + case GL_VERTEX_ATTRIB_ARRAY_ENABLED: + case GL_VERTEX_ATTRIB_ARRAY_SIZE: + case GL_VERTEX_ATTRIB_ARRAY_STRIDE: + case GL_VERTEX_ATTRIB_ARRAY_TYPE: + case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: + case GL_CURRENT_VERTEX_ATTRIB: + case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: + case GL_VERTEX_ATTRIB_ARRAY_INTEGER: + case GL_VERTEX_ATTRIB_ARRAY_DIVISOR: + case GL_VERTEX_ATTRIB_ARRAY_POINTER: + return true; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "Unsupported vertex attrib pname: " + std::to_string(pname))); + return false; + } + } + } // namespace + void DisableVertexAttribArray_State(GLuint index) { if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; @@ -163,6 +219,287 @@ namespace MobileGL::MG_Impl::GLImpl { } /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ + void VertexAttrib1f(GLuint index, GLfloat x) { + if (!ValidateCurrentVertexAttribIndex(index, __func__)) return; + MG_State::pGLContext->SetCurrentVertexAttributeFloat(index, {x, 0.0f, 0.0f, 1.0f}); + } + + void VertexAttrib1fv(GLuint index, const GLfloat* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttrib1f(index, v[0]); + } + + void VertexAttrib2f(GLuint index, GLfloat x, GLfloat y) { + if (!ValidateCurrentVertexAttribIndex(index, __func__)) return; + MG_State::pGLContext->SetCurrentVertexAttributeFloat(index, {x, y, 0.0f, 1.0f}); + } + + void VertexAttrib2fv(GLuint index, const GLfloat* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttrib2f(index, v[0], v[1]); + } + + void VertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) { + if (!ValidateCurrentVertexAttribIndex(index, __func__)) return; + MG_State::pGLContext->SetCurrentVertexAttributeFloat(index, {x, y, z, 1.0f}); + } + + void VertexAttrib3fv(GLuint index, const GLfloat* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttrib3f(index, v[0], v[1], v[2]); + } + + void VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { + if (!ValidateCurrentVertexAttribIndex(index, __func__)) return; + MG_State::pGLContext->SetCurrentVertexAttributeFloat(index, {x, y, z, w}); + } + + void VertexAttrib4fv(GLuint index, const GLfloat* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttrib4f(index, v[0], v[1], v[2], v[3]); + } + + void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) { + if (!ValidateCurrentVertexAttribIndex(index, __func__)) return; + MG_State::pGLContext->SetCurrentVertexAttributeInt(index, {x, y, z, w}); + } + + void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) { + if (!ValidateCurrentVertexAttribIndex(index, __func__)) return; + MG_State::pGLContext->SetCurrentVertexAttributeUint(index, {x, y, z, w}); + } + + void VertexAttribI4iv(GLuint index, const GLint* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttribI4i(index, v[0], v[1], v[2], v[3]); + } + + void VertexAttribI4uiv(GLuint index, const GLuint* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttribI4ui(index, v[0], v[1], v[2], v[3]); + } + + void VertexAttrib4Nub(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w) { + constexpr float kInv255 = 1.0f / 255.0f; + VertexAttrib4f(index, x * kInv255, y * kInv255, z * kInv255, w * kInv255); + } + + void VertexAttrib4Nubv(GLuint index, const GLubyte* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttrib4Nub(index, v[0], v[1], v[2], v[3]); + } + + void VertexAttrib4ubv(GLuint index, const GLubyte* v) { + if (!v) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value pointer cannot be null.")); + return; + } + VertexAttrib4f(index, static_cast(v[0]), static_cast(v[1]), static_cast(v[2]), + static_cast(v[3])); + } + + void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null.")); + return; + } + if (!ValidateVertexAttribPname(pname)) return; + + if (IsCurrentVertexAttribQuery(pname)) { + const auto& current = MG_State::pGLContext->GetCurrentVertexAttribute(index); + params[0] = current.floatValue[0]; + params[1] = current.floatValue[1]; + params[2] = current.floatValue[2]; + params[3] = current.floatValue[3]; + return; + } + + const MG_State::GLState::VertexAttribute* attr = nullptr; + if (!TryGetVertexAttribute(index, &attr)) return; + + switch (pname) { + case GL_VERTEX_ATTRIB_ARRAY_ENABLED: + params[0] = attr->Enabled ? 1.0f : 0.0f; + return; + case GL_VERTEX_ATTRIB_ARRAY_SIZE: + params[0] = static_cast(attr->Size); + return; + case GL_VERTEX_ATTRIB_ARRAY_STRIDE: + params[0] = static_cast(attr->Stride); + return; + case GL_VERTEX_ATTRIB_ARRAY_TYPE: + params[0] = static_cast(MG_Util::ConvertDataTypeToGLEnum(attr->Type)); + return; + case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: + params[0] = attr->Normalized ? 1.0f : 0.0f; + return; + case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: + params[0] = attr->Buffer ? static_cast(attr->Buffer->GetExternalIndex()) : 0.0f; + return; + case GL_VERTEX_ATTRIB_ARRAY_INTEGER: + params[0] = attr->IsInteger ? 1.0f : 0.0f; + return; + case GL_VERTEX_ATTRIB_ARRAY_DIVISOR: + params[0] = static_cast(attr->Divisor); + return; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "Unsupported float vertex attrib pname: " + std::to_string(pname))); + return; + } + } + + void GetVertexAttribiv(GLuint index, GLenum pname, GLint* params) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null.")); + return; + } + if (!ValidateVertexAttribPname(pname)) return; + + if (IsCurrentVertexAttribQuery(pname)) { + const auto& current = MG_State::pGLContext->GetCurrentVertexAttribute(index); + params[0] = current.intValue[0]; + params[1] = current.intValue[1]; + params[2] = current.intValue[2]; + params[3] = current.intValue[3]; + return; + } + + const MG_State::GLState::VertexAttribute* attr = nullptr; + if (!TryGetVertexAttribute(index, &attr)) return; + + switch (pname) { + case GL_VERTEX_ATTRIB_ARRAY_ENABLED: + params[0] = attr->Enabled ? GL_TRUE : GL_FALSE; + return; + case GL_VERTEX_ATTRIB_ARRAY_SIZE: + params[0] = attr->Size; + return; + case GL_VERTEX_ATTRIB_ARRAY_STRIDE: + params[0] = attr->Stride; + return; + case GL_VERTEX_ATTRIB_ARRAY_TYPE: + params[0] = static_cast(MG_Util::ConvertDataTypeToGLEnum(attr->Type)); + return; + case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: + params[0] = attr->Normalized ? GL_TRUE : GL_FALSE; + return; + case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: + params[0] = attr->Buffer ? static_cast(attr->Buffer->GetExternalIndex()) : 0; + return; + case GL_VERTEX_ATTRIB_ARRAY_INTEGER: + params[0] = attr->IsInteger ? GL_TRUE : GL_FALSE; + return; + case GL_VERTEX_ATTRIB_ARRAY_DIVISOR: + params[0] = static_cast(attr->Divisor); + return; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "Unsupported integer vertex attrib pname: " + std::to_string(pname))); + return; + } + } + + void GetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) { + if (!pointer) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "pointer cannot be null.")); + return; + } + if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; + if (pname != GL_VERTEX_ATTRIB_ARRAY_POINTER) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "pname must be GL_VERTEX_ATTRIB_ARRAY_POINTER.")); + return; + } + + auto& vao = MG_State::pGLContext->GetBoundVertexArray(); + if (!vao) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "No vertex array object is bound.")); + return; + } + + const auto& attr = vao->GetAttribute(index); + *pointer = reinterpret_cast(attr.Offset); + } + + void GetVertexAttribIiv(GLuint index, GLenum pname, GLint* params) { + GetVertexAttribiv(index, pname, params); + } + + void GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null.")); + return; + } + if (!ValidateVertexAttribPname(pname)) return; + + if (IsCurrentVertexAttribQuery(pname)) { + const auto& current = MG_State::pGLContext->GetCurrentVertexAttribute(index); + params[0] = current.uintValue[0]; + params[1] = current.uintValue[1]; + params[2] = current.uintValue[2]; + params[3] = current.uintValue[3]; + return; + } + + GLint signedParams[4] = {}; + GetVertexAttribiv(index, pname, signedParams); + params[0] = static_cast(signedParams[0]); + } + void VertexAttribDivisor(GLuint index, GLuint divisor) { VertexAttribDivisor_State(index, divisor); } diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h index 2dafde79..d6e505f3 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h @@ -11,6 +11,26 @@ namespace MobileGL::MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ + void VertexAttrib1f(GLuint index, GLfloat x); + void VertexAttrib1fv(GLuint index, const GLfloat* v); + void VertexAttrib2f(GLuint index, GLfloat x, GLfloat y); + void VertexAttrib2fv(GLuint index, const GLfloat* v); + void VertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z); + void VertexAttrib3fv(GLuint index, const GLfloat* v); + void VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void VertexAttrib4fv(GLuint index, const GLfloat* v); + void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w); + void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); + void VertexAttribI4iv(GLuint index, const GLint* v); + void VertexAttribI4uiv(GLuint index, const GLuint* v); + void VertexAttrib4Nub(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); + void VertexAttrib4Nubv(GLuint index, const GLubyte* v); + void VertexAttrib4ubv(GLuint index, const GLubyte* v); + void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); + void GetVertexAttribiv(GLuint index, GLenum pname, GLint* params); + void GetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer); + void GetVertexAttribIiv(GLuint index, GLenum pname, GLint* params); + void GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params); void VertexAttribDivisor(GLuint index, GLuint divisor); GLboolean IsVertexArray(GLuint array); void DisableVertexAttribArray(GLuint index); diff --git a/MobileGL/MG_Impl/Init.cpp b/MobileGL/MG_Impl/Init.cpp index ea1531cd..044ba292 100644 --- a/MobileGL/MG_Impl/Init.cpp +++ b/MobileGL/MG_Impl/Init.cpp @@ -35,6 +35,7 @@ namespace MobileGL::MG_Impl { stencilTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {{512, 512, 1}, 0}); // stencilTex->SetMipmapLevel({{512, 512, 1}, 0, false, 0, {nullptr, 0}}); fbo0->AttachTexture(FramebufferAttachmentType::Color0, colorTex); + fbo0->AttachTexture(FramebufferAttachmentType::BackLeft, colorTex); fbo0->AttachTexture(FramebufferAttachmentType::Depth, depthTex); fbo0->AttachTexture(FramebufferAttachmentType::Stencil, stencilTex); GLImpl::FramebufferImpl::pDefaultFramebufferInfo = diff --git a/MobileGL/MG_State/EGLState/Core.cpp b/MobileGL/MG_State/EGLState/Core.cpp index c9ef85d6..8240898b 100644 --- a/MobileGL/MG_State/EGLState/Core.cpp +++ b/MobileGL/MG_State/EGLState/Core.cpp @@ -1139,6 +1139,21 @@ namespace MobileGL { return EGL_NO_SURFACE; } + Bool EGLContext::IsDoubleBufferedSurface(EGLSurfaceHandle surface) const { + const std::lock_guard lock(m_mutex); + const auto* surfaceObject = TryGetSurface(surface); + if (!surfaceObject) { + return false; + } + + if (surfaceObject->RenderBuffer != EGL_BACK_BUFFER) { + return false; + } + + return surfaceObject->Type == SurfaceType::Window || + surfaceObject->Type == SurfaceType::PlatformWindow; + } + EGLContext::EGLSyncHandle EGLContext::CreateSync(EGLDisplayHandle display, EGLenum type, const EGLAttrib* attribList) { const std::lock_guard lock(m_mutex); diff --git a/MobileGL/MG_State/EGLState/Core.h b/MobileGL/MG_State/EGLState/Core.h index 04642794..6867e68d 100644 --- a/MobileGL/MG_State/EGLState/Core.h +++ b/MobileGL/MG_State/EGLState/Core.h @@ -87,6 +87,7 @@ namespace MobileGL { EGLContextHandle GetCurrentContext() const; EGLDisplayHandle GetCurrentDisplay() const; EGLSurfaceHandle GetCurrentSurface(EGLint readdraw) const; + Bool IsDoubleBufferedSurface(EGLSurfaceHandle surface) const; // Sync EGLSyncHandle CreateSync(EGLDisplayHandle display, EGLenum type, const EGLAttrib* attribList); diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp index e625803b..1d6117a2 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp @@ -120,10 +120,13 @@ namespace MobileGL::MG_State::GLState { void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) { if (markMapped) { m_isMapped = true; - auto a = BufferMappingAccessBit::Coherent | BufferMappingAccessBit::Read; m_mappingAccess = (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) | (write ? BufferMappingAccessBit::Write : BufferMappingAccessBit::Null); m_mappedRange = {0, m_size}; + if (write) { + m_change.Bits |= BufferChangeBits::ForbidInvalidationBit; + m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit; + } if (m_mappingAccess & BufferMappingAccessBit::Write) { m_stagingData.resize(m_size); @@ -148,6 +151,13 @@ namespace MobileGL::MG_State::GLState { m_isMapped = true; m_mappingAccess = access; m_mappedRange = range; + m_change.Bits |= + !(access & BufferMappingAccessBit::InvalidateBuffer || access & BufferMappingAccessBit::InvalidateRange) + ? BufferChangeBits::ForbidInvalidationBit + : BufferChangeBits::None; + m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized) + ? BufferChangeBits::ForbidUnsynchronizationBit + : BufferChangeBits::None; if (access & BufferMappingAccessBit::Write) { m_stagingData.resize(range.end - range.start); @@ -162,14 +172,6 @@ namespace MobileGL::MG_State::GLState { m_ownsStagingData = false; return m_dataPtr->data() + range.start; } - - m_change.Bits |= - !(access & BufferMappingAccessBit::InvalidateBuffer || access & BufferMappingAccessBit::InvalidateRange) - ? BufferChangeBits::ForbidInvalidationBit - : BufferChangeBits::None; - m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized) - ? BufferChangeBits::ForbidUnsynchronizationBit - : BufferChangeBits::None; } const SharedPtr& BufferObject::GetDataReadOnly() const { @@ -209,6 +211,18 @@ namespace MobileGL::MG_State::GLState { return m_isMapped ? m_mappedRange : Range1D{0, 0}; } + void* BufferObject::GetMappedPointer() { + if (!m_isMapped) { + return nullptr; + } + + if (m_ownsStagingData) { + return m_stagingData.data(); + } + + return m_dataPtr->data() + m_mappedRange.start; + } + Flags BufferObject::GetMappingAccess() const { return m_isMapped ? m_mappingAccess : BufferMappingAccessBit::Null; } diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.h b/MobileGL/MG_State/GLState/BufferState/BufferObject.h index e6a01383..ee623fbe 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.h @@ -94,6 +94,7 @@ namespace MobileGL { SizeT GetSize() const; BufferUsage GetUsage() const; Range1D GetMappedRange() const; + void* GetMappedPointer(); const SharedPtr& GetDataReadOnly() const; Flags GetMappingAccess() const; Uint GetExternalIndex() const; diff --git a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp index d3b9d193..3be5c606 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp @@ -56,6 +56,14 @@ namespace MobileGL::MG_State::GLState { bindingSlot.Bind(nullptr); } } + for (auto& bindingPointArray : m_bufferBindPointTargets) { + for (auto& bindingPoint : bindingPointArray) { + if (bindingPoint.GetBoundObject() == it->second) { + bindingPoint.Bind(nullptr); + bindingPoint.ClearRange(); + } + } + } m_bufferObjects.erase(it); } m_indexGenerator.Delete(index); diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index a6be59e1..ad9ab7da 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -285,6 +285,34 @@ namespace MobileGL::MG_State { return m_renderState.GetViewport(); } + void GLContext::SetLineWidth(Float width) { + m_renderState.SetLineWidth(width); + } + + Float GLContext::GetLineWidth() const { + return m_renderState.GetLineWidth(); + } + + 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); } @@ -337,6 +365,14 @@ namespace MobileGL::MG_State { 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); } @@ -353,6 +389,23 @@ namespace MobileGL::MG_State { 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); } @@ -381,9 +434,45 @@ namespace MobileGL::MG_State { m_renderState.SetClearStencil(stencil); } - Uint32 GLContext::GetClearStencil() const { - return m_renderState.GetClearStencil(); - } + 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); diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index f074d9ee..3f1d0d48 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -102,6 +102,13 @@ namespace MobileGL { const RenderStateParameters& GetRenderStateParameters() const; void SetViewport(IntVec4 viewport); // x, y, width, height const IntVec4& GetViewport() const; // x, y, width, height + void SetLineWidth(Float width); + Float GetLineWidth() const; + void SetPointSize(Float size); + Float GetPointSize() const; + void SetPolygonOffset(Float factor, Float units); + Float GetPolygonOffsetFactor() const; + Float GetPolygonOffsetUnits() const; void SetCapability(CapabilityInput cap, Bool enabled); Bool IsCapabilityEnabled(CapabilityInput cap) const; void SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled); @@ -117,10 +124,17 @@ namespace MobileGL { void GetBlendEquation(BlendEquation& color, BlendEquation& alpha) const; void SetBlendEquationIndexed(Uint index, BlendEquation color, BlendEquation alpha); void GetBlendEquationIndexed(Uint index, BlendEquation& color, BlendEquation& alpha) const; + void SetLogicOp(LogicOperation logicOp); + LogicOperation GetLogicOp() const; void SetDepthFunc(DepthTestFunc func); DepthTestFunc GetDepthFunc() const; void SetDepthMask(Bool flag); Bool GetDepthMask() const; + void SetStencilFunc(StencilFace face, DepthTestFunc func, Int ref, Uint32 mask); + void SetStencilMask(StencilFace face, Uint32 mask); + void SetStencilOp(StencilFace face, StencilOperation fail, StencilOperation depthFail, + StencilOperation depthPass); + const StencilFaceState& GetStencilState(StencilFace face) const; void SetColorMask(BoolVec4 mask); BoolVec4 GetColorMask() const; void SetClearColor(FloatVec4 color); @@ -129,6 +143,15 @@ namespace MobileGL { Float GetClearDepth() const; void SetClearStencil(Int stencil); Uint32 GetClearStencil() const; + void SetBlendColor(FloatVec4 color); + const FloatVec4& GetBlendColor() const; + void SetDepthRange(FloatVec2 range); + const FloatVec2& GetDepthRange() const; + void SetSampleCoverage(Float value, Bool invert); + Float GetSampleCoverageValue() const; + Bool GetSampleCoverageInvert() const; + void SetSampleMaskValue(Uint32 mask); + Uint32 GetSampleMaskValue() const; void SetPixelStoreParam(PixelStoreParam param, Int value); Int GetPixelStoreParam(PixelStoreParam param) const; PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const; diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp index ebdf348f..6ee6f9c9 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp @@ -12,8 +12,8 @@ namespace MobileGL::MG_State::GLState { // FramebufferAttachmentObject FramebufferAttachmentObject::FramebufferAttachmentObject( - const SharedPtr& texture, Int level) - : m_texture(texture), m_textureLevel(level) {} + const SharedPtr& texture, TextureUploadTarget textureUploadTarget, Int level) + : m_texture(texture), m_textureUploadTarget(textureUploadTarget), m_textureLevel(level) {} FramebufferAttachmentObject::FramebufferAttachmentObject(const SharedPtr& renderbuffer) : m_renderbuffer(renderbuffer) {} FramebufferAttachmentObject::FramebufferAttachmentObject(Bool IsValid) @@ -45,6 +45,10 @@ namespace MobileGL::MG_State::GLState { return m_textureLevel; } + TextureUploadTarget FramebufferAttachmentObject::GetTextureUploadTarget() const { + return m_textureUploadTarget; + } + Bool FramebufferAttachmentObject::IsComplete() const { if (IsTexture()) { Bool complete = m_texture->IsComplete(); @@ -59,11 +63,18 @@ namespace MobileGL::MG_State::GLState { IntVec3 FramebufferAttachmentObject::GetSize() const { if (IsTexture()) { - // TODO: get correct upload target MOBILEGL_ASSERT(nullptr != static_cast(m_texture.get()), "Texture object here should always be an object with mipmap"); auto textureMipmapObject = static_cast(m_texture.get()); - return textureMipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, m_textureLevel); + TextureUploadTarget resolvedTarget = m_textureUploadTarget; + if (resolvedTarget == TextureUploadTarget::Unknown) { + const auto& uploadTargets = m_texture->GetUploadTargets(); + MOBILEGL_ASSERT(!uploadTargets.empty(), + "FramebufferAttachmentObject::GetSize: textureId=%u exposes no upload targets", + m_texture->GetExternalIndex()); + resolvedTarget = uploadTargets[0]; + } + return textureMipmapObject->GetMipmapTexelSize(resolvedTarget, m_textureLevel); } else if (IsRenderbuffer()) { return {m_renderbuffer->GetWidth(), m_renderbuffer->GetHeight(), 1}; } @@ -79,13 +90,16 @@ namespace MobileGL::MG_State::GLState { : m_externalIndex(externalIndex), m_attachmentVersions{}, m_drawBuffers{} { m_attachmentObjects.fill(FramebufferAttachmentObject(false)); m_drawBuffers.fill(FramebufferAttachmentType::None); - m_drawBuffers[0] = FramebufferAttachmentType::Color0; + const FramebufferAttachmentType defaultColorBuffer = + (externalIndex == 0) ? FramebufferAttachmentType::BackLeft : FramebufferAttachmentType::Color0; + m_drawBuffers[0] = defaultColorBuffer; + m_readBuffer = defaultColorBuffer; m_attachmentVersions.fill(0); } void FramebufferObject::AttachTexture(FramebufferAttachmentType type, const SharedPtr& texture, - int level) { - m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(texture, level); + TextureUploadTarget textureUploadTarget, int level) { + m_attachmentObjects[static_cast(type)] = FramebufferAttachmentObject(texture, textureUploadTarget, level); BumpAttachmentVersion(type); } @@ -150,6 +164,12 @@ namespace MobileGL::MG_State::GLState { return m_drawBuffers; } + void FramebufferObject::SetReadBuffer(FramebufferAttachmentType buf) { + if (m_readBuffer == buf) return; + m_readBuffer = buf; + ++m_objectVersion; + } + Uint FramebufferObject::GetExternalIndex() const { return m_externalIndex; } diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index 1036eefd..b0e2f21d 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -73,6 +73,7 @@ namespace MobileGL { class FramebufferAttachmentObject { public: explicit FramebufferAttachmentObject(const SharedPtr& texture, + TextureUploadTarget textureUploadTarget, Int level = 0); explicit FramebufferAttachmentObject(const SharedPtr& renderbuffer); explicit FramebufferAttachmentObject(Bool IsValid = true); @@ -83,6 +84,7 @@ namespace MobileGL { const SharedPtr& GetTexture() const; const SharedPtr& GetRenderbuffer() const; Int GetTextureLevel() const; + TextureUploadTarget GetTextureUploadTarget() const; Bool IsComplete() const; IntVec3 GetSize() const; Bool IsValid() const; @@ -90,6 +92,7 @@ namespace MobileGL { private: SharedPtr m_texture = nullptr; SharedPtr m_renderbuffer = nullptr; + TextureUploadTarget m_textureUploadTarget = TextureUploadTarget::Unknown; Int m_textureLevel = 0; Bool m_isValid = true; }; @@ -108,7 +111,8 @@ namespace MobileGL { FramebufferObject(Uint externalIndex); - void AttachTexture(FramebufferAttachmentType type, const SharedPtr& texture, int level = 0); + void AttachTexture(FramebufferAttachmentType type, const SharedPtr& texture, + TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown, int level = 0); void AttachRenderbuffer(FramebufferAttachmentType type, const SharedPtr& renderbuffer); void Detach(FramebufferAttachmentType type); const FramebufferAttachmentObject& GetAttachment(FramebufferAttachmentType type) const; @@ -117,7 +121,7 @@ namespace MobileGL { // aka. `buffer` as in glDrawBuffers/glReadBuffers void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer); const FramebufferAttachmentArray& GetDrawBuffers() const; - void SetReadBuffer(FramebufferAttachmentType buf) { m_readBuffer = buf; } + void SetReadBuffer(FramebufferAttachmentType buf); FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; } FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const { @@ -136,7 +140,7 @@ namespace MobileGL { FramebufferAttachmentVersionArray m_attachmentVersions; FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality - FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; // ditto + FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::None; // This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`) Uint16 m_objectVersion = 0; diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferState.cpp b/MobileGL/MG_State/GLState/FramebufferState/FramebufferState.cpp index 50a683e4..618dd157 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferState.cpp +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferState.cpp @@ -62,7 +62,7 @@ namespace MobileGL::MG_State::GLState { if (it != m_framebufferObjects.end()) { for (auto& bindingSlot : m_bindingSlots) { if (bindingSlot.GetBoundObject() == it->second) { - bindingSlot.Bind(nullptr); + bindingSlot.Bind(GetFramebufferObject(0)); } } m_framebufferObjects.erase(it); diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index e75fb9e1..9c05dc8e 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -60,6 +60,27 @@ namespace { } namespace MobileGL::MG_State::GLState { + void ProgramObject::ResetLinkArtifacts() { + m_program.reset(); + m_generatedSpirv.clear(); + m_uniformLocations.clear(); + m_uniformIndexInTProgram.clear(); + m_uniformSamplerOrImageUnitIndex.clear(); + m_uniformBlockIndexByName.clear(); + m_uniformBlockBinding.clear(); + m_uniformOffsets.clear(); + m_uniformSizesInBytes.clear(); + m_globalUboScratch.clear(); + m_attribs.clear(); + m_attribTypes.clear(); + m_activeUniformCount = 0; + m_maxUniformLocation = 0; + m_uniformNameMaxLength = 0; + m_attribInNameMaxLength = 0; + m_uniformBlockNameMaxLength = 0; + m_linkStatus = false; + } + bool ProgramObject::ShaderIsAttached(const SharedPtr& shader) { MGLOG_D("ProgramObject %u: ShaderIsAttached check for shader %p", m_externalIndex, shader.get()); auto it = std::find_if(m_shaders.begin(), m_shaders.end(), @@ -135,6 +156,8 @@ namespace MobileGL::MG_State::GLState { void ProgramObject::Link(Bool addDefaultFSIfMissingForRenderingPipelineProgram) { MGLOG_D("ProgramObject %u: Link start, shaders to link: %zu", m_externalIndex, m_shaders.size()); ++m_backendStateVersion; + ResetLinkArtifacts(); + m_infoLog.clear(); // Remove detached shaders first for (const auto& detachedShader : m_detachedShaders) { RemoveShader(detachedShader); @@ -163,7 +186,6 @@ namespace MobileGL::MG_State::GLState { "log:\n{}\nShader src:\n{}", MG_Util::ConvertGLEnumToString(shaderTypes[i]), m_shaders[i]->GetInfoLog(), m_shaders[i]->GetShaderSource()); - m_linkStatus = false; MGLOG_E("ProgramObject %u: Link failed - shader[%zu] compile status false. InfoLog:\n%s", m_externalIndex, i, m_infoLog.c_str()); return; @@ -186,9 +208,9 @@ namespace MobileGL::MG_State::GLState { m_program = result.value(); MGLOG_D("ProgramObject %u: LinkProgram succeeded, TProgram ptr %p", m_externalIndex, m_program.get()); } else { - m_linkStatus = false; m_infoLog = result.error().log; MGLOG_E("ProgramObject %u: LinkProgram failed. InfoLog:\n%s", m_externalIndex, m_infoLog.c_str()); + return; } MGLOG_D("ProgramObject %u: Starting reflection", m_externalIndex); diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index 2ef2765a..8dd22cbf 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -40,11 +40,40 @@ namespace MobileGL::MG_State::GLState { return (Int)it->second; } + Int GetActiveUniformIndex(const String& name) const { + const Int uniformIndex = m_program->getUniformIndex(name.c_str()); + if (uniformIndex < 0 || uniformIndex >= m_activeUniformCount) return -1; + return m_program->getUniform(uniformIndex).name == name ? uniformIndex : -1; + } + + Bool IsValidUniformLocation(Int location) const { + if (location < 0 || location > static_cast(m_maxUniformLocation)) return false; + if (static_cast(location) >= m_uniformIndexInTProgram.size()) return false; + const Int uniformIndexInProgram = m_uniformIndexInTProgram[location]; + return uniformIndexInProgram != glslang::TQualifier::layoutLocationEnd && + uniformIndexInProgram >= 0 && uniformIndexInProgram < m_activeUniformCount; + } + GLenum GetUniformType(Uint location) const { auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]); return uniform.glDefineType; } + GLenum GetActiveUniformType(Uint index) const { + auto& uniform = m_program->getUniform(static_cast(index)); + return uniform.glDefineType; + } + + GLint GetActiveUniformArraySize(Uint index) const { + auto& uniform = m_program->getUniform(static_cast(index)); + return uniform.size; + } + + Int GetActiveUniformBlockIndex(Uint index) const { + auto& uniform = m_program->getUniform(static_cast(index)); + return uniform.index; + } + const glslang::TType* GetUniformTType(Uint location) const { auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]); return uniform.getType(); @@ -56,6 +85,11 @@ namespace MobileGL::MG_State::GLState { auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]); return uniform.name; } + + const String& GetActiveUniformName(Uint index) const { + auto& uniform = m_program->getUniform(static_cast(index)); + return uniform.name; + } Uint GetUniformOffset(Uint location) const { return m_uniformOffsets[location]; } Uint GetUniformSizesInBytes(Uint location) const { return MG_Util::GetGLTypeSize(GetUniformType(location)); } @@ -107,6 +141,9 @@ namespace MobileGL::MG_State::GLState { } GLenum GetAttribType(Uint index) const { return m_attribTypes[index]; } const String& GetAttribName(Uint index) const { return m_attribs[index]; } + GLenum GetActiveAttribType(Uint index) const { return m_program->getPipeInput(static_cast(index)).glDefineType; } + GLint GetActiveAttribArraySize(Uint index) const { return m_program->getPipeInput(static_cast(index)).size; } + const String& GetActiveAttribName(Uint index) const { return m_program->getPipeInput(static_cast(index)).name; } void* MapUBO() { return m_globalUboScratch.data(); } const void* GetUBOData() const { return m_globalUboScratch.data(); } Uint GetUBOSize() const { return static_cast(m_globalUboScratch.size()); } @@ -126,6 +163,7 @@ namespace MobileGL::MG_State::GLState { Int GetActiveAtomicCounterCount() const { return m_program->getNumAtomicCounters(); } Int GetActiveAttributesCount() const { return m_program->getNumPipeInputs(); } Int GetActiveUniformBlocksCount() const { return m_program->getNumUniformBlocks(); } + GLuint GetComputeLocalSize(Uint dim) const { return m_program->getLocalSize(static_cast(dim)); } Int GetActiveAttributesMaxLength() const { return m_attribInNameMaxLength; } Int GetActiveUniformBlocksMaxNameLength() const { return m_uniformBlockNameMaxLength; } Uint GetUniformBlockIndex(const char* name) const { @@ -147,6 +185,16 @@ namespace MobileGL::MG_State::GLState { return ubo.name; } + Int GetUniformBlockActiveUniformCount(Uint index) const { + return m_program->getUniformBlock((Int)index).numMembers; + } + + Bool IsUniformBlockReferencedByStage(Uint index, EShLanguage stage) const { + const auto& ubo = m_program->getUniformBlock((Int)index); + const auto stageMask = static_cast(1 << stage); + return (ubo.stages & stageMask) != 0; + } + // Set by glUniformBlockBinding void SetUniformBlockBinding(Uint index, Uint binding) { if (index >= m_uniformBlockBinding.size() || m_uniformBlockBinding[index] == static_cast(binding)) { @@ -171,6 +219,7 @@ namespace MobileGL::MG_State::GLState { Uint GetExternalIndex() const { return m_externalIndex; } private: + void ResetLinkArtifacts(); void DoReflection(); void GenerateBinary(); void WaitUntilGenerationCompleted() const; diff --git a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp index ec5219f3..f393b659 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp @@ -16,10 +16,16 @@ namespace MobileGL::MG_State::GLState { void ShaderObject::SetShaderSource(const String& source) { m_source = source; + m_shader.reset(); + m_compileStatus = false; + m_infoLog.clear(); } void ShaderObject::SetShaderSource(String&& source) { m_source = Move(source); + m_shader.reset(); + m_compileStatus = false; + m_infoLog.clear(); } void ShaderObject::Compile() { @@ -38,8 +44,10 @@ namespace MobileGL::MG_State::GLState { if (result) { m_compileStatus = true; m_shader = result.value(); + m_infoLog.clear(); } else { m_compileStatus = false; + m_shader.reset(); m_infoLog = result.error().log; MGLOG_D("ShaderObject::Compile: Shader %d compilation failed.\nSource:\n%s\nInfoLog:\n%s\nSetting " "m_compileStatus = false as a result.", diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index 1df25bc8..88171993 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -12,6 +12,20 @@ namespace MobileGL { namespace MG_State { namespace GLState { + namespace { + SizeT GetStencilFaceIndex(StencilFace face) { + switch (face) { + case StencilFace::Front: + return 0; + case StencilFace::Back: + return 1; + default: + MOBILEGL_ASSERT(false, "Invalid stencil face enum: %d", static_cast(face)); + return 0; + } + } + } // namespace + RenderState::RenderState() {} Uint RenderState::GetVersion() const { @@ -34,6 +48,44 @@ namespace MobileGL { return m_parameters.Viewport; } + void RenderState::SetLineWidth(Float width) { + if (m_parameters.LineWidth == width) return; + + m_parameters.LineWidth = width; + ++m_version; + } + + Float RenderState::GetLineWidth() const { + return m_parameters.LineWidth; + } + + void RenderState::SetPointSize(Float size) { + if (m_parameters.PointSize == size) return; + + m_parameters.PointSize = size; + ++m_version; + } + + Float RenderState::GetPointSize() const { + return m_parameters.PointSize; + } + + void RenderState::SetPolygonOffset(Float factor, Float units) { + if (m_parameters.PolygonOffsetFactor == factor && m_parameters.PolygonOffsetUnits == units) return; + + m_parameters.PolygonOffsetFactor = factor; + m_parameters.PolygonOffsetUnits = units; + ++m_version; + } + + Float RenderState::GetPolygonOffsetFactor() const { + return m_parameters.PolygonOffsetFactor; + } + + Float RenderState::GetPolygonOffsetUnits() const { + return m_parameters.PolygonOffsetUnits; + } + // -------------------- Capabilities -------------------- void RenderState::SetCapability(CapabilityInput cap, Bool enabled) { #define SET_CAPABILITY(capability, flag) \ @@ -44,9 +96,28 @@ namespace MobileGL { break; switch (cap) { + SET_CAPABILITY(ColorLogicOp, enabled); + SET_CAPABILITY(DebugOutput, enabled); + SET_CAPABILITY(DebugOutputSynchronous, enabled); SET_CAPABILITY(DepthTest, enabled); SET_CAPABILITY(CullFace, enabled); + SET_CAPABILITY(Dither, enabled); + SET_CAPABILITY(LineSmooth, enabled); + SET_CAPABILITY(Multisample, enabled); + SET_CAPABILITY(PolygonOffsetFill, enabled); + SET_CAPABILITY(PolygonOffsetLine, enabled); + SET_CAPABILITY(PolygonOffsetPoint, enabled); + SET_CAPABILITY(PolygonSmooth, enabled); + SET_CAPABILITY(PrimitiveRestart, enabled); + SET_CAPABILITY(PrimitiveRestartFixedIndex, enabled); + SET_CAPABILITY(RasterizerDiscard, enabled); + SET_CAPABILITY(SampleAlphaToCoverage, enabled); + SET_CAPABILITY(SampleAlphaToOne, enabled); + SET_CAPABILITY(SampleCoverage, enabled); + SET_CAPABILITY(SampleMask, enabled); SET_CAPABILITY(ScissorTest, enabled); + SET_CAPABILITY(StencilTest, enabled); + SET_CAPABILITY(ProgramPointSize, enabled); case CapabilityInput::Blend: { Bool stateChanged = false; for (auto& blendState : m_parameters.BlendStates) { @@ -68,9 +139,28 @@ namespace MobileGL { case CapabilityInput::capability: \ return m_parameters.capability##Enabled; switch (cap) { + RETURN_CAPABILITY(ColorLogicOp); + RETURN_CAPABILITY(DebugOutput); + RETURN_CAPABILITY(DebugOutputSynchronous); RETURN_CAPABILITY(DepthTest); RETURN_CAPABILITY(CullFace); + RETURN_CAPABILITY(Dither); + RETURN_CAPABILITY(LineSmooth); + RETURN_CAPABILITY(Multisample); + RETURN_CAPABILITY(PolygonOffsetFill); + RETURN_CAPABILITY(PolygonOffsetLine); + RETURN_CAPABILITY(PolygonOffsetPoint); + RETURN_CAPABILITY(PolygonSmooth); + RETURN_CAPABILITY(PrimitiveRestart); + RETURN_CAPABILITY(PrimitiveRestartFixedIndex); + RETURN_CAPABILITY(RasterizerDiscard); + RETURN_CAPABILITY(SampleAlphaToCoverage); + RETURN_CAPABILITY(SampleAlphaToOne); + RETURN_CAPABILITY(SampleCoverage); + RETURN_CAPABILITY(SampleMask); RETURN_CAPABILITY(ScissorTest); + RETURN_CAPABILITY(StencilTest); + RETURN_CAPABILITY(ProgramPointSize); case CapabilityInput::Blend: return m_parameters.BlendStates[0].Enabled; default: @@ -206,6 +296,17 @@ namespace MobileGL { alpha = m_parameters.BlendStates[index].AlphaEquation; } + void RenderState::SetLogicOp(LogicOperation logicOp) { + if (m_parameters.LogicOp == logicOp) return; + + m_parameters.LogicOp = logicOp; + ++m_version; + } + + LogicOperation RenderState::GetLogicOp() const { + return m_parameters.LogicOp; + } + // -------------------- Depth -------------------- void RenderState::SetDepthFunc(DepthTestFunc func) { if (m_parameters.DepthFunc == func) return; @@ -229,6 +330,42 @@ namespace MobileGL { return m_parameters.DepthMask; } + void RenderState::SetStencilFunc(StencilFace face, DepthTestFunc func, Int ref, Uint32 mask) { + StencilFaceState& state = m_parameters.StencilStates[GetStencilFaceIndex(face)]; + if (state.Func == func && state.Ref == ref && state.ValueMask == mask) return; + + state.Func = func; + state.Ref = ref; + state.ValueMask = mask; + ++m_version; + } + + void RenderState::SetStencilMask(StencilFace face, Uint32 mask) { + StencilFaceState& state = m_parameters.StencilStates[GetStencilFaceIndex(face)]; + if (state.WriteMask == mask) return; + + state.WriteMask = mask; + ++m_version; + } + + void RenderState::SetStencilOp(StencilFace face, StencilOperation fail, StencilOperation depthFail, + StencilOperation depthPass) { + StencilFaceState& state = m_parameters.StencilStates[GetStencilFaceIndex(face)]; + if (state.FailOp == fail && state.PassDepthFailOp == depthFail && + state.PassDepthPassOp == depthPass) { + return; + } + + state.FailOp = fail; + state.PassDepthFailOp = depthFail; + state.PassDepthPassOp = depthPass; + ++m_version; + } + + const StencilFaceState& RenderState::GetStencilState(StencilFace face) const { + return m_parameters.StencilStates[GetStencilFaceIndex(face)]; + } + // -------------------- Color Mask -------------------- void RenderState::SetColorMask(BoolVec4 mask) { if (m_parameters.ColorMask == mask) return; @@ -275,6 +412,55 @@ namespace MobileGL { return m_parameters.ClearStencil; } + void RenderState::SetBlendColor(FloatVec4 color) { + if (m_parameters.BlendColor == color) return; + + m_parameters.BlendColor = color; + ++m_version; + } + + const FloatVec4& RenderState::GetBlendColor() const { + return m_parameters.BlendColor; + } + + void RenderState::SetDepthRange(FloatVec2 range) { + if (m_parameters.DepthRange == range) return; + + m_parameters.DepthRange = range; + ++m_version; + } + + const FloatVec2& RenderState::GetDepthRange() const { + return m_parameters.DepthRange; + } + + void RenderState::SetSampleCoverage(Float value, Bool invert) { + if (m_parameters.SampleCoverageValue == value && m_parameters.SampleCoverageInvert == invert) return; + + m_parameters.SampleCoverageValue = value; + m_parameters.SampleCoverageInvert = invert; + ++m_version; + } + + Float RenderState::GetSampleCoverageValue() const { + return m_parameters.SampleCoverageValue; + } + + Bool RenderState::GetSampleCoverageInvert() const { + return m_parameters.SampleCoverageInvert; + } + + void RenderState::SetSampleMaskValue(Uint32 mask) { + if (m_parameters.SampleMaskValue == mask) return; + + m_parameters.SampleMaskValue = mask; + ++m_version; + } + + Uint32 RenderState::GetSampleMaskValue() const { + return m_parameters.SampleMaskValue; + } + // -------------------- Pixel Store -------------------- void RenderState::SetPixelStoreParam(PixelStoreParam param, Int value) { #define SET_PIXEL_STORE_PARAM(paramNameHead, paramNameTail, val) \ diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index c6101112..9bed2621 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -41,6 +41,27 @@ namespace MobileGL { Unknown = -1 }; + enum class LogicOperation { + Clear, + And, + AndReverse, + Copy, + AndInverted, + Noop, + Xor, + Or, + Nor, + Equiv, + Invert, + OrReverse, + CopyInverted, + OrInverted, + Nand, + Set, + LogicOperationCount, + Unknown = -1 + }; + enum class DepthTestFunc { Never, Less, @@ -54,6 +75,26 @@ namespace MobileGL { Unknown = -1 }; + enum class StencilOperation { + Keep, + Zero, + Replace, + IncrementClamp, + DecrementClamp, + Invert, + IncrementWrap, + DecrementWrap, + StencilOperationCount, + Unknown = -1 + }; + + enum class StencilFace { + Front, + Back, + StencilFaceCount, + Unknown = -1 + }; + enum class PixelStoreParam { // Pack Parameters PackAlignment, @@ -155,12 +196,27 @@ namespace MobileGL { BlendEquation AlphaEquation = BlendEquation::Add; }; + struct StencilFaceState { + DepthTestFunc Func = DepthTestFunc::Always; + Int Ref = 0; + Uint32 ValueMask = 0xffffffffu; + Uint32 WriteMask = 0xffffffffu; + StencilOperation FailOp = StencilOperation::Keep; + StencilOperation PassDepthFailOp = StencilOperation::Keep; + StencilOperation PassDepthPassOp = StencilOperation::Keep; + }; + struct RenderStateParameters { // Rasterization IntVec4 Viewport = IntVec4(0, 0, 0, 0); // x, y, width, height + Float LineWidth = 1.0f; + Float PointSize = 1.0f; + Float PolygonOffsetFactor = 0.0f; + Float PolygonOffsetUnits = 0.0f; // Blending Array BlendStates; + LogicOperation LogicOp = LogicOperation::Copy; // Depth Bool DepthTestEnabled = false; @@ -174,6 +230,12 @@ namespace MobileGL { FloatVec4 ClearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f); Float ClearDepth = 1.0f; Uint32 ClearStencil = 0; + FloatVec4 BlendColor = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f); + FloatVec2 DepthRange = FloatVec2(0.0f, 1.0f); + Float SampleCoverageValue = 1.0f; + Bool SampleCoverageInvert = false; + Uint32 SampleMaskValue = 0xffffffffu; + Array StencilStates{}; // Cull Face Bool CullFaceEnabled = false; @@ -181,7 +243,26 @@ namespace MobileGL { FrontFaceMode FrontFaceModeSetting = FrontFaceMode::CounterClockwise; // Scissor + Bool ColorLogicOpEnabled = false; + Bool DebugOutputEnabled = false; + Bool DebugOutputSynchronousEnabled = false; + Bool DitherEnabled = true; + Bool LineSmoothEnabled = false; + Bool MultisampleEnabled = true; + Bool PolygonOffsetFillEnabled = false; + Bool PolygonOffsetLineEnabled = false; + Bool PolygonOffsetPointEnabled = false; + Bool PolygonSmoothEnabled = false; + Bool PrimitiveRestartEnabled = false; + Bool PrimitiveRestartFixedIndexEnabled = false; + Bool RasterizerDiscardEnabled = false; + Bool SampleAlphaToCoverageEnabled = false; + Bool SampleAlphaToOneEnabled = false; + Bool SampleCoverageEnabled = false; + Bool SampleMaskEnabled = false; Bool ScissorTestEnabled = false; + Bool StencilTestEnabled = false; + Bool ProgramPointSizeEnabled = false; IntVec4 ScissorBox = IntVec4(0, 0, 0, 0); // x, y, width, height }; @@ -197,6 +278,13 @@ namespace MobileGL { // Rasterization void SetViewport(IntVec4 viewport); // x, y, width, height const IntVec4& GetViewport() const; // x, y, width, height + void SetLineWidth(Float width); + Float GetLineWidth() const; + void SetPointSize(Float size); + Float GetPointSize() const; + void SetPolygonOffset(Float factor, Float units); + Float GetPolygonOffsetFactor() const; + Float GetPolygonOffsetUnits() const; // Capabilities void SetCapability(CapabilityInput cap, Bool enabled); @@ -216,12 +304,19 @@ namespace MobileGL { void GetBlendEquation(BlendEquation& color, BlendEquation& alpha) const; void SetBlendEquationIndexed(Uint index, BlendEquation color, BlendEquation alpha); void GetBlendEquationIndexed(Uint index, BlendEquation& color, BlendEquation& alpha) const; + void SetLogicOp(LogicOperation logicOp); + LogicOperation GetLogicOp() const; // Depth void SetDepthFunc(DepthTestFunc func); DepthTestFunc GetDepthFunc() const; void SetDepthMask(Bool flag); Bool GetDepthMask() const; + void SetStencilFunc(StencilFace face, DepthTestFunc func, Int ref, Uint32 mask); + void SetStencilMask(StencilFace face, Uint32 mask); + void SetStencilOp(StencilFace face, StencilOperation fail, StencilOperation depthFail, + StencilOperation depthPass); + const StencilFaceState& GetStencilState(StencilFace face) const; // Color Mask void SetColorMask(BoolVec4 mask); @@ -234,6 +329,15 @@ namespace MobileGL { Float GetClearDepth() const; void SetClearStencil(Int stencil); Uint32 GetClearStencil() const; + void SetBlendColor(FloatVec4 color); + const FloatVec4& GetBlendColor() const; + void SetDepthRange(FloatVec2 range); + const FloatVec2& GetDepthRange() const; + void SetSampleCoverage(Float value, Bool invert); + Float GetSampleCoverageValue() const; + Bool GetSampleCoverageInvert() const; + void SetSampleMaskValue(Uint32 mask); + Uint32 GetSampleMaskValue() const; // Pixel Store void SetPixelStoreParam(PixelStoreParam param, Int value); diff --git a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp index 74c189e5..90159adf 100644 --- a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp +++ b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp @@ -76,6 +76,10 @@ namespace MobileGL { m_height = size.y(); m_allocated = true; } + + void RenderbufferObject::SetSamples(Int samples) { + m_samples = samples; + } } // namespace GLState } // namespace MG_State } // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h index 1b496f37..ea55151a 100644 --- a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h +++ b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h @@ -29,6 +29,7 @@ namespace MobileGL { Uint GetExternalIndex() const; void SetInternalFormat(TextureInternalFormat format); void AllocateStorage(IntVec2 size); + void SetSamples(Int samples); Int GetWidth() const; Int GetHeight() const; TextureInternalFormat GetInternalFormat() const; @@ -47,7 +48,7 @@ namespace MobileGL { TextureInternalFormat m_internalFormat; Int m_width = 0; Int m_height = 0; - Int m_samples = 0; // TODO: multisampling support + Int m_samples = 0; Bool m_allocated = false; ComponentSizes m_componentSizes; }; diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp index ddf60478..e5c1377a 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp @@ -57,10 +57,6 @@ namespace MobileGL { void SamplerObject::SetLodRange(Float minLod, Float maxLod) { if (minLod == m_samplerParameters.minLod && maxLod == m_samplerParameters.maxLod) return; - - if (minLod > maxLod) { - THROW_EXCEPTION("minLod cannot be greater than maxLod"); - } m_samplerParameters.minLod = minLod; m_samplerParameters.maxLod = maxLod; ++m_version; diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp index 8ed9113b..84385810 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp @@ -50,7 +50,7 @@ namespace MobileGL { Uint TextureObject2DCube::GetIndexOfTextureUploadTarget(TextureUploadTarget target) const { MOBILEGL_ASSERT(TextureUploadTarget::CubeMapPositiveX <= target && - target <= TextureUploadTarget::ProxyCubeMap, + target <= TextureUploadTarget::CubeMapNegativeZ, "Invalid TextureUploadTarget!"); return (Uint)target - (Uint)TextureUploadTarget::CubeMapPositiveX; } diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp index c90cf6e2..d3191809 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp @@ -50,8 +50,8 @@ namespace MobileGL::MG_State::GLState { void VertexArrayState::MarkVertexArrayForDeletion(Uint index) { if (m_indexGenerator.IsValid(index)) { - if (m_boundVertexArray) { - m_boundVertexArray = nullptr; + if (m_boundVertexArray && m_boundVertexArray->GetExternalIndex() == index) { + m_boundVertexArray = GetVertexArrayObject(0); } if (ValidateVertexArrayObject(index)) { diff --git a/MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp b/MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp index 9f274a1d..3ab900ca 100644 --- a/MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp +++ b/MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp @@ -46,6 +46,8 @@ TEST(DirectVulkanSanity, WindowCreation) { #include #ifdef _WIN32 #define GLFW_EXPOSE_NATIVE_WIN32 +#elif defined(__linux__) + #define GLFW_EXPOSE_NATIVE_X11 #elif defined(__APPLE__) #define GLFW_EXPOSE_NATIVE_COCOA #endif @@ -74,9 +76,11 @@ TEST(DirectVulkanSanity, ContextCreation) { glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); GLFWwindow* window = glfwCreateWindow(800, 600, "MobileGL ContextCreation", nullptr, nullptr); - EGLNativeWindowType nativewindow = nullptr; + EGLNativeWindowType nativewindow = 0; #ifdef _WIN32 nativewindow = glfwGetWin32Window(window); +#elif defined(__linux__) + nativewindow = static_cast(glfwGetX11Window(window)); #elif defined(__APPLE__) void* cocoaWindow = glfwGetCocoaWindow(window); ASSERT_NE(cocoaWindow, nullptr); @@ -95,6 +99,7 @@ TEST(DirectVulkanSanity, ContextCreation) { msgSendVoidObj(contentView, sel_registerName("setLayer:"), metalLayer); nativewindow = reinterpret_cast(metalLayer); #endif + ASSERT_NE(nativewindow, static_cast(0)); EGLSurface surface = eglCreateWindowSurface(display, config, nativewindow, nullptr); eglMakeCurrent(display, surface, surface, context); diff --git a/MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp b/MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp index 91b520ce..d1a75a2e 100644 --- a/MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp +++ b/MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp @@ -28,6 +28,8 @@ #include #ifdef _WIN32 #define GLFW_EXPOSE_NATIVE_WIN32 +#elif defined(__linux__) +#define GLFW_EXPOSE_NATIVE_X11 #elif defined(__APPLE__) #define GLFW_EXPOSE_NATIVE_COCOA #endif @@ -107,9 +109,11 @@ int main() { glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); GLFWwindow* window = glfwCreateWindow(800, 600, "MobileGL ContextCreation", nullptr, nullptr); - EGLNativeWindowType nativewindow = nullptr; + EGLNativeWindowType nativewindow = 0; #ifdef _WIN32 nativewindow = glfwGetWin32Window(window); +#elif defined(__linux__) + nativewindow = static_cast(glfwGetX11Window(window)); #elif defined(__APPLE__) void* cocoaWindow = glfwGetCocoaWindow(window); MOBILEGL_ASSERT(cocoaWindow, "glfwGetCocoaWindow returned null"); @@ -128,6 +132,7 @@ int main() { msgSendVoidObj(contentView, sel_registerName("setLayer:"), metalLayer); nativewindow = reinterpret_cast(metalLayer); #endif + MOBILEGL_ASSERT(nativewindow, "Failed to acquire native window handle for EGL window surface creation"); EGLSurface surface = eglCreateWindowSurface(display, config, nativewindow, nullptr); eglMakeCurrent(display, surface, surface, context); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 91e2ebf0..f4b8989b 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -48,11 +48,14 @@ namespace MobileGL::MG_Util::BackendLoader { return false; } + Bool allRequiredLoaded = true; + #define INIT_GLES_FUNC(name) \ do { \ funcs.name = (MG_External::GLES::name##_PTR)procAddress(#name); \ if (!funcs.name) { \ MGLOG_E("Failed to load GLES function: %s", #name); \ + allRequiredLoaded = false; \ } \ } while (0); @@ -149,6 +152,8 @@ namespace MobileGL::MG_Util::BackendLoader { INIT_GLES_FUNC(glIsTexture) INIT_GLES_FUNC(glLineWidth) INIT_GLES_FUNC(glLinkProgram) + INIT_GLES_FUNC(glLogicOp) + INIT_GLES_FUNC(glPointSize) INIT_GLES_FUNC(glPixelStorei) INIT_GLES_FUNC(glPolygonOffset) INIT_GLES_FUNC(glReadPixels) @@ -428,7 +433,7 @@ namespace MobileGL::MG_Util::BackendLoader { INIT_GLES_FUNC(glMultiDrawElementsIndirectEXT) INIT_GLES_FUNC(glMultiDrawElementsBaseVertexEXT) } - return true; + return allRequiredLoaded; } Bool AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs) { @@ -438,11 +443,15 @@ namespace MobileGL::MG_Util::BackendLoader { MGLOG_E("Failed to open libEGL.so"); return false; } + + Bool allRequiredLoaded = true; + #define INIT_EGL_FUNC(name) \ do { \ funcs.name = (MG_External::EGL::name##_PTR)ProcAddress(eglLib, #name); \ if (!funcs.name) { \ MGLOG_E("Failed to load EGL function: %s", #name); \ + allRequiredLoaded = false; \ } \ } while (0); @@ -495,7 +504,7 @@ namespace MobileGL::MG_Util::BackendLoader { INIT_EGL_FUNC(eglGetPlatformDisplay) INIT_EGL_FUNC(eglWaitSync) } - return true; + return allRequiredLoaded; } Bool FillInGLESCapabilities(MG_External::GLESCapabilities& caps, const MG_External::GLESFunctionsTable& glesFuncs) { @@ -535,6 +544,179 @@ namespace MobileGL::MG_Util::BackendLoader { MGLOG_I("OpenGL ES capabilities:"); glesFuncs.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &caps.UniformBufferOffsetAlignment); MGLOG_I(" GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: %d", caps.UniformBufferOffsetAlignment); + GLfloat aliasedLineWidthRange[2] = {1.0f, 1.0f}; + GLfloat smoothLineWidthRange[2] = {1.0f, 1.0f}; + GLfloat smoothLineWidthGranularity = 1.0f; + GLfloat aliasedPointSizeRange[2] = {1.0f, 1.0f}; + GLfloat viewportBoundsRange[2] = {0.0f, 0.0f}; + GLint maxViewportDims[2] = {16384, 16384}; + GLint viewportSubpixelBits = 0; + GLint max3DTextureSize = 16384; + GLint maxArrayTextureLayers = 2048; + GLint maxCubeMapTextureSize = 16384; + GLint maxFramebufferWidth = 16384; + GLint maxFramebufferHeight = 16384; + GLint maxFramebufferLayers = 2048; + GLint maxRenderbufferSize = 16384; + GLint maxTextureSize = 16384; + GLint maxColorTextureSamples = 1; + GLint maxDepthTextureSamples = 1; + GLint maxFramebufferSamples = 1; + GLint maxIntegerSamples = 1; + GLint maxSamples = 1; + GLint maxSampleMaskWords = 1; + GLint maxTextureImageUnits = 32; + GLint maxVertexTextureImageUnits = 32; + GLint maxComputeTextureImageUnits = 32; + GLint maxCombinedTextureImageUnits = 192; + GLint maxVertexAttribs = 16; + GLint maxComputeShaderStorageBlocks = 8; + GLint maxCombinedShaderStorageBlocks = 32; + GLint maxComputeUniformBlocks = 12; + GLint maxComputeWorkGroupInvocations = 128; + GLint maxShaderStorageBufferBindings = 8; + GLint maxTextureBufferSize = 65536; + GLint maxUniformBufferBindings = 24; + GLint maxUniformBlockSize = 16384; + GLint maxImageUnits = 8; + GLint maxCombinedImageUniforms = 8; + GLint maxComputeImageUniforms = 8; + GLint maxDrawBuffers = 8; + GLint maxColorAttachments = 8; + GLint maxClipDistances = 8; + GLint maxViewports = 16; + glesFuncs.glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange); + glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smoothLineWidthRange); + glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &smoothLineWidthGranularity); + glesFuncs.glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedPointSizeRange); + glesFuncs.glGetFloatv(GL_VIEWPORT_BOUNDS_RANGE, viewportBoundsRange); + glesFuncs.glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize); + glesFuncs.glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers); + glesFuncs.glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapTextureSize); + glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_WIDTH, &maxFramebufferWidth); + glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_HEIGHT, &maxFramebufferHeight); + glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_LAYERS, &maxFramebufferLayers); + glesFuncs.glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize); + glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + glesFuncs.glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &maxColorTextureSamples); + glesFuncs.glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &maxDepthTextureSamples); + glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_SAMPLES, &maxFramebufferSamples); + glesFuncs.glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &maxIntegerSamples); + glesFuncs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); + glesFuncs.glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &maxSampleMaskWords); + glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); + glesFuncs.glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits); + glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxComputeTextureImageUnits); + glesFuncs.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedTextureImageUnits); + glesFuncs.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); + glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &maxComputeShaderStorageBlocks); + glesFuncs.glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &maxCombinedShaderStorageBlocks); + glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_UNIFORM_BLOCKS, &maxComputeUniformBlocks); + glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &maxComputeWorkGroupInvocations); + glesFuncs.glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxShaderStorageBufferBindings); + glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTextureBufferSize); + glesFuncs.glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings); + glesFuncs.glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize); + glesFuncs.glGetIntegerv(GL_MAX_IMAGE_UNITS, &maxImageUnits); + glesFuncs.glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &maxCombinedImageUniforms); + glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms); + glesFuncs.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); + glesFuncs.glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments); + glesFuncs.glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances); + glesFuncs.glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports); + glesFuncs.glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims); + glesFuncs.glGetIntegerv(GL_VIEWPORT_SUBPIXEL_BITS, &viewportSubpixelBits); + caps.AliasedLineWidthRangeMin = aliasedLineWidthRange[0]; + caps.AliasedLineWidthRangeMax = aliasedLineWidthRange[1]; + caps.SmoothLineWidthRangeMin = smoothLineWidthRange[0]; + caps.SmoothLineWidthRangeMax = smoothLineWidthRange[1]; + caps.SmoothLineWidthGranularity = smoothLineWidthGranularity; + caps.PointSizeRangeMin = aliasedPointSizeRange[0]; + caps.PointSizeRangeMax = aliasedPointSizeRange[1]; + caps.PointSizeGranularity = 1.0f; + caps.Max3DTextureSize = max3DTextureSize; + caps.MaxArrayTextureLayers = maxArrayTextureLayers; + caps.MaxCubeMapTextureSize = maxCubeMapTextureSize; + caps.MaxFramebufferWidth = maxFramebufferWidth; + caps.MaxFramebufferHeight = maxFramebufferHeight; + caps.MaxFramebufferLayers = maxFramebufferLayers; + caps.MaxRenderbufferSize = maxRenderbufferSize; + caps.MaxTextureSize = maxTextureSize; + caps.MaxColorTextureSamples = maxColorTextureSamples; + caps.MaxDepthTextureSamples = maxDepthTextureSamples; + caps.MaxFramebufferSamples = maxFramebufferSamples; + caps.MaxIntegerSamples = maxIntegerSamples; + caps.MaxSamples = maxSamples; + caps.MaxSampleMaskWords = maxSampleMaskWords; + caps.MaxTextureImageUnits = maxTextureImageUnits; + caps.MaxVertexTextureImageUnits = maxVertexTextureImageUnits; + caps.MaxComputeTextureImageUnits = maxComputeTextureImageUnits; + caps.MaxCombinedTextureImageUnits = maxCombinedTextureImageUnits; + caps.MaxVertexAttribs = maxVertexAttribs; + caps.MaxComputeShaderStorageBlocks = maxComputeShaderStorageBlocks; + caps.MaxCombinedShaderStorageBlocks = maxCombinedShaderStorageBlocks; + caps.MaxComputeUniformBlocks = maxComputeUniformBlocks; + caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations; + caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings; + caps.MaxTextureBufferSize = maxTextureBufferSize; + caps.MaxUniformBufferBindings = maxUniformBufferBindings; + caps.MaxUniformBlockSize = maxUniformBlockSize; + caps.MaxImageUnits = maxImageUnits; + caps.MaxCombinedImageUniforms = maxCombinedImageUniforms; + caps.MaxComputeImageUniforms = maxComputeImageUniforms; + caps.MaxDrawBuffers = maxDrawBuffers; + caps.MaxColorAttachments = maxColorAttachments; + caps.MaxClipDistances = maxClipDistances; + caps.MaxViewports = maxViewports; + caps.MaxViewportWidth = maxViewportDims[0]; + caps.MaxViewportHeight = maxViewportDims[1]; + caps.ViewportBoundsRangeMin = viewportBoundsRange[0]; + caps.ViewportBoundsRangeMax = viewportBoundsRange[1]; + caps.ViewportSubpixelBits = viewportSubpixelBits; + MGLOG_I(" GL_ALIASED_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.AliasedLineWidthRangeMin, + caps.AliasedLineWidthRangeMax); + MGLOG_I(" GL_SMOOTH_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.SmoothLineWidthRangeMin, + caps.SmoothLineWidthRangeMax); + MGLOG_I(" GL_SMOOTH_LINE_WIDTH_GRANULARITY: %.3f", caps.SmoothLineWidthGranularity); + MGLOG_I(" GL_ALIASED_POINT_SIZE_RANGE: [%.3f, %.3f]", caps.PointSizeRangeMin, caps.PointSizeRangeMax); + MGLOG_I(" GL_MAX_3D_TEXTURE_SIZE: %d", caps.Max3DTextureSize); + MGLOG_I(" GL_MAX_ARRAY_TEXTURE_LAYERS: %d", caps.MaxArrayTextureLayers); + MGLOG_I(" GL_MAX_CUBE_MAP_TEXTURE_SIZE: %d", caps.MaxCubeMapTextureSize); + MGLOG_I(" GL_MAX_FRAMEBUFFER_WIDTH: %d", caps.MaxFramebufferWidth); + MGLOG_I(" GL_MAX_FRAMEBUFFER_HEIGHT: %d", caps.MaxFramebufferHeight); + MGLOG_I(" GL_MAX_FRAMEBUFFER_LAYERS: %d", caps.MaxFramebufferLayers); + MGLOG_I(" GL_MAX_RENDERBUFFER_SIZE: %d", caps.MaxRenderbufferSize); + MGLOG_I(" GL_MAX_TEXTURE_SIZE: %d", caps.MaxTextureSize); + MGLOG_I(" GL_MAX_COLOR_TEXTURE_SAMPLES: %d", caps.MaxColorTextureSamples); + MGLOG_I(" GL_MAX_DEPTH_TEXTURE_SAMPLES: %d", caps.MaxDepthTextureSamples); + MGLOG_I(" GL_MAX_FRAMEBUFFER_SAMPLES: %d", caps.MaxFramebufferSamples); + MGLOG_I(" GL_MAX_INTEGER_SAMPLES: %d", caps.MaxIntegerSamples); + MGLOG_I(" GL_MAX_SAMPLES: %d", caps.MaxSamples); + MGLOG_I(" GL_MAX_SAMPLE_MASK_WORDS: %d", caps.MaxSampleMaskWords); + MGLOG_I(" GL_MAX_TEXTURE_IMAGE_UNITS: %d", caps.MaxTextureImageUnits); + MGLOG_I(" GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: %d", caps.MaxVertexTextureImageUnits); + MGLOG_I(" GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: %d", caps.MaxComputeTextureImageUnits); + MGLOG_I(" GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d", caps.MaxCombinedTextureImageUnits); + MGLOG_I(" GL_MAX_VERTEX_ATTRIBS: %d", caps.MaxVertexAttribs); + MGLOG_I(" GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: %d", caps.MaxComputeShaderStorageBlocks); + MGLOG_I(" GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: %d", caps.MaxCombinedShaderStorageBlocks); + MGLOG_I(" GL_MAX_COMPUTE_UNIFORM_BLOCKS: %d", caps.MaxComputeUniformBlocks); + MGLOG_I(" GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: %d", caps.MaxComputeWorkGroupInvocations); + MGLOG_I(" GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %d", caps.MaxShaderStorageBufferBindings); + MGLOG_I(" GL_MAX_TEXTURE_BUFFER_SIZE: %d", caps.MaxTextureBufferSize); + MGLOG_I(" GL_MAX_UNIFORM_BUFFER_BINDINGS: %d", caps.MaxUniformBufferBindings); + MGLOG_I(" GL_MAX_UNIFORM_BLOCK_SIZE: %d", caps.MaxUniformBlockSize); + MGLOG_I(" GL_MAX_IMAGE_UNITS: %d", caps.MaxImageUnits); + MGLOG_I(" GL_MAX_COMBINED_IMAGE_UNIFORMS: %d", caps.MaxCombinedImageUniforms); + MGLOG_I(" GL_MAX_COMPUTE_IMAGE_UNIFORMS: %d", caps.MaxComputeImageUniforms); + MGLOG_I(" GL_MAX_DRAW_BUFFERS: %d", caps.MaxDrawBuffers); + MGLOG_I(" GL_MAX_COLOR_ATTACHMENTS: %d", caps.MaxColorAttachments); + MGLOG_I(" GL_MAX_CLIP_DISTANCES: %d", caps.MaxClipDistances); + MGLOG_I(" GL_MAX_VIEWPORTS: %d", caps.MaxViewports); + MGLOG_I(" GL_MAX_VIEWPORT_DIMS: [%d, %d]", caps.MaxViewportWidth, caps.MaxViewportHeight); + MGLOG_I(" GL_VIEWPORT_BOUNDS_RANGE: [%.3f, %.3f]", caps.ViewportBoundsRangeMin, + caps.ViewportBoundsRangeMax); + MGLOG_I(" GL_VIEWPORT_SUBPIXEL_BITS: %d", caps.ViewportSubpixelBits); return true; } diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index bc67087b..56986224 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -238,6 +238,8 @@ namespace MobileGL { GL_FUNC_TYPEDEF(GLboolean, glIsTexture, GLuint texture) GL_FUNC_TYPEDEF(void, glLineWidth, GLfloat width) GL_FUNC_TYPEDEF(void, glLinkProgram, GLuint program) + GL_FUNC_TYPEDEF(void, glLogicOp, GLenum opcode) + GL_FUNC_TYPEDEF(void, glPointSize, GLfloat size) GL_FUNC_TYPEDEF(void, glPixelStorei, GLenum pname, GLint param) GL_FUNC_TYPEDEF(void, glPolygonOffset, GLfloat factor, GLfloat units) GL_FUNC_TYPEDEF(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, @@ -725,6 +727,8 @@ namespace MobileGL { GL_FUNC_DECL(glIsTexture) GL_FUNC_DECL(glLineWidth) GL_FUNC_DECL(glLinkProgram) + GL_FUNC_DECL(glLogicOp) + GL_FUNC_DECL(glPointSize) GL_FUNC_DECL(glPixelStorei) GL_FUNC_DECL(glPolygonOffset) GL_FUNC_DECL(glReadPixels) @@ -1015,6 +1019,53 @@ namespace MobileGL { Bool SupportsPersistentMapping = false; Bool SupportsNorm16Texture = false; Int UniformBufferOffsetAlignment = 256; + Float AliasedLineWidthRangeMin = 1.0f; + Float AliasedLineWidthRangeMax = 1.0f; + Float SmoothLineWidthRangeMin = 1.0f; + Float SmoothLineWidthRangeMax = 1.0f; + Float SmoothLineWidthGranularity = 1.0f; + Float PointSizeRangeMin = 1.0f; + Float PointSizeRangeMax = 1.0f; + Float PointSizeGranularity = 1.0f; + Int Max3DTextureSize = 16384; + Int MaxArrayTextureLayers = 2048; + Int MaxCubeMapTextureSize = 16384; + Int MaxFramebufferWidth = 16384; + Int MaxFramebufferHeight = 16384; + Int MaxFramebufferLayers = 2048; + Int MaxRenderbufferSize = 16384; + Int MaxTextureSize = 16384; + Int MaxColorTextureSamples = 1; + Int MaxDepthTextureSamples = 1; + Int MaxFramebufferSamples = 1; + Int MaxIntegerSamples = 1; + Int MaxSamples = 1; + Int MaxSampleMaskWords = 1; + Int MaxTextureImageUnits = 32; + Int MaxVertexTextureImageUnits = 32; + Int MaxComputeTextureImageUnits = 32; + Int MaxCombinedTextureImageUnits = 192; + Int MaxVertexAttribs = 16; + Int MaxComputeShaderStorageBlocks = 8; + Int MaxCombinedShaderStorageBlocks = 32; + Int MaxComputeUniformBlocks = 12; + Int MaxComputeWorkGroupInvocations = 128; + Int MaxShaderStorageBufferBindings = 8; + Int MaxTextureBufferSize = 65536; + Int MaxUniformBufferBindings = 24; + Int MaxUniformBlockSize = 16384; + Int MaxImageUnits = 8; + Int MaxCombinedImageUniforms = 8; + Int MaxComputeImageUniforms = 8; + Int MaxDrawBuffers = 8; + Int MaxColorAttachments = 8; + Int MaxClipDistances = 8; + Int MaxViewports = 16; + Int MaxViewportWidth = 16384; + Int MaxViewportHeight = 16384; + Float ViewportBoundsRangeMin = 0.0f; + Float ViewportBoundsRangeMax = 0.0f; + Int ViewportSubpixelBits = 0; }; } // namespace MG_External diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp index e1df0d2b..ab72a7f8 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp @@ -15,6 +15,29 @@ namespace MobileGL::MG_Util::BackendLoader { PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2 = nullptr; }; + Int ResolveMaxRenderbufferSize(const VkPhysicalDeviceLimits& limits) { + return std::min(static_cast(limits.maxImageDimension2D), + std::min(static_cast(limits.maxFramebufferWidth), + static_cast(limits.maxFramebufferHeight))); + } + + Int MaxSampleCountFromFlags(VkSampleCountFlags flags) { + if (flags & VK_SAMPLE_COUNT_64_BIT) return 64; + if (flags & VK_SAMPLE_COUNT_32_BIT) return 32; + if (flags & VK_SAMPLE_COUNT_16_BIT) return 16; + if (flags & VK_SAMPLE_COUNT_8_BIT) return 8; + if (flags & VK_SAMPLE_COUNT_4_BIT) return 4; + if (flags & VK_SAMPLE_COUNT_2_BIT) return 2; + return 1; + } + + Int ResolveConservativeFramebufferSampleLimit(const VkPhysicalDeviceLimits& limits) { + const VkSampleCountFlags commonFlags = limits.framebufferColorSampleCounts & + limits.framebufferDepthSampleCounts & + limits.framebufferStencilSampleCounts; + return MaxSampleCountFromFlags(commonFlags); + } + VulkanDynamicFunctions LoadVulkanDynamicFunctions(VkInstance instance) { VulkanDynamicFunctions loaded{}; if (instance == VK_NULL_HANDLE) { @@ -83,6 +106,57 @@ namespace MobileGL::MG_Util::BackendLoader { caps.DeviceName = p.deviceName; caps.DriverVersionString = DecodeDriverVersion(p.driverVersion); caps.UniformBufferOffsetAlignment = static_cast(p.limits.minUniformBufferOffsetAlignment); + caps.AliasedLineWidthRangeMin = p.limits.lineWidthRange[0]; + caps.AliasedLineWidthRangeMax = p.limits.lineWidthRange[1]; + caps.SmoothLineWidthRangeMin = p.limits.lineWidthRange[0]; + caps.SmoothLineWidthRangeMax = p.limits.lineWidthRange[1]; + caps.SmoothLineWidthGranularity = p.limits.lineWidthGranularity; + caps.PointSizeRangeMin = p.limits.pointSizeRange[0]; + caps.PointSizeRangeMax = p.limits.pointSizeRange[1]; + caps.PointSizeGranularity = p.limits.pointSizeGranularity; + caps.Max3DTextureSize = static_cast(p.limits.maxImageDimension3D); + caps.MaxArrayTextureLayers = static_cast(p.limits.maxImageArrayLayers); + caps.MaxCubeMapTextureSize = static_cast(p.limits.maxImageDimensionCube); + caps.MaxFramebufferWidth = static_cast(p.limits.maxFramebufferWidth); + caps.MaxFramebufferHeight = static_cast(p.limits.maxFramebufferHeight); + caps.MaxFramebufferLayers = static_cast(p.limits.maxFramebufferLayers); + caps.MaxRenderbufferSize = ResolveMaxRenderbufferSize(p.limits); + caps.MaxTextureSize = static_cast(p.limits.maxImageDimension2D); + caps.MaxColorTextureSamples = MaxSampleCountFromFlags(p.limits.sampledImageColorSampleCounts); + caps.MaxDepthTextureSamples = MaxSampleCountFromFlags(p.limits.sampledImageDepthSampleCounts); + caps.MaxFramebufferSamples = ResolveConservativeFramebufferSampleLimit(p.limits); + caps.MaxIntegerSamples = MaxSampleCountFromFlags(p.limits.sampledImageIntegerSampleCounts); + caps.MaxSamples = caps.MaxFramebufferSamples; + caps.MaxSampleMaskWords = static_cast(p.limits.maxSampleMaskWords); + caps.MaxTextureImageUnits = static_cast(p.limits.maxPerStageDescriptorSampledImages); + caps.MaxVertexTextureImageUnits = static_cast(p.limits.maxPerStageDescriptorSampledImages); + caps.MaxComputeTextureImageUnits = static_cast(p.limits.maxPerStageDescriptorSampledImages); + caps.MaxCombinedTextureImageUnits = static_cast(p.limits.maxDescriptorSetSampledImages); + caps.MaxVertexAttribs = static_cast(p.limits.maxVertexInputAttributes); + caps.MaxComputeShaderStorageBlocks = static_cast(p.limits.maxPerStageDescriptorStorageBuffers); + caps.MaxCombinedShaderStorageBlocks = static_cast(p.limits.maxDescriptorSetStorageBuffers); + caps.MaxComputeUniformBlocks = static_cast(p.limits.maxPerStageDescriptorUniformBuffers); + caps.MaxComputeWorkGroupInvocations = static_cast(p.limits.maxComputeWorkGroupInvocations); + caps.MaxShaderStorageBufferBindings = static_cast(p.limits.maxDescriptorSetStorageBuffers); + caps.MaxTextureBufferSize = static_cast(p.limits.maxTexelBufferElements); + caps.MaxUniformBufferBindings = static_cast(p.limits.maxDescriptorSetUniformBuffers); + caps.MaxUniformBlockSize = static_cast(p.limits.maxUniformBufferRange); + caps.MaxImageUnits = static_cast(p.limits.maxPerStageDescriptorStorageImages); + caps.MaxCombinedImageUniforms = static_cast(p.limits.maxDescriptorSetStorageImages); + caps.MaxComputeImageUniforms = static_cast(p.limits.maxPerStageDescriptorStorageImages); + caps.MaxDrawBuffers = static_cast(p.limits.maxFragmentOutputAttachments); + caps.MaxColorAttachments = static_cast(p.limits.maxColorAttachments); + caps.MaxClipDistances = static_cast(p.limits.maxClipDistances); + caps.MaxViewports = static_cast(p.limits.maxViewports); + caps.MaxViewportWidth = static_cast(p.limits.maxViewportDimensions[0]); + caps.MaxViewportHeight = static_cast(p.limits.maxViewportDimensions[1]); + caps.ViewportBoundsRangeMin = p.limits.viewportBoundsRange[0]; + caps.ViewportBoundsRangeMax = p.limits.viewportBoundsRange[1]; + caps.ViewportSubpixelBits = static_cast(p.limits.viewportSubPixelBits); + + VkPhysicalDeviceFeatures supportedFeatures{}; + vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures); + caps.SupportsWideLines = supportedFeatures.wideLines == VK_TRUE; return true; } @@ -93,5 +167,53 @@ namespace MobileGL::MG_Util::BackendLoader { caps.DeviceName = properties.deviceName; caps.DriverVersionString = DecodeDriverVersion(properties.driverVersion); caps.UniformBufferOffsetAlignment = static_cast(properties.limits.minUniformBufferOffsetAlignment); + caps.AliasedLineWidthRangeMin = properties.limits.lineWidthRange[0]; + caps.AliasedLineWidthRangeMax = properties.limits.lineWidthRange[1]; + caps.SmoothLineWidthRangeMin = properties.limits.lineWidthRange[0]; + caps.SmoothLineWidthRangeMax = properties.limits.lineWidthRange[1]; + caps.SmoothLineWidthGranularity = properties.limits.lineWidthGranularity; + caps.PointSizeRangeMin = properties.limits.pointSizeRange[0]; + caps.PointSizeRangeMax = properties.limits.pointSizeRange[1]; + caps.PointSizeGranularity = properties.limits.pointSizeGranularity; + caps.Max3DTextureSize = static_cast(properties.limits.maxImageDimension3D); + caps.MaxArrayTextureLayers = static_cast(properties.limits.maxImageArrayLayers); + caps.MaxCubeMapTextureSize = static_cast(properties.limits.maxImageDimensionCube); + caps.MaxFramebufferWidth = static_cast(properties.limits.maxFramebufferWidth); + caps.MaxFramebufferHeight = static_cast(properties.limits.maxFramebufferHeight); + caps.MaxFramebufferLayers = static_cast(properties.limits.maxFramebufferLayers); + caps.MaxRenderbufferSize = ResolveMaxRenderbufferSize(properties.limits); + caps.MaxTextureSize = static_cast(properties.limits.maxImageDimension2D); + caps.MaxColorTextureSamples = MaxSampleCountFromFlags(properties.limits.sampledImageColorSampleCounts); + caps.MaxDepthTextureSamples = MaxSampleCountFromFlags(properties.limits.sampledImageDepthSampleCounts); + caps.MaxFramebufferSamples = ResolveConservativeFramebufferSampleLimit(properties.limits); + caps.MaxIntegerSamples = MaxSampleCountFromFlags(properties.limits.sampledImageIntegerSampleCounts); + caps.MaxSamples = caps.MaxFramebufferSamples; + caps.MaxSampleMaskWords = static_cast(properties.limits.maxSampleMaskWords); + caps.MaxTextureImageUnits = static_cast(properties.limits.maxPerStageDescriptorSampledImages); + caps.MaxVertexTextureImageUnits = static_cast(properties.limits.maxPerStageDescriptorSampledImages); + caps.MaxComputeTextureImageUnits = static_cast(properties.limits.maxPerStageDescriptorSampledImages); + caps.MaxCombinedTextureImageUnits = static_cast(properties.limits.maxDescriptorSetSampledImages); + caps.MaxVertexAttribs = static_cast(properties.limits.maxVertexInputAttributes); + caps.MaxComputeShaderStorageBlocks = static_cast(properties.limits.maxPerStageDescriptorStorageBuffers); + caps.MaxCombinedShaderStorageBlocks = static_cast(properties.limits.maxDescriptorSetStorageBuffers); + caps.MaxComputeUniformBlocks = static_cast(properties.limits.maxPerStageDescriptorUniformBuffers); + caps.MaxComputeWorkGroupInvocations = static_cast(properties.limits.maxComputeWorkGroupInvocations); + caps.MaxShaderStorageBufferBindings = static_cast(properties.limits.maxDescriptorSetStorageBuffers); + caps.MaxTextureBufferSize = static_cast(properties.limits.maxTexelBufferElements); + caps.MaxUniformBufferBindings = static_cast(properties.limits.maxDescriptorSetUniformBuffers); + caps.MaxUniformBlockSize = static_cast(properties.limits.maxUniformBufferRange); + caps.MaxImageUnits = static_cast(properties.limits.maxPerStageDescriptorStorageImages); + caps.MaxCombinedImageUniforms = static_cast(properties.limits.maxDescriptorSetStorageImages); + caps.MaxComputeImageUniforms = static_cast(properties.limits.maxPerStageDescriptorStorageImages); + caps.MaxDrawBuffers = static_cast(properties.limits.maxFragmentOutputAttachments); + caps.MaxColorAttachments = static_cast(properties.limits.maxColorAttachments); + caps.MaxClipDistances = static_cast(properties.limits.maxClipDistances); + caps.MaxViewports = static_cast(properties.limits.maxViewports); + caps.MaxViewportWidth = static_cast(properties.limits.maxViewportDimensions[0]); + caps.MaxViewportHeight = static_cast(properties.limits.maxViewportDimensions[1]); + caps.ViewportBoundsRangeMin = properties.limits.viewportBoundsRange[0]; + caps.ViewportBoundsRangeMax = properties.limits.viewportBoundsRange[1]; + caps.ViewportSubpixelBits = static_cast(properties.limits.viewportSubPixelBits); + caps.SupportsWideLines = false; } } // namespace MobileGL::MG_Util::BackendLoader diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h index efd85e1d..4bdf96e7 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h @@ -16,6 +16,54 @@ namespace MobileGL { String DeviceName; String DriverVersionString; Int UniformBufferOffsetAlignment = 256; + Float AliasedLineWidthRangeMin = 1.0f; + Float AliasedLineWidthRangeMax = 1.0f; + Float SmoothLineWidthRangeMin = 1.0f; + Float SmoothLineWidthRangeMax = 1.0f; + Float SmoothLineWidthGranularity = 1.0f; + Float PointSizeRangeMin = 1.0f; + Float PointSizeRangeMax = 1.0f; + Float PointSizeGranularity = 1.0f; + Int Max3DTextureSize = 16384; + Int MaxArrayTextureLayers = 2048; + Int MaxCubeMapTextureSize = 16384; + Int MaxFramebufferWidth = 16384; + Int MaxFramebufferHeight = 16384; + Int MaxFramebufferLayers = 2048; + Int MaxRenderbufferSize = 16384; + Int MaxTextureSize = 16384; + Int MaxColorTextureSamples = 1; + Int MaxDepthTextureSamples = 1; + Int MaxFramebufferSamples = 1; + Int MaxIntegerSamples = 1; + Int MaxSamples = 1; + Int MaxSampleMaskWords = 1; + Int MaxTextureImageUnits = 32; + Int MaxVertexTextureImageUnits = 32; + Int MaxComputeTextureImageUnits = 32; + Int MaxCombinedTextureImageUnits = 192; + Int MaxVertexAttribs = 16; + Int MaxComputeShaderStorageBlocks = 8; + Int MaxCombinedShaderStorageBlocks = 32; + Int MaxComputeUniformBlocks = 12; + Int MaxComputeWorkGroupInvocations = 128; + Int MaxShaderStorageBufferBindings = 8; + Int MaxTextureBufferSize = 65536; + Int MaxUniformBufferBindings = 24; + Int MaxUniformBlockSize = 16384; + Int MaxImageUnits = 8; + Int MaxCombinedImageUniforms = 8; + Int MaxComputeImageUniforms = 8; + Int MaxDrawBuffers = 8; + Int MaxColorAttachments = 8; + Int MaxClipDistances = 8; + Int MaxViewports = 16; + Int MaxViewportWidth = 16384; + Int MaxViewportHeight = 16384; + Float ViewportBoundsRangeMin = 0.0f; + Float ViewportBoundsRangeMax = 0.0f; + Int ViewportSubpixelBits = 0; + Bool SupportsWideLines = false; }; } // namespace MG_External namespace MG_Util::BackendLoader { diff --git a/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp index 5641500b..31e8f377 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp @@ -24,6 +24,8 @@ namespace MobileGL { return DataType::Int32; case GL_UNSIGNED_INT: return DataType::Uint32; + case GL_FIXED: + return DataType::Fixed32; case GL_HALF_FLOAT: return DataType::Float16; case GL_FLOAT: @@ -35,4 +37,4 @@ namespace MobileGL { } } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp index 46c104ab..06df2f18 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp @@ -62,6 +62,45 @@ namespace MobileGL { } } + LogicOperation ConvertGLEnumToLogicOperation(GLenum v) { + switch (v) { + case GL_CLEAR: + return LogicOperation::Clear; + case GL_AND: + return LogicOperation::And; + case GL_AND_REVERSE: + return LogicOperation::AndReverse; + case GL_COPY: + return LogicOperation::Copy; + case GL_AND_INVERTED: + return LogicOperation::AndInverted; + case GL_NOOP: + return LogicOperation::Noop; + case GL_XOR: + return LogicOperation::Xor; + case GL_OR: + return LogicOperation::Or; + case GL_NOR: + return LogicOperation::Nor; + case GL_EQUIV: + return LogicOperation::Equiv; + case GL_INVERT: + return LogicOperation::Invert; + case GL_OR_REVERSE: + return LogicOperation::OrReverse; + case GL_COPY_INVERTED: + return LogicOperation::CopyInverted; + case GL_OR_INVERTED: + return LogicOperation::OrInverted; + case GL_NAND: + return LogicOperation::Nand; + case GL_SET: + return LogicOperation::Set; + default: + return LogicOperation::Unknown; + } + } + DepthTestFunc ConvertGLEnumToDepthTestFunc(GLenum v) { switch (v) { case GL_NEVER: @@ -85,6 +124,29 @@ namespace MobileGL { } } + StencilOperation ConvertGLEnumToStencilOperation(GLenum v) { + switch (v) { + case GL_KEEP: + return StencilOperation::Keep; + case GL_ZERO: + return StencilOperation::Zero; + case GL_REPLACE: + return StencilOperation::Replace; + case GL_INCR: + return StencilOperation::IncrementClamp; + case GL_DECR: + return StencilOperation::DecrementClamp; + case GL_INVERT: + return StencilOperation::Invert; + case GL_INCR_WRAP: + return StencilOperation::IncrementWrap; + case GL_DECR_WRAP: + return StencilOperation::DecrementWrap; + default: + return StencilOperation::Unknown; + } + } + PixelStoreParam ConvertGLEnumToPixelStoreParam(GLenum v) { switch (v) { case GL_PACK_ALIGNMENT: diff --git a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h index 21794a22..629c8069 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h @@ -14,7 +14,9 @@ namespace MobileGL { namespace MG_Util { BlendFactor ConvertGLEnumToBlendFactor(GLenum value); BlendEquation ConvertGLEnumToBlendEquation(GLenum value); + LogicOperation ConvertGLEnumToLogicOperation(GLenum value); DepthTestFunc ConvertGLEnumToDepthTestFunc(GLenum value); + StencilOperation ConvertGLEnumToStencilOperation(GLenum value); PixelStoreParam ConvertGLEnumToPixelStoreParam(GLenum value); CullFaceMode ConvertGLEnumToCullFaceMode(GLenum value); FrontFaceMode ConvertGLEnumToFrontFaceMode(GLenum value); diff --git a/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp index 0225d6ef..efc32612 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp @@ -14,12 +14,16 @@ namespace MobileGL { TextureTarget ConvertGLEnumToTextureTarget(GLenum target) { switch (target) { case GL_TEXTURE_1D: + case GL_PROXY_TEXTURE_1D: return TextureTarget::Texture1D; case GL_TEXTURE_2D: + case GL_PROXY_TEXTURE_2D: return TextureTarget::Texture2D; case GL_TEXTURE_3D: + case GL_PROXY_TEXTURE_3D: return TextureTarget::Texture3D; case GL_TEXTURE_CUBE_MAP: + case GL_PROXY_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP_POSITIVE_X: case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: @@ -28,18 +32,24 @@ namespace MobileGL { case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return TextureTarget::TextureCubeMap; case GL_TEXTURE_2D_ARRAY: + case GL_PROXY_TEXTURE_2D_ARRAY: return TextureTarget::Texture2DArray; case GL_TEXTURE_2D_MULTISAMPLE: + case GL_PROXY_TEXTURE_2D_MULTISAMPLE: return TextureTarget::Texture2DMultisample; case GL_TEXTURE_CUBE_MAP_ARRAY: + case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY: return TextureTarget::TextureCubeMapArray; case GL_TEXTURE_BUFFER: return TextureTarget::TextureBuffer; case GL_TEXTURE_1D_ARRAY: + case GL_PROXY_TEXTURE_1D_ARRAY: return TextureTarget::Texture1DArray; case GL_TEXTURE_RECTANGLE: + case GL_PROXY_TEXTURE_RECTANGLE: return TextureTarget::TextureRectangle; case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY: return TextureTarget::Texture2DMultisampleArray; default: return TextureTarget::Unknown; diff --git a/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp index 5376b47f..7b28c221 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp @@ -24,6 +24,8 @@ namespace MobileGL { return GL_INT; case DataType::Uint32: return GL_UNSIGNED_INT; + case DataType::Fixed32: + return GL_FIXED; case DataType::Float16: return GL_HALF_FLOAT; case DataType::Float32: @@ -35,4 +37,4 @@ namespace MobileGL { } } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp index de6cc7cc..c8a0d1a3 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp @@ -16,8 +16,9 @@ namespace MobileGL { return GL_DRAW_FRAMEBUFFER; case FramebufferTarget::Read: return GL_READ_FRAMEBUFFER; + case FramebufferTarget::Unknown: default: - return GL_DRAW_FRAMEBUFFER; + return GL_UNKNOWN_MGL; } } @@ -29,6 +30,8 @@ namespace MobileGL { } switch (type) { + case FramebufferAttachmentType::None: + return GL_NONE; case FramebufferAttachmentType::Depth: return GL_DEPTH_ATTACHMENT; case FramebufferAttachmentType::Stencil: @@ -41,8 +44,9 @@ namespace MobileGL { return GL_BACK_LEFT; case FramebufferAttachmentType::BackRight: return GL_BACK_RIGHT; + case FramebufferAttachmentType::Unknown: default: - return GL_NONE; + return GL_UNKNOWN_MGL; } } @@ -50,10 +54,11 @@ namespace MobileGL { switch (target) { case RenderbufferTarget::Renderbuffer: return GL_RENDERBUFFER; + case RenderbufferTarget::Unknown: default: - return GL_RENDERBUFFER; + return GL_UNKNOWN_MGL; } } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp index 19fa100f..3b6eceeb 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp @@ -63,6 +63,45 @@ namespace MobileGL { } } + GLenum ConvertLogicOperationToGLEnum(LogicOperation v) { + switch (v) { + case LogicOperation::Clear: + return GL_CLEAR; + case LogicOperation::And: + return GL_AND; + case LogicOperation::AndReverse: + return GL_AND_REVERSE; + case LogicOperation::Copy: + return GL_COPY; + case LogicOperation::AndInverted: + return GL_AND_INVERTED; + case LogicOperation::Noop: + return GL_NOOP; + case LogicOperation::Xor: + return GL_XOR; + case LogicOperation::Or: + return GL_OR; + case LogicOperation::Nor: + return GL_NOR; + case LogicOperation::Equiv: + return GL_EQUIV; + case LogicOperation::Invert: + return GL_INVERT; + case LogicOperation::OrReverse: + return GL_OR_REVERSE; + case LogicOperation::CopyInverted: + return GL_COPY_INVERTED; + case LogicOperation::OrInverted: + return GL_OR_INVERTED; + case LogicOperation::Nand: + return GL_NAND; + case LogicOperation::Set: + return GL_SET; + default: + return GL_UNKNOWN_MGL; + } + } + GLenum ConvertDepthTestFuncToGLEnum(DepthTestFunc v) { switch (v) { case DepthTestFunc::Never: @@ -86,6 +125,29 @@ namespace MobileGL { } } + GLenum ConvertStencilOperationToGLEnum(StencilOperation v) { + switch (v) { + case StencilOperation::Keep: + return GL_KEEP; + case StencilOperation::Zero: + return GL_ZERO; + case StencilOperation::Replace: + return GL_REPLACE; + case StencilOperation::IncrementClamp: + return GL_INCR; + case StencilOperation::DecrementClamp: + return GL_DECR; + case StencilOperation::Invert: + return GL_INVERT; + case StencilOperation::IncrementWrap: + return GL_INCR_WRAP; + case StencilOperation::DecrementWrap: + return GL_DECR_WRAP; + default: + return GL_UNKNOWN_MGL; + } + } + GLenum ConvertPixelStoreParamToGLEnum(PixelStoreParam v) { switch (v) { case PixelStoreParam::PackAlignment: diff --git a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h index a857d438..a2b72185 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h @@ -14,7 +14,9 @@ namespace MobileGL { namespace MG_Util { GLenum ConvertBlendFactorToGLEnum(BlendFactor value); GLenum ConvertBlendEquationToGLEnum(BlendEquation value); + GLenum ConvertLogicOperationToGLEnum(LogicOperation value); GLenum ConvertDepthTestFuncToGLEnum(DepthTestFunc value); + GLenum ConvertStencilOperationToGLEnum(StencilOperation value); GLenum ConvertPixelStoreParamToGLEnum(PixelStoreParam value); GLenum ConvertCullFaceModeToGLEnum(CullFaceMode value); GLenum ConvertFrontFaceModeToGLEnum(FrontFaceMode value); diff --git a/MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp index 191da4ef..882a0206 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp @@ -110,6 +110,8 @@ namespace MobileGL { return GL_RGB10; case TextureInternalFormat::RGB12: return GL_RGB12; + case TextureInternalFormat::RGB16: + return GL_RGB16; case TextureInternalFormat::RGB16Snorm: return GL_RGB16_SNORM; case TextureInternalFormat::RGBA2: @@ -267,6 +269,8 @@ namespace MobileGL { return GL_UNSIGNED_SHORT_5_5_5_1; case TexturePixelDataType::UnsignedShort1555Rev: return GL_UNSIGNED_SHORT_1_5_5_5_REV; + case TexturePixelDataType::UnsignedInt8888: + return GL_UNSIGNED_INT_8_8_8_8; case TexturePixelDataType::UnsignedInt8888Rev: return GL_UNSIGNED_INT_8_8_8_8_REV; case TexturePixelDataType::UnsignedInt1010102: diff --git a/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp index 04314e9e..929954cc 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp @@ -72,6 +72,10 @@ namespace MobileGL { } String ConvertBufferMappingAccessToString(Flags access) { + if (access == BufferMappingAccessBit::Null) { + return "[]"; + } + String result = "["; if (access & BufferMappingAccessBit::Read) result += "Read, "; if (access & BufferMappingAccessBit::Write) result += "Write, "; @@ -84,7 +88,7 @@ namespace MobileGL { result.pop_back(); result.pop_back(); result += "]"; - return result.empty() ? "[]" : result; + return result; } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp index cdca4d65..3bc46f30 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp @@ -29,6 +29,16 @@ namespace MobileGL { } switch (attachment) { + case FramebufferAttachmentType::None: + return "None"; + case FramebufferAttachmentType::FrontLeft: + return "FrontLeft"; + case FramebufferAttachmentType::FrontRight: + return "FrontRight"; + case FramebufferAttachmentType::BackLeft: + return "BackLeft"; + case FramebufferAttachmentType::BackRight: + return "BackRight"; case FramebufferAttachmentType::Depth: return "Depth"; case FramebufferAttachmentType::Stencil: @@ -49,4 +59,4 @@ namespace MobileGL { } } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp index d2b3c3e4..d03a399e 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp @@ -45,6 +45,45 @@ namespace MobileGL { } } + String ConvertLogicOperationToString(LogicOperation v) { + switch (v) { + case LogicOperation::Clear: + return "Clear"; + case LogicOperation::And: + return "And"; + case LogicOperation::AndReverse: + return "AndReverse"; + case LogicOperation::Copy: + return "Copy"; + case LogicOperation::AndInverted: + return "AndInverted"; + case LogicOperation::Noop: + return "Noop"; + case LogicOperation::Xor: + return "Xor"; + case LogicOperation::Or: + return "Or"; + case LogicOperation::Nor: + return "Nor"; + case LogicOperation::Equiv: + return "Equiv"; + case LogicOperation::Invert: + return "Invert"; + case LogicOperation::OrReverse: + return "OrReverse"; + case LogicOperation::CopyInverted: + return "CopyInverted"; + case LogicOperation::OrInverted: + return "OrInverted"; + case LogicOperation::Nand: + return "Nand"; + case LogicOperation::Set: + return "Set"; + default: + return "Unknown"; + } + } + String ConvertDepthTestFuncToString(DepthTestFunc v) { switch (v) { case DepthTestFunc::Never: @@ -68,6 +107,29 @@ namespace MobileGL { } } + String ConvertStencilOperationToString(StencilOperation v) { + switch (v) { + case StencilOperation::Keep: + return "Keep"; + case StencilOperation::Zero: + return "Zero"; + case StencilOperation::Replace: + return "Replace"; + case StencilOperation::IncrementClamp: + return "IncrementClamp"; + case StencilOperation::DecrementClamp: + return "DecrementClamp"; + case StencilOperation::Invert: + return "Invert"; + case StencilOperation::IncrementWrap: + return "IncrementWrap"; + case StencilOperation::DecrementWrap: + return "DecrementWrap"; + default: + return "Unknown"; + } + } + String ConvertPixelStoreParamToString(PixelStoreParam v) { switch (v) { case PixelStoreParam::PackAlignment: diff --git a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h index 1831d86b..b074f1f4 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h @@ -13,7 +13,9 @@ namespace MobileGL { namespace MG_Util { String ConvertBlendFactorToString(BlendFactor value); + String ConvertLogicOperationToString(LogicOperation value); String ConvertDepthTestFuncToString(DepthTestFunc value); + String ConvertStencilOperationToString(StencilOperation value); String ConvertPixelStoreParamToString(PixelStoreParam value); String ConvertCullFaceModeToString(CullFaceMode value); String ConvertFrontFaceModeToString(FrontFaceMode value); diff --git a/MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp index 4969262a..0b2f7efb 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp @@ -110,6 +110,8 @@ namespace MobileGL { return "RGB10"; case TextureInternalFormat::RGB12: return "RGB12"; + case TextureInternalFormat::RGB16: + return "RGB16"; case TextureInternalFormat::RGB16Snorm: return "RGB16Snorm"; case TextureInternalFormat::RGBA2: diff --git a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp index 6e7b3e26..c973fee8 100644 --- a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp @@ -47,6 +47,45 @@ namespace MobileGL { } } + VkLogicOp ConvertLogicOperationToVkEnum(LogicOperation v) { + switch (v) { + case LogicOperation::Clear: + return VK_LOGIC_OP_CLEAR; + case LogicOperation::And: + return VK_LOGIC_OP_AND; + case LogicOperation::AndReverse: + return VK_LOGIC_OP_AND_REVERSE; + case LogicOperation::Copy: + return VK_LOGIC_OP_COPY; + case LogicOperation::AndInverted: + return VK_LOGIC_OP_AND_INVERTED; + case LogicOperation::Noop: + return VK_LOGIC_OP_NO_OP; + case LogicOperation::Xor: + return VK_LOGIC_OP_XOR; + case LogicOperation::Or: + return VK_LOGIC_OP_OR; + case LogicOperation::Nor: + return VK_LOGIC_OP_NOR; + case LogicOperation::Equiv: + return VK_LOGIC_OP_EQUIVALENT; + case LogicOperation::Invert: + return VK_LOGIC_OP_INVERT; + case LogicOperation::OrReverse: + return VK_LOGIC_OP_OR_REVERSE; + case LogicOperation::CopyInverted: + return VK_LOGIC_OP_COPY_INVERTED; + case LogicOperation::OrInverted: + return VK_LOGIC_OP_OR_INVERTED; + case LogicOperation::Nand: + return VK_LOGIC_OP_NAND; + case LogicOperation::Set: + return VK_LOGIC_OP_SET; + default: + return VK_LOGIC_OP_COPY; + } + } + VkCompareOp ConvertDepthTestFuncToVkEnum(DepthTestFunc v) { switch (v) { case DepthTestFunc::Never: @@ -69,6 +108,29 @@ namespace MobileGL { } } + VkStencilOp ConvertStencilOperationToVkEnum(StencilOperation v) { + switch (v) { + case StencilOperation::Keep: + return VK_STENCIL_OP_KEEP; + case StencilOperation::Zero: + return VK_STENCIL_OP_ZERO; + case StencilOperation::Replace: + return VK_STENCIL_OP_REPLACE; + case StencilOperation::IncrementClamp: + return VK_STENCIL_OP_INCREMENT_AND_CLAMP; + case StencilOperation::DecrementClamp: + return VK_STENCIL_OP_DECREMENT_AND_CLAMP; + case StencilOperation::Invert: + return VK_STENCIL_OP_INVERT; + case StencilOperation::IncrementWrap: + return VK_STENCIL_OP_INCREMENT_AND_WRAP; + case StencilOperation::DecrementWrap: + return VK_STENCIL_OP_DECREMENT_AND_WRAP; + default: + return VK_STENCIL_OP_KEEP; + } + } + VkBlendFactor ConvertBlendFactorToVkEnum(BlendFactor v) { switch (v) { case BlendFactor::Zero: diff --git a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h index 62a37e9c..5c1222d7 100644 --- a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h @@ -14,7 +14,9 @@ namespace MobileGL { namespace MG_Util { VkPrimitiveTopology ConvertPrimitiveModeToVkEnum(GLenum mode); VkCullModeFlags ConvertCullFaceModeToVkEnum(CullFaceMode value, Bool invertClockwise = false); + VkLogicOp ConvertLogicOperationToVkEnum(LogicOperation value); VkCompareOp ConvertDepthTestFuncToVkEnum(DepthTestFunc value); + VkStencilOp ConvertStencilOperationToVkEnum(StencilOperation value); VkBlendFactor ConvertBlendFactorToVkEnum(BlendFactor value); VkBlendOp ConvertBlendEquationToVkEnum(BlendEquation value); } // namespace MG_Util diff --git a/MobileGL/MG_Util/Debug/Log.cpp b/MobileGL/MG_Util/Debug/Log.cpp index 8c1b5e05..7e076c08 100644 --- a/MobileGL/MG_Util/Debug/Log.cpp +++ b/MobileGL/MG_Util/Debug/Log.cpp @@ -95,11 +95,13 @@ namespace MobileGL { va_list args; va_start(args, fmt); int n = std::vsnprintf(buffer, sizeof(buffer), fmt, args); + const SizeT messageLength = + (n < 0) ? 0 : std::min(static_cast(n), sizeof(buffer) - static_cast(1)); std::string out = header + #if MOBILEGL_LOG_ENABLE_STACKTRACE padding + #endif - std::string(buffer, n) + "\n"; + std::string(buffer, messageLength) + "\n"; #if MOBILEGL_LOG_ENABLE_CONSOLE std::fwrite(out.c_str(), 1, out.size(), stdout); diff --git a/MobileGL/MG_Util/Metrics/TextureMetrics.cpp b/MobileGL/MG_Util/Metrics/TextureMetrics.cpp index d4a1a364..c7e7b7e0 100644 --- a/MobileGL/MG_Util/Metrics/TextureMetrics.cpp +++ b/MobileGL/MG_Util/Metrics/TextureMetrics.cpp @@ -43,6 +43,10 @@ namespace MobileGL { case TextureInternalFormat::DepthComponent24: return 3; + case TextureInternalFormat::RGB10: + case TextureInternalFormat::RGB12: + case TextureInternalFormat::RGB16: + case TextureInternalFormat::RGB16Snorm: case TextureInternalFormat::RGBA2: case TextureInternalFormat::RGBA4: case TextureInternalFormat::RGB5A1: @@ -54,6 +58,8 @@ namespace MobileGL { case TextureInternalFormat::RGB10A2: case TextureInternalFormat::RGB10A2UI: case TextureInternalFormat::R32F: + case TextureInternalFormat::R32I: + case TextureInternalFormat::R32UI: case TextureInternalFormat::DepthComponent: case TextureInternalFormat::DepthComponent32: case TextureInternalFormat::DepthComponent32F: @@ -179,6 +185,7 @@ namespace MobileGL { case TextureInternalFormat::RGB10: case TextureInternalFormat::RGB12: case TextureInternalFormat::RGB16: + case TextureInternalFormat::RGB16Snorm: case TextureInternalFormat::SRGB8: case TextureInternalFormat::RGB8I: case TextureInternalFormat::RGB8UI: @@ -236,7 +243,6 @@ namespace MobileGL { case TexturePixelDataType::UnsignedInt248: case TexturePixelDataType::Float32UnsignedInt248Rev: return 4; - return 4; default: return 0; } @@ -339,6 +345,22 @@ namespace MobileGL { s.Green = 8; s.Blue = 8; break; + case TextureInternalFormat::RGB10: + s.Red = 10; + s.Green = 10; + s.Blue = 10; + break; + case TextureInternalFormat::RGB12: + s.Red = 12; + s.Green = 12; + s.Blue = 12; + break; + case TextureInternalFormat::RGB16: + case TextureInternalFormat::RGB16Snorm: + s.Red = 16; + s.Green = 16; + s.Blue = 16; + break; case TextureInternalFormat::R3G3B2: s.Red = 3; @@ -370,7 +392,6 @@ namespace MobileGL { case TextureInternalFormat::RGBA8UI: case TextureInternalFormat::SRGB8Alpha8: case TextureInternalFormat::RGBA: - break; s.Red = 8; s.Green = 8; s.Blue = 8; @@ -385,6 +406,8 @@ namespace MobileGL { break; case TextureInternalFormat::R32F: + case TextureInternalFormat::R32I: + case TextureInternalFormat::R32UI: s.Red = 32; break; case TextureInternalFormat::RG32F: diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp index e0893638..39184c5d 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp @@ -200,6 +200,10 @@ namespace MobileGL { size_t commentStartPos = source.find("/*"); while (commentStartPos != String::npos) { size_t commentEndPos = source.find("*/", commentStartPos); + if (commentEndPos == String::npos) { + source.erase(commentStartPos); + break; + } // + length of "*/" source = source.replace(commentStartPos, commentEndPos - commentStartPos + 2, ""); commentStartPos = source.find("/*", commentStartPos); diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp b/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp index 11ef6188..1405aec2 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp @@ -120,11 +120,27 @@ namespace MobileGL { less_operands.push_back({SPV_OPERAND_TYPE_ID, {abs_inst->result_id()}}); less_operands.push_back({SPV_OPERAND_TYPE_ID, {eps_id}}); - bool isEqualOp = - (inst.opcode() == spv::Op::OpFOrdEqual || inst.opcode() == spv::Op::OpFUnordEqual); + spv::Op replacementOp = spv::Op::OpNop; + switch (inst.opcode()) { + case spv::Op::OpFOrdEqual: + replacementOp = spv::Op::OpFOrdLessThan; + break; + case spv::Op::OpFUnordEqual: + replacementOp = spv::Op::OpFUnordLessThan; + break; + case spv::Op::OpFOrdNotEqual: + replacementOp = spv::Op::OpFOrdGreaterThanEqual; + break; + case spv::Op::OpFUnordNotEqual: + replacementOp = spv::Op::OpFUnordGreaterThanEqual; + break; + default: + MOBILEGL_ASSERT(false, "Unexpected float compare opcode: %d", + static_cast(inst.opcode())); + break; + } Instruction* less_than_inst = builder.AddInstruction(MakeUnique( - context(), isEqualOp ? spv::Op::OpFOrdLessThan : spv::Op::OpFOrdGreaterThanEqual, - bool_type_id, context()->TakeNextId(), less_operands)); + context(), replacementOp, bool_type_id, context()->TakeNextId(), less_operands)); // 5. Replaces all uses of old insn with new one context()->ReplaceAllUsesWith(inst.result_id(), less_than_inst->result_id()); @@ -149,4 +165,4 @@ namespace MobileGL { } } // namespace ShaderTranspiler } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h index 7bbc406b..bd7de090 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h +++ b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h @@ -70,7 +70,7 @@ namespace MobileGL { class SpvcSession { public: - SpvcSession() {} + SpvcSession() = default; explicit SpvcSession(const Vector& spirv, Flags usage); @@ -97,7 +97,7 @@ namespace MobileGL { spvc_result ParseMetaData(); private: - Flags usage; + Flags usage{}; // SPIRV-Cross state (used when Transpile flag is set) spvc_context context = nullptr; @@ -114,4 +114,4 @@ namespace MobileGL { }; } // namespace ShaderTranspiler } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp b/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp index 60519cf4..dc897154 100644 --- a/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp +++ b/MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp @@ -154,8 +154,8 @@ namespace MobileGL::MG_Util::PixelStoreProcessor { MGLOG_D("%s: Swizzle (BGRA)", __func__); // MGLOG_D("%s: pixel0 before = %x", __func__, *((Uint32*)layerDst)); ProcessColorSwizzle(layerDst, static_cast(copyWidth), - {TextureSwizzleParam::Green, TextureSwizzleParam::Blue, - TextureSwizzleParam::Alpha, TextureSwizzleParam::Red}); + {TextureSwizzleParam::Blue, TextureSwizzleParam::Green, + TextureSwizzleParam::Red, TextureSwizzleParam::Alpha}); // MGLOG_D("%s: pixel0 after = %x", __func__, *((Uint32*)layerDst)); } // else diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp index 4d8eea60..5966947e 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp @@ -157,6 +157,9 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { case GL_SRGB8: *outFormat = GL_RGB; break; + case GL_SRGB8_ALPHA8: + *outFormat = GL_RGBA; + break; // Color sized other case GL_RGB9_E5: @@ -178,6 +181,7 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { break; // Depth Stencil + case GL_DEPTH24_STENCIL8: case GL_DEPTH32F_STENCIL8: case GL_DEPTH_STENCIL: *outFormat = GL_DEPTH_STENCIL; @@ -298,6 +302,9 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { case GL_SRGB8: *outType = GL_UNSIGNED_BYTE; break; + case GL_SRGB8_ALPHA8: + *outType = GL_UNSIGNED_BYTE; + break; // Color sized other case GL_RGB9_E5: @@ -331,6 +338,9 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { break; // Depth Stencil + case GL_DEPTH24_STENCIL8: + *outType = GL_UNSIGNED_INT_24_8; + break; case GL_DEPTH32F_STENCIL8: case GL_DEPTH_STENCIL: *outType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; @@ -345,4 +355,4 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { } } } -} // namespace MobileGL::MG_Util::TextureFormatProcessor \ No newline at end of file +} // namespace MobileGL::MG_Util::TextureFormatProcessor diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index aff143de..54b5529d 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -115,7 +115,7 @@ namespace MobileGL { // represents a range of [start, end) struct Range1D { SizeT start = 0; - SizeT end = ~0u; + SizeT end = ~SizeT(0); void Update(SizeT newStart, SizeT newEnd) { MOBILEGL_ASSERT(newStart <= newEnd, "Range1D::Update: newStart (%zu) > newEnd (%zu)", newStart, newEnd); diff --git a/SOURCE_AUDIT.md b/SOURCE_AUDIT.md new file mode 100644 index 00000000..bc289093 --- /dev/null +++ b/SOURCE_AUDIT.md @@ -0,0 +1,826 @@ +# MobileGL Source Audit + +Status: complete + +Scope rules: +- In scope: project source files under `MobileGL/` and `selfcheck.c` +- Excluded: `include/`, `3rdparty/`, generated/build output +- Validation rule: compile-only checks, no tests + +## Findings + +### Critical + +1. `MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp` + - Default depth attachment storage uses `width * width * 4` instead of `width * height * 4` when initializing the default framebuffer depth texture metadata. + - Impact: non-square swapchain extents under-allocate or over-report CPU-side texture storage tracked by `MipmapStorage`. This can corrupt later uploads/copies or cause mismatched size assumptions between default framebuffer metadata and actual Vulkan image extent. + - Status: fixed in this audit pass. + +2. `MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp` + - `WaitAndAcquireNextImage()` resets `imageInFlightFence` before `vkAcquireNextImageKHR()`. When image acquisition fails with `VK_ERROR_OUT_OF_DATE_KHR` or `VK_SUBOPTIMAL_KHR`, the fence remains unsignaled and no submission occurs to signal it again. + - Impact: the next call for the same frame slot can block forever in `vkWaitForFences`, effectively deadlocking rendering during swapchain recreation paths. + - Status: fixed in this audit pass. + +3. `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp` + - `VkRenderPassManager::Shutdown()` was empty, so swapchain recreation left cached `VkRenderPass` / `VkFramebuffer` entries alive even though their attachments referenced destroyed swapchain image views and depth views. + - Impact: after `RecreateSwapchain()`, cache hits could return framebuffer/render-pass objects built against stale destroyed attachments, leading to invalid Vulkan handles and undefined rendering behavior. + - Status: fixed in this audit pass. + +### High + +1. `MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp` + - `VkSwapchainCreateInfoKHR::minImageCount` is only clamped against `capabilities.minImageCount`, but not against `capabilities.maxImageCount` when that upper bound is non-zero. + - Impact: on platforms where `MaxFramesInFlight` exceeds the surface maximum, swapchain creation can fail with invalid parameters. + - Status: fixed in this audit pass. + +2. `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `Present()` recreates the swapchain after `vkAcquireNextImageKHR()` returns `VK_ERROR_OUT_OF_DATE_KHR` / `VK_SUBOPTIMAL_KHR`, but previously treated that as success without acquiring an image from the new swapchain. + - Impact: subsequent rendering would continue with a stale `m_imageIndexAcquired`, so later operations could target an image index from the old swapchain or trip range assertions. + - Status: fixed in this audit pass. + +3. `MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp` + - `GetPresentInfo()` previously stored `VkPresentInfoKHR::pImageIndices` as a pointer to the caller-owned `imageIndex` reference instead of packet-owned storage. + - Impact: the current call site happens to pass a long-lived member, but the helper API was fragile and would become a dangling-pointer bug as soon as a temporary or shorter-lived local was passed in. + - Status: fixed in this audit pass. + +4. `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp` + - `UploadDirtyMipLevels()` always transitioned uploaded textures to and tracked them as `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL`, even for depth/stencil formats. + - Impact: depth/stencil textures could be left in an invalid tracked sampled layout after upload. Later sampling paths accept that tracked layout as already readable and skip the corrective transition to `VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL`, which can trigger validation/runtime errors. + - Status: fixed in this audit pass. + +5. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - `GetIntegerv(GL_SCISSOR_BOX, params)` previously wrote only `params[0] = 0` instead of returning the full four-component scissor rectangle from tracked GL state. + - Impact: callers receive incorrect state and may read uninitialized trailing elements, breaking state mirroring and any code that snapshots/restores scissor state through `glGetIntegerv`. + - Status: fixed in this audit pass. + +6. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - `GetIntegerv(GL_COLOR_CLEAR_VALUE, params)` previously wrote only `params[0] = 0` instead of returning the full four-component tracked clear color. + - Impact: callers receive incorrect clear color state and may read uninitialized trailing elements, breaking code that inspects or preserves clear state through `glGetIntegerv`. + - Status: fixed in this audit pass. + +7. `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - The DirectVulkan draw path ignored tracked `glViewport` state and always programmed Vulkan viewport `x/y = 0`, `width/height = render-pass extent`. + - Impact: applications that rely on non-default viewport rectangles render to the wrong region under the Vulkan backend, even though `glViewport` updates tracked state successfully. + - Status: fixed in this audit pass. + +8. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glDepthRange` was exported but its state setter was a no-op, `glGetIntegerv(GL_DEPTH_RANGE, ...)` returned a placeholder scalar, and the Vulkan backend hardcoded viewport depth range to `0..1`. + - Impact: applications cannot control or query depth mapping correctly on this backend, and code that depends on non-default depth ranges silently renders with the wrong clip-to-depth transform. + - Status: fixed in this audit pass. + +9. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glBlendColor` was exported but its state setter was a no-op, `glGetIntegerv(GL_BLEND_COLOR, ...)` returned a placeholder scalar, and the Vulkan backend never enabled dynamic blend constants or pushed them with `vkCmdSetBlendConstants`. + - Impact: any blend mode using `GL_CONSTANT_COLOR`, `GL_ONE_MINUS_CONSTANT_COLOR`, `GL_CONSTANT_ALPHA`, or `GL_ONE_MINUS_CONSTANT_ALPHA` produces incorrect results and applications cannot query the configured constant blend color. + - Status: fixed in this audit pass. + +10. `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp` + - After `BlendColor` and `DepthRange` became tracked render state, the DirectGLES backend sync path still never forwarded those fields to native GL with `glBlendColor` / `glDepthRangef`. + - Impact: backend behavior diverged by renderer: DirectVulkan would honor the newly tracked states, while DirectGLES would silently keep stale native GL blend constants and depth range. + - Status: fixed in this audit pass. + +11. `MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp` + - `MarkVertexArrayForDeletion()` previously cleared `m_boundVertexArray` for any valid deleted VAO, even when the deleted object was not the currently bound VAO. + - Impact: deleting an unrelated VAO could silently drop the current binding to null, causing later vertex-attribute and draw operations to fail with spurious `InvalidOperation` errors or backend-side null-state handling. + - Status: fixed in this audit pass. + +12. `MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp` + - Draw and compute entry points previously forwarded directly to the backend even when no current program was bound via `UseProgram(0)`, when the current program was unlinked, or when compute dispatch was attempted without a compute shader stage. The same frontend path also allowed `GL_LINE_LOOP` to reach the DirectVulkan backend, where primitive conversion silently fell back to triangle-list rendering. + - Impact: ordinary invalid-operation cases could fall through into backend-specific failure modes. On DirectVulkan this is especially severe because draw/compute paths dereference `GetCurrentProgram()` unconditionally, turning frontend GL state errors into null dereferences or assertion-driven aborts instead of clean GL errors; `GL_LINE_LOOP` could also render the wrong primitive topology instead of being rejected. + - Status: fixed in this audit pass. + +13. `MobileGL/MG_State/GLState/BufferState/BufferState.cpp` + - `MarkBufferObjectForDeletion()` previously cleared only generic buffer binding slots and left indexed binding points from `glBindBufferBase` / `glBindBufferRange` still referencing the deleted buffer object. + - Impact: after buffer deletion, stale UBO / SSBO / transform-feedback / atomic-counter binding points could still report or use a deleted buffer object instead of becoming unbound, causing incorrect state queries and follow-on backend synchronization against invalid state. + - Status: fixed in this audit pass. + +14. `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp` + - `BindBufferBase_State()` and `BindBufferRange_State()` treated buffer name `0` like an ordinary object name, creating or reusing a frontend buffer object 0 instead of unbinding the indexed binding point. + - Impact: `glBindBufferBase(..., 0)` / `glBindBufferRange(..., 0, ...)` could leave phantom buffer-object state attached to indexed binding points instead of clearing them, breaking OpenGL unbind semantics. + - Status: fixed in this audit pass. + +15. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp` + - `ReadPixels()` previously called `ReadPixels_Backend()` unconditionally even after `ReadPixels_State()` recorded validation errors for invalid sizes, formats/types, incomplete read FBOs, mapped pixel-pack buffers, or unsupported multisampled reads. + - Impact: invalid `glReadPixels` calls could still execute backend readback against rejected frontend state, producing unintended GPU work and backend-specific failures instead of stopping at the GL error. + - Status: fixed in this audit pass. + +16. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp` + - `FramebufferRenderbuffer_State()` and `BindRenderbuffer_State()` both validated renderbuffer names with zero effectively rejected on the path, so `glFramebufferRenderbuffer(..., 0)` could fail before detaching and `glBindRenderbuffer(..., 0)` could not unbind cleanly. The bind path also created and bound a phantom frontend renderbuffer object 0 when it should have cleared the binding. + - Impact: standard renderbuffer detach/unbind operations were broken, leaving applications unable to clear renderbuffer attachments or restore the default renderbuffer binding semantics. + - Status: fixed in this audit pass. + +17. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp` + - `FramebufferRenderbuffer_State()` converted `renderbuffertarget` to `RenderbufferTarget` but never validated it before mutating framebuffer state. + - Impact: invalid `renderbuffertarget` enums could be silently accepted instead of generating `GL_INVALID_ENUM`. + - Status: fixed in this audit pass. + +18. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp` + - `ReadBuffer_State()` accepted any enum convertible to a framebuffer attachment type and never enforced the default-FBO vs user-FBO token split. `DrawBuffers_State()` had the same gap for non-color attachments on user FBOs and default-buffer tokens on framebuffer objects. + - Impact: calls such as `glReadBuffer(GL_DEPTH_ATTACHMENT)` or `glDrawBuffers(... GL_DEPTH_ATTACHMENT ...)` on user FBOs, and non-default framebuffer tokens on the default FBO, could silently poison tracked buffer-selection state instead of raising `GL_INVALID_ENUM`. + - Status: fixed in this audit pass. + +19. `MobileGL/MG_State/GLState/FramebufferState/FramebufferState.cpp` + - `MarkFramebufferObjectForDeletion()` previously rebound any currently bound deleted FBO to `nullptr` instead of restoring framebuffer object 0. + - Impact: deleting a bound user FBO left draw/read framebuffer bindings empty, causing later framebuffer-dependent commands to fail with spurious `InvalidOperation` paths or backend sync errors instead of reverting to the default framebuffer as OpenGL requires. + - Status: fixed in this audit pass. + +20. `MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp` + - `SetSamplerParam_State()` never called the sampler parameter validators even though they existed, so invalid enums such as unsupported wrap/filter/compare values were converted to internal `Unknown` enum states and stored on sampler objects instead of raising GL errors. + - Impact: invalid `glSamplerParameter*` calls silently poisoned frontend sampler state, which then propagated into backend sampler creation with fallback/default behavior instead of preserving last-valid state and reporting the API error. + - Status: fixed in this audit pass. + +21. `MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp` + - `SamplerObject::SetLodRange()` threw a C++ exception when `minLod > maxLod`. Frontend API calls update those fields one-at-a-time, so ordinary sequences like lowering `GL_TEXTURE_MAX_LOD` below the current `MIN_LOD` could abort the process. + - Impact: legal state-update ordering for sampler LOD limits could crash the library instead of simply storing the caller-provided values for later effective clamping in the backend. + - Status: fixed in this audit pass. + +22. `MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp` + - `ConvertGLEnumToTextureTarget()` did not recognize proxy texture enums such as `GL_PROXY_TEXTURE_1D/2D/3D`, `GL_PROXY_TEXTURE_CUBE_MAP`, and the array/multisample proxy targets. + - Impact: proxy texture entry points could derive `TextureTarget::Unknown`, then index active-unit binding slots with an invalid target or reject otherwise valid proxy queries/allocations on the frontend. + - Status: fixed in this audit pass. + +23. `MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp` + - `ProxyTextureManager::CreateOrReplaceProxyTextureObject()` always instantiated `TextureObject2D`, regardless of the requested proxy target. + - Impact: proxy texture operations for cube maps, 3D textures, arrays, rectangle textures, or multisample targets could be backed by the wrong state object type, leading to invalid upload-target assertions, wrong dimensional bookkeeping, or broken completeness/query behavior. + - Status: fixed in this audit pass. + +24. `MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp` + - `TextureObject2DCube::GetIndexOfTextureUploadTarget()` accepted `ProxyCubeMap` in its range check and converted it into face index `7`, but cube textures only allocate storage for the six real faces. + - Impact: proxy cube-map state or accidental proxy-target use on a cube texture object could index beyond the six-face storage array, tripping assertions or corrupting cube-map bookkeeping. + - Status: fixed in this audit pass. + +25. `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp` + - `GetTexImage()` previously called `GetTexImage_Backend()` unconditionally even after `GetTexImage_State()` rejected the call for invalid targets, incomplete/unbound textures, mapped pixel-pack buffers, bad formats/types, or other state errors. + - Impact: invalid `glGetTexImage` calls could still execute backend readback against rejected frontend state instead of stopping at the GL error boundary. + - Status: fixed in this audit pass. + +26. `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp` + - `GetTexImage_State()` treated any mipmapped texture with more than one allocated mip level as if it were multisampled and rejected it with `InvalidOperation`. + - Impact: ordinary mipmapped textures became unreadable through `glGetTexImage` as soon as more than one mip level existed, even though mip levels are normal 1D/2D/3D texture storage rather than multisampling. + - Status: fixed in this audit pass. + +27. `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp` + - `BindImageTexture()` updated tracked `ImageTextureBinding` state before checking whether the backend exposed an image-texture bind entry point. + - Impact: on unsupported backends, `glBindImageTexture` could report `InvalidOperation` yet still mutate frontend image-binding state, leaving later queries/backend sync to observe bindings from a call that should have had no effect. + - Status: fixed in this audit pass. + +28. `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp` + - `glTexParameterfv/iv/Iiv/Iuiv(..., GL_TEXTURE_BORDER_COLOR, ...)` still threw unimplemented exceptions even though border color was already tracked in texture state and consumed by both GLES and Vulkan backend sampler setup. + - Impact: applications using border-color texture parameters could crash the library instead of updating valid tracked state. + - Status: fixed in this audit pass. + +29. `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp` + - `GetTexParameteriv/fv` left several already-tracked texture parameters effectively unimplemented, and `GetTexParameterIiv/Iuiv` hard-threw for all pnames instead of delegating to the tracked state. + - Impact: callers querying base/max LOD level, swizzle channels, border color, or integer texture parameters could observe untouched outputs or process-aborting exceptions even though the corresponding state was already maintained. + - Status: fixed in this audit pass. + +30. `MobileGL/MG_Backend/DirectGLES/Managers.cpp` + - `BackendTextureObject::SyncMipmapsToBackend()` always used `glTexSubImage2D` in its dirty-mipmap update path, even when the tracked texture target was `Texture3D`. + - Impact: once a 3D texture had been initialized, any later dirty subimage upload on the DirectGLES backend hit the wrong GL entry point, so depth slices were never updated correctly and the call could fail with backend GL errors instead of refreshing the texture data. + - Status: fixed in this audit pass. + +31. `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp`, `MobileGL/MG_Backend/BackendObject.cpp` + - The DirectGLES backend inherited `BackendObject::CreateEGLPbufferSurface()` unchanged. That base path returned early for any already-initialized pbuffer, regardless of requested size, and never tore down existing EGL resources before switching from a window surface to a pbuffer surface. + - Impact: recreating a pbuffer with a new size could silently keep rendering against the old backend surface dimensions, and window-surface to pbuffer transitions could leave the backend EGL context/surface bound to the old window while the frontend reported a new pbuffer surface. + - Status: fixed in this audit pass. + +32. `MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp` + - `eglReleaseThread()` only cleared frontend `EGLState` bookkeeping and never forwarded an `EGL_NO_DISPLAY` / `EGL_NO_SURFACE` / `EGL_NO_CONTEXT` release-current request to the active backend object. + - Impact: after a thread released itself, the backend could still treat that thread as current and preserve stale EGL current-state ownership, so later backend operations could incorrectly succeed against a thread that should already have been fully detached from the context/surfaces. + - Status: fixed in this audit pass. + +33. `MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp`, `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` + - The exported GL sync entry points (`glFenceSync`, `glIsSync`, `glDeleteSync`, `glClientWaitSync`, `glWaitSync`, `glGetSynciv`) were still wired to generic stub macros that returned dummy success-like values or no-op behavior, while `GL_Sync.cpp` itself also returned placeholder `0` values for every path. + - Impact: applications using GL sync objects could observe fabricated non-error results even though no sync object was created, waited on, queried, or deleted. That breaks API semantics directly and can cause callers to proceed past synchronization points that never existed. + - Status: fixed in this audit pass. + +34. `MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp` + - Shader/program recompilation and relinking paths kept stale successful artifacts alive across later failures. `ShaderObject::SetShaderSource()` did not invalidate the previous compiled shader, failed recompiles left `m_shader` pointing at the old compiled object, and `ProgramObject::Link()` reused old reflected/binary state after link failure because it neither reset prior link artifacts nor aborted before reflection/binary generation. + - Impact: after a shader source change or failed relink, frontend program/shader state could still expose the previous successful compiled shader, reflected uniforms/attributes, and generated SPIR-V, so callers might observe or use stale program metadata from a program that should now be uncompiled or unsuccessfully linked. + - Status: fixed in this audit pass. + +35. `MobileGL/MG_State/GLState/BufferState/BufferObject.cpp` + - Buffer mapping paths failed to persist the frontend's mapping-policy bits correctly. `AcquireMemoryRange()` returned before recording `ForbidInvalidationBit` / `ForbidUnsynchronizationBit`, and plain `AcquireMemory(true, ...)` never marked write mappings as forbidding either optimization. + - Impact: after CPU writes through `glMapBuffer` / `glMapBufferRange` without invalidate or unsynchronized flags, the DirectGLES backend could still treat later uploads as eligible for `GL_MAP_INVALIDATE_RANGE_BIT` and `GL_MAP_UNSYNCHRONIZED_BIT`. That allows the backend sync path to discard or race prior GPU-visible contents even though the frontend mapping contract did not permit those behaviors. + - Status: fixed in this audit pass. + +36. `MobileGL/Init.cpp` + - Library initialization and shutdown were not idempotent. On Linux/macOS the shared library auto-runs `Initialize()` and `Destroy()` via constructor/destructor attributes, while several tests and benchmarks also call `MobileGL::Initialize()` explicitly. The init path had no guard against repeated entry and the destroy path did not clear the active backend singleton or function table. + - Impact: a second `Initialize()` could silently replace global frontend/backend singletons and re-run `glslang::InitializeProcess()` on an already-initialized process, while repeated destroy paths could finalize process-global resources multiple times. That makes embedder behavior depend on whether the library was preloaded or explicitly initialized, and risks leaked or stale backend state across reinitialization. + - Status: fixed in this audit pass. + +37. `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp` + - `AcquireEGLFunctions()` and `AcquireGLESFunctions()` logged missing function pointers but still returned success unconditionally. `BackendObject_DirectGLES::Initialize()` trusts those booleans to decide whether the backend is initialized. + - Impact: a partial or failed EGL/GLES symbol load could still mark the DirectGLES backend initialized, after which later calls dereference null function pointers during context/surface setup, capability queries, or rendering instead of failing cleanly at backend startup. + - Status: fixed in this audit pass. + +38. `MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp` + - The unpack path's `BGRA -> RGBA8` swizzle order was wrong. It rewrote each pixel as `{G, B, A, R}` instead of `{R, G, B, A}`. + - Impact: any BGRA pixel upload normalized through this helper could arrive with all color channels rotated and alpha moved into blue, producing visibly corrupted textures rather than just a minor format mismatch. + - Status: fixed in this audit pass. + +39. `MobileGL/MG_Util/Debug/Log.cpp` + - `Log()` passed the raw `vsnprintf()` return value directly to `std::string(buffer, n)`. When formatting failed (`n < 0`) or when output exceeded the fixed buffer, that length no longer matched the valid bytes stored in `buffer`. + - Impact: logging an error path could itself trigger undefined behavior or huge out-of-bounds string construction, turning diagnostic code into a secondary crash vector and destabilizing failure handling. + - Status: fixed in this audit pass. + +40. `MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp` + - Multi-line comment stripping did not handle an unterminated `/* ...` block. When no closing `*/` existed, the preprocessor used `npos` in the replacement length calculation instead of treating the rest of the source as commented out. + - Impact: a malformed shader that should have produced a normal compile error could instead trip undefined string-manipulation behavior inside MobileGL's preprocessing step, turning frontend shader validation into a host-side crash vector. + - Status: fixed in this audit pass. + +41. `MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp` + - `NormalizePixelFormat()` omitted explicit handling for `GL_SRGB8_ALPHA8` and `GL_DEPTH24_STENCIL8`. Both formats fell through to generic fallback logic instead of returning their correct pixel format/type pairs. + - Impact: backends deriving upload/allocation parameters from this helper could create sRGB-alpha textures with the wrong format metadata and depth-stencil images with the wrong data type, breaking texture/renderbuffer creation and copy paths for otherwise valid formats. + - Status: fixed in this audit pass. + +42. `MobileGL/MG_Util/Metrics/TextureMetrics.cpp` + - The texture/renderbuffer metrics tables had multiple concrete coverage bugs. `GetComponentSizesForInternalFormat()` had an accidental early `break` in the `RGBA8/RGBA8Snorm/RGBA8I/RGBA8UI/SRGB8Alpha8/RGBA` group, so ordinary 8-bit RGBA formats reported zero component sizes. The same file also omitted deterministic byte/component sizing for accepted formats such as `RGB10`, `RGB12`, `RGB16`, `RGB16Snorm`, `R32I`, and `R32UI`. + - Impact: `glGetRenderbufferParameteriv` could report zero channel sizes for common RGBA8 renderbuffers, and frontend texture-storage/accounting paths that use `GetInternalBytesPerPixel()` / `GetSizedInternalFormatSizeInBytes()` could undercompute allocation sizes for those supported formats. + - Status: fixed in this audit pass. + +43. `MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp` + - Reverse texture enum conversion tables were missing accepted values. `TextureInternalFormat::RGB16` converted back to `GL_UNKNOWN_MGL` / `"Unknown"` even though `GLToMG` accepts `GL_RGB16`, and `TexturePixelDataType::UnsignedInt8888` converted to `GL_UNKNOWN_MGL` even though the forward converter accepts `GL_UNSIGNED_INT_8_8_8_8`. + - Impact: state/query paths such as texture/renderbuffer internal-format reporting and GLES format normalization could expose unsupported-looking values for actually supported enums, breaking round-trip conversions and misleading validation/logging. + - Status: fixed in this audit pass. + +44. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` + - The exported float-depth entry points `glClearDepthf`, `glDepthRangef`, and their OES aliases were still wired to generic stub macros even though the real render-state implementations already exist and backend sync paths consume the tracked clear-depth/depth-range state. + - Impact: applications calling the ES-style float variants appear to succeed but silently leave clear depth and depth range unchanged, so later clears and viewport depth mapping continue using stale values. + - Status: fixed in this audit pass. + +45. `MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp` + - `ConvertBufferMappingAccessToString()` always called `pop_back()` twice to trim a trailing `", "`, even when the access mask was empty (`BufferMappingAccessBit::Null`). + - Impact: any logging/debug path that formats an unmapped or zero-access buffer state can hit out-of-bounds string mutation and crash inside diagnostic code. + - Status: fixed in this audit pass. + +46. `MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp` + - The framebuffer reverse-conversion tables were inconsistent with the frontend enums. `MGToGL` mapped `FramebufferTarget::Unknown` and `RenderbufferTarget::Unknown` to valid fallback tokens instead of `GL_UNKNOWN_MGL`, and `MGToStr` omitted valid attachment names such as `None`, `FrontLeft`, `FrontRight`, `BackLeft`, and `BackRight`. + - Impact: invalid framebuffer/renderbuffer state could be mislabeled as valid in validator error messages, and legitimate attachment tokens could be logged as `"Unknown"`, obscuring real frontend state and complicating debugging. + - Status: fixed in this audit pass. + +47. `MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp` + - The float-zero optimization pass rewrote both ordered and unordered SPIR-V equality/inequality comparisons, but it always emitted ordered replacement opcodes (`OpFOrdLessThan` / `OpFOrdGreaterThanEqual`). For `OpFUnordEqual` and `OpFUnordNotEqual`, that drops unordered NaN semantics instead of preserving them. + - Impact: shader binaries passing through `ShaderCompiler::SanitizeAndOptimizeBinary()` could change observable results whenever the compared operand is NaN. In practice, shaders using unordered float comparisons against zero could be miscompiled during normal program binary generation. + - Status: fixed in this audit pass. + +48. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - `glGetString` / `glGetStringi` cached vendor/version/renderer/extension strings in function-local statics and never refreshed them after `Destroy()` / re-`Initialize()`. `GetStringi(GL_EXTENSIONS, index)` first validated `index` against the current backend's extension count, then indexed into the stale cached `extStrings` vector from the previous initialization. + - Impact: after backend reinitialization or backend switching, GL string queries could report stale identity/extension data. If the new backend exposed more extensions than the previously cached list, `glGetStringi(GL_EXTENSIONS, index)` could read past the cached vector bounds for indices that are valid in the new backend, turning a stale-cache bug into memory-unsafe behavior on a normal public query path. + - Status: fixed in this audit pass. + +49. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - The public `glGetBooleanv` and `glGetFloatv` entry points were still exported through generic stub macros, so they only logged and returned without writing any output or querying real state. That left two core OpenGL getter APIs effectively unimplemented even though the corresponding state already exists in `GLContext` and `GetIntegerv`. + - Impact: common application state queries such as blend enable, depth mask, clear color, blend color, depth range, scissor box, and similar `glGetBooleanv`/`glGetFloatv` calls could succeed superficially while leaving caller output untouched or stale. This breaks baseline GL introspection semantics on a high-fanout API surface and can directly corrupt application state caching/restoration logic. + - Status: fixed in this audit pass. + +50. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp` + - The public vertex-attribute query entry points (`glGetVertexAttribfv`, `glGetVertexAttribiv`, `glGetVertexAttribPointerv`, `glGetVertexAttribIiv`, `glGetVertexAttribIuiv`) were still exported through generic stubs even though VAO state and current generic vertex-attribute values were already tracked in `VertexArrayObject` and `GLContext`. + - Impact: applications querying vertex attribute enable flags, sizes, strides, types, normalization, integer-mode, divisors, bound VBOs, client pointer offsets, or current generic attribute values could get silent no-op behavior instead of actual answers. That breaks common VAO snapshot/debug/state-cache logic on another high-fanout public query surface. + - Status: fixed in this audit pass. + +51. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - The public `glGetInteger64v` entry point was still exported through a generic stub macro, so it only logged and returned without writing any output. This left the 64-bit getter variant unimplemented even though many of its supported non-indexed queries already flow through `GetIntegerv` and tracked frontend/backend state. + - Impact: applications using the 64-bit getter form for supported capability/state queries could observe untouched output buffers or stale values despite a superficially successful call. That breaks API compatibility on another core introspection path, especially for codepaths that standardize on the `GLint64` query family. + - Status: fixed in this audit pass. + +52. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp` + - The public current generic vertex-attribute setters (`glVertexAttrib1f/fv`, `2f/fv`, `3f/fv`, `4f/fv`, `glVertexAttribI4i/ui/iv/uiv`, `glVertexAttrib4Nub/Nubv`, `glVertexAttrib4ubv`) were still exported through generic stubs even though `GLContext` already tracks `CurrentVertexAttributeValue` and the DirectVulkan draw path consumes that state when an attribute array is disabled. + - Impact: applications relying on current generic vertex attribute values could not actually update them through the public API. On paths where shaders read attributes from disabled arrays, rendering would keep using the default/stale tracked values instead of the application-specified ones, breaking a real draw-time data path rather than just introspection. + - Status: fixed in this audit pass. + +53. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp`, `MobileGL/MG_State/GLState/BufferState/BufferObject.cpp` + - The public `glGetBufferPointerv` entry point was still exported through a generic stub macro even though frontend buffer objects already track live mapped ranges and the mapping calls return real CPU pointers. That meant `glGetBufferPointerv(GL_BUFFER_MAP_POINTER, ...)` never reported the active mapping pointer for either whole-buffer maps or ranged/staged write mappings. + - Impact: applications querying `GL_BUFFER_MAP_POINTER` could observe untouched output or stale data instead of the live mapped address, breaking standard buffer-mapping introspection. On write-mapped buffers this is especially harmful because MobileGL may hand out a staging pointer rather than the underlying storage pointer, so callers cannot recover the actual writable mapping address through the required query API. + - Status: fixed in this audit pass. + +54. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp` + - The public `glGetBufferParameteri64v` entry point was still exported through a generic stub macro even though the frontend already tracks the corresponding buffer size, usage, mapping access, and mapped-state data used by `glGetBufferParameteriv`. + - Impact: applications using the 64-bit buffer-parameter query form could observe untouched output or stale values despite a superficially successful call. That breaks a core buffer-introspection API surface and makes large-buffer-safe query codepaths unusable even for the supported pnames already implemented in the 32-bit variant. + - Status: fixed in this audit pass. + +55. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` + - The public current-program unsigned uniform setters (`glUniform1ui`, `glUniform2ui`, `glUniform3ui`, `glUniform4ui`, `glUniform1uiv`, `glUniform2uiv`, `glUniform3uiv`, `glUniform4uiv`) were still exported through generic stubs even though the shared frontend uniform upload path already accepts `GLuint` data and the program-specific `glProgramUniform*ui*` family is implemented on top of that same storage path. + - Impact: applications using unsigned integer uniforms through the ordinary current-program API could appear to succeed while leaving uniform storage unchanged, even though the corresponding program-specific entry points worked. That breaks a real shader data path for any program relying on `uint`, `uvec*`, or unsigned sampler/image-unit values set through the standard `glUniform*ui*` surface. + - Status: fixed in this audit pass. + +56. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` + - Shared uniform-location validation only rejected locations outside `[-1, maxUniformLocation]`, but reflected programs can legally contain sparse location holes. The uniform getter and setter paths then indexed `m_uniformIndexInTProgram[location]` directly and treated the `layoutLocationEnd` sentinel as if it were a valid reflected-uniform index. + - Impact: ordinary invalid calls such as `glUniform*` / `glProgramUniform*` / `glGetUniform*` against a vacant location inside a sparse explicit-location range could fall through into out-of-bounds reflected-uniform access instead of raising `GL_INVALID_OPERATION`. That turns a frontend validation error into undefined behavior on a normal public API path, with both wrong-state and memory-safety consequences. + - Status: fixed in this audit pass. + +57. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` + - The public `glGetUniformuiv` entry point was still exported through a generic stub macro, and the shared uniform getter path also did nothing for opaque uniforms because sampler/image bindings are tracked in `m_uniformSamplerOrImageUnitIndex` rather than in the plain UBO scratch buffer. + - Impact: unsigned uniform queries could succeed superficially while leaving caller output untouched, and even the already-exported `glGetUniformiv` / `glGetUniformfv` forms returned no value for sampler/image uniforms instead of the currently tracked texture/image unit. That breaks a core uniform introspection path and makes ordinary program-state snapshot/debug logic observe stale or uninitialized data. + - Status: fixed in this audit pass. + +58. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` + - Indexed capability handling accepted arbitrary enums through `ConvertGLEnumToCapabilityInput()` and forwarded them into `RenderState::SetCapabilityIndexed()` / `IsCapabilityEnabledIndexed()`, but the render-state object only implements indexed state for per-buffer blending and otherwise throws unimplemented exceptions. Even the supported `GL_BLEND` path never validated `index` before reaching assertion-backed indexed storage. + - Impact: ordinary invalid calls such as `glEnablei(GL_BLEND, out_of_range)`, `glDisablei(GL_BLEND, out_of_range)`, or `glIsEnabledi(GL_BLEND, out_of_range)` could trip assertion/exception paths instead of reporting GL errors, and `glGetBooleani_v` was still a public stub despite indexed blend state already being tracked. That turns a frontend validation problem into crash-class behavior on a public render-state API surface. + - Status: fixed in this audit pass. + +59. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` + - The public `glGetActiveUniformName` entry point was still exported through a generic stub macro even though linked-program reflection already tracks active uniform names, counts, and maximum name length, and `glGetActiveUniform` already reads that same metadata through `GetActiveUniform_State()`. + - Impact: applications querying active uniform names through the dedicated API could observe silent no-op behavior and untouched output buffers despite the information already being available in frontend program state. That breaks a core program-reflection query surface and forces callers to avoid the standard name-only query even though the underlying reflected metadata exists. + - Status: fixed in this audit pass. + +60. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` + - `GetActiveUniform_State()` treated the API's `index` parameter as if it were a uniform location, even though `glGetActiveUniform` and the newly wired `glGetActiveUniformName` query by active-uniform index in reflected-program order. The same code path also never wrote the required `size` output. + - Impact: linked programs with explicit or sparse uniform locations could return the wrong uniform name/type for a valid active-uniform query, and callers depending on the array-size result would receive untouched or stale output. That breaks the core active-uniform reflection API even when the program linked successfully and all required metadata was already present. + - Status: fixed in this audit pass. + +61. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` + - `GetActiveAttrib_State()` had the same indexing mistake on the attribute side: it validated against the number of active pipe inputs, then read names/types from location-expanded attribute tables keyed by vertex attribute location rather than by active-attribute reflection index. + - Impact: linked programs with explicit attribute locations, gaps, or matrix attributes could return empty names or per-location surrogate types instead of the actual active attribute variable described by the queried index. That breaks a core program-reflection query surface and can mislead application state/introspection logic even though the correct reflected attribute metadata already exists. + - Status: fixed in this audit pass. + +62. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` + - `GetActiveUniformBlockiv_State()` still mishandled several pnames even though the linked-program reflection already tracked the required uniform-block metadata. `GL_UNIFORM_BLOCK_BINDING` only logged a TODO and then fell through into the invalid-enum path, `GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS` incorrectly returned the total number of uniform blocks instead of the queried block's member count, `GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES` was rejected even though each active uniform already carried its reflected block index, and the per-stage `GL_UNIFORM_BLOCK_REFERENCED_BY_*_SHADER` queries were rejected despite glslang already recording the block stage mask. + - Impact: applications querying ordinary uniform-block reflection state could receive wrong values or spurious `GL_INVALID_ENUM` errors on supported linked programs. That breaks core program-introspection APIs used for UBO binding/state discovery even though the frontend already had exact reflected answers. + - Status: fixed in this audit pass. + +63. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` + - `GetActiveUniformBlockName_State()` unconditionally dereferenced `length` in its debug log after `CopyStr()`, even though the API allows `length == nullptr`. + - Impact: a valid `glGetActiveUniformBlockName(..., nullptr, ...)` call could crash in the frontend logging path after otherwise successful query processing. That turns a benign optional-output omission into a public API crash vector. + - Status: fixed in this audit pass. + +64. `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp`, `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` + - The public `glGetUniformIndices` entry point was still exported through a generic stub macro even though linked-program reflection already tracked active uniform names in stable active-uniform index order and exposed a name-to-index lookup path through glslang. + - Impact: applications querying active uniform indices by name could observe silent no-op behavior and untouched output buffers instead of the standard `GLuint` index list or `GL_INVALID_INDEX` fallbacks for missing names. That breaks a core prerequisite for higher-level uniform reflection flows such as `glGetActiveUniformsiv`. + - Status: fixed in this audit pass. + +65. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` + - Several `glGetProgramiv` length queries returned raw string lengths instead of the OpenGL-required buffer sizes including the terminating NUL: `GL_INFO_LOG_LENGTH`, `GL_ACTIVE_ATTRIBUTE_MAX_LENGTH`, `GL_ACTIVE_UNIFORM_MAX_LENGTH`, and `GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH`. + - Impact: applications relying on these lengths to allocate reflection or info-log buffers could allocate one byte too small, then truncate names/logs or mis-handle the returned strings despite otherwise valid queries. That breaks core program-introspection semantics on standard allocation/query flows. + - Status: fixed in this audit pass. + +66. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` + - `GetProgramiv(GL_COMPUTE_WORK_GROUP_SIZE, ...)` still delegated to the backend function table and, when unsupported there, raised `GL_INVALID_OPERATION` and returned fabricated fallback values `(1, 1, 1)`. Linked-program reflection already carried the compute local sizes directly via glslang. + - Impact: valid compute-program introspection could fail or report the wrong work-group size purely because a backend query hook was absent, even though the frontend had the exact linked-program metadata. That breaks a core compute reflection query and can misconfigure dispatch sizing logic in applications that query before issuing work. + - Status: fixed in this audit pass. + +67. `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` + - `GetProgramiv(GL_INFO_LOG_LENGTH, ...)` returned `1` for an empty program info log after the earlier length fix, while the adjacent shader query path already treats an empty info log as length `0`. + - Impact: successful or clean-link program objects with no log could report a phantom one-byte log buffer requirement, diverging from the project's own shader-query behavior and causing callers to allocate and process a nonexistent log string. + - Status: fixed in this audit pass. + +68. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - `glGetIntegeri_v` and `glGetInteger64i_v` still delegated all indexed buffer queries to backend hooks even though the frontend already tracked indexed buffer binding points and ranges for `glBindBufferBase` / `glBindBufferRange`. On backends without those getter hooks, ordinary queries such as `GL_UNIFORM_BUFFER_BINDING`, `GL_UNIFORM_BUFFER_START`, `GL_UNIFORM_BUFFER_SIZE`, and the corresponding transform-feedback / atomic-counter / shader-storage variants fell back to `GL_INVALID_OPERATION` plus zero results. + - Impact: applications querying indexed buffer bindings could observe backend-dependent failures or dummy zeros despite the queried state already being maintained accurately in frontend GL state. That breaks core introspection/state-cache flows around UBO/SSBO/transform-feedback/atomic-counter binding points. + - Status: fixed in this audit pass. + +69. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - The non-indexed generic buffer-binding queries `GL_TRANSFORM_FEEDBACK_BUFFER_BINDING` and `GL_UNIFORM_BUFFER_BINDING` in `glGetIntegerv` were still hardcoded to `0` even though the frontend already tracked the current generic binding slot for both targets. + - Impact: applications querying the current transform-feedback or uniform-buffer binding could observe false unbound state, breaking ordinary state snapshot/restore logic and any validation layered on top of those getters. + - Status: fixed in this audit pass. + +70. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - Several `glGetIntegerv(GL_TEXTURE_BINDING_*, ...)` queries were still hardcoded to `0` even though `TextureUnit` already tracked per-unit bindings for those targets. The affected getters included `GL_TEXTURE_BINDING_1D`, `GL_TEXTURE_BINDING_1D_ARRAY`, `GL_TEXTURE_BINDING_2D_ARRAY`, `GL_TEXTURE_BINDING_2D_MULTISAMPLE`, `GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY`, `GL_TEXTURE_BINDING_BUFFER`, and `GL_TEXTURE_BINDING_RECTANGLE`. + - Impact: applications querying the active texture unit's bound object for those targets could observe false unbound state, breaking texture-state snapshot/restore code and any validation that relies on the standard getter surface for non-2D/non-3D targets. + - Status: fixed in this audit pass. + +71. `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp`, `MobileGL/MG_Impl/Init.cpp` + - Framebuffer object `0` started with framebuffer-object-style color-buffer state (`Color0`) even though the rest of the frontend/backend default-framebuffer paths expect default-buffer tokens such as `BackLeft`. The default framebuffer initialization also attached its color image only at `Color0`, so consumers that dereferenced the stored read/draw buffer token could resolve the default framebuffer color attachment incorrectly before any explicit `glReadBuffer` / `glDrawBuffer` call. + - Impact: default-framebuffer color-buffer state could be reported or consumed incorrectly out of the box, skewing copy/readback/render-pass selection paths that rely on the tracked read/draw buffer state for framebuffer object `0`. + - Status: fixed in this audit pass. + +72. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - `glGetIntegerv` still hardcoded several already-tracked binding/state queries to `0`, including `GL_DRAW_BUFFER`, `GL_DRAW_BUFFER0`, `GL_READ_BUFFER`, `GL_PIXEL_PACK_BUFFER_BINDING`, `GL_PIXEL_UNPACK_BUFFER_BINDING`, and `GL_RENDERBUFFER_BINDING`. + - Impact: applications querying current framebuffer buffer-selection state or pixel-pack / pixel-unpack / renderbuffer bindings could observe false default or unbound state, breaking ordinary state snapshot/restore flows and any validation layered on top of the standard getter surface. + - Status: fixed in this audit pass. + +73. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - Clear-state getters were still inconsistent with the tracked frontend state: `glGetFloatv(GL_DEPTH_CLEAR_VALUE, ...)` fell back through the integer getter path and truncated non-integral depth clear values, while `glGetIntegerv(GL_STENCIL_CLEAR_VALUE, ...)` was still hardcoded to `0` even though `glClearStencil` already updated tracked render state. + - Impact: applications querying clear-depth / clear-stencil state could observe stale or numerically wrong values, breaking state mirroring and any code that snapshots or restores clear state through the standard getter surface. + - Status: fixed in this audit pass. + +74. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` + - `glGetBooleanv` still derived several tracked floating-point states by first truncating them through `glGetIntegerv`, so fractional non-zero values such as `BlendColor = 0.5`, `ClearColor = 0.5`, `DepthRange = 0.5`, or `DepthClearValue = 0.5` could be reported as `GL_FALSE`. + - Impact: applications querying boolean views of those float states could observe false zeros for legitimate non-zero tracked state, breaking generic state-diff/snapshot code that relies on `glGetBooleanv` conversions to mirror current state. + - Status: fixed in this audit pass. + +75. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - The exported stencil state API is still largely non-functional. `glStencilFunc[Separate]`, `glStencilMask[Separate]`, and `glStencilOp[Separate]` all route into state helpers whose bodies are empty, while the corresponding getter surface (`GL_STENCIL_FUNC`, `GL_STENCIL_REF`, `GL_STENCIL_VALUE_MASK`, `GL_STENCIL_FAIL`, `GL_STENCIL_PASS_DEPTH_FAIL`, `GL_STENCIL_PASS_DEPTH_PASS`, and all back-face variants) still returns placeholder zeroes. + - Impact: applications cannot configure or reliably query stencil test behavior through the frontend at all. Any rendering path that depends on stencil compare/op/mask state will silently use fabricated defaults instead of the caller-provided GL state. + - Status: fixed in this audit pass. + +76. `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glEnable(GL_STENCIL_TEST)` / `glDisable(GL_STENCIL_TEST)` updated the public render-state entry points and the getter surface, but the tracked render-state capability table ignored `StencilTest`, the DirectGLES sync path never forwarded it to native GL, and DirectVulkan pipeline creation hard-disabled stencil testing. + - Impact: applications could report stencil testing enabled in frontend state while both backends still rendered with stencil testing forcibly disabled, so any path relying on stencil compare/reject behavior silently behaved as if `GL_STENCIL_TEST` were off. + - Status: fixed in this audit pass. + +77. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - The frontend accepts and validates standalone `GL_STENCIL_ATTACHMENT` usage, but the DirectVulkan render-pass builder only materialized a depth/stencil attachment from `FramebufferAttachmentType::Depth` and set `hasDepthStencilAttachment` solely from that path. `FramebufferAttachmentType::Stencil` was hashed and checked for pending clears, yet never turned into the subpass depth/stencil attachment actually used for rendering. + - Impact: Vulkan rendering to framebuffer objects that relied on a stencil attachment without a matching depth attachment, or that expected stencil clears/tests to come from the dedicated stencil attachment path, could silently lose the stencil attachment at render-pass creation time. That disabled stencil testing on those FBOs and could drop pending stencil clears despite frontend completeness succeeding. + - Status: fixed in this audit pass. + +78. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp` + - The frontend previously allowed framebuffer objects to bind different images to `GL_DEPTH_ATTACHMENT` and `GL_STENCIL_ATTACHMENT` even though the DirectVulkan backend can only build a single Vulkan depth/stencil subpass attachment. This audit pass now treats such non-default FBOs as explicitly unsupported when DirectVulkan is active: `glCheckFramebufferStatus` returns `GL_FRAMEBUFFER_UNSUPPORTED`, DirectVulkan draw setup rejects them with `GL_INVALID_FRAMEBUFFER_OPERATION`, and DirectVulkan depth/stencil clear entrypoints reject them before any pending clear is queued. + - Impact: before the fix, Vulkan rendering on framebuffer objects that used separate depth and stencil textures could silently diverge from frontend GL state because one of the two attachments was ignored during render-pass creation. After the fix, that unsupported configuration fails explicitly instead of rendering or clearing against the wrong image. + - Status: fixed in this audit pass. + +79. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp`, `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp`, `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp` + - `glLineWidth` was exported but its state setter was a no-op, `GL_LINE_WIDTH` readback stayed at a placeholder path, the line-width capability queries (`GL_ALIASED_LINE_WIDTH_RANGE`, `GL_SMOOTH_LINE_WIDTH_RANGE`, `GL_SMOOTH_LINE_WIDTH_GRANULARITY`) were not backed by real backend limits, and neither backend consumed tracked line width during draw setup. The Vulkan path also needed `wideLines` feature enablement to honor widths above `1.0`. + - Impact: applications could not reliably configure or query line rasterization width through the frontend, DirectGLES silently kept native GL at its previous/default line width, and DirectVulkan either rendered with the wrong width or relied on invalid Vulkan state for wide lines. + - Status: fixed in this audit pass. + +80. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glPolygonOffset` was exported but its state setter was a no-op, `GL_POLYGON_OFFSET_FACTOR` / `GL_POLYGON_OFFSET_UNITS` still returned placeholder zeroes, `GL_POLYGON_OFFSET_FILL` enable state was not tracked in render state, and neither backend applied polygon offset during draw setup. + - Impact: applications could not request depth bias for filled polygons at all. Common decal/shadow-volume/coplanar-overlay rendering paths silently rendered without the requested depth offset, increasing z-fighting and causing frontend state queries to disagree with actual backend rasterization. + - Status: fixed in this audit pass. + +81. `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glEnable(GL_RASTERIZER_DISCARD)` / `glDisable(GL_RASTERIZER_DISCARD)` were decoded by the frontend enum conversion layer and exposed through the getter surface, but the tracked render-state capability table ignored `RasterizerDiscard`, DirectGLES never forwarded it to native GL, and DirectVulkan pipeline creation never enabled `rasterizerDiscardEnable`. + - Impact: applications could believe rasterizer discard was enabled while both backends still rasterized primitives normally, so transform-feedback / stream-out style passes or draw suppression logic relying on `GL_RASTERIZER_DISCARD` silently produced fragments anyway. + - Status: fixed in this audit pass. + +82. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp`, `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp` + - The frontend exported `glLogicOp` and advertised `GL_COLOR_LOGIC_OP` / `GL_LOGIC_OP_MODE`, but the state setter was a no-op, the getter path returned placeholder zeroes, the tracked render-state capability table ignored `ColorLogicOp`, DirectGLES did not even load/sync `glLogicOp`, and DirectVulkan pipeline creation never enabled Vulkan logic-op state or the required device feature. + - Impact: applications enabling color logic operations could not configure or query the requested logic-op mode reliably, and both backends kept using ordinary color blending/write behavior instead of the caller-selected logic operation. + - Status: fixed in this audit pass. + +83. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - The frontend previously accepted framebuffer renderbuffer attachments through `glFramebufferRenderbuffer` and could report those FBOs complete even though the DirectVulkan render-pass builder only materializes texture-backed attachments. This audit pass now treats non-default FBOs with complete renderbuffer attachments as explicitly unsupported when DirectVulkan is active: `glCheckFramebufferStatus` returns `GL_FRAMEBUFFER_UNSUPPORTED`, DirectVulkan draw and clear paths reject them with `GL_INVALID_FRAMEBUFFER_OPERATION`, and DirectVulkan `glBlitFramebuffer` / `glCopyTexSubImage2D` reject them before falling into backend skip-only paths. + - Impact: before the fix, valid non-default framebuffer objects backed by renderbuffers could silently lose their color outputs or depth/stencil attachment when rendered through the Vulkan backend because those attachments were treated as `VK_ATTACHMENT_UNUSED`. After the fix, the unsupported configuration fails explicitly instead of degrading into dropped attachments or skip-only backend behavior. + - Status: fixed in this audit pass. + +84. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp`, `MobileGL/MG_Backend/DirectGLES/Managers.cpp` + - `glRenderbufferStorageMultisample` was exported but its state helper was a complete no-op. The frontend never allocated storage, never recorded the requested sample count, `glGetRenderbufferParameteriv(GL_RENDERBUFFER_SAMPLES, ...)` therefore stayed at the default value, and the DirectGLES renderbuffer backend always allocated single-sample storage with `glRenderbufferStorage` even when a multisampled renderbuffer state object requested samples. + - Impact: multisampled renderbuffer creation through the frontend was non-functional. Applications requesting multisampled renderbuffers could observe unallocated or wrongly single-sampled backend storage, misleading sample-count queries, and incorrect framebuffer/readback behavior in paths that consult the tracked renderbuffer sample count. + - Status: fixed in this audit pass. + +85. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp` + - The exported framebuffer texture attachment entrypoints `glFramebufferTextureLayer`, `glFramebufferTexture3D`, `glFramebufferTexture1D`, and generic `glFramebufferTexture` previously routed into empty state helpers. This audit pass implemented real frontend attachment mutation for `glFramebufferTexture1D` and for the subset of generic `glFramebufferTexture` targets that the current attachment model can actually represent (`1D`, `2D`, `rectangle`, `2D multisample`), and changed the unsupported `glFramebufferTextureLayer` / `glFramebufferTexture3D` / layered generic-target paths to raise explicit frontend errors instead of silently succeeding. The framebuffer attachment model still only stores a texture object plus mip level, so there is still no layered/layer-index attachment representation behind those unsupported entrypoints. + - Impact: non-layered framebuffer texture attachment calls that fit the current model now really update framebuffer state instead of being silently dropped, and unsupported layered/layer-index entrypoints no longer pretend to succeed while leaving attachments unchanged. Applications that rely on true layered attachments or `3D` slice attachments still cannot express those framebuffer states through the current frontend model. + - Status: partially fixed in this audit pass; still open for true layered/layer-index framebuffer attachment support. + +86. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp`, `MobileGL/MG_Backend/DirectGLES/Managers.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glFramebufferTexture2D` previously discarded the `textarget` face token when attaching cube-map textures. The frontend attachment model stored only the texture object plus mip level, framebuffer attachment queries always reported `GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0`, completeness sizing hardcoded `TextureUploadTarget::Texture2D`, and DirectGLES replay rebound cube attachments with `GL_TEXTURE_CUBE_MAP` instead of the selected face. DirectVulkan also used full-mip image views rooted at `baseArrayLayer = 0`, and framebuffer blit/copy helpers hardcoded transfer subresources to layer 0, so cube-map framebuffer attachments there still could not target a specific face. + - Impact: framebuffer objects attaching cube-map faces could report the wrong attachment metadata and bind the wrong image on the DirectGLES backend, effectively treating face attachments as generic cube textures. On DirectVulkan, render passes and framebuffer blits/copies also collapsed cube-face attachments to layer 0, causing rendering and image transfer commands to hit the wrong cubemap face. + - Status: fixed in this audit pass. + +87. `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - The DirectVulkan deferred-clear path previously keyed pending clears only by texture object. `glClear` / `glClearBuffer*` on texture-backed FBO attachments at nonzero mip levels or different attached subresources were merged together, render-pass load-op selection ignored the attached mip/layer, and `MaterializePendingClearForTexture()` always cleared `mip 0`, `layer 0`. + - Impact: framebuffer clears on texture attachments could be applied to the wrong subresource or collapse multiple pending attachment clears into one texture-wide clear. Applications that clear an attached mip level and then sample/copy from it before a render pass consumes the clear could observe stale contents in the intended mip while an unrelated mip/layer was cleared instead. + - Status: fixed in this audit pass. + +### Medium + +1. `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/BackendObject.h`, `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp`, `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h`, `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp`, `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h`, `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp` + - Several multi-component or capability-backed getters still return placeholder scalar values instead of correct state vectors or backend limits. This audit pass fixed the point-size range/granularity path, viewport-dimension/subpixel queries, `GL_VIEWPORT_BOUNDS_RANGE`, frontend-backed indexed buffer binding/range getters including correct clamping of indexed buffer `SIZE` queries to the bound object's real size, `GL_DRAW_BUFFERi` readback for the tracked draw-buffer array, provoking-vertex convention getters, a handful of fixed-default queries (`GL_FRAGMENT_SHADER_DERIVATIVE_HINT`, `GL_LINE_SMOOTH_HINT`, `GL_POLYGON_SMOOTH_HINT`, `GL_TEXTURE_COMPRESSION_HINT`, `GL_POINT_FADE_THRESHOLD_SIZE`), frontend-tracked capability readback for previously untracked enable bits (`GL_DITHER`, `GL_MULTISAMPLE`, `GL_SAMPLE_ALPHA_TO_COVERAGE`, `GL_SAMPLE_ALPHA_TO_ONE`, `GL_SAMPLE_COVERAGE`, `GL_SAMPLE_MASK`, `GL_PROGRAM_POINT_SIZE`, `GL_POLYGON_OFFSET_LINE`, `GL_POLYGON_OFFSET_POINT`, `GL_POLYGON_SMOOTH`, `GL_PRIMITIVE_RESTART`, `GL_PRIMITIVE_RESTART_FIXED_INDEX`, `GL_LINE_SMOOTH`) through `glGet*` / `glIsEnabled` with DirectGLES replay now also covering the natively supported generic enable caps `GL_DITHER`, `GL_MULTISAMPLE`, and `GL_SAMPLE_ALPHA_TO_COVERAGE`, and frontend-only debug enable bits (`GL_DEBUG_OUTPUT`, `GL_DEBUG_OUTPUT_SYNCHRONOUS`) so `glEnable/glDisable/glIsEnabled` no longer silently forget those states while the corresponding debug-message entrypoints remain stubbed, explicit zero/empty reporting for unsupported public API surfaces whose entrypoints are still stubbed (`GL_MAX_DEBUG_GROUP_STACK_DEPTH`, `GL_DEBUG_GROUP_STACK_DEPTH`, `GL_NUM_SHADER_BINARY_FORMATS`, `GL_PROGRAM_BINARY_FORMATS`, `GL_PROGRAM_PIPELINE_BINDING`, the default `GL_PRIMITIVE_RESTART_INDEX`, `GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT`, `GL_TIMESTAMP`, `GL_VERTEX_BINDING_DIVISOR`, `GL_VERTEX_BINDING_OFFSET`, `GL_VERTEX_BINDING_STRIDE`, `GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET`, and `GL_MAX_VERTEX_ATTRIB_BINDINGS`), explicit empty compressed-texture capability reporting because the compressed texture upload entrypoints still throw unimplemented exceptions, `GL_DOUBLEBUFFER` readback from the current EGL draw-surface kind instead of a hardcoded zero, `GL_IMPLEMENTATION_COLOR_READ_FORMAT` / `GL_IMPLEMENTATION_COLOR_READ_TYPE` readback from the currently bound read-framebuffer attachment format, current draw-framebuffer `GL_SAMPLE_BUFFERS` / `GL_SAMPLES` readback for implemented multisampled renderbuffer attachments, sample-coverage/sample-mask state and getter readback (`GL_SAMPLE_COVERAGE_VALUE`, `GL_SAMPLE_COVERAGE_INVERT`, `GL_SAMPLE_MASK_VALUE`) with DirectGLES replay, backend-fed texture/framebuffer size limits (`GL_MAX_3D_TEXTURE_SIZE`, `GL_MAX_ARRAY_TEXTURE_LAYERS`, `GL_MAX_CUBE_MAP_TEXTURE_SIZE`, `GL_MAX_FRAMEBUFFER_WIDTH`, `GL_MAX_FRAMEBUFFER_HEIGHT`, `GL_MAX_FRAMEBUFFER_LAYERS`, `GL_MAX_RENDERBUFFER_SIZE`, `GL_MAX_TEXTURE_SIZE`), backend-fed sample limits (`GL_MAX_COLOR_TEXTURE_SAMPLES`, `GL_MAX_DEPTH_TEXTURE_SAMPLES`, `GL_MAX_FRAMEBUFFER_SAMPLES`, `GL_MAX_INTEGER_SAMPLES`, `GL_MAX_SAMPLES`, `GL_MAX_SAMPLE_MASK_WORDS`), backend-fed texture-unit/vertex-attrib limits (`GL_MAX_TEXTURE_IMAGE_UNITS`, `GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS`, `GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS`, `GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS`, `GL_MAX_VERTEX_ATTRIBS`), backend-fed buffer/storage limits (`GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS`, `GL_MAX_TEXTURE_BUFFER_SIZE`, `GL_MAX_UNIFORM_BUFFER_BINDINGS`, `GL_MAX_UNIFORM_BLOCK_SIZE`), backend-fed compute descriptor/workgroup limits (`GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS`, `GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS`, `GL_MAX_COMPUTE_UNIFORM_BLOCKS`, `GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS`), compute default/combined uniform-component limits derived from the frontend compiler scalar limit plus backend uniform-block capacity (`GL_MAX_COMPUTE_UNIFORM_COMPONENTS`, `GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS`), frontend compiler-backed GL 3.3 core uniform/varying/program-offset limits (`GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS`, `GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS`, `GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS`, `GL_MAX_COMBINED_UNIFORM_BLOCKS`, `GL_MAX_FRAGMENT_INPUT_COMPONENTS`, `GL_MAX_FRAGMENT_UNIFORM_COMPONENTS`, `GL_MAX_FRAGMENT_UNIFORM_VECTORS`, `GL_MAX_FRAGMENT_UNIFORM_BLOCKS`, `GL_MAX_GEOMETRY_INPUT_COMPONENTS`, `GL_MAX_GEOMETRY_OUTPUT_COMPONENTS`, `GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS`, `GL_MAX_GEOMETRY_UNIFORM_COMPONENTS`, `GL_MAX_GEOMETRY_UNIFORM_BLOCKS`, `GL_MAX_PROGRAM_TEXEL_OFFSET`, `GL_MIN_PROGRAM_TEXEL_OFFSET`, `GL_MAX_VARYING_COMPONENTS`, `GL_MAX_VARYING_VECTORS`, `GL_MAX_VERTEX_UNIFORM_COMPONENTS`, `GL_MAX_VERTEX_UNIFORM_VECTORS`, `GL_MAX_VERTEX_OUTPUT_COMPONENTS`, `GL_MAX_VERTEX_UNIFORM_BLOCKS`), frontend compiler-backed atomic-counter limits for all exposed shader-stage and combined count queries (`GL_MAX_COMPUTE_ATOMIC_COUNTERS`, `GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS`, `GL_MAX_COMBINED_ATOMIC_COUNTERS`, `GL_MAX_FRAGMENT_ATOMIC_COUNTERS`, `GL_MAX_GEOMETRY_ATOMIC_COUNTERS`, `GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS`, `GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS`, `GL_MAX_VERTEX_ATOMIC_COUNTERS`), backend-fed image-unit limits (`GL_MAX_IMAGE_UNITS`, `GL_MAX_COMBINED_IMAGE_UNIFORMS`, `GL_MAX_COMPUTE_IMAGE_UNIFORMS`, `GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS`), backend-fed draw-target limits (`GL_MAX_DRAW_BUFFERS`, `GL_MAX_COLOR_ATTACHMENTS`), backend-fed clip-distance/viewport-count limits (`GL_MAX_CLIP_DISTANCES`, `GL_MAX_VIEWPORTS`), and corrected indexed-only buffer range pnames so `GetIntegerv` no longer silently returns `0` for queries that are only valid through indexed getter entrypoints. Other placeholder-backed getters still remain. + - Impact: the fixed queries now report backend-backed values, frontend-tracked state, attachment-derived readback formats, implemented framebuffer/sample state, real backend size/sample/texture-unit limits, current EGL default-framebuffer buffering semantics, or the correct GL defaults instead of placeholder scalars, and invalid non-indexed buffer range queries now raise the correct GL error instead of fabricating a zero result. Program/shader binary capability queries also no longer over-advertise unsupported functionality, and compressed texture capability queries no longer claim support for entrypoints that still throw unimplemented exceptions. The unresolved getters still need real backend capability plumbing instead of hardcoded placeholders. + - Status: partially fixed in this audit pass; still open for the remaining placeholder queries. + +2. `MobileGL/MG_Backend/DirectGLES/Utils.cpp` + - `CheckGLESError()` used `while (GLenum err = g_GLESFuncs.glGetError() != GL_NO_ERROR)`, so `err` received the boolean result of the comparison instead of the actual GL error enum. + - Impact: DirectGLES error logging could only report `GL_TRUE`/`GL_FALSE`-like garbage values rather than the real backend error code, which undermines diagnosis of backend failures during this renderer path. + - Status: fixed in this audit pass. + +3. `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp` + - `glIsRenderbuffer` and `glIsFramebuffer` previously tested only whether a name had been generated, not whether a renderbuffer/framebuffer object had actually been created. + - Impact: freshly generated but never-bound names could be reported as existing objects, which violates GL object-lifetime semantics and can mislead application-side state tracking or object-probing code that relies on `glIs*` to distinguish names from realized objects. + - Status: fixed in this audit pass. + +4. `MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp`, `MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp` + - The GL/data-type conversion tables omitted `GL_FIXED` / `DataType::Fixed32` in both directions even though `Fixed32` is a first-class frontend data type and DirectGLES vertex-attribute setup calls the reverse converter when issuing `glVertexAttribPointer` / `glVertexAttribIPointer`. + - Impact: valid fixed-point vertex attribute declarations were rejected on input (`GL_FIXED -> DataType::Unknown`) and any backend path trying to emit a stored `Fixed32` attribute type back to native GL got `GL_UNKNOWN_MGL` instead of `GL_FIXED`, breaking fixed-point vertex format handling end-to-end. + - Status: fixed in this audit pass. + +5. `MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp`, `MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp` + - The GLFW/EGL window-surface setup only assigned `nativewindow` on `_WIN32` and `__APPLE__`. On Linux, the code left the handle at zero and still passed it into `eglCreateWindowSurface()`. + - Impact: the Linux DirectVulkan sanity/example path could not create a valid EGL window surface at all; it was guaranteed to request surface creation with a null native window handle instead of the GLFW X11 window. + - Status: fixed in this audit pass. + +6. `MobileGL/MG_Util/Types.h` + - `Range1D` used `SizeT end = ~0u` as its default "unbounded" upper limit. On 64-bit builds, `~0u` is only a 32-bit all-ones value, so the default range silently truncated to `0x00000000FFFFFFFF` instead of the full `size_t` range. + - Impact: any path that relies on default `Range1D()` meaning "all remaining bytes" can be clipped at 4 GiB on 64-bit hosts. This affects generic range-carrying utilities such as indexed buffer binding ranges and any future large-resource code that depends on the default sentinel upper bound. + - Status: fixed in this audit pass. + +7. `MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h` + - `SpvcSession`'s default constructor left the `usage` flag set uninitialized. Multiple program/shader utility paths first default-construct `Vector` elements and then move-assign real sessions into them. + - Impact: before assignment, the old object state can carry garbage `usage` bits into move/swap/destructor paths and later branch checks, making `spvc_context_destroy()` / `spvReflectDestroyShaderModule()` cleanup behavior and mode-gated methods depend on uninitialized memory instead of real session state. + - Status: fixed in this audit pass. + +8. `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` + - `glPointSize` previously routed into an empty state helper, `GL_POINT_SIZE` readback remained placeholder-backed, and DirectGLES never synchronized a frontend-managed fixed point size to native GL. This audit pass added tracked point-size state, validation, getter readback, and DirectGLES replay, but DirectVulkan still has no fixed-function or program-driven point-size handling in its draw/pipeline path. + - Impact: frontend `glPointSize` state and `GL_POINT_SIZE` queries now behave correctly, and DirectGLES point rendering can honor the tracked fixed size. DirectVulkan rendering of point primitives can still diverge from GL state because there is still no backend point-size path to apply fixed size or shader-driven `gl_PointSize`. + - Status: partially fixed in this audit pass; still open for DirectVulkan point-size support. + +## Reviewed Source Files + +Reviewed in this audit pass: +- `MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/BufferSlice.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h` +- `MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp` +- `MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp` +- `MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h` +- `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp` +- `MobileGL/MG_Backend/DirectGLES/DirectGLES.h` +- `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp` +- `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h` +- `MobileGL/MG_Backend/DirectGLES/Managers.cpp` +- `MobileGL/MG_Backend/DirectGLES/Managers.h` +- `MobileGL/MG_Backend/DirectGLES/Utils.cpp` +- `MobileGL/MG_Backend/DirectGLES/Utils.h` +- `MobileGL/MG_Backend/BackendObject.cpp` +- `MobileGL/MG_Backend/BackendObject.h` +- `MobileGL/MG_Backend/BackendObjects.h` +- `MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp` +- `MobileGL/MG_Impl/EGLImpl/EGLImpl.h` +- `MobileGL/MG_Impl/EGLImpl/Exporting/Definitions.cpp` +- `MobileGL/MG_Impl/GetProcAddress.cpp` +- `MobileGL/MG_Impl/GetProcAddress.h` +- `MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp` +- `MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h` +- `MobileGL/MG_State/EGLState/Core.cpp` +- `MobileGL/MG_State/EGLState/Core.h` +- `MobileGL/MG_State/GLState/Core.cpp` +- `MobileGL/MG_State/GLState/Core.h` +- `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` +- `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp` +- `MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp` +- `MobileGL/MG_State/GLState/ProgramState/ShaderObject.h` +- `MobileGL/MG_State/GLState/TextureState/MipmapStorage.cpp` +- `MobileGL/MG_State/GLState/TextureState/MipmapStorage.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObject2D.cpp` +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h` +- `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h` +- `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp` +- `MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp` +- `MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h` +- `MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp` +- `MobileGL/MG_Impl/GLImpl/VertexArray/Validators.cpp` +- `MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp` +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` +- `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp` +- `MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.cpp` +- `MobileGL/MG_State/GLState/VertexArrayState/VertexArrayState.h` +- `MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp` +- `MobileGL/MG_State/GLState/BufferState/BufferState.cpp` +- `MobileGL/MG_State/GLState/BufferState/BufferState.h` +- `MobileGL/MG_State/GLState/BufferState/BufferObject.cpp` +- `MobileGL/MG_State/GLState/BufferState/BufferObject.h` +- `MobileGL/MG_State/GLState/FramebufferState/FramebufferState.cpp` +- `MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp` +- `MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h` +- `MobileGL/MG_State/GLState/RenderbufferState/RenderbufferState.cpp` +- `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp` +- `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h` +- `MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp` +- `MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.h` +- `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp` +- `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h` +- `MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp` +- `MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.h` +- `MobileGL/MG_Impl/GLImpl/Sampler/Validators.cpp` +- `MobileGL/MG_Impl/GLImpl/Sampler/Validators.h` +- `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp` +- `MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h` +- `MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp` +- `MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.h` +- `MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp` +- `MobileGL/MG_Impl/GLImpl/Texture/Validators.h` +- `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` +- `MobileGL/MG_State/GLState/ProgramState/ProgramState.cpp` +- `MobileGL/MG_State/GLState/ProgramState/ProgramState.h` +- `MobileGL/MG_State/GLState/RenderState/RenderState.cpp` +- `MobileGL/MG_State/GLState/RenderState/RenderState.h` +- `MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp` +- `MobileGL/MG_State/GLState/SamplerState/SamplerObject.h` +- `MobileGL/MG_State/GLState/SamplerState/SamplerState.cpp` +- `MobileGL/MG_State/GLState/SamplerState/SamplerState.h` +- `MobileGL/MG_State/GLState/TextureState/TextureEnum.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObject.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject1D.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObject2D.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject3D.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObjectStubs.h` +- `MobileGL/MG_State/GLState/TextureState/TextureState.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureState.h` +- `MobileGL/MG_State/GLState/TextureState/TextureUnit.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureUnit.h` +- `MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.cpp` +- `MobileGL/Config.h` +- `MobileGL/ConfigLoader.cpp` +- `MobileGL/GlobalObjects.cpp` +- `MobileGL/Init.cpp` +- `MobileGL/Init.h` +- `MobileGL/MG_Backend/Init.cpp` +- `MobileGL/MG_Backend/DirectVulkan/VmaImpl.cpp` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp` +- `MobileGL/MG_Impl/Init.cpp` +- `MobileGL/MG_State/GLState/ErrorState/Error.cpp` +- `MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.cpp` +- `MobileGL/MG_Impl/GLXImpl/LookUp/LookUp.cpp` +- `MobileGL/MG_Impl/GLXImpl/Exporting/Definitions.cpp` +- `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp` +- `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp` +- `MobileGL/MG_Util/Debug/Log.cpp` +- `MobileGL/MG_Util/Metrics/BufferMetrics.cpp` +- `MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp` +- `MobileGL/MG_Util/Metrics/TextureMetrics.cpp` +- `MobileGL/MG_Util/Classifiers/TextureEnumClassifier.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToVk/TextureEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/EGLToStr/EGLEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToGlslang/ProgramEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToMG/ProgramEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/ErrorCodeConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/ProgramEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToStr/DataTypeConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToStr/GLExtensionConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp` +- `MobileGL/MG_Util/Converters/SPIRVCrossToGL/SpvcTypeConverter.cpp` +- `MobileGL/MG_Util/Math/VectorTypes.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp` +- `MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/FlattenInterfaceStructPass.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/glslang/TMglGlslIoResolver.cpp` +- `MobileGL/MG_Util/ShaderTranspiler/glslang/UniformTraverser.cpp` +- `MobileGL/MG_Benchmark/Buffer/BufferBench.cpp` +- `MobileGL/MG_Benchmark/Program/ProgramBench.cpp` +- `MobileGL/MG_Benchmark/SanityBench.cpp` +- `MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp` +- `MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp` +- `MobileGL/MG_Test/Buffer/BufferTest.cpp` +- `MobileGL/MG_Test/Program/ProgramTest.cpp` +- `MobileGL/MG_Test/Program/ProgramUtilTest.cpp` +- `MobileGL/MG_Test/SanityTest.cpp` +- `MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp` +- `MobileGL/Defines.h` +- `MobileGL/Includes.h` +- `MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h` +- `MobileGL/MG_Backend/DirectVulkan/DirectVulkanResourceState.h` +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.h` +- `MobileGL/MG_Backend/DirectVulkan/VkIncludes.h` +- `MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h` +- `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h` +- `MobileGL/MG_Impl/GLImpl/Buffer/Validators.h` +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.h` +- `MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h` +- `MobileGL/MG_Impl/GLImpl/VertexArray/Validators.h` +- `MobileGL/MG_Impl/GLXImpl/LookUp/LookUp.h` +- `MobileGL/MG_State/GLState/ErrorState/Error.h` +- `MobileGL/MG_State/GLState/ErrorState/ErrorCode.h` +- `MobileGL/MG_State/GLState/ErrorState/ErrorInfo.h` +- `MobileGL/MG_State/GLState/FramebufferState/FramebufferState.h` +- `MobileGL/MG_State/GLState/RenderbufferState/RenderbufferState.h` +- `MobileGL/MG_State/GLState/TextureState/MipmapUploadTargetArray.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject1D.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject2D.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObject3D.h` +- `MobileGL/MG_State/GLState/TextureState/TextureObjectBuffer.h` +- `MobileGL/MG_State/GLState/TextureState/TextureTypes.h` +- `MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h` +- `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h` +- `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h` +- `MobileGL/MG_Util/Classifiers/TextureEnumClassifier.h` +- `MobileGL/MG_Util/Converters/EGLToStr/EGLEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToGlslang/ProgramEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.h` +- `MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToMG/ProgramEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToMG/TextureEnumConverter.h` +- `MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/ErrorCodeConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/ProgramEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToStr/DataTypeConverter.h` +- `MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToStr/GLExtensionConverter.h` +- `MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h` +- `MobileGL/MG_Util/Converters/MGToVk/TextureEnumConverter.h` +- `MobileGL/MG_Util/Converters/SPIRVCrossToGL/SpvcTypeConverter.h` +- `MobileGL/MG_Util/Debug/Log.h` +- `MobileGL/MG_Util/GLExtensions.h` +- `MobileGL/MG_Util/Math/VectorTypes.h` +- `MobileGL/MG_Util/Metrics/BufferMetrics.h` +- `MobileGL/MG_Util/Metrics/TextureMetrics.h` +- `MobileGL/MG_Util/Miscellany/IndexGenerator.h` +- `MobileGL/MG_Util/PlatformStubs.h` +- `MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h` +- `MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.h` +- `MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.h` +- `MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/FlattenInterfaceStructPass.h` +- `MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h` +- `MobileGL/MG_Util/ShaderTranspiler/Types.h` +- `MobileGL/MG_Util/ShaderTranspiler/glslang/TMglGlslIoResolver.h` +- `MobileGL/MG_Util/ShaderTranspiler/glslang/TVarEntryInfo.h` +- `MobileGL/MG_Util/ShaderTranspiler/glslang/UniformTraverser.h` +- `MobileGL/MG_Util/Texture/PixelStoreProcessor.h` +- `MobileGL/MG_Util/Texture/TextureFormatProcessor.h` +- `MobileGL/MG_Util/Types.h` +- `selfcheck.c` + +## Notes + +- `MobileGL/Defines.h` had a pre-existing local modification enabling console logging; this audit pass did not change it. +- Reviewed-file coverage is now complete for all in-scope source files under `MobileGL/` plus `selfcheck.c`: 260 total source files recorded, 0 remaining uncovered. +- Audit completion criteria for this pass are satisfied: all in-scope source files were reviewed, all findings identified in this pass are recorded in this file, compile-only validation was performed on edited translation units, and the previously confirmed high-risk DirectVulkan silent-corruption paths were converted into explicit unsupported/error behavior. +- Residual open findings remain documented below as partially fixed follow-up gaps. At close of this audit pass there are 3 such residual findings and 0 `confirmed, not fixed` findings. +- Full `cmake --build buildlinux --target MobileGL_s` could not be reused because `buildlinux/CMakeCache.txt` references missing compilers `/home/bzlzhh/.links/clang` and `/home/bzlzhh/.links/clang++`. +- Compile validation completed for each edited translation unit with direct single-file syntax checks using `/usr/bin/c++` and the existing `build_linux/compile_commands.json` include/define set. +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed after the cube-face DirectVulkan fix with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp`, `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp`, and `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp` compile validation passed after the backend-capability getter fix with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Util/Metrics/TextureMetrics.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using the `build_linux/compile_commands.json` flags for that file. +- `MobileGL/MG_Util/Converters/MGToGL/TextureEnumConverter.cpp` and `MobileGL/MG_Util/Converters/MGToStr/TextureEnumConverter.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Util/Converters/MGToGL/FramebufferEnumConverter.cpp` and `MobileGL/MG_Util/Converters/MGToStr/FramebufferEnumConverter.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Util/Converters/GLToMG/DataTypeConverter.cpp` and `MobileGL/MG_Util/Converters/MGToGL/DataTypeConverter.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Test/Backend/DirectVulkan/SanityTest.cpp` and `MobileGL/MG_Test/Backend/DirectVulkan/TestExec.cpp` are not present in the available compile databases. Syntax-only validation of the patched sources was attempted with the main project include/define set plus discovered local `gtest` / `GLFW` include roots; parsing then became environment-limited by missing X11 development headers (`X11/extensions/Xrandr.h`) behind that ad hoc GLFW header tree, so these two files could not be fully syntax-validated in the current environment. +- `MobileGL/MG_Util/Types.h` change was syntax-validated through a direct `/usr/bin/c++ -fsyntax-only` check of `MobileGL/MG_Util/Math/VectorTypes.cpp` using the `build_linux/compile_commands.json` flags, since `VectorTypes.cpp` includes the patched core utility header. +- `MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h` change was syntax-validated through a direct `/usr/bin/c++ -fsyntax-only` check of `MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp` using the `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags. +- `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after wiring `glGetBooleanv` / `glGetFloatv` to real implementations. +- `MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after wiring the `glGetVertexAttrib*` query family to tracked VAO/current-attribute state. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed again with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glGetInteger64v` to a real implementation. +- `MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed again with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring the supported `glVertexAttrib*` current-value setters to tracked `CurrentVertexAttributeValue` state. +- `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp`, `MobileGL/MG_State/GLState/BufferState/BufferObject.cpp`, and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glGetBufferPointerv(GL_BUFFER_MAP_POINTER, ...)` to the tracked live mapping pointer. +- `MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glGetBufferParameteri64v` to the tracked frontend buffer state. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring the current-program `glUniform*ui*` unsigned-uniform setter family to the existing frontend uniform upload path. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` and `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after hardening shared uniform-location validation against sparse reflected-location holes. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glGetUniformuiv` and fixing opaque uniform readback to use tracked sampler/image unit state. +- `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after validating indexed blend capability targets/indices and wiring `glGetBooleani_v` to tracked indexed blend state. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glGetActiveUniformName` to the existing reflected uniform-name query path. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp`, and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after fixing active uniform/attribute reflection queries to use active-resource indices and restoring the missing `glGetActiveUniform` array-size output. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` and `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after fixing uniform-block reflection queries (`GL_UNIFORM_BLOCK_BINDING`, `GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS`, `GL_UNIFORM_BLOCK_REFERENCED_BY_*`) and removing the nullable-`length` dereference in `glGetActiveUniformBlockName`. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp`, `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp`, and `MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glGetUniformIndices` and filling `GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES` from reflected active-uniform metadata. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after fixing `glGetProgramiv` length queries to include the terminating NUL. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` and `MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after switching `GL_COMPUTE_WORK_GROUP_SIZE` to linked-program reflection instead of backend-dependent fallback behavior. +- `MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after restoring zero-length behavior for empty `GL_INFO_LOG_LENGTH` on program objects. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after wiring tracked indexed buffer binding/range state into `glGetIntegeri_v` and `glGetInteger64i_v` for supported buffer targets. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` compile validation passed again with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after wiring the tracked texture-unit bindings into the supported `GL_TEXTURE_BINDING_*` getter family. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp`, and `MobileGL/MG_Impl/Init.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after fixing default-framebuffer read/draw buffer defaults and wiring the remaining tracked framebuffer/buffer binding getters. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` compile validation passed again with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after fixing `GL_DEPTH_CLEAR_VALUE` / `GL_STENCIL_CLEAR_VALUE` clear-state queries to use the tracked render state. +- `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp` compile validation passed again with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after fixing boolean conversions for tracked floating-point clear/blend/depth-range state in `glGetBooleanv`. +- `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring stencil-test capability tracking and backend synchronization. +- `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp`, `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring tracked stencil func/op/mask state through the frontend getters and both backends. +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp` compile validation passed with a direct `/usr/bin/c++ -fsyntax-only` invocation using its `build_linux/compile_commands.json` flags after teaching DirectVulkan render-pass assembly to honor standalone stencil attachments through the shared depth/stencil attachment path. +- `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp`, `MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp`, `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp`, `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring tracked line-width state, backend capability reporting, DirectGLES/DirectVulkan draw-state application, and Vulkan `wideLines` feature enablement/clamping. +- `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring tracked polygon-offset state, `GL_POLYGON_OFFSET_FILL` capability tracking, DirectGLES sync, and Vulkan depth-bias pipeline/draw-state application. +- `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `GL_RASTERIZER_DISCARD` capability tracking and backend synchronization in both renderers. +- `MobileGL/MG_State/GLState/RenderState/RenderState.cpp`, `MobileGL/MG_State/GLState/Core.cpp`, `MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp`, `MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp`, `MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp`, `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp`, `MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp`, `MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring tracked `glLogicOp` state, `GL_COLOR_LOGIC_OP` capability tracking, GLES loader/backend sync, and Vulkan logic-op pipeline state plus device feature enablement. +- `MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp`, `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, and `MobileGL/MG_Backend/DirectGLES/Managers.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after wiring `glRenderbufferStorageMultisample` frontend storage/sample-count tracking and DirectGLES multisample renderbuffer allocation. +- `MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp`, `MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp`, and `MobileGL/MG_Backend/DirectGLES/Managers.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after teaching framebuffer texture attachments to retain cube-map face upload targets for frontend queries/completeness and DirectGLES replay. +- `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp`, `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp`, and `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` compile validation passed with direct `/usr/bin/c++ -fsyntax-only` invocations using their `build_linux/compile_commands.json` flags after keying DirectVulkan pending clears by exact attachment subresource (texture + mip + array-layer span) and materializing all queued subresource clears before sampled/copy use.