From c83d27a668fdf2a267af392beb5a902353f4d06a Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 12 Oct 2026 07:20:21 -0400 Subject: [PATCH] [Feat] (GL): advertise ARB_cull_distance where the backend has cull distances --- MobileGL/MG_Backend/BackendObject.h | 4 ++++ .../DirectGLES/BackendObject_DirectGLES.cpp | 8 ++++++- .../BackendObject_DirectVulkan.cpp | 8 ++++++- .../BackendLoader/BackendLoaderTest.cpp | 21 +++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 61c89320..9a41c53f 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -316,6 +316,10 @@ namespace MobileGL { struct AdvertisedFeatureSupport { // ES 3.2 or GL_EXT_tessellation_shader; VkPhysicalDeviceFeatures::tessellationShader. Bool TessellationShader = false; + // At least one cull distance: GL_EXT_clip_cull_distance with a non-zero + // GL_MAX_CULL_DISTANCES; VkPhysicalDeviceFeatures::shaderCullDistance with a non-zero + // maxCullDistances. + Bool CullDistance = false; }; // Coarse GPU vendor identity for gating device-specific quirks. Detected from the diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 9f731294..ab913c77 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -842,7 +842,8 @@ namespace MobileGL::MG_Backend::DirectGLES { capabilities.SupportsDrawIndirect, capabilities.SupportsDrawIndirect && capabilities.SupportsBaseInstance, capabilities.SupportsTextureView, capabilities.SupportsTextureCubeMapArray, - AdvertisedFeatureSupport{.TessellationShader = capabilities.SupportsTessellationShader}); + AdvertisedFeatureSupport{.TessellationShader = capabilities.SupportsTessellationShader, + .CullDistance = capabilities.MaxCullDistances > 0}); // Exactly what InitCapabilities forwards into the dynamic parameters: the base // extension and _primitive when the driver has them, _attachment not yet. auto& extensions = MutableRendererInfo().RendererGLInfo.Extensions; @@ -1523,6 +1524,11 @@ namespace MobileGL::MG_Backend::DirectGLES { if (features.TessellationShader) { extensions.push_back(E_GL_ARB_tessellation_shader); } + // gl_CullDistance goes through GL_EXT_clip_cull_distance, whose cull half the loader only + // reports where the driver answers a non-zero GL_MAX_CULL_DISTANCES. + if (features.CullDistance) { + extensions.push_back(E_GL_ARB_cull_distance); + } // Minecraft 26.3 checks this prerequisite before it even considers // GL_ARB_multi_draw_indirect. ES 3.1 supplies both single-draw entry points; the loader // folds the version and pointer checks into SupportsDrawIndirect. diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 2fff5f0e..3cedf0da 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -925,6 +925,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (features.TessellationShader) { extensions.push_back(E_GL_ARB_tessellation_shader); } + // Follows the shaderCullDistance device feature, the same gate GL_MAX_CULL_DISTANCES uses. + if (features.CullDistance) { + extensions.push_back(E_GL_ARB_cull_distance); + } // Vulkan's drawIndirectFirstInstance feature is optional. Direct base-instance calls work // without it, but ARB_base_instance also promises non-zero firstInstance in GPU indirect // commands; the renderer supplies true only when that word is legal and gl_InstanceID can @@ -1106,7 +1110,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { pVulkanRenderer && pVulkanRenderer->IsSamplerAnisotropySupported(), pVulkanRenderer && pVulkanRenderer->IsNonZeroIndirectBaseInstanceSupported(), m_vulkanCaps.SupportsImageCubeArray, - AdvertisedFeatureSupport{.TessellationShader = m_vulkanCaps.SupportsTessellationShader}); + AdvertisedFeatureSupport{.TessellationShader = m_vulkanCaps.SupportsTessellationShader, + .CullDistance = m_vulkanCaps.SupportsShaderCullDistance && + m_vulkanCaps.MaxCullDistances > 0}); // GL_EXT_fragment_shading_rate rides on VK_KHR_fragment_shading_rate's pipeline rate, and // its _primitive companion on the primitive rate; both only when the renderer enabled them. // _attachment is not advertised: no framebuffer carries a rate attachment to this backend. diff --git a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp index e1f13b51..366827af 100644 --- a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp +++ b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp @@ -1281,6 +1281,27 @@ TEST(CoreSurfaceAdvertisement, BatchIsUnconditionalAndTessellationFollowsTheDevi MobileGL::E_GL_ARB_tessellation_shader)); } +// ARB_cull_distance follows the backend's cull-distance capability, never the context version. +TEST(CoreSurfaceAdvertisement, CullDistanceFollowsTheDevice) { + const auto contains = [](const MobileGL::Vector& extensions, + MobileGL::GLExtension wanted) { + return std::find(extensions.begin(), extensions.end(), wanted) != extensions.end(); + }; + EXPECT_FALSE(contains(MobileGL::MG_Backend::DirectGLES::BuildAdvertisedExtensions(false, false, false, false, + false, false), + MobileGL::E_GL_ARB_cull_distance)); + EXPECT_FALSE(contains(MobileGL::MG_Backend::DirectVulkan::BuildAdvertisedExtensions(false, false, false, false, + false), + MobileGL::E_GL_ARB_cull_distance)); + const MobileGL::MG_Backend::AdvertisedFeatureSupport cull{.CullDistance = true}; + EXPECT_TRUE(contains(MobileGL::MG_Backend::DirectGLES::BuildAdvertisedExtensions(false, false, false, false, + false, false, cull), + MobileGL::E_GL_ARB_cull_distance)); + EXPECT_TRUE(contains(MobileGL::MG_Backend::DirectVulkan::BuildAdvertisedExtensions(false, false, false, false, + false, cull), + MobileGL::E_GL_ARB_cull_distance)); +} + // ES 3.2 has tessellation in core; ES 3.1 only through GL_EXT_tessellation_shader. Either way the // patch-size entry point has to have resolved. TEST(TessellationCapabilities, FollowTheVersionOrTheExtension) {