From aa5c33a42d438c8ee5802844348c009dde38168e Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 10 Jul 2026 22:16:47 -0400 Subject: [PATCH] [Feat] (MG_Backend/DirectVulkan): wire glPolygonMode and glColorMaski into pipeline creation Consume the polygon mode and per-draw-buffer color write masks that the frontend already tracks, with runtime fallback for the device features they require. glPolygonMode: - Add ConvertPolygonModeToVkEnum (GL_FILL/LINE/POINT -> VkPolygonMode). - Thread a polygonMode field through PipelineCreatePayload, fold it into the pipeline cache hash (distinct modes need distinct pipelines), and apply it in PipelineFactory instead of the hardcoded VK_POLYGON_MODE_FILL. - LINE/POINT require the fillModeNonSolid device feature: detect and enable it at device creation, cache m_fillModeNonSolidFeatureEnabled, and fall back to FILL at pipeline-build time when it is absent. glColorMaski: - The per-attachment color-blend loop now reads GetColorMaskIndexed(i) instead of the broadcast GetColorMask(), so each draw buffer gets its own write mask (already covered by the pipeline hash). - Divergent per-attachment masks require independentBlend: cache m_independentBlendFeatureEnabled (was enabled but never recorded) and fall back to draw buffer 0's mask for every attachment when it is absent. The internal depth-mipmap utility pipeline keeps VK_POLYGON_MODE_FILL (not GL-driven). Library builds clean; full SanityTest sweep green (30/30). --- .../DirectVulkan/Renderer/PipelineFactory.cpp | 3 +- .../DirectVulkan/Renderer/PipelineFactory.h | 1 + .../DirectVulkan/Renderer/VulkanRenderer.cpp | 29 ++++++++++++++----- .../DirectVulkan/Renderer/VulkanRenderer.h | 5 ++++ .../MGToVk/RenderStateEnumConverter.cpp | 14 +++++++++ .../MGToVk/RenderStateEnumConverter.h | 4 +++ 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp index 5e213084..f1936577 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp @@ -126,6 +126,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { 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.polygonMode, sizeof(payload.polygonMode))); 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))); @@ -217,7 +218,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { vpci.scissorCount = 1; VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; - raster.polygonMode = VK_POLYGON_MODE_FILL; + raster.polygonMode = payload.polygonMode; raster.cullMode = payload.cullMode; raster.frontFace = payload.frontFace; raster.depthBiasEnable = payload.depthBiasEnable ? VK_TRUE : VK_FALSE; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h index b76925be..867b9078 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.h @@ -29,6 +29,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkSampleCountFlagBits rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; Uint32 subpass = 0; VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL; VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT; VkFrontFace frontFace = VK_FRONT_FACE_CLOCKWISE; Bool depthTestEnable = false; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index db5465c5..2ca37004 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -2964,12 +2964,14 @@ void main() { 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) | - (mask.g() ? VK_COLOR_COMPONENT_G_BIT : 0u) | - (mask.b() ? VK_COLOR_COMPONENT_B_BIT : 0u) | - (mask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u)); + const VkPolygonMode requestedPolygonMode = + MG_Util::ConvertPolygonModeToVkEnum(MG_State::pGLContext->GetPolygonModeFront()); + // VK_POLYGON_MODE_LINE/_POINT require the fillModeNonSolid device feature; fall back to + // VK_POLYGON_MODE_FILL when the device lacks it. + const VkPolygonMode effectivePolygonMode = + (requestedPolygonMode == VK_POLYGON_MODE_FILL || m_fillModeNonSolidFeatureEnabled) + ? requestedPolygonMode + : VK_POLYGON_MODE_FILL; PipelineFactory::PipelineCreatePayload payload { .programHash = programObj.hash, @@ -2980,6 +2982,7 @@ void main() { .rasterizationSamples = renderPassEntry.sampleCount, .subpass = 0, .topology = MG_Util::ConvertPrimitiveModeToVkEnum(mode), + .polygonMode = effectivePolygonMode, .cullMode = cullFaceEnabled ? MG_Util::ConvertCullFaceModeToVkEnum(MG_State::pGLContext->GetCullFaceMode(), invertClockwise) : VK_CULL_MODE_NONE, @@ -3075,7 +3078,16 @@ void main() { MG_State::pGLContext->GetBlendFuncIndexed(i, srcRGB, dstRGB, srcAlpha, dstAlpha); MG_State::pGLContext->GetBlendEquationIndexed(i, colorEquation, alphaEquation); const Bool blendEnabled = MG_State::pGLContext->IsCapabilityEnabledIndexed(CapabilityInput::Blend, i); - VkColorComponentFlags attachmentColorWriteMask = colorWriteMask; + // Per-draw-buffer color write mask (glColorMaski). Divergent per-attachment masks require + // the independentBlend device feature; when it is absent, fall back to draw buffer 0's + // mask for every attachment (matching the non-indexed glColorMask broadcast). + const BoolVec4 bufferMask = + MG_State::pGLContext->GetColorMaskIndexed(m_independentBlendFeatureEnabled ? i : 0); + VkColorComponentFlags attachmentColorWriteMask = static_cast( + (bufferMask.r() ? VK_COLOR_COMPONENT_R_BIT : 0u) | + (bufferMask.g() ? VK_COLOR_COMPONENT_G_BIT : 0u) | + (bufferMask.b() ? VK_COLOR_COMPONENT_B_BIT : 0u) | + (bufferMask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u)); Bool effectiveBlendEnabled = blendEnabled; MG_State::GLState::ITextureObject* colorAttachmentTexture = nullptr; if (!isDefaultDrawFbo && i < drawBuffers.size()) { @@ -6283,6 +6295,9 @@ void main() { VkPhysicalDeviceFeatures deviceFeatures{}; deviceFeatures.geometryShader = supportedDeviceFeatures.geometryShader; deviceFeatures.independentBlend = supportedDeviceFeatures.independentBlend; + m_independentBlendFeatureEnabled = deviceFeatures.independentBlend == VK_TRUE; + deviceFeatures.fillModeNonSolid = supportedDeviceFeatures.fillModeNonSolid; + m_fillModeNonSolidFeatureEnabled = deviceFeatures.fillModeNonSolid == VK_TRUE; deviceFeatures.logicOp = supportedDeviceFeatures.logicOp; deviceFeatures.shaderClipDistance = supportedDeviceFeatures.shaderClipDistance; deviceFeatures.shaderCullDistance = supportedDeviceFeatures.shaderCullDistance; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 643660f8..3dc65b02 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -342,6 +342,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool m_multiDrawIndirectFeatureEnabled = false; Bool m_shaderDrawParametersExtensionEnabled = false; Bool m_shaderDrawParametersFeatureEnabled = false; + // fillModeNonSolid gates VK_POLYGON_MODE_LINE/_POINT (glPolygonMode); independentBlend gates + // per-draw-buffer color write masks (glColorMaski). Both are cached at device creation and + // drive a runtime fallback when the device lacks them. + Bool m_fillModeNonSolidFeatureEnabled = false; + Bool m_independentBlendFeatureEnabled = false; // Cached at device creation from the graphics queue family properties // and device limits; drives timer-query support. Uint32 m_timestampValidBits = 0; diff --git a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp index c973fee8..9063b86e 100644 --- a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp @@ -31,6 +31,20 @@ namespace MobileGL { } } + VkPolygonMode ConvertPolygonModeToVkEnum(GLenum mode) { + switch (mode) { + case GL_FILL: + return VK_POLYGON_MODE_FILL; + case GL_LINE: + return VK_POLYGON_MODE_LINE; + case GL_POINT: + return VK_POLYGON_MODE_POINT; + default: + MGLOG_W("Unrecognized polygon mode"); + return VK_POLYGON_MODE_FILL; + } + } + VkCullModeFlags ConvertCullFaceModeToVkEnum(CullFaceMode v, Bool invertClockwise) { switch (v) { case CullFaceMode::Front: diff --git a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h index 5c1222d7..8d7460b0 100644 --- a/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.h @@ -13,6 +13,10 @@ namespace MobileGL { namespace MG_Util { VkPrimitiveTopology ConvertPrimitiveModeToVkEnum(GLenum mode); + // Maps GL_FILL/GL_LINE/GL_POINT to the matching VkPolygonMode. LINE/POINT require the + // fillModeNonSolid device feature; the caller is responsible for falling back to FILL when + // that feature is unavailable. + VkPolygonMode ConvertPolygonModeToVkEnum(GLenum mode); VkCullModeFlags ConvertCullFaceModeToVkEnum(CullFaceMode value, Bool invertClockwise = false); VkLogicOp ConvertLogicOperationToVkEnum(LogicOperation value); VkCompareOp ConvertDepthTestFuncToVkEnum(DepthTestFunc value);