From 4c9ce5612447d605f4f56047e06dbdbb6e60fff5 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 12 Oct 2026 06:45:43 -0400 Subject: [PATCH] [Feat] (GL): advertise the 4.x-core extension batch and ARB_tessellation_shader where the device has tessellation --- MobileGL/MG_Backend/BackendObject.h | 9 +++ .../DirectGLES/BackendObject_DirectGLES.cpp | 29 +++++++- .../DirectGLES/BackendObject_DirectGLES.h | 8 ++- .../BackendObject_DirectVulkan.cpp | 28 +++++++- .../DirectVulkan/BackendObject_DirectVulkan.h | 3 +- .../BackendLoader/BackendLoaderTest.cpp | 72 +++++++++++++++++++ MobileGL/MG_Test/SanityTest.cpp | 51 +++++++++++++ .../MG_Util/BackendLoaders/OpenGL/Loader.cpp | 8 +++ .../MG_Util/BackendLoaders/OpenGL/Loader.h | 3 + .../MG_Util/BackendLoaders/Vulkan/Loader.cpp | 2 + .../MG_Util/BackendLoaders/Vulkan/Loader.h | 3 + 11 files changed, 208 insertions(+), 8 deletions(-) diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 638b4894..61c89320 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -309,6 +309,15 @@ namespace MobileGL { void (*SetSwapInterval)(Int interval); }; + // Device features that decide whether an extension string may be advertised, beyond the + // per-backend positional flags of BuildAdvertisedExtensions. Each is the PROBED driver + // capability (an ES version / extension on DirectGLES, a VkPhysicalDeviceFeatures bit on + // DirectVulkan), never a vendor guess. Defaults describe the no-device case. + struct AdvertisedFeatureSupport { + // ES 3.2 or GL_EXT_tessellation_shader; VkPhysicalDeviceFeatures::tessellationShader. + Bool TessellationShader = false; + }; + // Coarse GPU vendor identity for gating device-specific quirks. Detected from the // Vulkan physical-device vendorID or the GLES GL_VENDOR/GL_RENDERER strings; stays // Unknown when detection is inconclusive, in which case auto-gated quirks stay off. diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index b9452e63..9f731294 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -841,7 +841,8 @@ namespace MobileGL::MG_Backend::DirectGLES { AreTimerQueriesSupported(), capabilities.SupportsTextureFilterAnisotropy, capabilities.SupportsDrawIndirect, capabilities.SupportsDrawIndirect && capabilities.SupportsBaseInstance, - capabilities.SupportsTextureView, capabilities.SupportsTextureCubeMapArray); + capabilities.SupportsTextureView, capabilities.SupportsTextureCubeMapArray, + AdvertisedFeatureSupport{.TessellationShader = capabilities.SupportsTessellationShader}); // 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; @@ -1383,7 +1384,8 @@ namespace MobileGL::MG_Backend::DirectGLES { Vector BuildAdvertisedExtensions(Bool timerQueriesSupported, Bool anisotropicFilteringSupported, Bool drawIndirectSupported, Bool nonZeroIndirectBaseInstanceSupported, - Bool textureViewSupported, Bool cubeMapArraySupported) { + Bool textureViewSupported, Bool cubeMapArraySupported, + const AdvertisedFeatureSupport& features) { Vector extensions = { // The version tokens have to reach the version the backend actually claims: // TargetGLVersion is {4,6,0}, and a list that stopped at OpenGL40 told an @@ -1494,10 +1496,33 @@ namespace MobileGL::MG_Backend::DirectGLES { // glDepthRangeArrayv / glDepthRangeIndexed / glGetFloati_v / glGetDoublei_v, over the // 16 viewports GL_MAX_VIEWPORTS reports and the per-viewport routing emulation. E_GL_ARB_viewport_array, + // Core in the 4.x versions this backend already claims, and named so a context of an + // older requested version reaches the same, already-implemented surface (the conformance + // suite gates its GL 3.x runs of these on the string alone): + // - transform feedback objects, pause/resume and glDrawTransformFeedback (4.0); + E_GL_ARB_transform_feedback2, + // - multiple capture buffers per stream, gl_SkipComponents / gl_NextBuffer, the + // indexed queries and glDrawTransformFeedbackStream (4.0); + E_GL_ARB_transform_feedback3, + // - GL_RGB10_A2UI (3.3), GL_RGB9_E5 (3.0) and the integer colour formats (3.0); + E_GL_ARB_texture_rgb10_a2ui, E_GL_EXT_texture_shared_exponent, E_GL_EXT_texture_integer, + // - the eleven pipeline-statistics query targets (4.6). Accepted by glBeginQuery and + // answered with GL_QUERY_COUNTER_BITS = 0, the spec's own "this counter is not + // instrumented" signal (see IsPipelineStatisticsQueryTarget in GL_Query.cpp); + E_GL_ARB_pipeline_statistics_query, + // - mix() over integer and boolean vectors in GLSL below 4.50. The frontend compiles it + // to OpSelect, which the backend prints back as a core boolean mix. + E_GL_EXT_shader_integer_mix, // Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the // extension explicitly permits. It is also the only thing that // exposes glProgramParameteri before GL 4.1. E_GL_ARB_get_program_binary}; + // Tessellation is core from GL 4.0, but on the host it is ES 3.2 or + // GL_EXT_tessellation_shader, so the string follows the driver. GL_Query also reads it to + // open the two tessellation pipeline-statistics targets. + if (features.TessellationShader) { + extensions.push_back(E_GL_ARB_tessellation_shader); + } // 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/DirectGLES/BackendObject_DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h index 3e618956..25ce807b 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h @@ -114,12 +114,14 @@ namespace MobileGL::MG_Backend::DirectGLES { // The full OpenGL extension list Espryt advertises (glGetString(GL_EXTENSIONS)) // for a device whose timer queries / anisotropic filtering / native indirect draws / - // non-zero indirect baseInstance semantics / EXT-OES texture views are (or are not) usable. - // The MOBILEGL_DISABLE_TIMERQUERY escape hatch is applied inside. + // non-zero indirect baseInstance semantics / EXT-OES texture views are (or are not) usable, + // plus the feature set in `features`. The MOBILEGL_DISABLE_TIMERQUERY escape hatch is applied + // inside. Vector BuildAdvertisedExtensions(Bool timerQueriesSupported, Bool anisotropicFilteringSupported, Bool drawIndirectSupported, Bool nonZeroIndirectBaseInstanceSupported, - Bool textureViewSupported, Bool cubeMapArraySupported); + Bool textureViewSupported, Bool cubeMapArraySupported, + const AdvertisedFeatureSupport& features = {}); // Format: , OpenGL ES . — the exact string an // initialized backend returns from GetBackendAPIVersionString (and that ends up diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index d3c83794..2fff5f0e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -792,7 +792,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { Vector BuildAdvertisedExtensions(Bool shaderSubgroupSupported, Bool timerQueriesSupported, Bool anisotropicFilteringSupported, Bool nonZeroIndirectBaseInstanceSupported, - Bool cubeMapArraySupported) { + Bool cubeMapArraySupported, + const AdvertisedFeatureSupport& features) { Vector extensions = { // The version tokens have to reach the version the backend actually claims: // TargetGLVersion is {4,6,0}, and a list that stopped at OpenGL40 told an @@ -898,10 +899,32 @@ namespace MobileGL::MG_Backend::DirectVulkan { // glDepthRangeArrayv / glDepthRangeIndexed / glGetFloati_v / glGetDoublei_v, over the // 16 viewports GL_MAX_VIEWPORTS reports. E_GL_ARB_viewport_array, + // Core in the 4.x versions this backend already claims, and named so a context of an + // older requested version reaches the same, already-implemented surface (the conformance + // suite gates its GL 3.x runs of these on the string alone): + // - transform feedback objects, pause/resume and glDrawTransformFeedback (4.0); + E_GL_ARB_transform_feedback2, + // - multiple capture buffers per stream, gl_SkipComponents / gl_NextBuffer, the + // indexed queries and glDrawTransformFeedbackStream (4.0); + E_GL_ARB_transform_feedback3, + // - GL_RGB10_A2UI (3.3), GL_RGB9_E5 (3.0) and the integer colour formats (3.0); + E_GL_ARB_texture_rgb10_a2ui, E_GL_EXT_texture_shared_exponent, E_GL_EXT_texture_integer, + // - the eleven pipeline-statistics query targets (4.6). Accepted by glBeginQuery and + // answered with GL_QUERY_COUNTER_BITS = 0, the spec's own "this counter is not + // instrumented" signal (see IsPipelineStatisticsQueryTarget in GL_Query.cpp); + E_GL_ARB_pipeline_statistics_query, + // - mix() over integer and boolean vectors in GLSL below 4.50. The frontend compiles it + // to OpSelect, which SPIR-V consumes directly. + E_GL_EXT_shader_integer_mix, // Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the // extension explicitly permits. It is also the only thing that // exposes glProgramParameteri before GL 4.1. E_GL_ARB_get_program_binary}; + // Follows the tessellationShader device feature; GL_Query also reads the string to open + // the two tessellation pipeline-statistics targets. + if (features.TessellationShader) { + extensions.push_back(E_GL_ARB_tessellation_shader); + } // 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 @@ -1082,7 +1105,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { subgroupSupportAdvertised, pVulkanRenderer && pVulkanRenderer->IsTimerQuerySupported(), pVulkanRenderer && pVulkanRenderer->IsSamplerAnisotropySupported(), pVulkanRenderer && pVulkanRenderer->IsNonZeroIndirectBaseInstanceSupported(), - m_vulkanCaps.SupportsImageCubeArray); + m_vulkanCaps.SupportsImageCubeArray, + AdvertisedFeatureSupport{.TessellationShader = m_vulkanCaps.SupportsTessellationShader}); // 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_Backend/DirectVulkan/BackendObject_DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h index 2b642161..19e48a61 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h @@ -115,7 +115,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { Vector BuildAdvertisedExtensions(Bool shaderSubgroupSupported, Bool timerQueriesSupported, Bool anisotropicFilteringSupported, Bool nonZeroIndirectBaseInstanceSupported, - Bool cubeMapArraySupported); + Bool cubeMapArraySupported, + const AdvertisedFeatureSupport& features = {}); // Format: , Vulkan , Driver — the exact // string an initialized backend returns from GetBackendAPIVersionString (and that diff --git a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp index d859a6e8..e1f13b51 100644 --- a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp +++ b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp @@ -1244,6 +1244,78 @@ TEST(CorePlumbingAdvertisement, IsUnconditionalAndIdenticalOnBothBackends) { } } +// Strings for functionality that is core in the 4.x versions MobileGL claims, named so contexts of +// an older requested version (the conformance suite's GL 3.x runs gate on the string alone) reach +// the same surface. Unconditional on both backends; only the tessellation string follows a probed +// device capability. +TEST(CoreSurfaceAdvertisement, BatchIsUnconditionalAndTessellationFollowsTheDevice) { + const auto contains = [](const MobileGL::Vector& extensions, + MobileGL::GLExtension wanted) { + return std::find(extensions.begin(), extensions.end(), wanted) != extensions.end(); + }; + const MobileGL::GLExtension expected[] = { + MobileGL::E_GL_ARB_transform_feedback2, MobileGL::E_GL_ARB_transform_feedback3, + MobileGL::E_GL_ARB_texture_rgb10_a2ui, MobileGL::E_GL_EXT_texture_shared_exponent, + MobileGL::E_GL_EXT_texture_integer, MobileGL::E_GL_ARB_pipeline_statistics_query, + MobileGL::E_GL_EXT_shader_integer_mix}; + + const auto es = MobileGL::MG_Backend::DirectGLES::BuildAdvertisedExtensions(false, false, false, false, false, + false); + const auto vk = MobileGL::MG_Backend::DirectVulkan::BuildAdvertisedExtensions(false, false, false, false, false); + for (const auto extension : expected) { + EXPECT_TRUE(contains(es, extension)) << "DirectGLES does not advertise extension " << extension; + EXPECT_TRUE(contains(vk, extension)) << "DirectVulkan does not advertise extension " << extension; + } + // Withheld until shaders can actually use the gl_FragCoord layout qualifiers it adds. + EXPECT_FALSE(contains(es, MobileGL::E_GL_ARB_fragment_coord_conventions)); + EXPECT_FALSE(contains(vk, MobileGL::E_GL_ARB_fragment_coord_conventions)); + + EXPECT_FALSE(contains(es, MobileGL::E_GL_ARB_tessellation_shader)); + EXPECT_FALSE(contains(vk, MobileGL::E_GL_ARB_tessellation_shader)); + const MobileGL::MG_Backend::AdvertisedFeatureSupport tessellation{.TessellationShader = true}; + EXPECT_TRUE(contains(MobileGL::MG_Backend::DirectGLES::BuildAdvertisedExtensions(false, false, false, false, + false, false, tessellation), + MobileGL::E_GL_ARB_tessellation_shader)); + EXPECT_TRUE(contains(MobileGL::MG_Backend::DirectVulkan::BuildAdvertisedExtensions(false, false, false, false, + false, tessellation), + MobileGL::E_GL_ARB_tessellation_shader)); +} + +// 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) { + const auto funcs = MakeFakeGLESFunctions(); + auto withPatch = funcs; + withPatch.glPatchParameteri = [](GLenum, GLint) {}; + + ResetFakeDriver(); + g_fake.maxVertexSsboBlocks = 0; + MobileGL::MG_External::GLESCapabilities es31; + ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(es31, withPatch)); + EXPECT_FALSE(es31.SupportsTessellationShader); + + ResetFakeDriver(); + g_fake.maxVertexSsboBlocks = 0; + g_fake.extensions.emplace_back("GL_EXT_tessellation_shader"); + MobileGL::MG_External::GLESCapabilities es31Ext; + ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(es31Ext, withPatch)); + EXPECT_TRUE(es31Ext.SupportsTessellationShader); + + ResetFakeDriver(); + g_fake.maxVertexSsboBlocks = 0; + g_fake.glesMinorVersion = 2; + MobileGL::MG_External::GLESCapabilities es32NoEntryPoint; + ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(es32NoEntryPoint, funcs)); + EXPECT_FALSE(es32NoEntryPoint.SupportsTessellationShader); + + ResetFakeDriver(); + g_fake.maxVertexSsboBlocks = 0; + g_fake.glesMinorVersion = 2; + MobileGL::MG_External::GLESCapabilities es32; + ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(es32, withPatch)); + EXPECT_TRUE(es32.SupportsTessellationShader); +} + // Minecraft 26.3 checks ARB_draw_indirect before it considers the already-advertised // ARB_multi_draw_indirect, then separately requires ARB_base_instance before enabling its terrain // indirect path. Pin both strings and, just as importantly, the non-zero firstInstance gate. diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index d96e4aec..b954000f 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -1571,6 +1571,57 @@ void main() { mgColor = vec4(float(gl_MaxCullDistances)); } MG_State::pGLContext = Move(previousContext); } +// GL_EXT_shader_integer_mix (KHR-GL4x.shader_integer_mix.*): a 3.30 shader that requires the +// extension must see its macro and must be able to mix integer and boolean vectors under a +// boolean selector. Both backends carry the result: SPIR-V lowers the call to OpSelect, which +// ESSL 3.10+ and GLSL 4.50 print back as a core boolean mix. +TEST(ShaderCompilerSanity, ExtShaderIntegerMixIsUsableAt330) { + using namespace MobileGL; + + auto previousContext = MG_State::pGLContext.Take(); + auto previousBackend = Move(MG_Backend::pActiveBackendObject); + MG_State::pGLContext = MakeUnique(); + MG_Backend::pActiveBackendObject = MakeUnique(MG_Backend::DynamicBackendParameters{}); + const auto env = MG_Util::ShaderTranspiler::CaptureCompileEnv(); + + const auto compile = [&env](GLenum stage, const String& source) { + return MG_Util::ShaderTranspiler::ShaderCompiler::CompileShader({ + .shaderType = stage, + .sourceStr = source, + .env = env.get(), + }); + }; + + const String defineProbe = R"(#version 330 +#extension GL_EXT_shader_integer_mix: require +#if !defined GL_EXT_shader_integer_mix +# error GL_EXT_shader_integer_mix is not defined +#elif GL_EXT_shader_integer_mix != 1 +# error GL_EXT_shader_integer_mix is not equal to 1 +#endif +void main(void) { gl_Position = vec4(0); } +)"; + auto define = compile(GL_VERTEX_SHADER, defineProbe); + EXPECT_TRUE(define) << (define ? String() : define.error().log); + + const String mixes = R"(#version 330 +#extension GL_EXT_shader_integer_mix: enable +uniform ivec4 a; uniform ivec4 b; uniform uvec4 c; uniform uvec4 d; uniform bvec4 sel; +flat out ivec4 i; flat out uvec4 u; flat out ivec4 bo; +void main(void) { + i = mix(a, b, sel); + u = mix(c, d, sel); + bo = ivec4(mix(bvec4(a), bvec4(b), sel)); + gl_Position = vec4(0); +} +)"; + auto mixed = compile(GL_VERTEX_SHADER, mixes); + EXPECT_TRUE(mixed) << (mixed ? String() : mixed.error().log); + + MG_Backend::pActiveBackendObject = Move(previousBackend); + MG_State::pGLContext = Move(previousContext); +} + TEST(GetterSanity, ReportsKhrSubgroupDynamicParameters) { using namespace MobileGL; diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 927a6877..58e0dfa4 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -1123,6 +1123,7 @@ namespace MobileGL::MG_Util::BackendLoader { // Combined with the three entry points below; DirectGLES emulates baseInstance when this // comes out false, so a stub pointer counting as support would silently break the draws. Bool hasBaseInstanceExtension = false; + Bool hasTessellationShaderExtension = false; for (GLint i = 0; i < extCount; ++i) { const char* extension = (const char*)glesFuncs.glGetStringi(GL_EXTENSIONS, i); if (extension) { @@ -1180,6 +1181,9 @@ namespace MobileGL::MG_Util::BackendLoader { if (std::strcmp(extension, "GL_EXT_base_instance") == 0) { hasBaseInstanceExtension = true; } + if (std::strcmp(extension, "GL_EXT_tessellation_shader") == 0) { + hasTessellationShaderExtension = true; + } if (std::strcmp(extension, "GL_EXT_disjoint_timer_query") == 0) { caps.SupportsDisjointTimerQuery = true; } @@ -1281,6 +1285,8 @@ namespace MobileGL::MG_Util::BackendLoader { glesFuncs.glDrawElementsIndirect != nullptr; caps.SupportsDrawElementsBaseVertex = (esAtLeast32 || hasDrawElementsBaseVertexExtension) && glesFuncs.glDrawElementsBaseVertex != nullptr; + caps.SupportsTessellationShader = (esAtLeast32 || (esAtLeast31 && hasTessellationShaderExtension)) && + glesFuncs.glPatchParameteri != nullptr; caps.SupportsComputeShader = esAtLeast31 && glesFuncs.glDispatchCompute != nullptr && glesFuncs.glMemoryBarrier != nullptr && glesFuncs.glCreateShader != nullptr && @@ -1308,6 +1314,8 @@ namespace MobileGL::MG_Util::BackendLoader { MGLOG_I(" draw elements base vertex (ES 3.2 core or EXT/OES_draw_elements_base_vertex): %s", caps.SupportsDrawElementsBaseVertex ? "yes" : "no"); MGLOG_I(" compute shaders (ES 3.1 core): %s", caps.SupportsComputeShader ? "yes" : "no"); + MGLOG_I(" tessellation shaders (ES 3.2 core or EXT_tessellation_shader): %s", + caps.SupportsTessellationShader ? "yes" : "no"); MGLOG_I(" base instance (EXT_base_instance; emulated by attribute offsets when absent): %s", caps.SupportsBaseInstance ? "yes" : "no"); MGLOG_I(" clip distances (EXT_clip_cull_distance): %s", caps.SupportsClipDistance ? "yes" : "no"); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index bf105692..dd5b15eb 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1114,6 +1114,9 @@ namespace MobileGL { Bool SupportsTextureBorderClamp = false; // GL_TEXTURE_CUBE_MAP_ARRAY: ES 3.2 core, or EXT/OES_texture_cube_map_array before it. Bool SupportsTextureCubeMapArray = false; + // Tessellation stages: ES 3.2 core, or GL_EXT_tessellation_shader on 3.1, with + // glPatchParameteri resolved either way. + Bool SupportsTessellationShader = false; // GL_EXT_texture_view / GL_OES_texture_view: two ES texture names sharing one // storage, i.e. the only way DirectGLES can answer glTextureView at all. ES never // made this core - not even in 3.2 - so unlike every other capability here there is diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp index 2a7126c2..ad01a739 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp @@ -259,6 +259,7 @@ namespace MobileGL::MG_Util::BackendLoader { supportedFeatures.vertexPipelineStoresAndAtomics == VK_TRUE; caps.SupportsFragmentStoresAndAtomics = supportedFeatures.fragmentStoresAndAtomics == VK_TRUE; caps.SupportsGeometryShader = supportedFeatures.geometryShader == VK_TRUE; + caps.SupportsTessellationShader = supportedFeatures.tessellationShader == VK_TRUE; caps.SupportsShaderClipDistance = supportedFeatures.shaderClipDistance == VK_TRUE; caps.SupportsShaderCullDistance = supportedFeatures.shaderCullDistance == VK_TRUE; caps.MaxShaderStorageBlockSize = static_cast(p.limits.maxStorageBufferRange); @@ -372,6 +373,7 @@ namespace MobileGL::MG_Util::BackendLoader { caps.SupportsVertexPipelineStoresAndAtomics = false; caps.SupportsFragmentStoresAndAtomics = false; caps.SupportsGeometryShader = false; + caps.SupportsTessellationShader = false; caps.SupportsShaderClipDistance = false; caps.SupportsShaderCullDistance = false; caps.MaxShaderStorageBlockSize = static_cast(properties.limits.maxStorageBufferRange); diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h index 2e370cf8..5a3cef61 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h @@ -117,6 +117,9 @@ namespace MobileGL { Bool SupportsVertexPipelineStoresAndAtomics = false; Bool SupportsFragmentStoresAndAtomics = false; Bool SupportsGeometryShader = false; + // VkPhysicalDeviceFeatures::tessellationShader, which VulkanRenderer enables wherever + // the physical device has it. + Bool SupportsTessellationShader = false; // VkPhysicalDeviceFeatures::shaderClipDistance. maxClipDistances is a LIMIT and is // reported whatever the feature says, so the limit alone does not mean a module may // declare ClipDistance - VulkanRenderer enables the feature only where the physical