diff --git a/MobileGL/Defines.h b/MobileGL/Defines.h index 96e1eb0e..a123d187 100644 --- a/MobileGL/Defines.h +++ b/MobileGL/Defines.h @@ -35,7 +35,7 @@ // ====================== MobileGL configurations ======================= // #ifndef MOBILEGL_LOG_ACTIVE_LEVEL -#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_DEBUG +#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_INFO #endif #define MOBILEGL_LOG_ENABLE_CONSOLE 0 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 265cafcf..eb9789ef 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -112,6 +112,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; SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; Uint32 SubgroupSize = 0; Uint32 SubgroupSupportedStages = 0; diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 5d7a764f..98a643bc 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -34,18 +34,11 @@ namespace MobileGL::MG_Backend::DirectGLES { } void BackendObject_DirectGLES::Initialize() { - if (!MG_Util::BackendLoader::AcquireEGLFunctions(m_EGLFunctions)) { - MGLOG_E("Failed to acquire EGL functions for DirectGLES backend"); - return; - } - if (!MG_Util::BackendLoader::AcquireGLESFunctions(m_GLESFunctions, m_EGLFunctions.eglGetProcAddress)) { - MGLOG_E("Failed to acquire GLES functions for DirectGLES backend"); - return; - } - m_initialized = true; - + MG_Util::BackendLoader::AcquireEGLFunctions(m_EGLFunctions); + MG_Util::BackendLoader::AcquireGLESFunctions(m_GLESFunctions, m_EGLFunctions.eglGetProcAddress); DirectGLES::SetEGLFuncsTable(m_EGLFunctions); DirectGLES::SetGLESFuncsTable(m_GLESFunctions); + m_initialized = true; } Bool BackendObject_DirectGLES::InitCapabilities() { @@ -97,6 +90,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); } @@ -221,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 91a6cea1..541dd4fb 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -65,6 +65,34 @@ namespace MobileGL::MG_Backend::DirectGLES { Uint32 baseInstance = 0; }; + struct DrawArraysIndirectCommand { + Uint32 count = 0; + Uint32 instanceCount = 0; + Uint32 first = 0; + Uint32 baseInstance = 0; + }; + + const Uint8* ResolveIndirectCommandBytes(const void* indirect, SizeT requiredBytes, const char* label) { + auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); + if (drawBuffer) { + drawBuffer->MarkPersistentMappedRangeDirty(); + const auto drawData = drawBuffer->GetDataReadOnly(); + const SizeT commandOffset = reinterpret_cast(indirect); + if (!drawData || commandOffset + requiredBytes > drawData->size()) { + MGLOG_E("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label); + return nullptr; + } + return drawData->data() + commandOffset; + } + + if (!indirect) { + MGLOG_E("%s skipped: indirect pointer is null", label); + return nullptr; + } + + return reinterpret_cast(indirect); + } + namespace DebugImpl { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG void ErrorLopper::Loop(const std::function& func) { @@ -425,7 +453,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 @@ -538,6 +575,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 @@ -556,6 +621,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 @@ -576,6 +645,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; @@ -1006,24 +1114,17 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } - auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); - if (!drawBuffer) { - MGLOG_E("MultiDrawElementsIndirect skipped: no GL_DRAW_INDIRECT_BUFFER is bound"); - return; - } - drawBuffer->MarkPersistentMappedRangeDirty(); - const auto drawData = drawBuffer->GetDataReadOnly(); - const SizeT commandOffset = reinterpret_cast(indirect); - const SizeT commandBytes = commandOffset + static_cast(stride) * static_cast(drawcount - 1) + - sizeof(DrawElementsIndirectCommand); - if (!drawData || commandBytes > drawData->size()) { - MGLOG_E("MultiDrawElementsIndirect skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range"); + const auto* commandBytes = ResolveIndirectCommandBytes( + indirect, + static_cast(stride) * static_cast(drawcount - 1) + sizeof(DrawElementsIndirectCommand), + "MultiDrawElementsIndirect"); + if (!commandBytes) { return; } for (GLsizei i = 0; i < drawcount; ++i) { DrawElementsIndirectCommand cmd{}; - std::memcpy(&cmd, drawData->data() + commandOffset + static_cast(i) * stride, sizeof(cmd)); + std::memcpy(&cmd, commandBytes + static_cast(i) * stride, sizeof(cmd)); if (cmd.count == 0 || cmd.instanceCount == 0) { continue; } @@ -1112,14 +1213,41 @@ namespace MobileGL::MG_Backend::DirectGLES { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER DebugImpl::OpenGLScopeMarker marker(__func__); #endif - DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer; + if (drawcount <= 0) { + return; + } + if (stride == 0) { + stride = sizeof(DrawArraysIndirectCommand); + } + if (stride < static_cast(sizeof(DrawArraysIndirectCommand))) { + MGLOG_E("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu", + stride, sizeof(DrawArraysIndirectCommand)); + return; + } + + DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing; PrepareForDraw(syncBit); - for (GLsizei i = 0; i < drawcount; ++i) { - const GLvoid* cmd = reinterpret_cast(reinterpret_cast(indirect) + - i * (stride ? stride : sizeof(GLsizei) * 4)); - g_GLESFuncs.glDrawArraysIndirect(mode, cmd); + const auto* commandBytes = ResolveIndirectCommandBytes( + indirect, + static_cast(stride) * static_cast(drawcount - 1) + sizeof(DrawArraysIndirectCommand), + "MultiDrawArraysIndirect"); + if (!commandBytes) { + return; } + + for (GLsizei i = 0; i < drawcount; ++i) { + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes + static_cast(i) * stride, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + continue; + } + SetCurrentBaseInstance(cmd.baseInstance); + g_GLESFuncs.glDrawArraysInstanced( + mode, static_cast(cmd.first), static_cast(cmd.count), + static_cast(cmd.instanceCount)); + } + SetCurrentBaseInstance(0); } void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, @@ -1137,8 +1265,11 @@ namespace MobileGL::MG_Backend::DirectGLES { void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) { - // Not supported in OpenGL ES - MGLOG_W("DrawElementsInstancedBaseVertexBaseInstance is not supported in OpenGL ES."); + DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::Instancing; + PrepareForDraw(syncBit); + SetCurrentBaseInstance(baseinstance); + g_GLESFuncs.glDrawElementsInstancedBaseVertex(mode, count, type, indices, instancecount, basevertex); + SetCurrentBaseInstance(0); } void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, @@ -1150,8 +1281,11 @@ namespace MobileGL::MG_Backend::DirectGLES { void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLuint baseinstance) { - // Not supported in OpenGL ES - MGLOG_W("DrawElementsInstancedBaseInstance is not supported in OpenGL ES."); + DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::Instancing; + PrepareForDraw(syncBit); + SetCurrentBaseInstance(baseinstance); + g_GLESFuncs.glDrawElementsInstanced(mode, count, type, indices, instancecount); + SetCurrentBaseInstance(0); } void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) { @@ -1161,15 +1295,42 @@ namespace MobileGL::MG_Backend::DirectGLES { } void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) { - DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer; + DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing; PrepareForDraw(syncBit); - g_GLESFuncs.glDrawElementsIndirect(mode, type, indirect); + + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + MGLOG_E("DrawElementsIndirect skipped: unsupported index type 0x%x", type); + return; + } + + const auto* commandBytes = + ResolveIndirectCommandBytes(indirect, sizeof(DrawElementsIndirectCommand), "DrawElementsIndirect"); + if (!commandBytes) { + return; + } + + DrawElementsIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + return; + } + + SetCurrentBaseInstance(cmd.baseInstance); + const auto indexByteOffset = static_cast(cmd.firstIndex) * indexSize; + g_GLESFuncs.glDrawElementsInstancedBaseVertex( + mode, static_cast(cmd.count), type, reinterpret_cast(indexByteOffset), + static_cast(cmd.instanceCount), cmd.baseVertex); + SetCurrentBaseInstance(0); } void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) { - // Not supported in OpenGL ES - MGLOG_W("DrawArraysInstancedBaseInstance is not supported in OpenGL ES."); + DrawSyncBit syncBit = DrawSyncBit::Instancing; + PrepareForDraw(syncBit); + SetCurrentBaseInstance(baseinstance); + g_GLESFuncs.glDrawArraysInstanced(mode, first, count, instancecount); + SetCurrentBaseInstance(0); } void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { @@ -1179,9 +1340,26 @@ namespace MobileGL::MG_Backend::DirectGLES { } void DrawArraysIndirect(GLenum mode, const void* indirect) { - DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer; + DrawSyncBit syncBit = DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing; PrepareForDraw(syncBit); - g_GLESFuncs.glDrawArraysIndirect(mode, indirect); + + const auto* commandBytes = + ResolveIndirectCommandBytes(indirect, sizeof(DrawArraysIndirectCommand), "DrawArraysIndirect"); + if (!commandBytes) { + return; + } + + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + return; + } + + SetCurrentBaseInstance(cmd.baseInstance); + g_GLESFuncs.glDrawArraysInstanced( + mode, static_cast(cmd.first), static_cast(cmd.count), + static_cast(cmd.instanceCount)); + SetCurrentBaseInstance(0); } void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index e36d239d..a8c9dc50 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -541,7 +541,9 @@ namespace MobileGL::MG_Backend::DirectGLES { static_cast(baseSize.y()), static_cast(baseSize.z()), 0, - 0}; + 0, + stateTextureObject->GetSamples(), + stateTextureObject->HasFixedSampleLocations()}; switch (stateTextureObject->GetStorageType()) { case TextureStorageType::Mipmap: { auto* textureMipmapObject = @@ -565,6 +567,33 @@ namespace MobileGL::MG_Backend::DirectGLES { &glFormat, &glType); const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + if (TextureImpl::IsMultisampleTextureTarget(targetInternal)) { + DebugImpl::ErrorLopper::Clear(); + g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + switch (targetInternal) { + case TextureTarget::Texture2DMultisample: + g_GLESFuncs.glTexStorage2DMultisample( + target, static_cast(stateTextureObject->GetSamples()), glInternalFormat, + static_cast(baseSize.x()), static_cast(baseSize.y()), + stateTextureObject->HasFixedSampleLocations() ? GL_TRUE : GL_FALSE); + break; + case TextureTarget::Texture2DMultisampleArray: + g_GLESFuncs.glTexStorage3DMultisample( + target, static_cast(stateTextureObject->GetSamples()), glInternalFormat, + static_cast(baseSize.x()), static_cast(baseSize.y()), + static_cast(baseSize.z()), + stateTextureObject->HasFixedSampleLocations() ? GL_TRUE : GL_FALSE); + break; + default: + MOBILEGL_ASSERT(false, "Unexpected multisample target: %d", static_cast(targetInternal)); + break; + } + for (const auto& uploadTarget : uploadTargets) { + for (SizeT level = 0; level < mipmapCount; ++level) { + textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); + } + } + } else { for (auto& uploadTarget : uploadTargets) { for (SizeT level = 0; level < mipmapCount; ++level) { auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); @@ -620,11 +649,24 @@ namespace MobileGL::MG_Backend::DirectGLES { textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); } } + } m_isInitialized = true; } { // Update all dirty mipmap levels + if (TextureImpl::IsMultisampleTextureTarget(targetInternal)) { + const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); + for (const auto& uploadTarget : uploadTargets) { + for (SizeT level = 0; level < mipmapCount; ++level) { + if (textureMipmapObject->IsStorageDirty(uploadTarget, level)) { + textureMipmapObject->MarkStorageDirty(uploadTarget, level, false); + } + } + } + break; + } + const auto mipmapCount = textureMipmapObject->GetMipmapLevelCount(); GLenum glInternalFormat, glType, glFormat; TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, @@ -657,10 +699,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); } } @@ -763,6 +822,12 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } + const auto& samplerParams = samplerObject->GetAllSamplerParameters(); + if (TextureImpl::IsMultisampleTextureTarget(targetInternal)) { + m_cacheSamplerParameters = samplerParams; + return; + } + Bind(target); DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); @@ -770,7 +835,6 @@ namespace MobileGL::MG_Backend::DirectGLES { // Update built-in sampler parameters MGLOG_D("Updating sampler parameters for texture with ID: %u", m_backendTextureId); - const auto& samplerParams = samplerObject->GetAllSamplerParameters(); #define SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \ if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \ @@ -857,6 +921,13 @@ namespace MobileGL::MG_Backend::DirectGLES { return; } + if (TextureImpl::IsMultisampleTextureTarget(targetInternal)) { + m_cacheLodRange = stateTextureObject->GetLevelRange(); + m_cacheSwizzleParams = stateTextureObject->GetAllSwizzleParams(); + m_cacheBorderColor = stateTextureObject->GetBorderColor(); + return; + } + Bind(target); DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); @@ -993,7 +1064,11 @@ namespace MobileGL::MG_Backend::DirectGLES { return false; } backendTextureObject->SyncMipmapsToBackend(textureObject); - 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(), @@ -1523,7 +1598,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; @@ -1535,15 +1611,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 455bf88a..54cf7b45 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -162,12 +162,16 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace TextureImpl { inline Bool IsSupportedTextureTarget(TextureTarget target) { if (target == TextureTarget::Texture1D || target == TextureTarget::TextureRectangle || - target == TextureTarget::Texture2DMultisampleArray || target == TextureTarget::Texture1DArray || - target == TextureTarget::Texture2DMultisample || target == TextureTarget::Texture2DArray) + target == TextureTarget::Texture1DArray || target == TextureTarget::Texture2DArray) return false; return true; } + inline Bool IsMultisampleTextureTarget(TextureTarget target) { + return target == TextureTarget::Texture2DMultisample || + target == TextureTarget::Texture2DMultisampleArray; + } + inline Bool SupportsWrapR(TextureTarget target) { return target == TextureTarget::Texture3D || target == TextureTarget::TextureCubeMap; } @@ -179,11 +183,14 @@ namespace MobileGL::MG_Backend::DirectGLES { SizeT depth = 0; SizeT mipmapLevels = 0; Uint bufferExternalIndex = 0; + Int samples = 0; + Bool fixedSampleLocations = true; bool operator==(const StateTextureBasicInfo& other) const { return internalFormat == other.internalFormat && width == other.width && height == other.height && depth == other.depth && mipmapLevels == other.mipmapLevels && - bufferExternalIndex == other.bufferExternalIndex; + bufferExternalIndex == other.bufferExternalIndex && samples == other.samples && + fixedSampleLocations == other.fixedSampleLocations; } bool operator!=(const StateTextureBasicInfo& other) const { return !(*this == other); } @@ -322,6 +329,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 ff59df3a..557394b1 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -283,6 +283,54 @@ namespace MobileGL::MG_Backend::DirectVulkan { static constexpr SizeT kMaxAdvertisedShaderStorageBlockSize = 512ull * 1024ull * 1024ull; 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; m_dynamicParameters.MaxShaderStorageBlockSize = std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize); if (m_vulkanCaps.SupportsShaderSubgroup) { diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index d6ad277d..0651103a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -39,6 +39,21 @@ namespace MobileGL::MG_Backend::DirectVulkan { GLint computeWorkGroupSize[3] = {1, 1, 1}; }; + struct DrawElementsIndirectCommand { + Uint32 count = 0; + Uint32 instanceCount = 0; + Uint32 firstIndex = 0; + Int32 baseVertex = 0; + Uint32 baseInstance = 0; + }; + + struct DrawArraysIndirectCommand { + Uint32 count = 0; + Uint32 instanceCount = 0; + Uint32 first = 0; + Uint32 baseInstance = 0; + }; + UnorderedMap g_programResourceCaches; String NormalizeDescriptorName(const SpvReflectDescriptorBinding& binding) { @@ -193,6 +208,122 @@ namespace MobileGL::MG_Backend::DirectVulkan { name[copyLength] = '\0'; } } + + const Uint8* ResolveIndirectCommandBytes(const void* indirect, SizeT requiredBytes, const char* label) { + auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); + if (drawBuffer) { + drawBuffer->MarkPersistentMappedRangeDirty(); + const auto drawData = drawBuffer->GetDataReadOnly(); + const SizeT commandOffset = reinterpret_cast(indirect); + if (!drawData || commandOffset + requiredBytes > drawData->size()) { + MGLOG_E("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label); + return nullptr; + } + return drawData->data() + commandOffset; + } + + if (!indirect) { + MGLOG_E("%s skipped: indirect pointer is null", label); + return nullptr; + } + + return reinterpret_cast(indirect); + } + + Vector GetUniformBlockActiveVariables(const MG_State::GLState::ProgramObject& program, + GLuint blockIndex) { + Vector activeVariables; + const Uint uniformCount = program.GetUniformCount(); + activeVariables.reserve(uniformCount); + for (Uint uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex) { + if (program.GetActiveUniformBlockIndex(uniformIndex) == static_cast(blockIndex)) { + activeVariables.push_back(uniformIndex); + } + } + return activeVariables; + } + + GLuint FindProgramInputIndex(const MG_State::GLState::ProgramObject& program, const String& name) { + const Int activeCount = program.GetActiveAttributesCount(); + for (Int index = 0; index < activeCount; ++index) { + if (program.GetActiveAttribName(index) == name) { + return static_cast(index); + } + } + return GL_INVALID_INDEX; + } + + GLuint FindProgramOutputIndex(const MG_State::GLState::ProgramObject& program, const String& name) { + const Int activeCount = program.GetActiveFragmentOutputCount(); + for (Int index = 0; index < activeCount; ++index) { + if (program.GetActiveFragmentOutputName(index) == name) { + return static_cast(index); + } + } + return GL_INVALID_INDEX; + } + + GLint GetProgramOutputLocation(const MG_State::GLState::ProgramObject& program, const String& name) { + const Int activeCount = program.GetActiveFragmentOutputCount(); + for (Int index = 0; index < activeCount; ++index) { + if (program.GetActiveFragmentOutputName(index) == name) { + return program.GetFragmentOutputLocation(index); + } + } + return -1; + } + + GLint GetProgramResourceActiveCount(const MG_State::GLState::ProgramObject& program, GLenum programInterface, + const ProgramResourceCache& cache) { + switch (programInterface) { + case GL_SHADER_STORAGE_BLOCK: + return static_cast(cache.storageBlocks.size()); + case GL_BUFFER_VARIABLE: + return static_cast(cache.bufferVariables.size()); + case GL_UNIFORM_BLOCK: + return program.GetActiveUniformBlocksCount(); + case GL_UNIFORM: + return static_cast(program.GetUniformCount()); + case GL_PROGRAM_INPUT: + return program.GetActiveAttributesCount(); + case GL_PROGRAM_OUTPUT: + return program.GetActiveFragmentOutputCount(); + default: + return 0; + } + } + + GLint GetProgramResourceMaxNameLength(const MG_State::GLState::ProgramObject& program, GLenum programInterface, + const ProgramResourceCache& cache) { + switch (programInterface) { + case GL_SHADER_STORAGE_BLOCK: { + SizeT maxLength = 0; + for (const auto& block : cache.storageBlocks) maxLength = std::max(maxLength, block.name.size() + 1); + return static_cast(maxLength); + } + case GL_BUFFER_VARIABLE: { + SizeT maxLength = 0; + for (const auto& var : cache.bufferVariables) maxLength = std::max(maxLength, var.name.size() + 1); + return static_cast(maxLength); + } + case GL_UNIFORM_BLOCK: + return program.GetActiveUniformBlocksMaxNameLength() + 1; + case GL_UNIFORM: + return program.GetUniformMaxLength() + 1; + case GL_PROGRAM_INPUT: + return program.GetActiveAttributesMaxLength() + 1; + case GL_PROGRAM_OUTPUT: { + SizeT maxLength = 0; + const Int activeCount = program.GetActiveFragmentOutputCount(); + for (Int index = 0; index < activeCount; ++index) { + maxLength = std::max(maxLength, program.GetActiveFragmentOutputName(index).size() + 1); + } + return static_cast(maxLength); + } + default: + return 0; + } + } } // namespace GLuint GetShaderStorageBlockIndex(const MG_State::GLState::ProgramObject& program, const String& name) { @@ -256,7 +387,44 @@ namespace MobileGL::MG_Backend::DirectVulkan { pVulkanRenderer->MultiDrawElementsIndirectCount(mode, type, indirect, 0, drawcount, stride); } void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) { - MGLOG_W("DirectVulkan::MultiDrawArraysIndirect is not implemented yet (drawcount=%d)", drawcount); + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArraysIndirect called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawArraysIndirect called with null GL context"); + + if (drawcount <= 0) { + return; + } + if (stride == 0) { + stride = sizeof(DrawArraysIndirectCommand); + } + if (stride < static_cast(sizeof(DrawArraysIndirectCommand))) { + MGLOG_E("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu", + stride, sizeof(DrawArraysIndirectCommand)); + return; + } + + const auto* commandBytes = ResolveIndirectCommandBytes( + indirect, + static_cast(stride) * static_cast(drawcount - 1) + sizeof(DrawArraysIndirectCommand), + "MultiDrawArraysIndirect"); + if (!commandBytes) { + return; + } + + for (GLsizei i = 0; i < drawcount; ++i) { + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes + static_cast(i) * stride, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + continue; + } + + DrawCmd payload{}; + payload.mode = mode; + payload.params.vertexCount = cmd.count; + payload.params.instanceCount = cmd.instanceCount; + payload.params.firstVertex = cmd.first; + payload.params.firstInstance = cmd.baseInstance; + pVulkanRenderer->DrawArrays(payload); + } } void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { @@ -266,23 +434,152 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { - MGLOG_W("DirectVulkan::MultiDrawArraysIndirectCount is not implemented yet (maxdrawcount=%d)", maxdrawcount); + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArraysIndirectCount called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawArraysIndirectCount called with null GL context"); + + if (maxdrawcount <= 0) { + return; + } + if (stride == 0) { + stride = sizeof(DrawArraysIndirectCommand); + } + if (stride < static_cast(sizeof(DrawArraysIndirectCommand))) { + MGLOG_E("MultiDrawArraysIndirectCount skipped: stride %d is smaller than command size %zu", + stride, sizeof(DrawArraysIndirectCommand)); + return; + } + + auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); + if (!parameterBuffer || drawcount < 0 || static_cast(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) { + MGLOG_E("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range"); + return; + } + + parameterBuffer->MarkPersistentMappedRangeDirty(); + const auto parameterData = parameterBuffer->GetDataReadOnly(); + if (!parameterData) { + MGLOG_E("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read parameter buffer"); + return; + } + + Uint32 actualDrawCount = 0; + std::memcpy(&actualDrawCount, parameterData->data() + drawcount, sizeof(actualDrawCount)); + actualDrawCount = std::min(actualDrawCount, static_cast(maxdrawcount)); + MultiDrawArraysIndirect(mode, indirect, static_cast(actualDrawCount), stride); } void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, - const void* indices, GLint basevertex) {} - void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) {} + const void* indices, GLint basevertex) { + (void)start; + (void)end; + DrawElementsBaseVertex(mode, count, type, indices, basevertex); + } + void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) { + (void)start; + (void)end; + DrawElements(mode, count, type, indices); + } void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, - GLsizei instancecount, GLint basevertex, GLuint baseinstance) {} + GLsizei instancecount, GLint basevertex, GLuint baseinstance) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsInstancedBaseVertexBaseInstance called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElementsInstancedBaseVertexBaseInstance called with null GL context"); + + DrawIndexedCmd payload{}; + payload.mode = mode; + payload.indexBufferView.indexType = type; + payload.indexBufferView.indexByteOffset = reinterpret_cast(indices); + payload.indexBufferView.indexByteSize = count * MG_Util::GetGLTypeSize(type); + payload.params.indexCount = count; + payload.params.instanceCount = instancecount; + payload.params.firstIndex = 0; + payload.params.vertexOffset = basevertex; + payload.params.firstInstance = static_cast(baseinstance); + pVulkanRenderer->DrawElements(payload); + } void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, - GLsizei instancecount, GLint basevertex) {} + GLsizei instancecount, GLint basevertex) { + DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, instancecount, basevertex, 0); + } void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, - GLsizei instancecount, GLuint baseinstance) {} - void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) {} - void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) {} + GLsizei instancecount, GLuint baseinstance) { + DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, instancecount, 0, baseinstance); + } + void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) { + DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, instancecount, 0, 0); + } + void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsIndirect called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElementsIndirect called with null GL context"); + + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + MGLOG_E("DrawElementsIndirect skipped: unsupported index type 0x%x", type); + return; + } + + const auto* commandBytes = + ResolveIndirectCommandBytes(indirect, sizeof(DrawElementsIndirectCommand), "DrawElementsIndirect"); + if (!commandBytes) { + return; + } + + DrawElementsIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + return; + } + + DrawIndexedCmd payload{}; + payload.mode = mode; + payload.indexBufferView.indexType = type; + payload.indexBufferView.indexByteOffset = static_cast(cmd.firstIndex) * indexSize; + payload.indexBufferView.indexByteSize = static_cast(cmd.count) * indexSize; + payload.params.indexCount = cmd.count; + payload.params.instanceCount = cmd.instanceCount; + payload.params.firstIndex = 0; + payload.params.vertexOffset = cmd.baseVertex; + payload.params.firstInstance = static_cast(cmd.baseInstance); + pVulkanRenderer->DrawElements(payload); + } void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, - GLuint baseinstance) {} - void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {} - void DrawArraysIndirect(GLenum mode, const void* indirect) {} + GLuint baseinstance) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArraysInstancedBaseInstance called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArraysInstancedBaseInstance called with null GL context"); + + DrawCmd payload{}; + payload.mode = mode; + payload.params.vertexCount = count; + payload.params.instanceCount = instancecount; + payload.params.firstVertex = first; + payload.params.firstInstance = baseinstance; + pVulkanRenderer->DrawArrays(payload); + } + void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { + DrawArraysInstancedBaseInstance(mode, first, count, instancecount, 0); + } + void DrawArraysIndirect(GLenum mode, const void* indirect) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArraysIndirect called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArraysIndirect called with null GL context"); + + const auto* commandBytes = + ResolveIndirectCommandBytes(indirect, sizeof(DrawArraysIndirectCommand), "DrawArraysIndirect"); + if (!commandBytes) { + return; + } + + DrawArraysIndirectCommand cmd{}; + std::memcpy(&cmd, commandBytes, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + return; + } + + DrawCmd payload{}; + payload.mode = mode; + payload.params.vertexCount = cmd.count; + payload.params.instanceCount = cmd.instanceCount; + payload.params.firstVertex = cmd.first; + payload.params.firstInstance = cmd.baseInstance; + pVulkanRenderer->DrawArrays(payload); + } void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::CopyTexImage2D called with null VulkanRenderer"); @@ -461,31 +758,35 @@ namespace MobileGL::MG_Backend::DirectVulkan { auto* programObject = TryGetDirectVulkanProgram(program); if (!programObject) return; auto& cache = GetProgramResourceCache(*programObject); - if (programInterface == GL_SHADER_STORAGE_BLOCK) { - if (pname == GL_ACTIVE_RESOURCES) { - *params = static_cast(cache.storageBlocks.size()); - } else if (pname == GL_MAX_NAME_LENGTH) { - SizeT maxLength = 0; - for (const auto& block : cache.storageBlocks) maxLength = std::max(maxLength, block.name.size() + 1); - *params = static_cast(maxLength); + switch (pname) { + case GL_ACTIVE_RESOURCES: + *params = GetProgramResourceActiveCount(*programObject, programInterface, cache); + return; + case GL_MAX_NAME_LENGTH: + *params = GetProgramResourceMaxNameLength(*programObject, programInterface, cache); + return; + case GL_MAX_NUM_ACTIVE_VARIABLES: + if (programInterface == GL_SHADER_STORAGE_BLOCK) { + SizeT maxCount = 0; + for (const auto& block : cache.storageBlocks) { + maxCount = std::max(maxCount, block.activeVariables.size()); + } + *params = static_cast(maxCount); + } else if (programInterface == GL_UNIFORM_BLOCK) { + GLint maxCount = 0; + const Int activeBlocks = programObject->GetActiveUniformBlocksCount(); + for (Int index = 0; index < activeBlocks; ++index) { + maxCount = std::max(maxCount, programObject->GetUniformBlockActiveUniformCount(index)); + } + *params = maxCount; } else { *params = 0; } return; - } - if (programInterface == GL_BUFFER_VARIABLE) { - if (pname == GL_ACTIVE_RESOURCES) { - *params = static_cast(cache.bufferVariables.size()); - } else if (pname == GL_MAX_NAME_LENGTH) { - SizeT maxLength = 0; - for (const auto& var : cache.bufferVariables) maxLength = std::max(maxLength, var.name.size() + 1); - *params = static_cast(maxLength); - } else { - *params = 0; - } + default: + *params = 0; return; } - *params = 0; } GLuint GetProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar* name) { @@ -493,17 +794,30 @@ namespace MobileGL::MG_Backend::DirectVulkan { auto* programObject = TryGetDirectVulkanProgram(program); if (!programObject) return GL_INVALID_INDEX; auto& cache = GetProgramResourceCache(*programObject); + const String resourceName = name; if (programInterface == GL_SHADER_STORAGE_BLOCK) { return GetShaderStorageBlockIndex(*programObject, name); } if (programInterface == GL_BUFFER_VARIABLE) { - const String resourceName = name; const auto it = std::find_if(cache.bufferVariables.begin(), cache.bufferVariables.end(), [&](const BufferVariableResource& var) { return var.name == resourceName; }); return it == cache.bufferVariables.end() ? GL_INVALID_INDEX : static_cast(std::distance(cache.bufferVariables.begin(), it)); } + if (programInterface == GL_UNIFORM_BLOCK) { + return programObject->GetUniformBlockIndex(name); + } + if (programInterface == GL_UNIFORM) { + const Int activeUniformIndex = programObject->GetActiveUniformIndex(resourceName); + return activeUniformIndex >= 0 ? static_cast(activeUniformIndex) : GL_INVALID_INDEX; + } + if (programInterface == GL_PROGRAM_INPUT) { + return FindProgramInputIndex(*programObject, resourceName); + } + if (programInterface == GL_PROGRAM_OUTPUT) { + return FindProgramOutputIndex(*programObject, resourceName); + } return GL_INVALID_INDEX; } @@ -520,6 +834,23 @@ namespace MobileGL::MG_Backend::DirectVulkan { CopyResourceName(cache.bufferVariables[index].name, bufSize, length, name); return; } + if (programInterface == GL_UNIFORM_BLOCK && programObject->IsActiveUniformBlock(index)) { + CopyResourceName(programObject->GetUniformBlockName(index), bufSize, length, name); + return; + } + if (programInterface == GL_UNIFORM && index < programObject->GetUniformCount()) { + CopyResourceName(programObject->GetActiveUniformName(index), bufSize, length, name); + return; + } + if (programInterface == GL_PROGRAM_INPUT && index < static_cast(programObject->GetActiveAttributesCount())) { + CopyResourceName(programObject->GetActiveAttribName(index), bufSize, length, name); + return; + } + if (programInterface == GL_PROGRAM_OUTPUT && + index < static_cast(programObject->GetActiveFragmentOutputCount())) { + CopyResourceName(programObject->GetActiveFragmentOutputName(index), bufSize, length, name); + return; + } if (length) *length = 0; if (name && bufSize > 0) name[0] = '\0'; } @@ -589,6 +920,155 @@ namespace MobileGL::MG_Backend::DirectVulkan { writeValue(0); break; } + } else if (programInterface == GL_UNIFORM_BLOCK && + programObject->IsActiveUniformBlock(index)) { + const auto activeVariables = GetUniformBlockActiveVariables(*programObject, index); + switch (prop) { + case GL_NAME_LENGTH: + writeValue(static_cast(programObject->GetUniformBlockName(index).size() + 1)); + break; + case GL_BUFFER_BINDING: + writeValue(static_cast(programObject->GetUniformBlockBinding(index))); + break; + case GL_BUFFER_DATA_SIZE: + writeValue(static_cast(programObject->GetUBOSizeAt(index))); + break; + case GL_NUM_ACTIVE_VARIABLES: + writeValue(static_cast(activeVariables.size())); + break; + case GL_ACTIVE_VARIABLES: + for (const GLuint variableIndex : activeVariables) { + writeValue(static_cast(variableIndex)); + } + break; + case GL_REFERENCED_BY_VERTEX_SHADER: + writeValue(programObject->IsUniformBlockReferencedByStage(index, EShLangVertex) ? GL_TRUE + : GL_FALSE); + break; + case GL_REFERENCED_BY_FRAGMENT_SHADER: + writeValue(programObject->IsUniformBlockReferencedByStage(index, EShLangFragment) ? GL_TRUE + : GL_FALSE); + break; + case GL_REFERENCED_BY_COMPUTE_SHADER: + writeValue(programObject->IsUniformBlockReferencedByStage(index, EShLangCompute) ? GL_TRUE + : GL_FALSE); + break; + case GL_REFERENCED_BY_GEOMETRY_SHADER: + case GL_REFERENCED_BY_TESS_CONTROL_SHADER: + case GL_REFERENCED_BY_TESS_EVALUATION_SHADER: + writeValue(GL_FALSE); + break; + default: + writeValue(0); + break; + } + } else if (programInterface == GL_UNIFORM && index < programObject->GetUniformCount()) { + const auto& uniformName = programObject->GetActiveUniformName(index); + const GLint location = programObject->GetUniformLocation(uniformName); + switch (prop) { + case GL_NAME_LENGTH: + writeValue(static_cast(uniformName.size() + 1)); + break; + case GL_TYPE: + writeValue(static_cast(programObject->GetActiveUniformType(index))); + break; + case GL_ARRAY_SIZE: + writeValue(programObject->GetActiveUniformArraySize(index)); + break; + case GL_BLOCK_INDEX: + writeValue(programObject->GetActiveUniformBlockIndex(index)); + break; + case GL_LOCATION: + writeValue(location); + break; + case GL_OFFSET: + writeValue(location >= 0 && programObject->IsValidUniformLocation(location) + ? static_cast(programObject->GetUniformOffset(location)) + : 0); + break; + case GL_ARRAY_STRIDE: + case GL_MATRIX_STRIDE: + case GL_IS_ROW_MAJOR: + case GL_TOP_LEVEL_ARRAY_SIZE: + case GL_TOP_LEVEL_ARRAY_STRIDE: + case GL_REFERENCED_BY_VERTEX_SHADER: + case GL_REFERENCED_BY_FRAGMENT_SHADER: + case GL_REFERENCED_BY_COMPUTE_SHADER: + case GL_REFERENCED_BY_GEOMETRY_SHADER: + case GL_REFERENCED_BY_TESS_CONTROL_SHADER: + case GL_REFERENCED_BY_TESS_EVALUATION_SHADER: + writeValue(0); + break; + default: + writeValue(0); + break; + } + } else if (programInterface == GL_PROGRAM_INPUT && + index < static_cast(programObject->GetActiveAttributesCount())) { + const auto& resourceName = programObject->GetActiveAttribName(index); + switch (prop) { + case GL_NAME_LENGTH: + writeValue(static_cast(resourceName.size() + 1)); + break; + case GL_TYPE: + writeValue(static_cast(programObject->GetActiveAttribType(index))); + break; + case GL_ARRAY_SIZE: + writeValue(programObject->GetActiveAttribArraySize(index)); + break; + case GL_LOCATION: + writeValue(programObject->GetAttributeLocation(resourceName)); + break; + case GL_REFERENCED_BY_VERTEX_SHADER: + writeValue(GL_TRUE); + break; + case GL_REFERENCED_BY_FRAGMENT_SHADER: + case GL_REFERENCED_BY_COMPUTE_SHADER: + case GL_REFERENCED_BY_GEOMETRY_SHADER: + case GL_REFERENCED_BY_TESS_CONTROL_SHADER: + case GL_REFERENCED_BY_TESS_EVALUATION_SHADER: + case GL_IS_PER_PATCH: + case GL_LOCATION_INDEX: + writeValue(0); + break; + default: + writeValue(0); + break; + } + } else if (programInterface == GL_PROGRAM_OUTPUT && + index < static_cast(programObject->GetActiveFragmentOutputCount())) { + const auto& resourceName = programObject->GetActiveFragmentOutputName(index); + switch (prop) { + case GL_NAME_LENGTH: + writeValue(static_cast(resourceName.size() + 1)); + break; + case GL_TYPE: + writeValue(static_cast(programObject->GetFragmentOutputType(index))); + break; + case GL_ARRAY_SIZE: + writeValue(programObject->GetActiveFragmentOutputArraySize(index)); + break; + case GL_LOCATION: + writeValue(programObject->GetFragmentOutputLocation(index)); + break; + case GL_LOCATION_INDEX: + writeValue(0); + break; + case GL_REFERENCED_BY_FRAGMENT_SHADER: + writeValue(GL_TRUE); + break; + case GL_REFERENCED_BY_VERTEX_SHADER: + case GL_REFERENCED_BY_COMPUTE_SHADER: + case GL_REFERENCED_BY_GEOMETRY_SHADER: + case GL_REFERENCED_BY_TESS_CONTROL_SHADER: + case GL_REFERENCED_BY_TESS_EVALUATION_SHADER: + case GL_IS_PER_PATCH: + writeValue(0); + break; + default: + writeValue(0); + break; + } } else { writeValue(0); } @@ -602,13 +1082,21 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (programInterface == GL_UNIFORM) { return programObject->GetUniformLocation(name); } + if (programInterface == GL_PROGRAM_INPUT) { + return programObject->GetAttributeLocation(name); + } + if (programInterface == GL_PROGRAM_OUTPUT) { + return GetProgramOutputLocation(*programObject, name); + } return -1; } GLint GetProgramResourceLocationIndex(GLuint program, GLenum programInterface, const GLchar* name) { - (void)program; - (void)programInterface; - (void)name; + auto* programObject = TryGetDirectVulkanProgram(program); + if (!programObject || !name) return -1; + if (programInterface == GL_PROGRAM_OUTPUT) { + return GetProgramOutputLocation(*programObject, name) >= 0 ? 0 : -1; + } return -1; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp index 855554b9..db4ce534 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,14 +212,14 @@ 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; } frame.imageAvailableSemaphoreConsumed = false; - 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 bb66bb1b..d5bdb785 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}; }; @@ -54,7 +54,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..967e64aa 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp @@ -33,13 +33,32 @@ namespace MobileGL::MG_Backend::DirectVulkan { XXHASH_VERIFY(XXH64_update(m_hashState, &payload.pipelineLayout, sizeof(payload.pipelineLayout))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.renderPass, sizeof(payload.renderPass))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.colorAttachmentCount, sizeof(payload.colorAttachmentCount))); + XXHASH_VERIFY(XXH64_update(m_hashState, &payload.rasterizationSamples, sizeof(payload.rasterizationSamples))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.subpass, sizeof(payload.subpass))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.topology, sizeof(payload.topology))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.cullMode, sizeof(payload.cullMode))); 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 +105,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,23 +130,43 @@ 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}; - ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + ms.rasterizationSamples = payload.rasterizationSamples; VkPipelineDepthStencilStateCreateInfo depthStencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; depthStencil.depthTestEnable = payload.depthTestEnable ? VK_TRUE : VK_FALSE; 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..b76925be 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h @@ -26,13 +26,27 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; VkRenderPass renderPass = VK_NULL_HANDLE; Uint32 colorAttachmentCount = 1; + VkSampleCountFlagBits rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; Uint32 subpass = 0; VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT; 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..20c2a16d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp @@ -12,7 +12,23 @@ #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 Bool PendingClearMatchesTextureIdentity(const PendingClearKey& key, const TextureIdentity& identity) { + return key.texture == identity.texture && key.textureLifetimeId == identity.lifetimeId; + } + + 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 +39,31 @@ 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, + .textureLifetimeId = texture ? texture->GetLifetimeId() : 0, + .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() { @@ -31,7 +71,74 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void VkClearManager::Shutdown() { + const std::lock_guard lock(m_mutex); + m_pendingClears.clear(); + m_aliveObjects.clear(); + } + TextureIdentity VkClearManager::MakeTextureIdentity(MG_State::GLState::ITextureObject* texture) { + return TextureIdentity { + .texture = texture, + .lifetimeId = texture ? texture->GetLifetimeId() : 0, + }; + } + + void VkClearManager::MergeClearPayload(ClearAttachmentPayload& dst, const ClearAttachmentPayload& src) { + dst.mask |= src.mask; + if ((src.mask & GL_COLOR_BUFFER_BIT) != 0) { + dst.color = src.color; + } + if ((src.mask & GL_DEPTH_BUFFER_BIT) != 0) { + dst.depth = src.depth; + } + if ((src.mask & GL_STENCIL_BUFFER_BIT) != 0) { + dst.stencil = src.stencil; + } + } + + void VkClearManager::ErasePendingClearsForTextureLocked(const TextureIdentity& identity) { + Vector keysToErase; + keysToErase.reserve(m_pendingClears.size()); + for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) { + if (PendingClearMatchesTextureIdentity(it->first, identity)) { + keysToErase.emplace_back(it->first); + } + } + for (const auto& key : keysToErase) { + m_pendingClears.erase(key); + } + m_aliveObjects.erase(identity); + } + + Bool VkClearManager::LockTextureIdentityLocked(const TextureIdentity& identity, + SharedPtr& outTexture) { + outTexture.reset(); + if (identity.texture == nullptr) { + return false; + } + + auto aliveIt = m_aliveObjects.find(identity); + if (aliveIt == m_aliveObjects.end()) { + ErasePendingClearsForTextureLocked(identity); + return false; + } + + outTexture = aliveIt->second.lock(); + if (!outTexture || outTexture.get() != identity.texture || outTexture->GetLifetimeId() != identity.lifetimeId) { + ErasePendingClearsForTextureLocked(identity); + outTexture.reset(); + return false; + } + + return true; + } + + Bool VkClearManager::LockTextureLocked(const PendingClearKey& key, + SharedPtr& outTexture) { + return LockTextureIdentityLocked(TextureIdentity{ + .texture = key.texture, + .lifetimeId = key.textureLifetimeId, + }, outTexture); } void VkClearManager::QueueClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload, @@ -40,88 +147,143 @@ 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); } } } void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload, const SharedPtr& texture) { - if (clearPayload.mask == 0) { + if (clearPayload.mask == 0 || !texture) { return; } - WeakPtr weakTexturePtr = texture; - if (weakTexturePtr.expired()) + + const PendingClearKey key = MakePendingClearKey(texture.get()); + const std::lock_guard lock(m_mutex); + m_aliveObjects[MakeTextureIdentity(texture.get())] = texture; + auto& pending = m_pendingClears[key]; + MergeClearPayload(pending, clearPayload); + } + + void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload, + const MG_State::GLState::FramebufferAttachmentObject& attachment) { + if (clearPayload.mask == 0 || !attachment.IsTexture() || attachment.IsRenderbuffer()) { return; - auto* pTexture = weakTexturePtr.lock().get(); - m_aliveObjects[pTexture] = weakTexturePtr; - auto& pending = m_pendingClears[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; + const auto texture = attachment.GetTexture(); + if (!texture) { + return; } + + const PendingClearKey key = MakePendingClearKey(attachment); + const std::lock_guard lock(m_mutex); + m_aliveObjects[MakeTextureIdentity(texture.get())] = texture; + auto& pending = m_pendingClears[key]; + MergeClearPayload(pending, clearPayload); } Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) { - return m_pendingClears.find(texture) != m_pendingClears.end(); - } - - 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()); + if (texture == nullptr) { 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(), + const Uint64 lifetimeId = texture->GetLifetimeId(); + const std::lock_guard lock(m_mutex); + for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) { + if (it->first.texture == texture && it->first.textureLifetimeId == lifetimeId) { + SharedPtr liveTexture; + return LockTextureLocked(it->first, liveTexture); + } + } + return false; + } + + Bool VkClearManager::HasPendingClear(const PendingClearKey& key) { + if (key.texture == nullptr) { + return false; + } + + const std::lock_guard lock(m_mutex); + if (m_pendingClears.find(key) == m_pendingClears.end()) { + return false; + } + + SharedPtr liveTexture; + return LockTextureLocked(key, liveTexture); + } + + 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) { + SharedPtr liveTexture; + return GetPendingClear(key, outPayload, liveTexture); + } + + Bool VkClearManager::GetPendingClear(const PendingClearKey& key, ClearAttachmentPayload& outPayload, + SharedPtr& outTexture) { + if (key.texture == nullptr) { + return false; + } + + const std::lock_guard lock(m_mutex); + if (!LockTextureLocked(key, outTexture)) { + return false; + } + auto it = m_pendingClears.find(key); + if (it == m_pendingClears.end()) { + outTexture.reset(); + return false; + } + + outPayload = it->second; + MGLOG_D("%s: Got pending clear for texture@%p lifetime=%llu, mip=%u layer=%u count=%u mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__, + static_cast(key.texture), + static_cast(key.textureLifetimeId), + 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,27 +291,93 @@ 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) { + return false; + } + + const Uint64 lifetimeId = texture->GetLifetimeId(); + const std::lock_guard lock(m_mutex); + SharedPtr liveTexture; + if (!LockTextureIdentityLocked(MakeTextureIdentity(texture), liveTexture)) { + return false; + } + for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) { + if (it->first.texture == texture && it->first.textureLifetimeId == lifetimeId) { + outEntries.emplace_back(PendingClearEntry{.key = it->first, .payload = it->second}); + } + } + return !outEntries.empty(); + } + void VkClearManager::PopPendingClear(MG_State::GLState::ITextureObject* texture) { - MGLOG_D("%s: Pop pending clear for texture %d", __func__, texture->GetExternalIndex()); - m_aliveObjects.erase(texture); - m_pendingClears.erase(texture); + if (texture == nullptr) { + return; + } + + const TextureIdentity identity = MakeTextureIdentity(texture); + MGLOG_D("%s: Pop all pending clears for texture %d", __func__, texture->GetExternalIndex()); + const std::lock_guard lock(m_mutex); + ErasePendingClearsForTextureLocked(identity); + } + + void VkClearManager::PopPendingClear(const PendingClearKey& key) { + if (key.texture == nullptr) { + return; + } + + { + const std::lock_guard lock(m_mutex); + auto it = m_pendingClears.find(key); + if (it != m_pendingClears.end()) { + m_pendingClears.erase(it); + } + } + + MGLOG_D("%s: Pop pending clear for texture@%p lifetime=%llu mip=%u layer=%u count=%u", __func__, + static_cast(key.texture), static_cast(key.textureLifetimeId), + key.mipLevel, key.baseArrayLayer, key.layerCount); + } + + void VkClearManager::PopPendingClear(const MG_State::GLState::FramebufferAttachmentObject& attachment) { + if (!attachment.IsTexture() || attachment.IsRenderbuffer() || !attachment.GetTexture()) { + return; + } + PopPendingClear(MakePendingClearKey(attachment)); } SizeT VkClearManager::CollectGarbage() { + const std::lock_guard lock(m_mutex); m_gcCounter++; if (m_gcCounter != 0) { return 0; } - SizeT count = 0; - for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) { - auto current = it++; - if (current->second.expired()) { - m_pendingClears.erase(current->first); - m_aliveObjects.erase(current); - ++count; + Vector expiredTextures; + expiredTextures.reserve(m_aliveObjects.size()); + for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end(); ++it) { + if (it->second.expired()) { + expiredTextures.emplace_back(it->first); } } - return count; + if (expiredTextures.empty()) { + return 0; + } + + for (const auto& identity : expiredTextures) { + ErasePendingClearsForTextureLocked(identity); + } + return expiredTextures.size(); } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h index 9bfb1d75..3400c214 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h @@ -14,6 +14,7 @@ #include "MG_Util/Math/VectorTypes.h" #include +#include namespace MobileGL::MG_Backend::DirectVulkan { struct ClearFramebufferPayload { @@ -29,8 +30,64 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 stencil = 0; }; + struct PendingClearKey { + MG_State::GLState::ITextureObject* texture = nullptr; + Uint64 textureLifetimeId = 0; + Uint32 mipLevel = 0; + Uint32 baseArrayLayer = 0; + Uint32 layerCount = 1; + + Bool operator==(const PendingClearKey& other) const { + return texture == other.texture && textureLifetimeId == other.textureLifetimeId && + mipLevel == other.mipLevel && + baseArrayLayer == other.baseArrayLayer && layerCount == other.layerCount; + } + }; + + struct TextureIdentity { + MG_State::GLState::ITextureObject* texture = nullptr; + Uint64 lifetimeId = 0; + + Bool operator==(const TextureIdentity& other) const { + return texture == other.texture && lifetimeId == other.lifetimeId; + } + }; + + struct PendingClearEntry { + PendingClearKey key{}; + ClearAttachmentPayload payload{}; + }; + + struct PendingClearKeyHash { + SizeT operator()(const PendingClearKey& key) const { + const SizeT textureHash = std::hash{}(key.texture); + const SizeT textureLifetimeHash = std::hash{}(key.textureLifetimeId); + 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 ^= textureLifetimeHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= mipHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= layerHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + hash ^= layerCountHash + 0x9e3779b9u + (hash << 6) + (hash >> 2); + return hash; + } + }; + + struct TextureIdentityHash { + SizeT operator()(const TextureIdentity& key) const { + SizeT hash = std::hash{}(key.texture); + hash ^= std::hash{}(key.lifetimeId) + 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 +95,33 @@ 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 PendingClearKey& key, ClearAttachmentPayload& outPayload, + SharedPtr& outTexture); + 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: + static TextureIdentity MakeTextureIdentity(MG_State::GLState::ITextureObject* texture); + static void MergeClearPayload(ClearAttachmentPayload& dst, const ClearAttachmentPayload& src); + void ErasePendingClearsForTextureLocked(const TextureIdentity& identity); + Bool LockTextureIdentityLocked(const TextureIdentity& identity, + SharedPtr& outTexture); + Bool LockTextureLocked(const PendingClearKey& key, + SharedPtr& outTexture); + Uint8 m_gcCounter = 0; - UnorderedMap m_pendingClears; - UnorderedMap> m_aliveObjects; + mutable std::mutex m_mutex; + std::unordered_map m_pendingClears; + std::unordered_map, TextureIdentityHash> 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 bda3a202..b7f736e5 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, @@ -78,6 +104,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { return info; } + IntVec2 ResolveRenderPassFramebufferExtent(Bool isDefaultFbo, const TextureSize& attachmentExtent, + VkExtent2D swapchainExtent) { + if (isDefaultFbo) { + return {static_cast(swapchainExtent.width), static_cast(swapchainExtent.height)}; + } + return {attachmentExtent.x(), attachmentExtent.y()}; + } + VkRenderPassManager::VkRenderPassManager(VkDevice device, const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager, SwapchainObject& swapchainObject): @@ -96,12 +130,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void VkRenderPassManager::Shutdown() { + m_renderPasses.clear(); + RenderPassEntry::s_textureResourcesScratch.clear(); + s_activeRenderPass = {}; + s_hasActiveRenderPass = false; } VkRenderPassManager::HashType VkRenderPassManager::ComputeHash( const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) const { XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion)); - const Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get()); + const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); if (isDefaultFbo) { XXHASH_VERIFY(XXH64_update(m_hashState, &swapchainImageIndex, sizeof(swapchainImageIndex))); } @@ -132,25 +170,34 @@ namespace MobileGL::MG_Backend::DirectVulkan { contentPtr = att.GetRenderbuffer().get(); XXHASH_VERIFY(XXH64_update(m_hashState, &contentPtr, sizeof(contentPtr))); if (att.IsTexture()) { + const Uint64 textureLifetimeId = att.GetTexture()->GetLifetimeId(); + XXHASH_VERIFY(XXH64_update(m_hashState, &textureLifetimeId, sizeof(textureLifetimeId))); 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(); auto* resource = m_textureManager.SyncTextureAndGetDescriptor(*texture); if (resource != nullptr) { imageIdentity = reinterpret_cast(resource->image); + XXHASH_VERIFY(XXH64_update(m_hashState, &resource->sampleCount, sizeof(resource->sampleCount))); + } else { + const VkSampleCountFlagBits fallbackSampleCount = VK_SAMPLE_COUNT_1_BIT; + XXHASH_VERIFY(XXH64_update(m_hashState, &fallbackSampleCount, sizeof(fallbackSampleCount))); } XXHASH_VERIFY(XXH64_update(m_hashState, &imageIdentity, sizeof(imageIdentity))); } 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))); @@ -159,8 +206,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED; if (isDefaultFbo) { - if (attachment >= FramebufferAttachmentType::Color0 && - attachment <= FramebufferAttachmentType::Color31) { + const Bool isDefaultColorAttachment = + attachment == FramebufferAttachmentType::Color0 || + (attachment >= FramebufferAttachmentType::FrontLeft && + attachment <= FramebufferAttachmentType::BackRight); + if (isDefaultColorAttachment) { currentLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex); } else if (attachment == FramebufferAttachmentType::Depth || attachment == FramebufferAttachmentType::Stencil) { @@ -197,18 +247,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; } @@ -232,13 +282,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (it != m_renderPasses.end()) return it->second; - Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get()); + Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); // Color attachment auto& drawbufs = fbo.GetDrawBuffers(); const Uint32 colorAttachmentSlotCount = static_cast(drawbufs.size()); - Int width = 0; - Int height = 0; + const VkExtent2D swapchainExtent = m_swapchainObject.GetExtent(); + // Default framebuffer attachments are frontend placeholders; Vulkan framebuffer extent must match the swapchain. + const IntVec2 defaultFramebufferExtent = + ResolveRenderPassFramebufferExtent(isDefaultFbo, {0, 0, 0}, swapchainExtent); + Int width = defaultFramebufferExtent.x(); + Int height = defaultFramebufferExtent.y(); Vector attachmentDescriptions; attachmentDescriptions.reserve(colorAttachmentSlotCount + 1); // Keep the full GL draw buffer slot span so fragment outputs targeting GL_NONE map to VK_ATTACHMENT_UNUSED. @@ -256,6 +310,21 @@ namespace MobileGL::MG_Backend::DirectVulkan { textureResources.reserve(colorAttachmentSlotCount + 1); Vector attachmentViews; attachmentViews.reserve(colorAttachmentSlotCount + 1); + VkSampleCountFlagBits renderPassSampleCount = VK_SAMPLE_COUNT_1_BIT; + Bool hasRenderPassSampleCount = false; + const auto adoptRenderPassSampleCount = [&](VkSampleCountFlagBits sampleCount, + const char* attachmentKind, + Int attachmentId) { + if (!hasRenderPassSampleCount) { + renderPassSampleCount = sampleCount; + hasRenderPassSampleCount = true; + return; + } + MOBILEGL_ASSERT(renderPassSampleCount == sampleCount, + "GetOrCreateRenderPass: mismatched sample count %d on %s attachment %d (expected %d)", + static_cast(sampleCount), attachmentKind, attachmentId, + static_cast(renderPassSampleCount)); + }; // This should automatically work on default & offscreen FBO // assuming default FBO has the right param for (Uint32 i = 0; i < colorAttachmentSlotCount; ++i) { @@ -273,17 +342,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { // Color attachment description VkAttachmentDescription& desc = attachmentDescriptions.back(); switch (textureTarget) { - case TextureTarget::Texture2D: { - auto* texture2d = - static_cast(texture); + case TextureTarget::Texture2D: + case TextureTarget::Texture2DMultisample: { desc.flags = 0; desc.format = isDefaultFbo ? m_swapchainObject.GetSurfaceFormat().format : MG_Util::ConvertTextureInternalFormatToVkEnum( - texture2d->GetFormat()); - desc.samples = VK_SAMPLE_COUNT_1_BIT; + texture->GetFormat()); + VkSampleCountFlagBits attachmentSampleCount = 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 : @@ -297,13 +365,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (hasClear) { pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { .attachmentIndex = attachmentIndex, - .texture = texture + .key = VkClearManager::MakePendingClearKey(att) }); } + const IntVec2 attachmentExtent = + ResolveRenderPassFramebufferExtent(isDefaultFbo, att.GetSize(), swapchainExtent); if (width == 0) - width = att.GetSize().x(); + width = attachmentExtent.x(); if (height == 0) - height = att.GetSize().y(); + height = attachmentExtent.y(); if (isDefaultFbo) { const auto& swapchainViews = m_swapchainObject.GetImageViews(); @@ -315,6 +385,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { .swapchainImageIndex = swapchainImageIndex, .finalLayout = desc.finalLayout, }); + attachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; textureResources.emplace_back(nullptr); attachmentViews.emplace_back(swapchainViews[swapchainImageIndex]); } else { @@ -323,17 +394,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i); textureResources.emplace_back(textureResource); desc.format = textureResource->format; + attachmentSampleCount = textureResource->sampleCount; trackedColorLayout = textureResource->layout; trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo { .target = TrackedAttachmentTarget::Texture, - .texture = texture, + .texture = att.GetTexture(), .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); } + desc.samples = attachmentSampleCount; + adoptRenderPassSampleCount(attachmentSampleCount, "color", texture->GetExternalIndex()); if (!hasClear && trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { MGLOG_W("GetOrCreateRenderPass: color attachment textureId=%d starts with undefined layout and no clear; " @@ -357,19 +436,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 ? @@ -385,10 +480,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { depthAttachmentDescription.format = isDefaultFbo ? m_swapchainObject.GetDepthStencilFormat() : MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat()); + VkSampleCountFlagBits depthAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; if (!isDefaultFbo) { depthAttachmentDescription.format = depthTextureResource->format; + depthAttachmentSampleCount = depthTextureResource->sampleCount; } - depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachmentDescription.samples = depthAttachmentSampleCount; + adoptRenderPassSampleCount(depthAttachmentSampleCount, "depth/stencil", texture.GetExternalIndex()); const auto loadInfo = ResolveDepthStencilAttachmentLoadInfo(trackedDepthLayout, clearDepth, clearStencil); depthAttachmentDescription.loadOp = loadInfo.depthLoadOp; @@ -405,7 +503,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (hasClear) { pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { .attachmentIndex = depthAttachmentIndex, - .texture = &texture + .key = VkClearManager::MakePendingClearKey(*selectedDepthStencilAttachment) }); } if (isDefaultFbo) { @@ -426,17 +524,26 @@ namespace MobileGL::MG_Backend::DirectVulkan { texture.GetExternalIndex()); trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo { .target = TrackedAttachmentTarget::Texture, - .texture = &texture, + .texture = selectedDepthStencilAttachment->GetTexture(), .textureMipLevel = attachmentMipLevel, .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"); + const IntVec2 attachmentExtent = + ResolveRenderPassFramebufferExtent(isDefaultFbo, selectedDepthStencilAttachment->GetSize(), + swapchainExtent); if (width == 0 || height == 0) { - width = depthAtt.GetSize().x(); - height = depthAtt.GetSize().y(); + width = attachmentExtent.x(); + height = attachmentExtent.y(); } } attachmentDescriptions.emplace_back(depthAttachmentDescription); @@ -453,7 +560,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; @@ -496,13 +603,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { static_cast(attachmentViews.size()), static_cast(colorAttachmentRefs.size()), hasDepthStencilAttachment, + renderPassSampleCount, extent, 1 }; - MGLOG_D("VkRenderPassManager::GetOrCreateRenderPass: hash=0x%llx compatibilityHash=0x%llx attachmentCount=%u colorAttachmentCount=%u extent=%dx%d", + MGLOG_D("VkRenderPassManager::GetOrCreateRenderPass: hash=0x%llx compatibilityHash=0x%llx attachmentCount=%u colorAttachmentCount=%u samples=%d extent=%dx%d", static_cast(hash), static_cast(compatibilityHash), renderPassEntry.attachmentCount, renderPassEntry.colorAttachmentCount, + static_cast(renderPassEntry.sampleCount), extent.x(), extent.y()); auto [insertedIt, _] = m_renderPasses.emplace(hash, Move(renderPassEntry)); @@ -526,11 +635,12 @@ 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)) { + SharedPtr liveTexture; + if (!s_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) { continue; } if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) { @@ -538,7 +648,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { clearPayload.color.x(), clearPayload.color.y(), clearPayload.color.z(), - ResolveColorClearAlpha(pending.texture, clearPayload.color.w()) + ResolveColorClearAlpha(liveTexture.get(), clearPayload.color.w()) }; } if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { @@ -554,7 +664,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; @@ -573,11 +683,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { switch (trackedAttachment.target) { case TrackedAttachmentTarget::Texture: MOBILEGL_ASSERT(s_textureManager != nullptr, "EndRenderPass: texture manager is null"); - s_textureManager->UpdateTrackedImageLayoutAfterAttachmentWrite( - commandBuffer, - trackedAttachment.texture, - trackedAttachment.textureMipLevel, - trackedAttachment.finalLayout); + if (const auto texture = trackedAttachment.texture.lock()) { + s_textureManager->UpdateTrackedImageLayoutAfterAttachmentWrite( + commandBuffer, + texture.get(), + trackedAttachment.textureMipLevel, + trackedAttachment.finalLayout); + } break; case TrackedAttachmentTarget::SwapchainColor: MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null"); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h index fc3b95d0..c6eec76a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h @@ -26,12 +26,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { struct PendingClearAttachmentInfo { Uint32 attachmentIndex = 0; - MG_State::GLState::ITextureObject* texture = nullptr; + PendingClearKey key{}; }; struct TrackedAttachmentLayoutInfo { TrackedAttachmentTarget target = TrackedAttachmentTarget::Texture; - MG_State::GLState::ITextureObject* texture = nullptr; + WeakPtr texture; Uint32 textureMipLevel = 0; Uint32 swapchainImageIndex = 0; VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -45,6 +45,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo( VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil); + IntVec2 ResolveRenderPassFramebufferExtent(Bool isDefaultFbo, const TextureSize& attachmentExtent, + VkExtent2D swapchainExtent); struct RenderPassEntry { static inline VkDevice s_device; @@ -58,6 +60,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 attachmentCount = 0; Uint32 colorAttachmentCount = 0; Bool hasDepthStencilAttachment = false; + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT; IntVec2 extent = {0, 0}; Uint32 subpass = 0; @@ -73,6 +76,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { std::swap(attachmentCount, that.attachmentCount); std::swap(colorAttachmentCount, that.colorAttachmentCount); std::swap(hasDepthStencilAttachment, that.hasDepthStencilAttachment); + std::swap(sampleCount, that.sampleCount); std::swap(extent, that.extent); std::swap(subpass, that.subpass); } @@ -86,6 +90,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 attachmentCount, Uint32 colorAttachmentCount, Bool hasDepthStencilAttachment, + VkSampleCountFlagBits sampleCount, IntVec2 extent, int subpass): hash(hash), renderPass(renderpass), @@ -96,6 +101,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { attachmentCount(attachmentCount), colorAttachmentCount(colorAttachmentCount), hasDepthStencilAttachment(hasDepthStencilAttachment), + sampleCount(sampleCount), extent(extent), subpass(subpass) {} diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index 82c6835c..0223d7b2 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -46,6 +46,41 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 arrayLayers = 1; }; + static Bool IsMultisampleTextureUploadTarget(TextureUploadTarget target) { + return target == TextureUploadTarget::Texture2DMultisample || + target == TextureUploadTarget::ProxyTexture2DMultisample || + target == TextureUploadTarget::Texture2DMultisampleArray || + target == TextureUploadTarget::ProxyTexture2DMultisampleArray; + } + + static Bool TryResolveSampleCountFlagBits(Int requestedSamples, VkSampleCountFlagBits& outSampleCount) { + switch (requestedSamples) { + case 1: + outSampleCount = VK_SAMPLE_COUNT_1_BIT; + return true; + case 2: + outSampleCount = VK_SAMPLE_COUNT_2_BIT; + return true; + case 4: + outSampleCount = VK_SAMPLE_COUNT_4_BIT; + return true; + case 8: + outSampleCount = VK_SAMPLE_COUNT_8_BIT; + return true; + case 16: + outSampleCount = VK_SAMPLE_COUNT_16_BIT; + return true; + case 32: + outSampleCount = VK_SAMPLE_COUNT_32_BIT; + return true; + case 64: + outSampleCount = VK_SAMPLE_COUNT_64_BIT; + return true; + default: + return false; + } + } + static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) { return target >= TextureUploadTarget::CubeMapPositiveX && target <= TextureUploadTarget::CubeMapNegativeZ; @@ -71,6 +106,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) { @@ -115,6 +156,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } + VkTextureManager::TextureIdentity VkTextureManager::MakeTextureIdentity( + MG_State::GLState::ITextureObject* texture) { + return TextureIdentity{ + .texture = texture, + .lifetimeId = texture ? texture->GetLifetimeId() : 0, + }; + } + static void GetImageTransitionDestinationState(VkImageLayout newLayout, VkPipelineStageFlags& outDstStageMask, VkAccessFlags& outDstAccessMask) { @@ -462,6 +511,20 @@ namespace MobileGL::MG_Backend::DirectVulkan { case TextureUploadTarget::ProxyTextureRectangle: outShape = {}; return true; + case TextureUploadTarget::Texture2DMultisample: + case TextureUploadTarget::ProxyTexture2DMultisample: + outShape = {}; + return true; + case TextureUploadTarget::Texture2DMultisampleArray: + case TextureUploadTarget::ProxyTexture2DMultisampleArray: + MOBILEGL_ASSERT(texelSize.z() > 0, + "TryResolveTextureShapeInfo: invalid 2D multisample array depth=%d for textureId=%d", + texelSize.z(), texture.GetExternalIndex()); + outShape.imageType = VK_IMAGE_TYPE_2D; + outShape.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + outShape.depth = 1; + outShape.arrayLayers = static_cast(texelSize.z()); + return true; case TextureUploadTarget::Texture3D: case TextureUploadTarget::ProxyTexture3D: MOBILEGL_ASSERT(texelSize.z() > 0, @@ -525,6 +588,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VkTextureManager::Shutdown() { DestroyDeferredReleases(); m_textureResources.clear(); + m_aliveObjects.clear(); m_device = VK_NULL_HANDLE; m_physicalDevice = VK_NULL_HANDLE; @@ -545,28 +609,56 @@ namespace MobileGL::MG_Backend::DirectVulkan { CollectDeferredReleases(frameIndex); } + void VkTextureManager::EraseTrackedTexture(const TextureIdentity& identity) { + auto resourceIt = m_textureResources.find(identity); + if (resourceIt != m_textureResources.end()) { + DeferResourceRelease(Move(resourceIt->second)); + m_textureResources.erase(resourceIt); + } + m_aliveObjects.erase(identity); + } + + void VkTextureManager::PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture) { + if (texture == nullptr) { + return; + } + + Vector staleAliases; + staleAliases.reserve(m_aliveObjects.size()); + for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end(); ++it) { + if (it->first.texture != texture) { + continue; + } + const auto liveTexture = it->second.lock(); + if (!liveTexture || liveTexture.get() != texture || + liveTexture->GetLifetimeId() != it->first.lifetimeId) { + staleAliases.emplace_back(it->first); + } + } + for (const auto& identity : staleAliases) { + EraseTrackedTexture(identity); + } + } + VkTextureManager::TextureResource* VkTextureManager::SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture) { MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE"); - auto aliveIt = m_aliveObjects.find(&texture); + const TextureIdentity identity = MakeTextureIdentity(&texture); + auto aliveIt = m_aliveObjects.find(identity); if (aliveIt != m_aliveObjects.end() && aliveIt->second.expired()) { - auto resourceIt = m_textureResources.find(&texture); - if (resourceIt != m_textureResources.end()) { - DeferResourceRelease(Move(resourceIt->second)); - m_textureResources.erase(resourceIt); - } - m_aliveObjects.erase(aliveIt); + EraseTrackedTexture(aliveIt->first); } const auto& liveTexture = MG_State::pGLContext->GetTextureObject(texture.GetExternalIndex()); if (liveTexture && liveTexture.get() == &texture) { - m_aliveObjects[&texture] = WeakPtr(liveTexture); + m_aliveObjects[identity] = WeakPtr(liveTexture); + PruneStaleTextureAliases(&texture); } - auto it = m_textureResources.find(&texture); + auto it = m_textureResources.find(identity); if (it == m_textureResources.end()) { TextureResource initial{}; - auto [insertIt, _] = m_textureResources.emplace(&texture, Move(initial)); + auto [insertIt, _] = m_textureResources.emplace(identity, Move(initial)); it = insertIt; } @@ -594,7 +686,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 +695,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); @@ -623,7 +762,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo); const VkImageAspectFlags sampledAspect = ResolveSampledImageViewAspectMask(resource->aspect); perMipSampledView = CreateImageView(resource->image, resource->format, sampledAspect, 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); @@ -635,7 +774,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VkTextureManager::UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout) { MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayout: texture is null"); - auto it = m_textureResources.find(texture); + auto it = m_textureResources.find(MakeTextureIdentity(texture)); MOBILEGL_ASSERT(it != m_textureResources.end(), "UpdateTrackedImageLayout: textureId=%d has no tracked resource", texture->GetExternalIndex()); MOBILEGL_ASSERT(it->second.image != VK_NULL_HANDLE, @@ -648,7 +787,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 writtenMipLevel, VkImageLayout newLayout) { MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayoutAfterAttachmentWrite: texture is null"); - auto it = m_textureResources.find(texture); + auto it = m_textureResources.find(MakeTextureIdentity(texture)); MOBILEGL_ASSERT(it != m_textureResources.end(), "UpdateTrackedImageLayoutAfterAttachmentWrite: textureId=%d has no tracked resource", texture->GetExternalIndex()); @@ -753,6 +892,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (resource == nullptr) { return false; } + if (resource->sampleCount != VK_SAMPLE_COUNT_1_BIT) { + MGLOG_D("TransitionTextureForStorageImage: multisample textureId=%d is not exposed as a storage image", + texture.GetExternalIndex()); + return false; + } if (resource->layout == VK_IMAGE_LAYOUT_GENERAL) { return true; } @@ -813,20 +957,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (m_gcCounter != 0) { return 0; } - SizeT count = 0; - for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) { - auto current = it++; - if (current->second.expired()) { - auto resourceIt = m_textureResources.find(current->first); - if (resourceIt != m_textureResources.end()) { - DeferResourceRelease(Move(resourceIt->second)); - m_textureResources.erase(resourceIt); - } - m_aliveObjects.erase(current); - ++count; + + Vector expiredTextures; + expiredTextures.reserve(m_aliveObjects.size()); + for (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end(); ++it) { + if (it->second.expired()) { + expiredTextures.emplace_back(it->first.texture); } } - return count; + for (auto* texture : expiredTextures) { + PruneStaleTextureAliases(texture); + } + return expiredTextures.size(); } Bool VkTextureManager::SyncTexture(MG_State::GLState::ITextureObject &texture, @@ -905,7 +1047,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { MGLOG_D("%s: no mip levels", __func__); return false; } - const Uint32 backingMipLevels = std::max(mipLevels, ComputeFullMipLevelCount(texelSize)); + const Bool isMultisampleTexture = IsMultisampleTextureUploadTarget(uploadTarget); + const Uint32 backingMipLevels = + isMultisampleTexture ? 1u : std::max(mipLevels, ComputeFullMipLevelCount(texelSize)); TextureShapeInfo shapeInfo{}; const Bool supportedShape = TryResolveTextureShapeInfo(texture, uploadTarget, texelSize, shapeInfo); MOBILEGL_ASSERT(supportedShape, @@ -919,6 +1063,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { MGLOG_D("%s: not Texture2D, unsupported", __func__); return false; } + VkSampleCountFlagBits resolvedSampleCount = VK_SAMPLE_COUNT_1_BIT; + if (isMultisampleTexture && + !TryResolveSampleCountFlagBits(texture.GetSamples(), resolvedSampleCount)) { + MGLOG_D("%s: unsupported multisample count=%d for textureId=%d target=%s", __func__, + texture.GetSamples(), texture.GetExternalIndex(), + MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str()); + return false; + } const Bool compatible = resource.image != VK_NULL_HANDLE && resource.format == format && resource.extent.width == static_cast(texelSize.x()) && @@ -926,6 +1078,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { resource.depth == shapeInfo.depth && resource.arrayLayers == shapeInfo.arrayLayers && resource.viewType == shapeInfo.viewType && + resource.sampleCount == resolvedSampleCount && resource.mipLevels == backingMipLevels; if (compatible) { if (resource.perMipViews.size() != backingMipLevels) { @@ -945,6 +1098,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { resource.depth == shapeInfo.depth && resource.arrayLayers == shapeInfo.arrayLayers && resource.viewType == shapeInfo.viewType && + resource.sampleCount == resolvedSampleCount && + resolvedSampleCount == VK_SAMPLE_COUNT_1_BIT && resource.mipLevels < backingMipLevels && resource.layout != VK_IMAGE_LAYOUT_UNDEFINED; @@ -972,16 +1127,33 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkFormatProperties formatProperties{}; vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &formatProperties); const Bool supportsStorageImage = + !isMultisampleTexture && (aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0 && (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0; - imageInfo.usage = - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - (supportsStorageImage ? VK_IMAGE_USAGE_STORAGE_BIT : 0) | - ((aspect & VK_IMAGE_ASPECT_COLOR_BIT) ? - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) | - ((aspect & VK_IMAGE_ASPECT_DEPTH_BIT || aspect & VK_IMAGE_ASPECT_STENCIL_BIT) ? - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0); - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | + (supportsStorageImage ? VK_IMAGE_USAGE_STORAGE_BIT : 0) | + ((aspect & VK_IMAGE_ASPECT_COLOR_BIT) ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) | + (((aspect & VK_IMAGE_ASPECT_DEPTH_BIT) || (aspect & VK_IMAGE_ASPECT_STENCIL_BIT)) ? + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : + 0); + if (!isMultisampleTexture) { + imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + imageInfo.samples = resolvedSampleCount; + if (isMultisampleTexture) { + VkImageFormatProperties imageFormatProperties{}; + const VkResult imageFormatResult = vkGetPhysicalDeviceImageFormatProperties( + m_physicalDevice, format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, + imageInfo.flags, &imageFormatProperties); + if (imageFormatResult != VK_SUCCESS || + (imageFormatProperties.sampleCounts & resolvedSampleCount) == 0) { + MGLOG_D("%s: sampleCount=%d is unsupported for textureId=%d target=%s format=%d usage=0x%x", + __func__, texture.GetSamples(), texture.GetExternalIndex(), + MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(), + static_cast(format), static_cast(imageInfo.usage)); + return false; + } + } imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VmaAllocationCreateInfo allocationInfo{}; allocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; @@ -1001,6 +1173,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { resource.format = format; resource.aspect = aspect; resource.viewType = shapeInfo.viewType; + resource.sampleCount = resolvedSampleCount; resource.syncedTextureParamsVersion = 0; if (preservedResource) { @@ -1116,13 +1289,13 @@ 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); + baseMipLevel, levelCount, 0, resource.arrayLayers, &sampledComponents); if (resource.fullView == VK_NULL_HANDLE) { return false; } const VkImageAspectFlags sampledAspect = ResolveSampledImageViewAspectMask(resource.aspect); resource.sampledView = CreateImageView(resource.image, resource.format, sampledAspect, resource.viewType, - baseMipLevel, levelCount, resource.arrayLayers, &sampledComponents); + baseMipLevel, levelCount, 0, resource.arrayLayers, &sampledComponents); if (resource.sampledView == VK_NULL_HANDLE) { return false; } @@ -1135,6 +1308,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{}; @@ -1149,7 +1323,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; @@ -1306,17 +1480,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)"); @@ -1344,7 +1519,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 debe1658..1727481f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace MobileGL::MG_State::GLState { class ITextureObject; @@ -20,6 +21,23 @@ class ITextureObject; namespace MobileGL::MG_Backend::DirectVulkan { class VkTextureManager { public: + struct TextureIdentity { + MG_State::GLState::ITextureObject* texture = nullptr; + Uint64 lifetimeId = 0; + + Bool operator==(const TextureIdentity& other) const { + return texture == other.texture && lifetimeId == other.lifetimeId; + } + }; + + struct TextureIdentityHash { + SizeT operator()(const TextureIdentity& key) const { + SizeT hash = std::hash{}(key.texture); + hash ^= std::hash{}(key.lifetimeId) + 0x9e3779b9u + (hash << 6) + (hash >> 2); + return hash; + } + }; + struct InitInfo { VkDevice device = VK_NULL_HANDLE; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; @@ -30,12 +48,38 @@ 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; VkImageView sampledView = VK_NULL_HANDLE; Vector perMipViews; Vector perMipSampledViews; + UnorderedMap attachmentViews; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; VkExtent2D extent = {0, 0}; Uint32 depth = 1; @@ -46,6 +90,7 @@ public: VkFormat format = VK_FORMAT_UNDEFINED; VkImageAspectFlags aspect = VK_IMAGE_ASPECT_NONE; VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D; + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT; Uint16 syncedTextureParamsVersion = 0; TextureResource() = default; @@ -57,6 +102,7 @@ public: std::swap(this->sampledView, that.sampledView); 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); @@ -67,6 +113,7 @@ public: std::swap(this->format, that.format); std::swap(this->aspect, that.aspect); std::swap(this->viewType, that.viewType); + std::swap(this->sampleCount, that.sampleCount); std::swap(this->syncedTextureParamsVersion, that.syncedTextureParamsVersion); } @@ -87,6 +134,11 @@ 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); } @@ -94,6 +146,7 @@ public: sampledView = VK_NULL_HANDLE; perMipViews.clear(); perMipSampledViews.clear(); + attachmentViews.clear(); image = VK_NULL_HANDLE; allocation = nullptr; layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -106,6 +159,7 @@ public: format = VK_FORMAT_UNDEFINED; aspect = VK_IMAGE_ASPECT_NONE; viewType = VK_IMAGE_VIEW_TYPE_2D; + sampleCount = VK_SAMPLE_COUNT_1_BIT; syncedTextureParamsVersion = 0; } @@ -124,6 +178,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, @@ -154,6 +211,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, @@ -172,6 +230,9 @@ private: void DeferViewRelease(VkImageView view); void CollectDeferredReleases(Uint32 frameIndex); void DestroyDeferredReleases(); + static TextureIdentity MakeTextureIdentity(MG_State::GLState::ITextureObject* texture); + void EraseTrackedTexture(const TextureIdentity& identity); + void PruneStaleTextureAliases(MG_State::GLState::ITextureObject* texture); VkDevice m_device = VK_NULL_HANDLE; VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; @@ -181,8 +242,8 @@ private: Uint32 m_currentFrameIndex = 0; Uint8 m_gcCounter = 0; - UnorderedMap> m_aliveObjects; - UnorderedMap m_textureResources; + std::unordered_map, TextureIdentityHash> m_aliveObjects; + std::unordered_map m_textureResources; Vector> m_deferredReleases; Vector> m_deferredViewReleases; }; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 276730b8..8f7853c9 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, @@ -617,7 +689,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (trackedAttachment.target != TrackedAttachmentTarget::Texture) { continue; } - if (trackedAttachment.texture == &texture) { + const auto trackedTexture = trackedAttachment.texture.lock(); + if (trackedTexture && trackedTexture.get() == &texture) { return true; } } @@ -632,6 +705,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 +896,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 +923,8 @@ void main() { IntVec2 extent = {0, 0}; Uint32 mipLevel = 0; Uint32 mipLevelCount = 1; + Uint32 baseArrayLayer = 0; + Uint32 layerCount = 1; const char* label = nullptr; }; @@ -932,9 +1065,33 @@ void main() { static Bool ResolveColorBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer, Uint32 swapchainImageIndex, SwapchainObject& swapchainObject, VkTextureManager& textureManager, BlitImageBinding& outBinding) { - const Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get()); + const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); const FramebufferAttachmentType attachmentType = isReadFramebuffer ? fbo.GetReadBuffer() : fbo.GetDrawBuffers()[0]; + outBinding.label = isReadFramebuffer ? "read" : "draw"; + + if (isDefaultFbo) { + const Bool defaultColorAttachment = + attachmentType == FramebufferAttachmentType::Color0 || + (attachmentType >= FramebufferAttachmentType::FrontLeft && + attachmentType <= FramebufferAttachmentType::BackRight); + if (!defaultColorAttachment) { + MGLOG_E("BlitFramebuffer skipped: default framebuffer color attachment %d is not supported", + static_cast(attachmentType)); + return false; + } + outBinding.image = swapchainObject.GetImage(swapchainImageIndex); + outBinding.trackedLayout = nullptr; + outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + const auto extent = swapchainObject.GetExtent(); + outBinding.extent = {static_cast(extent.width), static_cast(extent.height)}; + outBinding.mipLevel = 0; + outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; + return true; + } + if (attachmentType < FramebufferAttachmentType::Color0 || attachmentType > FramebufferAttachmentType::Color31) { MGLOG_E("BlitFramebuffer only supports color attachments right now (attachment=%d)", static_cast(attachmentType)); @@ -958,18 +1115,6 @@ void main() { auto* texture = attachment.GetTexture().get(); MOBILEGL_ASSERT(texture != nullptr, "ResolveColorBlitBinding: texture attachment is null"); - outBinding.label = isReadFramebuffer ? "read" : "draw"; - - if (isDefaultFbo) { - outBinding.image = swapchainObject.GetImage(swapchainImageIndex); - outBinding.trackedLayout = nullptr; - outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - const auto extent = swapchainObject.GetExtent(); - outBinding.extent = {static_cast(extent.width), static_cast(extent.height)}; - outBinding.mipLevel = 0; - outBinding.mipLevelCount = 1; - return true; - } auto* resource = textureManager.SyncTextureAndGetDescriptor(*texture); if (resource == nullptr) { @@ -990,6 +1135,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; } @@ -998,8 +1145,7 @@ void main() { VkTextureManager& textureManager, VkImageAspectFlags requiredAspectMask, BlitImageBinding& outBinding) { - const Bool isDefaultFbo = - (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get()); + const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); const auto attachmentType = ResolveFramebufferCopyAttachmentType(fbo, isReadFramebuffer, requiredAspectMask); if (attachmentType == FramebufferAttachmentType::None) { MGLOG_E("BlitFramebuffer skipped: unsupported aspect mask=0x%x", @@ -1013,6 +1159,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 +1215,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 +1249,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; } @@ -1108,8 +1260,7 @@ void main() { VkTextureManager& textureManager, VkImageAspectFlags requiredAspectMask, BlitImageBinding& outBinding) { - const Bool isDefaultFbo = - (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get()); + const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); const auto attachmentType = ResolveFramebufferCopyAttachmentType(fbo, true, requiredAspectMask); if (attachmentType == FramebufferAttachmentType::None) { MGLOG_E("CopyTexSubImage2D skipped: unsupported source aspect mask=0x%x", @@ -1123,6 +1274,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 +1332,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; } @@ -1399,7 +1554,7 @@ void main() { ProgramFactory::CompileOptionFlags flags = ProgramFactory::CompileOptionBit::PositionZRemap; const auto& currentDrawFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); - if (currentDrawFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { + if (currentDrawFBO != nullptr && currentDrawFBO->IsDefaultFramebuffer()) { flags |= ProgramFactory::CompileOptionBit::PositionYFlip; switch (preTransform) { case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: @@ -2021,6 +2176,7 @@ void main() { .pipelineLayout = programObj.pipelineLayout, .renderPass = renderPassEntry.renderPass, .colorAttachmentCount = renderPassEntry.colorAttachmentCount, + .rasterizationSamples = renderPassEntry.sampleCount, .subpass = 0, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .cullMode = VK_CULL_MODE_NONE, @@ -2292,14 +2448,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}; @@ -2471,6 +2620,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) | @@ -2484,6 +2643,7 @@ void main() { .pipelineLayout = programObj.pipelineLayout, .renderPass = renderPassEntry.renderPass, .colorAttachmentCount = renderPassEntry.colorAttachmentCount, + .rasterizationSamples = renderPassEntry.sampleCount, .subpass = 0, .topology = MG_Util::ConvertPrimitiveModeToVkEnum(mode), .cullMode = cullFaceEnabled @@ -2492,19 +2652,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( @@ -2519,8 +2712,7 @@ void main() { const auto& drawFboBinding = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); MOBILEGL_ASSERT(drawFboBinding != nullptr, "GetOrCreatePipeline: draw framebuffer is null"); - const Bool isDefaultDrawFbo = - drawFboBinding.get() == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get(); + const Bool isDefaultDrawFbo = drawFboBinding->IsDefaultFramebuffer(); const auto& drawBuffers = drawFboBinding->GetDrawBuffers(); auto resolveCompleteColorAttachmentTexture = [&](Uint32 drawBufferIndex) -> MG_State::GLState::ITextureObject* { if (isDefaultDrawFbo || drawBufferIndex >= drawBuffers.size()) { @@ -2681,6 +2873,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()); @@ -2812,14 +3008,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{}; @@ -2964,6 +3157,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(), @@ -2977,6 +3174,10 @@ void main() { const MG_State::GLState::FramebufferObject& framebuffer, GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload) { m_clearManager->CollectGarbage(); + if (IsUnsupportedFramebufferForDirectVulkan(framebuffer)) { + RecordUnsupportedFramebufferError(__func__); + return; + } auto queueAttachmentClear = [&](FramebufferAttachmentType attachmentType) { if (attachmentType == FramebufferAttachmentType::None) { @@ -2986,11 +3187,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) { @@ -3145,8 +3342,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, @@ -3165,53 +3362,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()); @@ -3229,8 +3438,7 @@ void main() { GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLenum filter) { - const Bool drawIsDefaultFbo = - (&drawFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get()); + const Bool drawIsDefaultFbo = drawFbo.IsDefaultFramebuffer(); if (!drawIsDefaultFbo) { return false; } @@ -3279,14 +3487,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}; @@ -3396,6 +3597,11 @@ void main() { 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) { @@ -3408,10 +3614,8 @@ void main() { VkRenderPassManager::EndRenderPass(frame.commandBuffer); } - const Bool readIsDefaultFbo = - (readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); - const Bool drawIsDefaultFbo = - (drawFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer(); + const Bool drawIsDefaultFbo = drawFbo->IsDefaultFramebuffer(); if (isColorBlit && drawIsDefaultFbo && RequiresShaderBlitToDefaultFramebuffer(m_swapchainObject.GetPreTransform())) { if (TryBlitToDefaultFramebufferWithShader(frame, *readFbo, *drawFbo, @@ -3518,13 +3722,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}; @@ -3654,14 +3858,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) { @@ -3744,6 +3948,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) { @@ -3755,8 +3964,7 @@ void main() { VkRenderPassManager::EndRenderPass(frame.commandBuffer); } - const Bool readIsDefaultFbo = - (readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer(); BlitImageBinding dstBinding{}; if (!ResolveTextureCopyDestinationBinding(*destinationTexture, static_cast(level), *m_textureManager, @@ -3842,13 +4050,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, @@ -3957,8 +4165,7 @@ void main() { VkRenderPassManager::EndRenderPass(frame.commandBuffer); } - const Bool readIsDefaultFbo = - (readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer(); BlitImageBinding srcBinding{}; if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, srcBinding)) { @@ -4688,7 +4895,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()); @@ -4974,10 +5182,14 @@ 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; deviceFeatures.shaderInt64 = supportedDeviceFeatures.shaderInt64; deviceFeatures.drawIndirectFirstInstance = supportedDeviceFeatures.drawIndirectFirstInstance; + m_logicOpFeatureEnabled = deviceFeatures.logicOp == VK_TRUE; VkDeviceCreateInfo deviceCreateInfo{}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -5042,18 +5254,24 @@ 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 shaderInt64=%s drawIndirectFirstInstance=%s", + MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s logicOp=%s shaderClipDistance=%s " + "shaderCullDistance=%s wideLines=%s shaderInt64=%s drawIndirectFirstInstance=%s", supportedDeviceFeatures.geometryShader ? "true" : "false", supportedDeviceFeatures.independentBlend ? "true" : "false", + supportedDeviceFeatures.logicOp ? "true" : "false", supportedDeviceFeatures.shaderClipDistance ? "true" : "false", supportedDeviceFeatures.shaderCullDistance ? "true" : "false", + supportedDeviceFeatures.wideLines ? "true" : "false", supportedDeviceFeatures.shaderInt64 ? "true" : "false", supportedDeviceFeatures.drawIndirectFirstInstance ? "true" : "false"); - MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s shaderInt64=%s drawIndirectFirstInstance=%s", + MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s logicOp=%s shaderClipDistance=%s " + "shaderCullDistance=%s wideLines=%s shaderInt64=%s drawIndirectFirstInstance=%s", deviceFeatures.geometryShader ? "true" : "false", deviceFeatures.independentBlend ? "true" : "false", + deviceFeatures.logicOp ? "true" : "false", deviceFeatures.shaderClipDistance ? "true" : "false", deviceFeatures.shaderCullDistance ? "true" : "false", + deviceFeatures.wideLines ? "true" : "false", deviceFeatures.shaderInt64 ? "true" : "false", deviceFeatures.drawIndirectFirstInstance ? "true" : "false"); VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice"); @@ -5362,12 +5580,13 @@ 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)) { + SharedPtr liveTexture; + if (!m_clearManager->GetPendingClear(pending.key, clearPayload, liveTexture)) { continue; } @@ -5380,7 +5599,7 @@ void main() { clearPayload.color.x(), clearPayload.color.y(), clearPayload.color.z(), - ResolveColorClearAlpha(pending.texture, clearPayload.color.w()) + ResolveColorClearAlpha(liveTexture.get(), clearPayload.color.w()) }; } else { if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { @@ -5397,7 +5616,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 4b80756e..e2dae33d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -217,6 +217,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 ac029a6f..90dc3ae8 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -1268,6 +1268,7 @@ namespace MobileGL::MG_Impl::GLImpl { if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, pointIndex); + SharedPtr bufferObject; if (buffer == 0) { point.Bind(nullptr); point.SetRange(Range1D(0, 0)); @@ -1278,11 +1279,15 @@ namespace MobileGL::MG_Impl::GLImpl { if (!doesBufferObjectCreated) { MG_State::pGLContext->CreateBufferObject(buffer); } - auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); + bufferObject = MG_State::pGLContext->GetBufferObject(buffer); 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) { @@ -1292,6 +1297,7 @@ namespace MobileGL::MG_Impl::GLImpl { if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); + SharedPtr bufferObject; if (buffer == 0) { point.Bind(nullptr); point.SetRange(Range1D(0, 0)); @@ -1302,10 +1308,14 @@ namespace MobileGL::MG_Impl::GLImpl { if (!doesBufferObjectCreated) { MG_State::pGLContext->CreateBufferObject(buffer); } - auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); + bufferObject = MG_State::pGLContext->GetBufferObject(buffer); point.Bind(bufferObject); - point.SetRange(Range1D(offset, offset + size)); + if (bufferObject) { + point.SetRange(Range1D(offset, offset + size)); + } else { + point.ClearRange(); + } } /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ @@ -1313,14 +1323,12 @@ namespace MobileGL::MG_Impl::GLImpl { GetBufferParameteriv_State(target, pname, params); } - void GetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params) { - GetBufferParameteri64v_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/Drawing/GL_Drawing.cpp b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp index 406e7fc1..a4b7a77f 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); @@ -182,6 +238,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); } @@ -194,6 +251,7 @@ namespace MobileGL::MG_Impl::GLImpl { "Backend does not support indirect compute dispatch.")); return; } + if (!ValidateCurrentProgramForCompute(__func__)) return; dispatchComputeIndirect(indirect); } @@ -221,10 +279,14 @@ 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); } @@ -256,65 +318,93 @@ namespace MobileGL::MG_Impl::GLImpl { 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); } @@ -323,6 +413,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 5bea76ac..2f11f3fc 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -17,6 +17,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__) { @@ -76,7 +77,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) @@ -95,7 +96,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) @@ -117,10 +118,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) @@ -135,9 +136,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) @@ -190,14 +191,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) @@ -240,28 +241,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_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_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_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_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, 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) @@ -300,9 +301,9 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramBinary, GLuint program, GLenum binary DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramParameteri, GLuint program, GLenum pname, GLint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramParameteri, program, pname, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateFramebuffer, target, numAttachments, attachments) DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage3D, target, levels, internalformat, width, height, depth) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInternalformativ, target, internalformat, pname, bufSize, params) +DECLARE_GL_FUNCTION_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, TexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage3D, target, levels, internalformat, width, height, depth) +DECLARE_GL_FUNCTION_HEAD(void, GetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInternalformativ, target, internalformat, pname, bufSize, params) DECLARE_GL_FUNCTION_HEAD(void, DispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchCompute, num_groups_x, num_groups_y, num_groups_z) DECLARE_GL_FUNCTION_HEAD(void, DispatchComputeIndirect, GLintptr indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchComputeIndirect, indirect) DECLARE_GL_FUNCTION_HEAD(void, DrawArraysIndirect, GLenum mode, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysIndirect, mode, indirect) @@ -358,11 +359,11 @@ 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) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMultisamplefv, GLenum pname, GLuint index, GLfloat* val) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMultisamplefv, pname, index, val) +DECLARE_GL_FUNCTION_HEAD(void, TexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations) +DECLARE_GL_FUNCTION_HEAD(void, GetMultisamplefv, GLenum pname, GLuint index, GLfloat* val) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetMultisamplefv, pname, index, val) DECLARE_GL_FUNCTION_HEAD(void, SampleMaski, GLuint maskNumber, GLbitfield mask) DECLARE_GL_FUNCTION_END_NO_RETURN(void, SampleMaski, maskNumber, mask) DECLARE_GL_FUNCTION_HEAD(void, GetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexLevelParameteriv, target, level, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetTexLevelParameterfv, GLenum target, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexLevelParameterfv, target, level, pname, params) @@ -418,7 +419,7 @@ DECLARE_GL_FUNCTION_HEAD(void, GetSamplerParameterIiv, GLuint sampler, GLenum pn DECLARE_GL_FUNCTION_HEAD(void, GetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSamplerParameterIuiv, sampler, pname, params) DECLARE_GL_FUNCTION_HEAD(void, TexBuffer, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexBuffer, target, internalformat, buffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, TexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexBufferRange, target, internalformat, buffer, offset, size) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations) +DECLARE_GL_FUNCTION_HEAD(void, TexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations) DECLARE_GL_FUNCTION_HEAD(void*, MapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) DECLARE_GL_FUNCTION_END(void*, MapBufferRange, target, offset, length, access) DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearIndex, GLfloat c) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearIndex, c) DECLARE_GL_FUNCTION_STUB_HEAD(void, IndexMask, GLuint mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, IndexMask, mask) @@ -830,8 +831,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) @@ -840,11 +841,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_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_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) @@ -1029,19 +1030,19 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferParameteriv, GLuint frame DECLARE_GL_FUNCTION_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params) DECLARE_GL_FUNCTION_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers) DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisample, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageMultisample, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height) DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params) DECLARE_GL_FUNCTION_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTextures, target, n, textures) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBuffer, GLuint texture, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBuffer, texture, internalformat, buffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferRange, GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferRange, texture, internalformat, buffer, offset, size) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage1D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage1D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width) DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2DMultisample, texture, samples, internalformat, width, height, fixedsamplelocations) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3DMultisample, texture, samples, internalformat, width, height, depth, fixedsamplelocations) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage1D, texture, level, xoffset, width, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2DMultisample, texture, samples, internalformat, width, height, fixedsamplelocations) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage3DMultisample, texture, samples, internalformat, width, height, depth, fixedsamplelocations) +DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage1D, texture, level, xoffset, width, format, type, pixels) DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data) DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, data) DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data) @@ -1049,20 +1050,20 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage1D, GLuint texture, GLint DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage2D, texture, level, xoffset, yoffset, x, y, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, x, y, width, height) DECLARE_GL_FUNCTION_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterf, texture, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterfv, GLuint texture, GLenum pname, const GLfloat* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterfv, texture, pname, param) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterfv, GLuint texture, GLenum pname, const GLfloat* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterfv, texture, pname, param) DECLARE_GL_FUNCTION_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteri, texture, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIiv, GLuint texture, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIiv, texture, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIuiv, GLuint texture, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIuiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterIiv, GLuint texture, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterIiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterIuiv, GLuint texture, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterIuiv, texture, pname, params) DECLARE_GL_FUNCTION_HEAD(void, TextureParameteriv, GLuint texture, GLenum pname, const GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteriv, texture, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateTextureMipmap, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateTextureMipmap, texture) +DECLARE_GL_FUNCTION_HEAD(void, GenerateTextureMipmap, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenerateTextureMipmap, texture) DECLARE_GL_FUNCTION_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTextureUnit, unit, texture) DECLARE_GL_FUNCTION_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureImage, GLuint texture, GLint level, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureImage, texture, level, bufSize, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameterfv, GLuint texture, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameterfv, GLuint texture, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterfv, GLuint texture, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIiv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIiv, texture, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIuiv, GLuint texture, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIuiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterfv, GLuint texture, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterIiv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterIiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterIuiv, GLuint texture, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterIuiv, texture, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameteriv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameteriv, texture, pname, params) DECLARE_GL_FUNCTION_HEAD(void, CreateVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateVertexArrays, n, arrays) DECLARE_GL_FUNCTION_HEAD(void, DisableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index) @@ -1490,9 +1491,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) @@ -1760,25 +1761,25 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixPopEXT, GLenum mode) DECLARE_GL_FUNCTI DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixPushEXT, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixPushEXT, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, ClientAttribDefaultEXT, GLbitfield mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClientAttribDefaultEXT, mask) DECLARE_GL_FUNCTION_STUB_HEAD(void, PushClientAttribDefaultEXT, GLbitfield mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PushClientAttribDefaultEXT, mask) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterfEXT, GLuint texture, GLenum target, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterfEXT, texture, target, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterfvEXT, GLuint texture, GLenum target, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterfvEXT, texture, target, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameteriEXT, GLuint texture, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameteriEXT, texture, target, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterivEXT, GLuint texture, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterivEXT, texture, target, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterfEXT, GLuint texture, GLenum target, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterf, texture, pname, param) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterfvEXT, GLuint texture, GLenum target, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterfv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameteriEXT, GLuint texture, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteri, texture, pname, param) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterivEXT, GLuint texture, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteriv, texture, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureImage1DEXT, GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureImage1DEXT, texture, target, level, internalformat, width, border, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureImage2DEXT, GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureImage2DEXT, texture, target, level, internalformat, width, height, border, format, type, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage1DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage1DEXT, texture, target, level, xoffset, width, format, type, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage2DEXT, texture, target, level, xoffset, yoffset, width, height, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage1DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage1D, texture, level, xoffset, width, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureImage1DEXT, GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureImage1DEXT, texture, target, level, internalformat, x, y, width, border) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureImage2DEXT, GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureImage2DEXT, texture, target, level, internalformat, x, y, width, height, border) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage1DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage1DEXT, texture, target, level, xoffset, x, y, width) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage2DEXT, texture, target, level, xoffset, yoffset, x, y, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureImageEXT, GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureImageEXT, texture, target, level, format, type, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterfvEXT, GLuint texture, GLenum target, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterfvEXT, texture, target, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterivEXT, GLuint texture, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterivEXT, texture, target, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameterfvEXT, GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameterfvEXT, texture, target, level, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameterivEXT, GLuint texture, GLenum target, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameterivEXT, texture, target, level, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterfvEXT, GLuint texture, GLenum target, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterivEXT, GLuint texture, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameteriv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameterfvEXT, GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameterivEXT, GLuint texture, GLenum target, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureImage3DEXT, GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureImage3DEXT, texture, target, level, internalformat, width, height, depth, border, format, type, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage3DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage3DEXT, texture, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage3DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage3DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage3DEXT, texture, target, level, xoffset, yoffset, zoffset, x, y, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindMultiTextureEXT, GLenum texunit, GLenum target, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindMultiTextureEXT, texunit, target, texture) DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiTexCoordPointerEXT, GLenum texunit, GLint size, GLenum type, GLsizei stride, const void* pointer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiTexCoordPointerEXT, texunit, size, type, stride, pointer) @@ -1853,10 +1854,10 @@ DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointervEXT, GLuint buffer, GLenum DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferSubDataEXT, buffer, offset, size, data) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferEXT, GLuint texture, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferEXT, texture, target, internalformat, buffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiTexBufferEXT, GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiTexBufferEXT, texunit, target, internalformat, buffer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIivEXT, GLuint texture, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIivEXT, texture, target, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIuivEXT, GLuint texture, GLenum target, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIuivEXT, texture, target, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIivEXT, GLuint texture, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIivEXT, texture, target, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIuivEXT, GLuint texture, GLenum target, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIuivEXT, texture, target, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterIivEXT, GLuint texture, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterIiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterIuivEXT, GLuint texture, GLenum target, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterIuiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterIivEXT, GLuint texture, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterIiv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterIuivEXT, GLuint texture, GLenum target, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterIuiv, texture, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiTexParameterIivEXT, GLenum texunit, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiTexParameterIivEXT, texunit, target, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiTexParameterIuivEXT, GLenum texunit, GLenum target, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiTexParameterIuivEXT, texunit, target, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMultiTexParameterIivEXT, GLenum texunit, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMultiTexParameterIivEXT, texunit, target, pname, params) @@ -1886,7 +1887,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramivEXT, GLuint program, GLenum DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramStringEXT, GLuint program, GLenum target, GLenum pname, void* string) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramStringEXT, program, target, pname, string) DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageEXT, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height) DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameterivEXT, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisampleEXT, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisampleEXT, renderbuffer, samples, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageMultisampleEXT, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisampleCoverageEXT, GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisampleCoverageEXT, renderbuffer, coverageSamples, colorSamples, internalformat, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, CheckNamedFramebufferStatusEXT, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLenum, CheckNamedFramebufferStatusEXT, framebuffer, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture1DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture1DEXT, framebuffer, attachment, textarget, texture, level) @@ -1894,7 +1895,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture2DEXT, GLuint framebu DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture3DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture3DEXT, framebuffer, attachment, textarget, texture, level, zoffset) DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferAttachmentParameterivEXT, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameterivEXT, framebuffer, attachment, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateTextureMipmapEXT, GLuint texture, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateTextureMipmapEXT, texture, target) +DECLARE_GL_FUNCTION_HEAD(void, GenerateTextureMipmapEXT, GLuint texture, GLenum target) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenerateTextureMipmap, texture) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateMultiTexMipmapEXT, GLenum texunit, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateMultiTexMipmapEXT, texunit, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferDrawBufferEXT, GLuint framebuffer, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferDrawBufferEXT, framebuffer, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferDrawBuffersEXT, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferDrawBuffersEXT, framebuffer, n, bufs) @@ -1948,11 +1949,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix3x4dvEXT, GLuint program DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix4x2dvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix4x2dvEXT, program, location, count, transpose, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix4x3dvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix4x3dvEXT, program, location, count, transpose, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferRangeEXT, GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferRangeEXT, texture, target, internalformat, buffer, offset, size) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage1DEXT, texture, target, levels, internalformat, width) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2DEXT, texture, target, levels, internalformat, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3DEXT, texture, target, levels, internalformat, width, height, depth) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2DMultisampleEXT, GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2DMultisampleEXT, texture, target, samples, internalformat, width, height, fixedsamplelocations) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3DMultisampleEXT, GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3DMultisampleEXT, texture, target, samples, internalformat, width, height, depth, fixedsamplelocations) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2DMultisampleEXT, GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2DMultisample, texture, samples, internalformat, width, height, fixedsamplelocations) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3DMultisampleEXT, GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage3DMultisample, texture, samples, internalformat, width, height, depth, fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayBindVertexBufferEXT, GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayBindVertexBufferEXT, vaobj, bindingindex, buffer, offset, stride) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexAttribFormatEXT, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexAttribFormatEXT, vaobj, attribindex, size, type, normalized, relativeoffset) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexAttribIFormatEXT, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexAttribIFormatEXT, vaobj, attribindex, size, type, relativeoffset) @@ -2086,7 +2087,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearColorIuiEXT, GLuint red, GLuint green, DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, AreTexturesResidentEXT, GLsizei n, const GLuint* textures, GLboolean* residences) DECLARE_GL_FUNCTION_STUB_END(GLboolean, AreTexturesResidentEXT, n, textures, residences) DECLARE_GL_FUNCTION_STUB_HEAD(void, PrioritizeTexturesEXT, GLsizei n, const GLuint* textures, const GLclampf* priorities) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PrioritizeTexturesEXT, n, textures, priorities) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureNormalEXT, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureNormalEXT, mode) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage1DEXT, target, levels, internalformat, width) +DECLARE_GL_FUNCTION_HEAD(void, TexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage1D, target, levels, internalformat, width) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryObjecti64vEXT, GLuint id, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryObjecti64vEXT, id, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryObjectui64vEXT, GLuint id, GLenum pname, GLuint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryObjectui64vEXT, id, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindBufferOffsetEXT, GLenum target, GLuint index, GLuint buffer, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindBufferOffsetEXT, target, index, buffer, offset) @@ -2297,7 +2298,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMapParameterfvNV, GLenum target, GLenum p DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMapAttribParameterivNV, GLenum target, GLuint index, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMapAttribParameterivNV, target, index, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMapAttribParameterfvNV, GLenum target, GLuint index, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMapAttribParameterfvNV, target, index, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, EvalMapsNV, GLenum target, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EvalMapsNV, target, mode) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMultisamplefvNV, GLenum pname, GLuint index, GLfloat* val) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMultisamplefvNV, pname, index, val) +DECLARE_GL_FUNCTION_HEAD(void, GetMultisamplefvNV, GLenum pname, GLuint index, GLfloat* val) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetMultisamplefv, pname, index, val) DECLARE_GL_FUNCTION_STUB_HEAD(void, SampleMaskIndexedNV, GLuint index, GLbitfield mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SampleMaskIndexedNV, index, mask) DECLARE_GL_FUNCTION_STUB_HEAD(void, TexRenderbufferNV, GLenum target, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexRenderbufferNV, target, renderbuffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteFencesNV, GLsizei n, const GLuint* fences) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteFencesNV, n, fences) diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index 33e3d1cf..13b17e0e 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, @@ -59,12 +193,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 AllocateRenderbufferStorage_State(const SharedPtr& renderbufferObject, @@ -85,6 +260,7 @@ namespace MobileGL::MG_Impl::GLImpl { } renderbufferObject->AllocateStorage({width, height}); renderbufferObject->SetInternalFormat(format); + renderbufferObject->SetSamples(0); } void RenderbufferStorage_State(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { @@ -96,11 +272,11 @@ namespace MobileGL::MG_Impl::GLImpl { } 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) { @@ -173,6 +349,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; @@ -242,6 +438,34 @@ namespace MobileGL::MG_Impl::GLImpl { "NamedRenderbufferStorage_State"); } + void NamedRenderbufferStorageMultisample_State(GLuint renderbuffer, GLsizei samples, GLenum internalformat, + GLsizei width, GLsizei height) { + auto renderbufferObject = + GetNamedRenderbufferObject_State(renderbuffer, "NamedRenderbufferStorageMultisample_State"); + if (!renderbufferObject) 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", "NamedRenderbufferStorageMultisample_State", + "Sample count must be non-negative.")); + return; + } + if (width < 0 || height < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "NamedRenderbufferStorageMultisample_State", + "Width and height must be non-negative.")); + return; + } + + renderbufferObject->AllocateStorage({width, height}); + renderbufferObject->SetInternalFormat(format); + renderbufferObject->SetSamples(samples); + } + void GenFramebuffers_State(GLsizei n, GLuint* framebuffers) { if (n < 0) { MG_State::pGLContext->RecordError( @@ -293,12 +517,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) { @@ -318,6 +561,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(); @@ -343,15 +591,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) { - FramebufferTexture2D_State(target, attachment, GL_TEXTURE_2D, texture, level); + 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 NamedFramebufferTexture_State(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) { @@ -382,7 +674,15 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - framebufferObject->AttachTexture(attachmentType, textureObject, level); + TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown; + if (!ResolveRepresentableFramebufferTextureUploadTarget(*textureObject, textureUploadTarget)) { + RecordUnsupportedFramebufferTextureAttachmentError( + "NamedFramebufferTexture_State", + "Layered or multi-image framebuffer texture targets are not fully represented by the current framebuffer attachment model."); + return; + } + + framebufferObject->AttachTexture(attachmentType, textureObject, textureUploadTarget, level); } void FramebufferRenderbuffer_State(GLenum target, GLenum attachment, GLenum renderbuffertarget, @@ -396,7 +696,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) { @@ -467,9 +768,9 @@ namespace MobileGL::MG_Impl::GLImpl { return; } if (!fbo) { - MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", __func__, - "Framebuffer object is null.")); + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Framebuffer object is null.")); return; } @@ -489,20 +790,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( @@ -576,6 +877,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); } @@ -749,10 +1087,16 @@ 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; } GLenum CheckNamedFramebufferStatus_State(GLuint framebuffer, GLenum target) { @@ -768,8 +1112,14 @@ namespace MobileGL::MG_Impl::GLImpl { auto framebufferObject = GetNamedFramebufferObject_State(framebuffer, "CheckNamedFramebufferStatus_State"); if (!framebufferObject) return GL_FRAMEBUFFER_UNDEFINED; - 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 GetFramebufferAttachmentParameteriv_Object( @@ -866,17 +1216,22 @@ namespace MobileGL::MG_Impl::GLImpl { } 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); } @@ -985,7 +1340,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); @@ -994,7 +1349,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 @@ -1002,7 +1357,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 @@ -1010,7 +1365,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 @@ -1021,7 +1376,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 @@ -1029,7 +1384,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 @@ -1039,7 +1394,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()) { @@ -1047,7 +1402,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() || @@ -1056,7 +1411,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 @@ -1065,7 +1420,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; } } @@ -1080,7 +1435,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 @@ -1090,7 +1445,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; } } @@ -1102,9 +1457,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) { @@ -1113,7 +1470,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); } @@ -1170,6 +1527,11 @@ namespace MobileGL::MG_Impl::GLImpl { NamedRenderbufferStorage_State(renderbuffer, internalformat, width, height); } + void NamedRenderbufferStorageMultisample(GLuint renderbuffer, GLsizei samples, GLenum internalformat, + GLsizei width, GLsizei height) { + NamedRenderbufferStorageMultisample_State(renderbuffer, samples, internalformat, width, height); + } + void GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname, GLint* params) { GetNamedRenderbufferParameteriv_State(renderbuffer, pname, params); } diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h index 6e1e551a..f7ecb37a 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h @@ -26,6 +26,8 @@ namespace MobileGL::MG_Impl::GLImpl { void GenRenderbuffers(GLsizei n, GLuint* renderbuffers); void CreateRenderbuffers(GLsizei n, GLuint* renderbuffers); void NamedRenderbufferStorage(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + void NamedRenderbufferStorageMultisample(GLuint renderbuffer, GLsizei samples, GLenum internalformat, + GLsizei width, GLsizei height); void GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname, GLint* params); void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); void NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index 37d1c753..2f0ab69d 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,46 +260,38 @@ 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: - extensionsString.clear(); - for (auto& ext : rendererInfo.RendererGLInfo.Extensions) { + extensionsString.clear(); + for (const auto& ext : rendererInfo.RendererGLInfo.Extensions) { if (!extensionsString.empty()) { extensionsString += " "; } @@ -117,6 +331,232 @@ namespace MobileGL::MG_Impl::GLImpl { 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( @@ -124,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; @@ -142,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; @@ -153,40 +655,60 @@ namespace MobileGL::MG_Impl::GLImpl { getInteger64i(target, index, data); } - void GetInteger64v(GLenum pname, GLint64* data) { + void GetInteger64v(GLenum pname, GLint64* params) { MGLOG_D("glGetInteger64v, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); - if (!data) { + if (!params) { MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, - MakeUnique("MG_Impl/GLImpl", __func__, "data pointer cannot be null")); + MakeUnique("MG_Impl/GLImpl", __func__, "params pointer cannot be null")); return; } switch (pname) { case GL_MAX_SHADER_STORAGE_BLOCK_SIZE: if (MG_Backend::pActiveBackendObject) { - *data = static_cast( + params[0] = static_cast( MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBlockSize); } else { - *data = static_cast(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize); + params[0] = static_cast(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize); } return; case GL_SUBGROUP_SIZE_KHR: case GL_SUBGROUP_SUPPORTED_STAGES_KHR: case GL_SUBGROUP_SUPPORTED_FEATURES_KHR: case GL_SUBGROUP_QUAD_ALL_STAGES_KHR: { - GLint params = 0; - GetIntegerv(pname, ¶ms); - *data = static_cast(params); + GLint value = 0; + GetIntegerv(pname, &value); + params[0] = static_cast(value); return; } default: - *data = 0; - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique("MG_Impl/GLImpl", __func__, - std::format("Unsupported integer64 pname {}.", - MG_Util::ConvertGLEnumToString(pname)))); + break; + } + + 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; } } @@ -212,9 +734,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) @@ -226,9 +758,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); @@ -267,11 +804,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(); @@ -282,7 +824,7 @@ 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_SUBGROUP_SIZE_KHR: *params = static_cast(dynamicParameters.SubgroupSize); @@ -297,31 +839,33 @@ namespace MobileGL::MG_Impl::GLImpl { *params = dynamicParameters.SubgroupQuadOperationsInAllStages ? GL_TRUE : GL_FALSE; 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]); @@ -339,13 +883,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; @@ -367,26 +911,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(); @@ -408,67 +991,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 @@ -480,74 +1075,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 @@ -556,31 +1154,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 @@ -589,77 +1187,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); @@ -686,10 +1284,19 @@ 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_PARAMETER_BUFFER_BINDING_ARB: { auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); @@ -697,16 +1304,20 @@ namespace MobileGL::MG_Impl::GLImpl { 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; @@ -716,23 +1327,20 @@ namespace MobileGL::MG_Impl::GLImpl { MG_Util::ConvertProvokingVertexModeToGLEnum(MG_State::pGLContext->GetProvokingVertexMode())); 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 = @@ -750,25 +1358,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(); @@ -778,16 +1412,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(); @@ -795,80 +1434,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); @@ -878,15 +1547,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); @@ -895,9 +1579,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); @@ -906,38 +1595,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); @@ -969,19 +1672,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(); @@ -992,13 +1695,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 @@ -1008,10 +1712,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 450089a0..3700e2c2 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h @@ -13,8 +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* data); + 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 646943b5..ca2d6ff9 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); } } @@ -748,19 +815,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]; @@ -786,20 +846,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]; @@ -829,19 +882,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]; @@ -869,17 +915,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); @@ -905,17 +945,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); @@ -945,17 +979,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); @@ -1037,22 +1065,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( @@ -1088,7 +1146,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) { @@ -1170,6 +1228,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); } @@ -1210,6 +1278,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); } @@ -1271,6 +1343,20 @@ namespace MobileGL::MG_Impl::GLImpl { 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); } @@ -1304,7 +1390,19 @@ namespace MobileGL::MG_Impl::GLImpl { } void Uniform1uiv(GLint location, GLsizei count, const GLuint* value) { - Uniform1uiv_State(location, count, 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) { diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h index e4b1358f..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); @@ -46,6 +51,9 @@ namespace MobileGL::MG_Impl::GLImpl { 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); @@ -55,6 +63,9 @@ namespace MobileGL::MG_Impl::GLImpl { 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 0ef19709..becc7ebc 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) { @@ -212,7 +364,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) { @@ -304,7 +457,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) { @@ -374,6 +528,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( @@ -388,6 +546,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( @@ -418,6 +580,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 0b32a50c..24c55f26 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 39c091c9..97ed09ad 100644 --- a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp @@ -9,6 +9,9 @@ #include "GL_Sync.h" #include +#include +#include +#include #include namespace MobileGL::MG_Impl::GLImpl { @@ -16,6 +19,7 @@ namespace MobileGL::MG_Impl::GLImpl { struct SyncObject { GLenum Condition = GL_SYNC_GPU_COMMANDS_COMPLETE; GLbitfield Flags = 0; + GLsync BackendHandle = nullptr; }; UnorderedMap> g_syncObjects; @@ -25,6 +29,15 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", funcName, Move(message))); } + const MG_External::GLESFunctionsTable* TryGetDirectGLESFunctions() { + auto* activeBackend = MG_Backend::pActiveBackendObject.get(); + if (!activeBackend) return nullptr; + + auto* directGLESBackend = + dynamic_cast(activeBackend); + return directGLESBackend ? &directGLESBackend->GetGLESFunctions() : nullptr; + } + SyncObject* GetSyncObject(GLsync sync, const char* funcName) { auto it = g_syncObjects.find(sync); if (sync == nullptr || it == g_syncObjects.end()) { @@ -48,6 +61,12 @@ namespace MobileGL::MG_Impl::GLImpl { auto syncObject = MakeUnique(); syncObject->Condition = condition; syncObject->Flags = flags; + if (const auto* glesFuncs = TryGetDirectGLESFunctions(); glesFuncs && glesFuncs->glFenceSync) { + syncObject->BackendHandle = glesFuncs->glFenceSync(condition, flags); + if (syncObject->BackendHandle == nullptr) { + return nullptr; + } + } GLsync handle = reinterpret_cast(syncObject.get()); g_syncObjects[handle] = Move(syncObject); return handle; @@ -64,7 +83,15 @@ namespace MobileGL::MG_Impl::GLImpl { "Flags can only contain GL_SYNC_FLUSH_COMMANDS_BIT."); return GL_WAIT_FAILED; } - if (!GetSyncObject(sync, "ClientWaitSync")) return GL_WAIT_FAILED; + auto* syncObject = GetSyncObject(sync, "ClientWaitSync"); + if (!syncObject) return GL_WAIT_FAILED; + if (syncObject->BackendHandle != nullptr) { + if (const auto* glesFuncs = TryGetDirectGLESFunctions(); glesFuncs && glesFuncs->glClientWaitSync) { + return glesFuncs->glClientWaitSync(syncObject->BackendHandle, flags, timeout); + } + RecordSyncError("ClientWaitSync", ErrorCode::InvalidOperation, "Backend sync wait is unavailable."); + return GL_WAIT_FAILED; + } return GL_ALREADY_SIGNALED; } @@ -77,7 +104,15 @@ namespace MobileGL::MG_Impl::GLImpl { RecordSyncError("WaitSync", ErrorCode::InvalidValue, "Timeout must be GL_TIMEOUT_IGNORED."); return; } - (void)GetSyncObject(sync, "WaitSync"); + auto* syncObject = GetSyncObject(sync, "WaitSync"); + if (!syncObject) return; + if (syncObject->BackendHandle != nullptr) { + if (const auto* glesFuncs = TryGetDirectGLESFunctions(); glesFuncs && glesFuncs->glWaitSync) { + glesFuncs->glWaitSync(syncObject->BackendHandle, flags, timeout); + return; + } + RecordSyncError("WaitSync", ErrorCode::InvalidOperation, "Backend sync wait is unavailable."); + } } void DeleteSync(GLsync sync) { @@ -87,6 +122,11 @@ namespace MobileGL::MG_Impl::GLImpl { RecordSyncError("DeleteSync", ErrorCode::InvalidValue, "Sync object is not valid."); return; } + if (it->second->BackendHandle != nullptr) { + if (const auto* glesFuncs = TryGetDirectGLESFunctions(); glesFuncs && glesFuncs->glDeleteSync) { + glesFuncs->glDeleteSync(it->second->BackendHandle); + } + } g_syncObjects.erase(it); } @@ -95,9 +135,20 @@ namespace MobileGL::MG_Impl::GLImpl { RecordSyncError("GetSynciv", ErrorCode::InvalidValue, "bufSize must be non-negative."); return; } + if (bufSize > 0 && values == nullptr) { + RecordSyncError("GetSynciv", ErrorCode::InvalidValue, + "values must not be null when bufSize is positive."); + return; + } auto* syncObject = GetSyncObject(sync, "GetSynciv"); if (!syncObject) return; + if (syncObject->BackendHandle != nullptr) { + if (const auto* glesFuncs = TryGetDirectGLESFunctions(); glesFuncs && glesFuncs->glGetSynciv) { + glesFuncs->glGetSynciv(syncObject->BackendHandle, pname, bufSize, length, values); + return; + } + } GLint value = 0; switch (pname) { diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 831a942f..613ea72a 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -31,6 +31,158 @@ 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]))); + } + + Bool SetTextureSwizzleParamsFromInts(const SharedPtr& textureObject, + const GLint* params, const char* caller) { + Vec4 swizzleParams; + for (int i = 0; i < 4; ++i) { + swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(params[i]); + if (TextureSwizzleParam::Unknown == swizzleParams[i]) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", caller, "`params` is not valid.")); + return false; + } + } + textureObject->SetSwizzleParamRGBA(swizzleParams); + return true; + } + + template + void WithTemporarilyBoundNamedTexture(const SharedPtr& textureObject, + Fn&& fn) { + if (!textureObject) return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureObject->GetTarget()); + const auto previousBinding = bindingSlot.GetBoundObject(); + bindingSlot.Bind(textureObject); + fn(MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget())); + bindingSlot.Bind(previousBinding); + } + + SizeT ComputeTextureStorageByteSize(TextureInternalFormat textureInternalFormat, GLsizei width, GLsizei height, + GLsizei depth) { + GLenum realInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat); + GLenum realFormat = GL_RGBA; + GLenum realType = GL_UNSIGNED_BYTE; + MG_Util::TextureFormatProcessor::NormalizePixelFormat( + realInternalFormat, PixelFormatNormalizeOptionBit::None, &realInternalFormat, &realFormat, &realType); + return static_cast(width) * static_cast(height) * static_cast(depth) * + MG_Util::GetInternalBytesPerPixel(textureInternalFormat, + MG_Util::ConvertGLEnumToTexturePixelDataType(realType)); + } + + Bool IsMultisampleTextureTarget(TextureTarget target) { + return target == TextureTarget::Texture2DMultisample || + target == TextureTarget::Texture2DMultisampleArray; + } + + Int GetMaxSupportedTextureSamples(TextureInternalFormat textureInternalFormat) { + if (MG_Backend::pActiveBackendObject == nullptr) { + return std::numeric_limits::max(); + } + + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + if (MG_Util::IsDepthFormatInternalFormat(textureInternalFormat) || + MG_Util::IsStencilFormatInternalFormat(textureInternalFormat)) { + return std::max(dynamicParameters.MaxDepthTextureSamples, 1); + } + + GLenum normalizedInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat); + GLenum normalizedFormat = GL_RGBA; + GLenum normalizedType = GL_UNSIGNED_BYTE; + MG_Util::TextureFormatProcessor::NormalizePixelFormat( + normalizedInternalFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, + &normalizedFormat, &normalizedType); + const Bool isIntegerFormat = normalizedFormat == GL_RED_INTEGER || normalizedFormat == GL_RG_INTEGER || + normalizedFormat == GL_RGB_INTEGER || normalizedFormat == GL_RGBA_INTEGER; + return std::max(isIntegerFormat ? dynamicParameters.MaxIntegerSamples + : dynamicParameters.MaxColorTextureSamples, + 1); + } + + Bool ValidateTextureMultisampleStorage(TextureTarget textureTarget, GLsizei samples, GLsizei width, + GLsizei height, GLsizei depth, TextureInternalFormat textureInternalFormat, + const char* caller) { + if (!IsMultisampleTextureTarget(textureTarget)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", caller, + "Target is not a multisample texture target.")); + return false; + } + if (samples <= 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "Sample count must be positive.")); + return false; + } + if (width < 0 || height < 0 || depth < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "Texture size must be non-negative.")); + return false; + } + if (textureTarget == TextureTarget::Texture2DMultisample && depth != 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, + "2D multisample textures must use depth 1.")); + return false; + } + if (textureTarget == TextureTarget::Texture2DMultisampleArray && depth == 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, + "2D multisample array textures must have at least one layer.")); + return false; + } + + const Int maxSamples = GetMaxSupportedTextureSamples(textureInternalFormat); + if (samples > maxSamples) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique( + "MG_Impl/GLImpl", caller, + std::format("Sample count {} exceeds the supported maximum {} for this texture format.", + samples, maxSamples))); + return false; + } + return true; + } + + void AllocateMultisampleTextureStorage(const SharedPtr& textureObject, + TextureUploadTarget textureUploadTarget, + TextureInternalFormat textureInternalFormat, GLsizei samples, + GLsizei width, GLsizei height, GLsizei depth, + GLboolean fixedsamplelocations) { + MOBILEGL_ASSERT(textureObject != nullptr, "AllocateMultisampleTextureStorage requires a texture object"); + MOBILEGL_ASSERT(textureObject->GetStorageType() == TextureStorageType::Mipmap, + "AllocateMultisampleTextureStorage requires mipmap-backed storage"); + + auto* textureMipmapObject = static_cast(textureObject.get()); + textureObject->SetInternalFormat(textureInternalFormat); + textureObject->SetSamples(samples); + textureObject->SetFixedSampleLocations(fixedsamplelocations == GL_TRUE); + textureMipmapObject->AllocateStorage(textureUploadTarget, 0, {{width, height, depth}, 0}); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, 0, false); + } + } // namespace + const SharedPtr& GetTextureObjectByName(GLuint texture, const char* caller) { if (texture == 0 || !TextureImpl::ValidateTextureName(texture, true)) return nullTextureObject; @@ -273,7 +425,84 @@ namespace MobileGL::MG_Impl::GLImpl { void TexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) { - // TODO: implement + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + if (!TextureImpl::ValidateTextureLevelNumber(level)) return; + if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, height)) return; + if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return; + if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + auto& textureObject = bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height, zoffset, + depth)) + return; + if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureObject->GetFormat(), + texturePixelDataType)) + return; + + MOBILEGL_ASSERT(nullptr != static_cast(textureObject.get()), + "Texture object here should always be an object with mipmap"); + auto textureMipmapObject = static_cast(textureObject.get()); + + const void* originalPixels = pixels; + const auto& pixelUnpackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); + if (pixelUnpackBufferObject) { + originalPixels = reinterpret_cast(pixelUnpackBufferObject->GetDataReadOnly()->data()) + + reinterpret_cast(pixels); + } + if (!originalPixels) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "No data supplied from pixels parameter and no PBO bound.")); + return; + } + + SizeT inputSize = 0; + void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack( + originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureObject->GetFormat(), + textureInputFormat, texturePixelDataType, {width, height, depth}, false, inputSize); + if (!processedPixels || inputSize == 0) { + if (processedPixels) free(processedPixels); + return; + } + + const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level); + const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureObject->GetFormat(), texturePixelDataType); + const SizeT srcRowSize = static_cast(width) * internalBpp; + const SizeT srcSliceSize = static_cast(height) * srcRowSize; + const SizeT destRowSize = static_cast(texelSize.x()) * internalBpp; + const SizeT destSliceSize = static_cast(texelSize.y()) * destRowSize; + + const auto* srcData = static_cast(processedPixels); + Uint8* destData = static_cast(textureMipmapObject->MapMipmapData(textureUploadTarget, level)); + if (destData) { + for (GLsizei z = 0; z < depth; ++z) { + for (GLsizei y = 0; y < height; ++y) { + const SizeT destRowOffset = + static_cast(zoffset + z) * destSliceSize + + static_cast(yoffset + y) * destRowSize + + static_cast(xoffset) * internalBpp; + const SizeT srcRowOffset = + static_cast(z) * srcSliceSize + static_cast(y) * srcRowSize; + Memcpy(destData + destRowOffset, srcData + srcRowOffset, srcRowSize); + } + } + } + + free(processedPixels); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true); + MaybeAutoGenerateMipmap(target, textureObject, false, level); } void TexSubImage2D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, @@ -395,7 +624,66 @@ namespace MobileGL::MG_Impl::GLImpl { void TexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid* pixels) { - // TODO: implement + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + if (!TextureImpl::ValidateTextureLevelNumber(level)) return; + if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, 1)) return; + if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return; + if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + auto& textureObject = bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width)) return; + if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureObject->GetFormat(), + texturePixelDataType)) + return; + + MOBILEGL_ASSERT(nullptr != static_cast(textureObject.get()), + "Texture object here should always be an object with mipmap"); + auto textureMipmapObject = static_cast(textureObject.get()); + + const void* originalPixels = pixels; + const auto& pixelUnpackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); + if (pixelUnpackBufferObject) { + originalPixels = reinterpret_cast(pixelUnpackBufferObject->GetDataReadOnly()->data()) + + reinterpret_cast(pixels); + } + if (!originalPixels) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "No data supplied from pixels parameter and no PBO bound.")); + return; + } + + SizeT inputSize = 0; + void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack( + originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureObject->GetFormat(), + textureInputFormat, texturePixelDataType, {width, 1, 1}, false, inputSize); + if (!processedPixels || inputSize == 0) { + if (processedPixels) free(processedPixels); + return; + } + + const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureObject->GetFormat(), texturePixelDataType); + const SizeT copySize = static_cast(width) * internalBpp; + Uint8* destData = static_cast(textureMipmapObject->MapMipmapData(textureUploadTarget, level)); + if (destData) { + Memcpy(destData + static_cast(xoffset) * internalBpp, processedPixels, copySize); + } + + free(processedPixels); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true); + MaybeAutoGenerateMipmap(target, textureObject, false, level); } // TexParameteriv/TexParameterfv are introduced in OpenGL 4.0, so do not support them for now. @@ -503,29 +791,19 @@ 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: { - // ======================= Converting ================================ TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); - - // ======================= Processing ================================ auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); if (!textureObject) return; - - Vec4 swizzleParams; - for (int i = 0; i < 4; i++) { - swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(static_cast(params[i])); - if (TextureSwizzleParam::Unknown == swizzleParams[i]) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique("MG_Impl/GLImpl", __func__, "`params` is not valid.")); - return; - } + GLint signedParams[4] = {static_cast(params[0]), static_cast(params[1]), + static_cast(params[2]), static_cast(params[3])}; + if (!SetTextureSwizzleParamsFromInts(textureObject, signedParams, __func__)) { + return; } - textureObject->SetSwizzleParamRGBA(swizzleParams); break; } default: @@ -544,30 +822,17 @@ 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: { - // ======================= Converting ================================ TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); - - // ======================= Processing ================================ auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); if (!textureObject) return; - - Vec4 swizzleParams; - for (int i = 0; i < 4; i++) { - swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(static_cast(params[i])); - if (TextureSwizzleParam::Unknown == swizzleParams[i]) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique("MG_Impl/GLImpl", __func__, "`params` is not valid.")); - return; - } + if (!SetTextureSwizzleParamsFromInts(textureObject, params, __func__)) { + return; } - textureObject->SetSwizzleParamRGBA(swizzleParams); break; } default: @@ -579,29 +844,23 @@ 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: { - // ======================= Converting ================================ TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); - - // ======================= Processing ================================ auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); if (!textureObject) return; - - Vec4 swizzleParams; - for (int i = 0; i < 4; i++) { - swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(static_cast(params[i])); - if (TextureSwizzleParam::Unknown == swizzleParams[i]) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique("MG_Impl/GLImpl", __func__, "`params` is not valid.")); - return; - } + GLint signedParams[4] = {static_cast(params[0]), static_cast(params[1]), + static_cast(params[2]), static_cast(params[3])}; + if (!SetTextureSwizzleParamsFromInts(textureObject, signedParams, __func__)) { + return; } - textureObject->SetSwizzleParamRGBA(swizzleParams); break; } default: @@ -613,7 +872,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: { @@ -646,12 +910,84 @@ namespace MobileGL::MG_Impl::GLImpl { void TexImage3DMultisample_State(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { - // TODO: implement + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + + if (!TextureImpl::ValidateTextureTarget(textureTarget)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + if (textureTarget != TextureTarget::Texture2DMultisampleArray) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "Target must be GL_TEXTURE_2D_MULTISAMPLE_ARRAY or its proxy.")); + return; + } + + textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA, + TexturePixelDataType::UnsignedByte); + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + if (!ValidateTextureMultisampleStorage(textureTarget, samples, width, height, depth, textureInternalFormat, + __func__)) + return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + const Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget); + auto& textureObject = + isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget) + : bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + AllocateMultisampleTextureStorage(textureObject, textureUploadTarget, textureInternalFormat, samples, width, + height, depth, fixedsamplelocations); } void TexImage2DMultisample_State(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) { - // TODO: implement + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + + if (!TextureImpl::ValidateTextureTarget(textureTarget)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + if (textureTarget != TextureTarget::Texture2DMultisample) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "Target must be GL_TEXTURE_2D_MULTISAMPLE or its proxy.")); + return; + } + + textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA, + TexturePixelDataType::UnsignedByte); + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + if (!ValidateTextureMultisampleStorage(textureTarget, samples, width, height, 1, textureInternalFormat, + __func__)) + return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + const Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget); + auto& textureObject = + isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget) + : bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + AllocateMultisampleTextureStorage(textureObject, textureUploadTarget, textureInternalFormat, samples, width, + height, 1, fixedsamplelocations); } void TexImage3D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, @@ -895,8 +1231,75 @@ namespace MobileGL::MG_Impl::GLImpl { void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { - // TODO: implement - THROW_UNIMPL_EXCEPTION; + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat); + + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + if (!TextureImpl::ValidateTextureLevelNumber(level)) return; + if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, 1)) return; + if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return; + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + if (!TextureImpl::ValidateTextureBorderNumber(border)) return; + if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureInternalFormat, + texturePixelDataType)) + return; + if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return; + + textureInternalFormat = + MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType); + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget); + auto& textureObject = + isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget) + : bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + + if (internalFormat == GL_ALPHA || format == GL_ALPHA) { + textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero, + TextureSwizzleParam::Zero, TextureSwizzleParam::Red}); + } + + const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType); + const SizeT internalBytes = static_cast(width) * internalBpp; + textureObject->SetInternalFormat(textureInternalFormat); + + const void* originalPixels = pixels; + const auto& pixelUnpackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); + if (pixelUnpackBufferObject) { + originalPixels = reinterpret_cast(pixelUnpackBufferObject->GetDataReadOnly()->data()) + + reinterpret_cast(pixels); + } + + MOBILEGL_ASSERT(nullptr != static_cast(textureObject.get()), + "Texture object here should always be an object with mipmap"); + auto textureMipmapObject = static_cast(textureObject.get()); + if (!isProxy) { + textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, 1, 1}, internalBytes}); + } + + if (!originalPixels) { + return; + } + + SizeT imageSize = 0; + void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack( + originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureInternalFormat, + textureInputFormat, texturePixelDataType, {width, 1, 1}, false, imageSize); + if (processedPixels && imageSize > 0) { + DataPtr texelInput{processedPixels, std::min(imageSize, internalBytes)}; + textureMipmapObject->UpdateMipmapSubData(textureUploadTarget, level, texelInput); + } + + free(processedPixels); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true); + MaybeAutoGenerateMipmap(target, textureObject, isProxy, level); } void TexBuffer_State(GLenum target, GLenum internalformat, GLuint buffer) { @@ -949,13 +1352,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) { @@ -998,9 +1405,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()); @@ -1076,14 +1522,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 = @@ -1191,6 +1673,16 @@ namespace MobileGL::MG_Impl::GLImpl { *params = (GLint)MG_Util::ConvertTextureInternalFormatToGLEnum(textureObject->GetFormat()); } break; + case GL_TEXTURE_SAMPLES: + if (params) { + *params = textureObject->GetSamples(); + } + break; + case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS: + if (params) { + *params = textureObject->HasFixedSampleLocations() ? GL_TRUE : GL_FALSE; + } + break; case GL_TEXTURE_RED_TYPE: case GL_TEXTURE_GREEN_TYPE: case GL_TEXTURE_BLUE_TYPE: @@ -1281,6 +1773,16 @@ namespace MobileGL::MG_Impl::GLImpl { *params = (GLfloat)MG_Util::ConvertTextureInternalFormatToGLEnum(textureObject->GetFormat()); } break; + case GL_TEXTURE_SAMPLES: + if (params) { + *params = static_cast(textureObject->GetSamples()); + } + break; + case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS: + if (params) { + *params = textureObject->HasFixedSampleLocations() ? 1.0f : 0.0f; + } + break; case GL_TEXTURE_RED_TYPE: case GL_TEXTURE_GREEN_TYPE: case GL_TEXTURE_BLUE_TYPE: @@ -1529,7 +2031,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); @@ -1542,7 +2044,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 @@ -1550,7 +2052,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 @@ -1558,7 +2060,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 @@ -1566,7 +2068,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 @@ -1581,7 +2083,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 @@ -1589,7 +2091,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 @@ -1602,7 +2104,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 @@ -1612,7 +2114,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; } } @@ -1625,11 +2127,11 @@ namespace MobileGL::MG_Impl::GLImpl { ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", "No stencil buffer for stencil index format")); - return; + return false; } } - if (textureObject->GetStorageType() != TextureStorageType::Mipmap) return; + return true; } void CopyTextureImageToClientOrPBO_State(const SharedPtr& textureObject, @@ -1723,6 +2225,41 @@ namespace MobileGL::MG_Impl::GLImpl { } } + void TextureStorage1D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + if (levels < 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "levels must be positive.")); + return; + } + if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return; + + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA, + TexturePixelDataType::UnsignedByte); + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + const auto textureUploadTarget = GetPrimaryUploadTarget(textureObject); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + auto* textureMipmapObject = static_cast(textureObject.get()); + + textureObject->SetInternalFormat(textureInternalFormat); + for (GLsizei level = 0; level < levels; ++level) { + const GLsizei levelWidth = std::max(1, width >> level); + const SizeT byteSize = ComputeTextureStorageByteSize(textureInternalFormat, levelWidth, 1, 1); + textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, 1, 1}, byteSize}); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false); + } + } + void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { auto textureObject = GetTextureObjectByName(texture, __func__); if (!textureObject) return; @@ -1768,6 +2305,123 @@ namespace MobileGL::MG_Impl::GLImpl { } } + void TextureStorage3D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + if (levels < 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "levels must be positive.")); + return; + } + if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return; + + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA, + TexturePixelDataType::UnsignedByte); + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + const auto textureUploadTarget = GetPrimaryUploadTarget(textureObject); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + auto* textureMipmapObject = static_cast(textureObject.get()); + + textureObject->SetInternalFormat(textureInternalFormat); + for (GLsizei level = 0; level < levels; ++level) { + const GLsizei levelWidth = std::max(1, width >> level); + const GLsizei levelHeight = std::max(1, height >> level); + const GLsizei levelDepth = std::max(1, depth >> level); + const SizeT byteSize = ComputeTextureStorageByteSize(textureInternalFormat, levelWidth, levelHeight, + levelDepth); + textureMipmapObject->AllocateStorage(textureUploadTarget, level, + {{levelWidth, levelHeight, levelDepth}, byteSize}); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false); + } + } + + void TextureStorage2DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLboolean fixedsamplelocations) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + TexStorage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations); + }); + } + + void TextureStorage3DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + TexStorage3DMultisample(target, samples, internalformat, width, height, depth, fixedsamplelocations); + }); + } + + void TexStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { + const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + if (!TextureImpl::ValidateTextureTarget(textureTarget)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + auto& textureObject = bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + + TextureStorage1D(textureObject->GetExternalIndex(), levels, internalformat, width); + } + + void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { + const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + if (!TextureImpl::ValidateTextureTarget(textureTarget)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + auto& textureObject = bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + + TextureStorage2D(textureObject->GetExternalIndex(), levels, internalformat, width, height); + } + + void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth) { + const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + if (!TextureImpl::ValidateTextureTarget(textureTarget)) return; + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget); + auto& textureObject = bindingSlot.GetBoundObject(); + if (!TextureImpl::ValidateTextureObject(textureObject)) return; + + TextureStorage3D(textureObject->GetExternalIndex(), levels, internalformat, width, height, depth); + } + + void TexStorage2DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLboolean fixedsamplelocations) { + TexImage2DMultisample_State(target, samples, internalformat, width, height, fixedsamplelocations); + } + + void TexStorage3DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { + TexImage3DMultisample_State(target, samples, internalformat, width, height, depth, fixedsamplelocations); + } + + void TextureSubImage1D(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, + const void* pixels) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + TexSubImage1D_State(target, level, xoffset, width, format, type, pixels); + }); + } + void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) { auto textureObject = GetTextureObjectByName(texture, __func__); @@ -1844,6 +2498,14 @@ namespace MobileGL::MG_Impl::GLImpl { free(processedPixels); } + void TextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + TexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); + }); + } + void TextureParameteri(GLuint texture, GLenum pname, GLint param) { auto textureObject = GetTextureObjectByName(texture, __func__); TextureParameterObject_State(textureObject, pname, param, __func__); @@ -1854,25 +2516,32 @@ namespace MobileGL::MG_Impl::GLImpl { TextureParameterObjectf_State(textureObject, pname, param, __func__); } + void TextureParameterfv(GLuint texture, GLenum pname, const GLfloat* params) { + if (!params) return; + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { TexParameterfv_State(target, pname, params); }); + } + void TextureParameteriv(GLuint texture, GLenum pname, const GLint* params) { if (!params) return; - if (pname == GL_TEXTURE_SWIZZLE_RGBA) { - auto textureObject = GetTextureObjectByName(texture, __func__); - if (!textureObject) return; - Vec4 swizzleParams; - for (int i = 0; i < 4; i++) { - swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(static_cast(params[i])); - if (TextureSwizzleParam::Unknown == swizzleParams[i]) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique("MG_Impl/GLImpl", __func__, "`params` is not valid.")); - return; - } - } - textureObject->SetSwizzleParamRGBA(swizzleParams); - return; - } - TextureParameteri(texture, pname, *params); + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { TexParameteriv_State(target, pname, params); }); + } + + void TextureParameterIiv(GLuint texture, GLenum pname, const GLint* params) { + if (!params) return; + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + TexParameterIiv_State(target, pname, params); + }); + } + + void TextureParameterIuiv(GLuint texture, GLenum pname, const GLuint* params) { + if (!params) return; + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + TexParameterIuiv_State(target, pname, params); + }); } void BindTextureUnit(GLuint unit, GLuint texture) { @@ -1954,37 +2623,36 @@ namespace MobileGL::MG_Impl::GLImpl { void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params) { auto textureObject = GetTextureObjectByName(texture, __func__); - GetTextureParameterObjectiv_State(textureObject, pname, params, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameteriv_State(target, pname, params); }); + } + + void GetTextureParameterfv(GLuint texture, GLenum pname, GLfloat* params) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameterfv_State(target, pname, params); }); + } + + void GetTextureParameterIiv(GLuint texture, GLenum pname, GLint* params) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameterIiv_State(target, pname, params); }); + } + + void GetTextureParameterIuiv(GLuint texture, GLenum pname, GLuint* params) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameterIuiv_State(target, pname, params); }); } void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params) { auto textureObject = GetTextureObjectByName(texture, __func__); - if (!textureObject || !params) return; - if (!TextureImpl::ValidateTextureLevelNumber(level)) return; - if (textureObject->GetStorageType() != TextureStorageType::Mipmap) return; + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + GetTexLevelParameteriv_State(target, level, pname, params); + }); + } - auto textureUploadTarget = GetPrimaryUploadTarget(textureObject); - auto* textureMipmapObject = static_cast(textureObject.get()); - switch (pname) { - case GL_TEXTURE_WIDTH: - *params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x(); - break; - case GL_TEXTURE_HEIGHT: - *params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y(); - break; - case GL_TEXTURE_DEPTH: - *params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z(); - break; - case GL_TEXTURE_INTERNAL_FORMAT: - *params = (GLint)MG_Util::ConvertTextureInternalFormatToGLEnum(textureObject->GetFormat()); - break; - default: - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique("MG_Impl/GLImpl", __func__, - "pname is not a valid texture level parameter.")); - return; - } + void GetTextureLevelParameterfv(GLuint texture, GLint level, GLenum pname, GLfloat* params) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { + GetTexLevelParameterfv_State(target, level, pname, params); + }); } void BindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, @@ -2025,9 +2693,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( @@ -2036,6 +2701,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); } @@ -2043,11 +2711,14 @@ namespace MobileGL::MG_Impl::GLImpl { GenerateMipmap_Backend(target); } + void GenerateTextureMipmap(GLuint texture) { + auto textureObject = GetTextureObjectByName(texture, __func__); + WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GenerateMipmap_Backend(target); }); + } + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { - GetTexImage_State(target, level, format, type, pixels); - if (MG_Backend::pActiveBackendObject != nullptr && - MG_Backend::pActiveBackendObject->GetBackendType() == BackendType::DirectVulkan && - MG_Backend::gBackendFunctionsTable.GL.GetTexImage != nullptr) { + if (!GetTexImage_State(target, level, format, type, pixels)) return; + if (MG_Backend::gBackendFunctionsTable.GL.GetTexImage != nullptr) { GetTexImage_Backend(target, level, format, type, pixels); return; } @@ -2059,6 +2730,127 @@ namespace MobileGL::MG_Impl::GLImpl { __func__); } + void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) { + if (!params || bufSize <= 0) return; + + const TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + const Bool isRenderbufferTarget = target == GL_RENDERBUFFER; + if (!isRenderbufferTarget && !TextureImpl::ValidateTextureTarget(textureTarget)) return; + + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA, + TexturePixelDataType::UnsignedByte); + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + + GLenum preferredInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat); + GLenum imageFormat = GL_RGBA; + GLenum imageType = GL_UNSIGNED_BYTE; + MG_Util::TextureFormatProcessor::NormalizePixelFormat(preferredInternalFormat, PixelFormatNormalizeOptionBit::None, + &preferredInternalFormat, &imageFormat, &imageType); + + auto writeValues = [&](std::initializer_list values) { + GLsizei index = 0; + for (GLint value : values) { + if (index >= bufSize) break; + params[index++] = value; + } + while (index < bufSize) { + params[index++] = 0; + } + }; + + const Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(textureInternalFormat); + const Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(textureInternalFormat); + const Bool isIntegerFormat = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER || + imageFormat == GL_RGB_INTEGER || imageFormat == GL_RGBA_INTEGER; + const Bool isLayeredTarget = target == GL_TEXTURE_3D || target == GL_TEXTURE_1D_ARRAY || + target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_CUBE_MAP || + target == GL_TEXTURE_CUBE_MAP_ARRAY || target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY; + + GLint maxSamples = 1; + if (MG_Backend::pActiveBackendObject) { + const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters(); + if (isDepthFormat || isStencilFormat) { + maxSamples = dynamicParameters.MaxDepthTextureSamples; + } else if (isIntegerFormat) { + maxSamples = dynamicParameters.MaxIntegerSamples; + } else { + maxSamples = dynamicParameters.MaxColorTextureSamples; + } + maxSamples = std::max(maxSamples, 1); + } + + switch (pname) { + case GL_INTERNALFORMAT_SUPPORTED: + writeValues({GL_TRUE}); + return; + case GL_INTERNALFORMAT_PREFERRED: + writeValues({static_cast(preferredInternalFormat)}); + return; + case GL_TEXTURE_IMAGE_FORMAT: + writeValues({static_cast(imageFormat)}); + return; + case GL_TEXTURE_IMAGE_TYPE: + writeValues({static_cast(imageType)}); + return; + case GL_COLOR_COMPONENTS: + writeValues({(!isDepthFormat && !isStencilFormat) ? GL_TRUE : GL_FALSE}); + return; + case GL_DEPTH_COMPONENTS: + writeValues({isDepthFormat ? GL_TRUE : GL_FALSE}); + return; + case GL_STENCIL_COMPONENTS: + writeValues({isStencilFormat ? GL_TRUE : GL_FALSE}); + return; + case GL_FRAMEBUFFER_RENDERABLE: + writeValues({GL_FULL_SUPPORT}); + return; + case GL_FRAMEBUFFER_RENDERABLE_LAYERED: + writeValues({isLayeredTarget ? GL_FULL_SUPPORT : GL_NONE}); + return; + case GL_NUM_SAMPLE_COUNTS: + writeValues({maxSamples > 1 ? 2 : 1}); + return; + case GL_SAMPLES: + if (maxSamples > 1) { + writeValues({maxSamples, 1}); + } else { + writeValues({1}); + } + return; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "pname is not supported by GetInternalformativ.")); + return; + } + } + + void GetMultisamplefv(GLenum pname, GLuint index, GLfloat* val) { + if (val == nullptr) return; + if (pname != GL_SAMPLE_POSITION) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, "Only GL_SAMPLE_POSITION is supported.")); + return; + } + + const Int maxSamples = MG_Backend::pActiveBackendObject != nullptr + ? std::max(MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxSamples, 1) + : 1; + if (static_cast(index) >= maxSamples) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "Sample index is out of range.")); + return; + } + + // Keep sample positions deterministic even before the backend exposes vendor-specific patterns. + val[0] = 0.5f; + val[1] = 0.5f; + } + void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) { TexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h index fbfead3b..febf458b 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h @@ -16,18 +16,45 @@ namespace MobileGL::MG_Impl::GLImpl { void GenerateMipmap(GLenum target); void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); void CreateTextures(GLenum target, GLsizei n, GLuint* textures); + void TextureStorage1D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + void TextureStorage3D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth); + void TextureStorage2DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLboolean fixedsamplelocations); + void TextureStorage3DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); + void TextureSubImage1D(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, + const void* pixels); void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); + void TextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels); void TextureParameterf(GLuint texture, GLenum pname, GLfloat param); + void TextureParameterfv(GLuint texture, GLenum pname, const GLfloat* params); void TextureParameteri(GLuint texture, GLenum pname, GLint param); + void TextureParameterIiv(GLuint texture, GLenum pname, const GLint* params); + void TextureParameterIuiv(GLuint texture, GLenum pname, const GLuint* params); void TextureParameteriv(GLuint texture, GLenum pname, const GLint* params); + void GenerateTextureMipmap(GLuint texture); void BindTextureUnit(GLuint unit, GLuint texture); void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels); void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels); + void GetTextureParameterfv(GLuint texture, GLenum pname, GLfloat* params); + void GetTextureParameterIiv(GLuint texture, GLenum pname, GLint* params); + void GetTextureParameterIuiv(GLuint texture, GLenum pname, GLuint* params); void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params); + void GetTextureLevelParameterfv(GLuint texture, GLint level, GLenum pname, GLfloat* params); void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params); + void TexStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); + void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth); + void TexStorage2DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLboolean fixedsamplelocations); + void TexStorage3DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, + GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels); void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, @@ -59,6 +86,8 @@ namespace MobileGL::MG_Impl::GLImpl { void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* params); void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params); void GetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat* params); + void GetMultisamplefv(GLenum pname, GLuint index, GLfloat* val); + void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params); void GetCompressedTexImage(GLenum target, GLint level, void* img); void GenTextures(GLsizei n, GLuint* textures); void DeleteTextures(GLsizei n, const GLuint* textures); 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/Texture/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp index d6d36ef3..98e36501 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp @@ -230,6 +230,18 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl { return false; } } + if (target == TextureUploadTarget::Texture2DMultisample || + target == TextureUploadTarget::ProxyTexture2DMultisample || + target == TextureUploadTarget::Texture2DMultisampleArray || + target == TextureUploadTarget::ProxyTexture2DMultisampleArray) { + if (level != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "ValidateTextureLevelWithUploadTarget", + "Level must be zero for multisample textures")); + return false; + } + } return true; } diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp index e03d9e15..704a509a 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp @@ -12,8 +12,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 + SharedPtr GetNamedVertexArrayObject_State(GLuint vaobj, const char* caller) { if (!VertexArrayImpl::ValidateVertexArrayName(vaobj)) return nullptr; @@ -271,6 +327,290 @@ 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]); + params[1] = static_cast(signedParams[1]); + params[2] = static_cast(signedParams[2]); + params[3] = static_cast(signedParams[3]); + } + void CreateVertexArrays(GLsizei n, GLuint* arrays) { CreateVertexArrays_State(n, arrays); } diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h index 10a07729..4c2b72dd 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 CreateVertexArrays(GLsizei n, GLuint* arrays); void DisableVertexArrayAttrib(GLuint vaobj, GLuint index); void EnableVertexArrayAttrib(GLuint vaobj, GLuint index); diff --git a/MobileGL/MG_Impl/Init.cpp b/MobileGL/MG_Impl/Init.cpp index ea1531cd..b80a8a96 100644 --- a/MobileGL/MG_Impl/Init.cpp +++ b/MobileGL/MG_Impl/Init.cpp @@ -35,6 +35,10 @@ 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::FrontLeft, colorTex); + fbo0->AttachTexture(FramebufferAttachmentType::FrontRight, colorTex); + fbo0->AttachTexture(FramebufferAttachmentType::BackLeft, colorTex); + fbo0->AttachTexture(FramebufferAttachmentType::BackRight, 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 9fc9b250..43dc031e 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp @@ -161,10 +161,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); @@ -189,6 +192,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; m_change.Bits |= !(access & BufferMappingAccessBit::InvalidateBuffer || access & BufferMappingAccessBit::InvalidateRange) 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 98512322..086e615f 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 1787fbfe..e216726f 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..ef85ad40 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 { @@ -127,6 +131,7 @@ namespace MobileGL { Uint16 GetObjectVersion() const { return m_objectVersion; } Uint GetExternalIndex() const; + Bool IsDefaultFramebuffer() const { return m_externalIndex == 0; } private: void BumpAttachmentVersion(FramebufferAttachmentType type); @@ -136,7 +141,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 5f28eee6..f67e5c7d 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..a18c4441 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)); } @@ -91,6 +125,12 @@ namespace MobileGL::MG_State::GLState { Int GetActiveFragmentOutputCount() const { return m_program ? m_program->getNumPipeOutputs() : 0; } + const String& GetActiveFragmentOutputName(Uint index) const { + MOBILEGL_ASSERT(m_program != nullptr, "ProgramObject::GetActiveFragmentOutputName: program is null"); + MOBILEGL_ASSERT(index < static_cast(m_program->getNumPipeOutputs()), + "ProgramObject::GetActiveFragmentOutputName: index=%u out of range", index); + return m_program->getPipeOutput(static_cast(index)).name; + } Int GetFragmentOutputLocation(Uint index) const { MOBILEGL_ASSERT(m_program != nullptr, "ProgramObject::GetFragmentOutputLocation: program is null"); MOBILEGL_ASSERT(index < static_cast(m_program->getNumPipeOutputs()), @@ -98,6 +138,12 @@ namespace MobileGL::MG_State::GLState { index); return static_cast(m_program->getPipeOutput(static_cast(index)).layoutLocation()); } + GLint GetActiveFragmentOutputArraySize(Uint index) const { + MOBILEGL_ASSERT(m_program != nullptr, "ProgramObject::GetActiveFragmentOutputArraySize: program is null"); + MOBILEGL_ASSERT(index < static_cast(m_program->getNumPipeOutputs()), + "ProgramObject::GetActiveFragmentOutputArraySize: index=%u out of range", index); + return m_program->getPipeOutput(static_cast(index)).size; + } GLenum GetFragmentOutputType(Uint index) const { MOBILEGL_ASSERT(m_program != nullptr, "ProgramObject::GetFragmentOutputType: program is null"); MOBILEGL_ASSERT(index < static_cast(m_program->getNumPipeOutputs()), @@ -107,6 +153,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 +175,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 +197,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 +231,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 06a0530e..f7bcd382 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 32ea5e1e..eca56acc 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, @@ -162,12 +203,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; @@ -181,6 +237,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; @@ -189,7 +251,26 @@ namespace MobileGL { ProvokingVertexMode ProvokingVertexModeSetting = ProvokingVertexMode::LastVertex; // 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 }; @@ -205,6 +286,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); @@ -224,12 +312,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); @@ -242,6 +337,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/TextureObject.cpp b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp index 076d7647..25d428c3 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.cpp @@ -13,9 +13,15 @@ namespace MobileGL { namespace MG_State { namespace GLState { + static std::atomic s_nextTextureLifetimeId = 1; + // TextureObjectBase implementations + Uint64 TextureObjectBase::AllocateLifetimeId() { + return s_nextTextureLifetimeId.fetch_add(1, std::memory_order_relaxed); + } + TextureObjectBase::TextureObjectBase(TextureTarget target, Uint externalIndex) - : m_target(target), m_externalIndex(externalIndex) { + : m_externalIndex(externalIndex), m_lifetimeId(AllocateLifetimeId()), m_target(target) { m_sampler = MakeShared(0); } @@ -139,6 +145,26 @@ namespace MobileGL { return m_textureParamsVersion; } + Int TextureObjectBase::GetSamples() const { + return m_samples; + } + + void TextureObjectBase::SetSamples(Int samples) { + m_samples = samples; + } + + Bool TextureObjectBase::HasFixedSampleLocations() const { + return m_fixedSampleLocations; + } + + void TextureObjectBase::SetFixedSampleLocations(Bool fixedSampleLocations) { + m_fixedSampleLocations = fixedSampleLocations; + } + + Uint64 TextureObjectBase::GetLifetimeId() const { + return m_lifetimeId; + } + Uint TextureObjectWithOneMipmap::GetMipmapLevelCount() const { return m_textureStorage.GetLevelCount(); } diff --git a/MobileGL/MG_State/GLState/TextureState/TextureObject.h b/MobileGL/MG_State/GLState/TextureState/TextureObject.h index bdda99dc..897ea6eb 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObject.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObject.h @@ -40,6 +40,11 @@ namespace MobileGL::MG_State::GLState { virtual void SetBaseLevel(Uint baseLevel) = 0; virtual void SetMaxLevel(Uint maxLevel) = 0; virtual Uint16 GetTextureParamsVersion() const = 0; + virtual Int GetSamples() const = 0; + virtual void SetSamples(Int samples) = 0; + virtual Bool HasFixedSampleLocations() const = 0; + virtual void SetFixedSampleLocations(Bool fixedSampleLocations) = 0; + virtual Uint64 GetLifetimeId() const = 0; protected: virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0; @@ -67,9 +72,17 @@ namespace MobileGL::MG_State::GLState { void SetBaseLevel(Uint baseLevel) override; void SetMaxLevel(Uint maxLevel) override; Uint16 GetTextureParamsVersion() const override; + Int GetSamples() const override; + void SetSamples(Int samples) override; + Bool HasFixedSampleLocations() const override; + void SetFixedSampleLocations(Bool fixedSampleLocations) override; + Uint64 GetLifetimeId() const override; protected: + static Uint64 AllocateLifetimeId(); + const Uint m_externalIndex; + const Uint64 m_lifetimeId; const TextureTarget m_target = TextureTarget::Unknown; TextureInternalFormat m_internalFormat = TextureInternalFormat::Unknown; SharedPtr m_sampler = nullptr; @@ -78,6 +91,8 @@ namespace MobileGL::MG_State::GLState { TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha}; UintVec2 m_levelRange = {0, 1000}; Uint16 m_textureParamsVersion = 0; + Int m_samples = 0; + Bool m_fixedSampleLocations = true; }; class TextureObjectMipmap : public TextureObjectBase { 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/TextureState/TextureObjectStubs.h b/MobileGL/MG_State/GLState/TextureState/TextureObjectStubs.h index a143c3eb..b29d6764 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureObjectStubs.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureObjectStubs.h @@ -15,12 +15,11 @@ namespace MobileGL { namespace GLState { /* These texture types are not yet implemented: * TextureRectangle, - * Texture2DMultisample, * TextureBuffer, * Texture1DArray, * Texture2DArray, * TextureCubeMapArray, - * Texture2DMultisampleArray + * Texture2DMultisampleArray layered attachment behavior */ #define STUB_TEXTURE_OBJECT_CLASS_DEFINITION(className, texTarget, uploadTargets) \ class className : public TextureObjectWithOneMipmap { \ @@ -58,4 +57,4 @@ namespace MobileGL { } // namespace GLState } // namespace MG_State -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_State/GLState/TextureState/TextureState.cpp b/MobileGL/MG_State/GLState/TextureState/TextureState.cpp index 439abe95..d58727e1 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureState.cpp +++ b/MobileGL/MG_State/GLState/TextureState/TextureState.cpp @@ -57,7 +57,7 @@ namespace MobileGL::MG_State::GLState { textureObject = MakeShared(index); break; - // These texture types are stubbed: + // These texture types are still stubbed: case TextureTarget::TextureRectangle: textureObject = MakeShared(index); break; 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_Test/Framebuffer/FramebufferTest.cpp b/MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp index 030302cc..11221c5f 100644 --- a/MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp +++ b/MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp @@ -105,6 +105,20 @@ TEST_F(FramebufferTest, CreateFramebuffersCreatesObjectsImmediately) { EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(FramebufferTest, DefaultFramebufferIdentityTracksFramebufferNameZero) { + const auto defaultFramebuffer = MG_State::pGLContext->GetFramebufferObject(0); + ASSERT_NE(defaultFramebuffer, nullptr); + EXPECT_TRUE(defaultFramebuffer->IsDefaultFramebuffer()); + const auto defaultFramebufferCopy = *defaultFramebuffer; + EXPECT_TRUE(defaultFramebufferCopy.IsDefaultFramebuffer()); + + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + const auto userFramebuffer = MG_State::pGLContext->GetFramebufferObject(framebuffer); + ASSERT_NE(userFramebuffer, nullptr); + EXPECT_FALSE(userFramebuffer->IsDefaultFramebuffer()); +} + TEST_F(FramebufferTest, NamedFramebufferTextureAttachesWithoutChangingBindings) { GLuint framebuffer = 0; MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); @@ -150,6 +164,27 @@ TEST_F(FramebufferTest, NamedDepthFramebufferTextureStorageIsCompleteWithoutBind EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(FramebufferTest, FramebufferTextureBumpsAttachmentVersionOnlyOnce) { + GLuint framebuffer = 0; + GLuint texture = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + MG_Impl::GLImpl::TextureStorage2D(texture, 1, GL_RGBA8, 64, 32); + MG_Impl::GLImpl::BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); + + const auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + const auto beforeVersions = framebufferObject->GetAllFramebufferAttachmentVersions(); + const auto beforeObjectVersion = framebufferObject->GetObjectVersion(); + + MG_Impl::GLImpl::FramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); + + const auto afterVersions = framebufferObject->GetAllFramebufferAttachmentVersions(); + EXPECT_EQ(afterVersions[static_cast(FramebufferAttachmentType::Color0)], + beforeVersions[static_cast(FramebufferAttachmentType::Color0)] + 1); + EXPECT_EQ(framebufferObject->GetObjectVersion(), beforeObjectVersion + 1); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(FramebufferTest, ReadPixelsAllowsPersistentMappedPixelPackBuffer) { GLuint framebuffer = 0; GLuint texture = 0; @@ -238,6 +273,46 @@ TEST_F(FramebufferTest, NamedFramebufferDrawBuffersDoNotModifyDefaultFramebuffer EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(FramebufferTest, DefaultFramebufferReadBufferAcceptsGLBackAlias) { + const auto defaultRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + MG_Impl::GLImpl::ReadBuffer(GL_BACK); + + EXPECT_EQ(defaultRead->GetReadBuffer(), FramebufferAttachmentType::BackLeft); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, DefaultFramebufferDrawBufferAcceptsGLFrontAlias) { + const auto defaultDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + + MG_Impl::GLImpl::DrawBuffer(GL_FRONT); + + EXPECT_EQ(defaultDraw->GetDrawBuffers()[0], FramebufferAttachmentType::FrontLeft); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, DefaultFramebufferProvidesTextureAttachmentsForFrontAndBackAliases) { + const auto defaultFramebuffer = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + ASSERT_NE(defaultFramebuffer, nullptr); + + const auto& frontLeft = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::FrontLeft); + const auto& frontRight = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::FrontRight); + const auto& backLeft = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::BackLeft); + const auto& backRight = defaultFramebuffer->GetAttachment(FramebufferAttachmentType::BackRight); + + EXPECT_TRUE(frontLeft.IsTexture()); + EXPECT_TRUE(frontRight.IsTexture()); + EXPECT_TRUE(backLeft.IsTexture()); + EXPECT_TRUE(backRight.IsTexture()); + EXPECT_TRUE(frontLeft.IsComplete()); + EXPECT_TRUE(frontRight.IsComplete()); + EXPECT_TRUE(backLeft.IsComplete()); + EXPECT_TRUE(backRight.IsComplete()); +} + TEST_F(FramebufferTest, ClearNamedFramebufferfvUsesNamedObjectWithoutChangingBindings) { GLuint framebuffer = 0; MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index 4b506476..c462696d 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -165,6 +165,18 @@ TEST(DirectVulkanSanity, AdvertisesTextureStorageForDirectStateAccess) { EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_texture_storage), extensions.end()); } +TEST(DirectVulkanSanity, RenderPassExtentUsesSwapchainSizeOnlyForDefaultFramebuffer) { + using MobileGL::MG_Backend::DirectVulkan::ResolveRenderPassFramebufferExtent; + + const MobileGL::TextureSize attachmentExtent = {512, 512, 1}; + const VkExtent2D swapchainExtent = {3200u, 1440u}; + + EXPECT_EQ(ResolveRenderPassFramebufferExtent(true, attachmentExtent, swapchainExtent), + MobileGL::IntVec2(3200, 1440)); + EXPECT_EQ(ResolveRenderPassFramebufferExtent(false, attachmentExtent, swapchainExtent), + MobileGL::IntVec2(512, 512)); +} + TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) { MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend; const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo; diff --git a/MobileGL/MG_Test/Texture/CMakeLists.txt b/MobileGL/MG_Test/Texture/CMakeLists.txt index b6860ba7..b1ca17ea 100644 --- a/MobileGL/MG_Test/Texture/CMakeLists.txt +++ b/MobileGL/MG_Test/Texture/CMakeLists.txt @@ -18,3 +18,21 @@ target_link_libraries( include(GoogleTest) gtest_discover_tests(TextureTest) + +add_executable( + VkClearManagerTest + VkClearManagerTest.cpp +) + +target_include_directories(VkClearManagerTest PRIVATE + ${MGL_ROOT}/include + ${MGL_ROOT}/MobileGL +) + +target_link_libraries( + VkClearManagerTest PRIVATE + GTest::gtest_main + ${LINK_LIBRARIES} +) + +gtest_discover_tests(VkClearManagerTest) diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index 61c16cce..9ec245d7 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -99,6 +99,57 @@ TEST_F(TextureTest, BoundTexSubImage2DUsesCompactRowsAfterUnpackProcessing) { EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(TextureTest, BoundTexStorage2DAllocatesRedTextureForSubImageUpdates) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture); + + MG_Impl::GLImpl::TexStorage2D(GL_TEXTURE_2D, 1, GL_R8, 32, 32); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + auto* mipmapObject = static_cast(textureObject.get()); + ASSERT_NE(mipmapObject, nullptr); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 0), IntVec3(32, 32, 1)); + EXPECT_TRUE(textureObject->IsComplete()); + + const Uint8 pixels[4 * 4] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + }; + MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 20, 28, 4, 4, GL_RED, GL_UNSIGNED_BYTE, pixels); + + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, TextureStorage2DMultisampleTracksNamedObjectState) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &texture); + + MG_Impl::GLImpl::TextureStorage2DMultisample(texture, 4, GL_RGBA8, 8, 6, GL_TRUE); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + ASSERT_NE(textureObject, nullptr); + EXPECT_EQ(textureObject->GetTarget(), TextureTarget::Texture2DMultisample); + EXPECT_EQ(textureObject->GetSamples(), 4); + EXPECT_TRUE(textureObject->HasFixedSampleLocations()); + + auto* textureMipmapObject = static_cast(textureObject.get()); + ASSERT_NE(textureMipmapObject, nullptr); + EXPECT_EQ(textureMipmapObject->GetMipmapLevelCount(), 1u); + EXPECT_EQ(textureMipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2DMultisample, 0), IntVec3(8, 6, 1)); + EXPECT_FALSE(textureMipmapObject->IsStorageDirty(TextureUploadTarget::Texture2DMultisample, 0)); + + GLint samples = 0; + GLint fixed = 0; + MG_Impl::GLImpl::GetTextureLevelParameteriv(texture, 0, GL_TEXTURE_SAMPLES, &samples); + MG_Impl::GLImpl::GetTextureLevelParameteriv(texture, 0, GL_TEXTURE_FIXED_SAMPLE_LOCATIONS, &fixed); + EXPECT_EQ(samples, 4); + EXPECT_EQ(fixed, GL_TRUE); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(TextureTest, GetTextureImageReadsNamedObjectWithoutBinding) { GLuint texture = 0; MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); @@ -208,6 +259,108 @@ TEST_F(TextureTest, TextureParameterfModifiesNamedObjectWithoutBinding) { EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(TextureTest, TextureStorage1DAndSubImageModifyNamedObjectOnly) { + GLuint namedTexture = 0; + GLuint boundTexture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_1D, 1, &namedTexture); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_1D, 1, &boundTexture); + MG_Impl::GLImpl::BindTextureUnit(0, boundTexture); + + const auto boundObjectBefore = + MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture1D).GetBoundObject(); + + MG_Impl::GLImpl::TextureStorage1D(namedTexture, 2, GL_RGBA8, 4); + const Uint8 pixels[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + }; + MG_Impl::GLImpl::TextureSubImage1D(namedTexture, 0, 0, 4, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(namedTexture); + auto* mipmapObject = static_cast(textureObject.get()); + ASSERT_NE(mipmapObject, nullptr); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture1D, 0), IntVec3(4, 1, 1)); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture1D, 1), IntVec3(2, 1, 1)); + EXPECT_TRUE(mipmapObject->IsStorageDirty(TextureUploadTarget::Texture1D, 0)); + + const auto* stored = static_cast(mipmapObject->MapMipmapData(TextureUploadTarget::Texture1D, 0)); + ASSERT_NE(stored, nullptr); + EXPECT_EQ(std::memcmp(stored, pixels, sizeof(pixels)), 0); + + EXPECT_EQ(MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture1D).GetBoundObject(), + boundObjectBefore); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, TextureStorage3DAndSubImageModifyNamedObjectOnly) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_3D, 1, &texture); + MG_Impl::GLImpl::TextureStorage3D(texture, 2, GL_R8, 2, 2, 2); + + const Uint8 pixels[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + }; + MG_Impl::GLImpl::TextureSubImage3D(texture, 0, 0, 0, 0, 2, 2, 2, GL_RED, GL_UNSIGNED_BYTE, pixels); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + auto* mipmapObject = static_cast(textureObject.get()); + ASSERT_NE(mipmapObject, nullptr); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture3D, 0), IntVec3(2, 2, 2)); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture3D, 1), IntVec3(1, 1, 1)); + EXPECT_TRUE(mipmapObject->IsStorageDirty(TextureUploadTarget::Texture3D, 0)); + + const auto* stored = static_cast(mipmapObject->MapMipmapData(TextureUploadTarget::Texture3D, 0)); + ASSERT_NE(stored, nullptr); + EXPECT_EQ(std::memcmp(stored, pixels, sizeof(pixels)), 0); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, NamedTextureVectorParametersAndGettersWorkWithoutBinding) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + + const GLfloat borderColor[] = {0.25f, 0.5f, 0.75f, 1.0f}; + const GLint swizzle[] = {GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA}; + MG_Impl::GLImpl::TextureParameterfv(texture, GL_TEXTURE_BORDER_COLOR, borderColor); + MG_Impl::GLImpl::TextureParameterIiv(texture, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + + GLfloat reportedBorder[4] = {}; + GLint reportedSwizzle[4] = {}; + MG_Impl::GLImpl::GetTextureParameterfv(texture, GL_TEXTURE_BORDER_COLOR, reportedBorder); + MG_Impl::GLImpl::GetTextureParameterIiv(texture, GL_TEXTURE_SWIZZLE_RGBA, reportedSwizzle); + + EXPECT_FLOAT_EQ(reportedBorder[0], borderColor[0]); + EXPECT_FLOAT_EQ(reportedBorder[1], borderColor[1]); + EXPECT_FLOAT_EQ(reportedBorder[2], borderColor[2]); + EXPECT_FLOAT_EQ(reportedBorder[3], borderColor[3]); + EXPECT_EQ(reportedSwizzle[0], GL_BLUE); + EXPECT_EQ(reportedSwizzle[1], GL_GREEN); + EXPECT_EQ(reportedSwizzle[2], GL_RED); + EXPECT_EQ(reportedSwizzle[3], GL_ALPHA); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, GetInternalformativReportsBasicTextureMetadata) { + GLint params[4] = {}; + + MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_INTERNALFORMAT_SUPPORTED, 1, params); + EXPECT_EQ(params[0], GL_TRUE); + + MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_TEXTURE_IMAGE_FORMAT, 1, params); + EXPECT_EQ(params[0], GL_RGBA); + + MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_TEXTURE_IMAGE_TYPE, 1, params); + EXPECT_EQ(params[0], GL_UNSIGNED_BYTE); + + MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_3D, GL_DEPTH24_STENCIL8, GL_FRAMEBUFFER_RENDERABLE_LAYERED, 1, + params); + EXPECT_EQ(params[0], GL_FULL_SUPPORT); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(TextureTest, NormalizeDepth24Stencil8UsesPackedDepthStencilType) { GLenum internalFormat = 0; GLenum format = 0; diff --git a/MobileGL/MG_Test/Texture/VkClearManagerTest.cpp b/MobileGL/MG_Test/Texture/VkClearManagerTest.cpp new file mode 100644 index 00000000..c142100d --- /dev/null +++ b/MobileGL/MG_Test/Texture/VkClearManagerTest.cpp @@ -0,0 +1,110 @@ +// MobileGL - MobileGL/MG_Test/Texture/VkClearManagerTest.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include + +#include "Includes.h" +#include "Init.h" +#include +#include +#include + +using namespace MobileGL; +using namespace MobileGL::MG_Backend::DirectVulkan; + +class VkClearManagerTest : public ::testing::Test { +protected: + void SetUp() override { MobileGL::Initialize(); } +}; + +TEST_F(VkClearManagerTest, CollectGarbageRemovesExpiredTexturesAndTheirPendingClears) { + VkClearManager clearManager; + ASSERT_TRUE(clearManager.Initialize()); + + constexpr SizeT kTextureCount = 64; + Vector textureNames(kTextureCount, 0); + Vector pendingKeys; + pendingKeys.reserve(kTextureCount); + + const ClearAttachmentPayload clearPayload{ + .mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, + .color = FloatVec4(0.25f, 0.5f, 0.75f, 1.0f), + .depth = 0.5f, + }; + + for (SizeT i = 0; i < kTextureCount; ++i) { + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &textureNames[i]); + ASSERT_NE(textureNames[i], 0u); + + { + const auto textureObject = MG_State::pGLContext->GetTextureObject(textureNames[i]); + ASSERT_NE(textureObject, nullptr); + + const PendingClearKey key = VkClearManager::MakePendingClearKey(textureObject.get()); + clearManager.QueueClear(clearPayload, textureObject); + + EXPECT_TRUE(clearManager.HasPendingClear(key)); + pendingKeys.emplace_back(key); + } + } + + MG_Impl::GLImpl::DeleteTextures(static_cast(textureNames.size()), textureNames.data()); + + for (Int i = 0; i < 255; ++i) { + EXPECT_EQ(clearManager.CollectGarbage(), 0u); + } + EXPECT_EQ(clearManager.CollectGarbage(), kTextureCount); + + ClearAttachmentPayload outPayload{}; + for (const auto& key : pendingKeys) { + EXPECT_FALSE(clearManager.HasPendingClear(key)); + EXPECT_FALSE(clearManager.GetPendingClear(key, outPayload)); + } + + GLuint freshTexture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &freshTexture); + ASSERT_NE(freshTexture, 0u); + + const auto freshTextureObject = MG_State::pGLContext->GetTextureObject(freshTexture); + ASSERT_NE(freshTextureObject, nullptr); + EXPECT_FALSE(clearManager.HasPendingClear(freshTextureObject.get())); + + MG_Impl::GLImpl::DeleteTextures(1, &freshTexture); + clearManager.Shutdown(); +} + +TEST_F(VkClearManagerTest, StalePendingClearsAreRejectedBeforePeriodicGarbageCollection) { + VkClearManager clearManager; + ASSERT_TRUE(clearManager.Initialize()); + + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + ASSERT_NE(texture, 0u); + + PendingClearKey key{}; + { + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + ASSERT_NE(textureObject, nullptr); + + key = VkClearManager::MakePendingClearKey(textureObject.get()); + clearManager.QueueClear(ClearAttachmentPayload{ + .mask = GL_COLOR_BUFFER_BIT, + .color = FloatVec4(1.0f, 0.25f, 0.5f, 0.75f), + }, textureObject); + EXPECT_TRUE(clearManager.HasPendingClear(key)); + } + + MG_Impl::GLImpl::DeleteTextures(1, &texture); + + ClearAttachmentPayload outPayload{}; + EXPECT_FALSE(clearManager.HasPendingClear(key)); + EXPECT_FALSE(clearManager.GetPendingClear(key, outPayload)); + EXPECT_FALSE(clearManager.HasPendingClear(key)); + + clearManager.Shutdown(); +} diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 91e2ebf0..8510b59f 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -41,11 +41,11 @@ namespace MobileGL::MG_Util::BackendLoader { #endif } - Bool AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs, + void AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs, MG_External::EGL::eglGetProcAddress_PTR procAddress) { if (!procAddress) { MGLOG_E("eglGetProcAddress is nullptr, cannot load GLES functions"); - return false; + return; } #define INIT_GLES_FUNC(name) \ @@ -149,6 +149,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,16 +430,16 @@ namespace MobileGL::MG_Util::BackendLoader { INIT_GLES_FUNC(glMultiDrawElementsIndirectEXT) INIT_GLES_FUNC(glMultiDrawElementsBaseVertexEXT) } - return true; } - Bool AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs) { + void AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs) { static const Vector EGLLibNames = {"libEGL.so"}; void* eglLib = OpenLib(EGLLibNames); if (!eglLib) { MGLOG_E("Failed to open libEGL.so"); - return false; + return; } + #define INIT_EGL_FUNC(name) \ do { \ funcs.name = (MG_External::EGL::name##_PTR)ProcAddress(eglLib, #name); \ @@ -495,7 +497,6 @@ namespace MobileGL::MG_Util::BackendLoader { INIT_EGL_FUNC(eglGetPlatformDisplay) INIT_EGL_FUNC(eglWaitSync) } - return true; } Bool FillInGLESCapabilities(MG_External::GLESCapabilities& caps, const MG_External::GLESFunctionsTable& glesFuncs) { @@ -535,6 +536,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..93b00bb9 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,13 +1019,60 @@ 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 namespace MG_Util::BackendLoader { - Bool AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs, + void AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs, MG_External::EGL::eglGetProcAddress_PTR procAddress); - Bool AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs); + void AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs); Bool FillInGLESCapabilities(MG_External::GLESCapabilities& caps, const MG_External::GLESFunctionsTable& glesFuncs); } // namespace MG_Util::BackendLoader diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp index 68941de2..dd333671 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp @@ -18,6 +18,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) { @@ -104,6 +127,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; caps.MaxShaderStorageBlockSize = static_cast(p.limits.maxStorageBufferRange); const Bool supportsShaderSubgroup = vk.vkGetPhysicalDeviceProperties2 && HasUsableShaderSubgroupSupport(subgroupProps); @@ -137,6 +211,54 @@ 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; caps.MaxShaderStorageBlockSize = static_cast(properties.limits.maxStorageBufferRange); caps.SupportsShaderSubgroup = false; caps.SubgroupSize = 0; diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h index dae1620d..9819ea33 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; SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; Bool SupportsShaderSubgroup = false; Uint32 SubgroupSize = 0; 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/FramebufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp index 60718d75..98b7c379 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/FramebufferEnumConverter.cpp @@ -36,10 +36,12 @@ namespace MobileGL { return FramebufferAttachmentType::Depth; case GL_STENCIL_ATTACHMENT: return FramebufferAttachmentType::Stencil; + case GL_FRONT: case GL_FRONT_LEFT: return FramebufferAttachmentType::FrontLeft; case GL_FRONT_RIGHT: return FramebufferAttachmentType::FrontRight; + case GL_BACK: case GL_BACK_LEFT: return FramebufferAttachmentType::BackLeft; case GL_BACK_RIGHT: @@ -59,4 +61,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 ea5b82cc..45bf157b 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 1fa7ce33..97c126ef 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 6a0f1f28..49c9f3ae 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 7273871d..1759ed2e 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 ddeb797d..89bc24c1 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp @@ -74,6 +74,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, "; @@ -86,7 +90,7 @@ namespace MobileGL { result.pop_back(); result.pop_back(); result += "]"; - return result.empty() ? "[]" : result; + return result; } } // namespace MG_Util } // 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 f83511e0..24258fb9 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 5d66b73a..1f4f9103 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 e6c88619..900773c2 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp @@ -314,6 +314,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 16fa920f..0a94bef0 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: @@ -299,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: 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);