diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 754d7846..638b4894 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -411,6 +411,8 @@ namespace MobileGL { Int MaxImageUnits = 8; Int MaxCombinedImageUniforms = 8; Int MaxVertexImageUniforms = 0; + Int MaxTessControlImageUniforms = 0; + Int MaxTessEvaluationImageUniforms = 0; Int MaxGeometryImageUniforms = 0; Int MaxFragmentImageUniforms = 8; Int MaxComputeImageUniforms = 8; diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 3438de0d..b9452e63 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -1820,6 +1820,10 @@ namespace MobileGL::MG_Backend::DirectGLES { m_dynamicParameters.MaxCombinedImageUniforms}); }; m_dynamicParameters.MaxVertexImageUniforms = clampStageImageUniforms(m_GLESCapabilities.MaxVertexImageUniforms); + m_dynamicParameters.MaxTessControlImageUniforms = + clampStageImageUniforms(m_GLESCapabilities.MaxTessControlImageUniforms); + m_dynamicParameters.MaxTessEvaluationImageUniforms = + clampStageImageUniforms(m_GLESCapabilities.MaxTessEvaluationImageUniforms); m_dynamicParameters.MaxGeometryImageUniforms = clampStageImageUniforms(m_GLESCapabilities.MaxGeometryImageUniforms); m_dynamicParameters.MaxFragmentImageUniforms = diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 513aeb81..d4b8a792 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -11188,6 +11188,8 @@ namespace MobileGL::MG_Backend::DirectGLES { [[maybe_unused]] Int AdvertisedStageImageUniformLimit(ShaderStage stage) { switch (stage) { case ShaderStage::Vertex: return g_GLESCapabilities.MaxVertexImageUniforms; + case ShaderStage::TessControl: return g_GLESCapabilities.MaxTessControlImageUniforms; + case ShaderStage::TessEval: return g_GLESCapabilities.MaxTessEvaluationImageUniforms; case ShaderStage::Geometry: return g_GLESCapabilities.MaxGeometryImageUniforms; case ShaderStage::Fragment: return g_GLESCapabilities.MaxFragmentImageUniforms; case ShaderStage::Compute: return g_GLESCapabilities.MaxComputeImageUniforms; diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 86d86bbb..d3c83794 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -1281,6 +1281,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { // device reports it, so these are the exact limits the logical device can compile and run. m_dynamicParameters.MaxVertexImageUniforms = m_vulkanCaps.SupportsVertexPipelineStoresAndAtomics ? maxPerStageImageUniforms : 0; + // The tessellation stages are vertex-pipeline stages too, so the same feature gates them. + m_dynamicParameters.MaxTessControlImageUniforms = m_dynamicParameters.MaxVertexImageUniforms; + m_dynamicParameters.MaxTessEvaluationImageUniforms = m_dynamicParameters.MaxVertexImageUniforms; m_dynamicParameters.MaxGeometryImageUniforms = m_vulkanCaps.SupportsVertexPipelineStoresAndAtomics && m_vulkanCaps.SupportsGeometryShader ? maxPerStageImageUniforms diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index 4fd30392..be14b57e 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -2195,10 +2195,14 @@ namespace MobileGL::MG_Impl::GLImpl { *params = kFrontendMaxTessEvaluationAtomicCounterBuffers; return; case GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS: - *params = 0; + *params = MG_Backend::pActiveBackendObject + ? MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxTessControlImageUniforms + : MG_Backend::DynamicBackendParameters{}.MaxTessControlImageUniforms; return; case GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS: - *params = 0; + *params = MG_Backend::pActiveBackendObject + ? MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxTessEvaluationImageUniforms + : MG_Backend::DynamicBackendParameters{}.MaxTessEvaluationImageUniforms; return; case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS: *params = StageStorageBlockCount(&MG_Backend::DynamicBackendParameters::MaxTessControlShaderStorageBlocks); diff --git a/MobileGL/MG_Pipe/PipeFields.def b/MobileGL/MG_Pipe/PipeFields.def index f25db0b4..b5d74103 100644 --- a/MobileGL/MG_Pipe/PipeFields.def +++ b/MobileGL/MG_Pipe/PipeFields.def @@ -379,6 +379,7 @@ F(MaxComputeWorkGroupCount) F(MaxComputeWorkGroupSize) F(MaxShaderStorageBufferBindings) \ F(MaxTextureBufferSize) F(TextureBufferOffsetAlignment) F(MaxUniformBufferBindings) \ F(MaxUniformBlockSize) F(MaxImageUnits) F(MaxCombinedImageUniforms) F(MaxVertexImageUniforms) \ + F(MaxTessControlImageUniforms) F(MaxTessEvaluationImageUniforms) \ F(MaxGeometryImageUniforms) F(MaxFragmentImageUniforms) F(MaxComputeImageUniforms) \ F(MaxDrawBuffers) F(MaxColorAttachments) F(MaxClipDistances) F(MaxCullDistances) \ F(MaxCombinedClipAndCullDistances) F(MaxViewports) F(LayerProvokingVertex) \ diff --git a/MobileGL/MG_Pipe/generated/PipeWireLayout.inc b/MobileGL/MG_Pipe/generated/PipeWireLayout.inc index e9fa9628..28935029 100644 --- a/MobileGL/MG_Pipe/generated/PipeWireLayout.inc +++ b/MobileGL/MG_Pipe/generated/PipeWireLayout.inc @@ -632,6 +632,8 @@ inline constexpr WireLayoutMember kMGPipeWireLayoutMembers[] = { {"DynamicBackendParameters.MaxImageUnits", offsetof(DynamicBackendParameters, MaxImageUnits), sizeof(((DynamicBackendParameters*)nullptr)->MaxImageUnits)}, {"DynamicBackendParameters.MaxCombinedImageUniforms", offsetof(DynamicBackendParameters, MaxCombinedImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxCombinedImageUniforms)}, {"DynamicBackendParameters.MaxVertexImageUniforms", offsetof(DynamicBackendParameters, MaxVertexImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxVertexImageUniforms)}, + {"DynamicBackendParameters.MaxTessControlImageUniforms", offsetof(DynamicBackendParameters, MaxTessControlImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxTessControlImageUniforms)}, + {"DynamicBackendParameters.MaxTessEvaluationImageUniforms", offsetof(DynamicBackendParameters, MaxTessEvaluationImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxTessEvaluationImageUniforms)}, {"DynamicBackendParameters.MaxGeometryImageUniforms", offsetof(DynamicBackendParameters, MaxGeometryImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxGeometryImageUniforms)}, {"DynamicBackendParameters.MaxFragmentImageUniforms", offsetof(DynamicBackendParameters, MaxFragmentImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxFragmentImageUniforms)}, {"DynamicBackendParameters.MaxComputeImageUniforms", offsetof(DynamicBackendParameters, MaxComputeImageUniforms), sizeof(((DynamicBackendParameters*)nullptr)->MaxComputeImageUniforms)}, diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.cpp index 826adda6..23218c93 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramLinkTask.cpp @@ -255,9 +255,9 @@ namespace { // its linker never counts uniforms against them - so a program declaring one image uniform // more than the limit linked cleanly and then rendered nothing. // - // The limits are the ones glGetIntegerv answers (MG_Impl/GLImpl/Getter/GL_Getter.cpp), the - // hardcoded tessellation zeros included: a program may not exceed a limit the implementation - // advertises, whatever the driver underneath would have taken. + // The limits are the ones glGetIntegerv answers (MG_Impl/GLImpl/Getter/GL_Getter.cpp): a + // program may not exceed a limit the implementation advertises, whatever the driver + // underneath would have taken. // // Counts the APPLICATION's image uniforms. The DirectGLES read/write split emits a second // declaration for an image a stage both reads and writes (MG_Backend/DirectGLES/Utils.h), but @@ -279,8 +279,8 @@ namespace { "GL_MAX_FRAGMENT_IMAGE_UNIFORMS", "GL_MAX_COMPUTE_IMAGE_UNIFORMS"}; constexpr SizeT kStageCount = sizeof(kStages) / sizeof(kStages[0]); const Int limits[kStageCount] = {env.params.MaxVertexImageUniforms, - 0, - 0, + env.params.MaxTessControlImageUniforms, + env.params.MaxTessEvaluationImageUniforms, env.params.MaxGeometryImageUniforms, env.params.MaxFragmentImageUniforms, env.params.MaxComputeImageUniforms}; diff --git a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp index 08d85e2e..d859a6e8 100644 --- a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp +++ b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp @@ -46,6 +46,8 @@ namespace { GLint glesMinorVersion = 1; GLint maxVertexImageUniforms = 2; GLint maxGeometryImageUniforms = 3; + GLint maxTessControlImageUniforms = 6; + GLint maxTessEvaluationImageUniforms = 7; GLint maxFragmentImageUniforms = 4; GLint maxComputeImageUniforms = 5; bool maxGeometryImageUniformsQueried = false; @@ -194,6 +196,14 @@ namespace { g_fake.maxGeometryImageUniformsQueried = true; *data = g_fake.maxGeometryImageUniforms; break; + case GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS: + g_fake.maxGeometryImageUniformsQueried = true; + *data = g_fake.maxTessControlImageUniforms; + break; + case GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS: + g_fake.maxGeometryImageUniformsQueried = true; + *data = g_fake.maxTessEvaluationImageUniforms; + break; case GL_MAX_FRAGMENT_IMAGE_UNIFORMS: *data = g_fake.maxFragmentImageUniforms; break; @@ -689,6 +699,8 @@ TEST(ImageUniformCapabilities, QueriesRealPerStageLimitsAndConservativelyGatesGe ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(es31Caps, funcs)); EXPECT_EQ(es31Caps.MaxVertexImageUniforms, g_fake.maxVertexImageUniforms); EXPECT_EQ(es31Caps.MaxGeometryImageUniforms, 0); + EXPECT_EQ(es31Caps.MaxTessControlImageUniforms, 0); + EXPECT_EQ(es31Caps.MaxTessEvaluationImageUniforms, 0); EXPECT_EQ(es31Caps.MaxFragmentImageUniforms, g_fake.maxFragmentImageUniforms); EXPECT_EQ(es31Caps.MaxComputeImageUniforms, g_fake.maxComputeImageUniforms); EXPECT_FALSE(g_fake.maxGeometryImageUniformsQueried); @@ -700,6 +712,8 @@ TEST(ImageUniformCapabilities, QueriesRealPerStageLimitsAndConservativelyGatesGe ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(es32Caps, funcs)); EXPECT_EQ(es32Caps.MaxVertexImageUniforms, g_fake.maxVertexImageUniforms); EXPECT_EQ(es32Caps.MaxGeometryImageUniforms, g_fake.maxGeometryImageUniforms); + EXPECT_EQ(es32Caps.MaxTessControlImageUniforms, g_fake.maxTessControlImageUniforms); + EXPECT_EQ(es32Caps.MaxTessEvaluationImageUniforms, g_fake.maxTessEvaluationImageUniforms); EXPECT_EQ(es32Caps.MaxFragmentImageUniforms, g_fake.maxFragmentImageUniforms); EXPECT_EQ(es32Caps.MaxComputeImageUniforms, g_fake.maxComputeImageUniforms); EXPECT_TRUE(g_fake.maxGeometryImageUniformsQueried); diff --git a/MobileGL/MG_Test/Program/ProgramInterfaceTest.cpp b/MobileGL/MG_Test/Program/ProgramInterfaceTest.cpp index 3ac4306b..bff01d63 100644 --- a/MobileGL/MG_Test/Program/ProgramInterfaceTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramInterfaceTest.cpp @@ -951,6 +951,71 @@ void main() { } } + // The same rule for the tessellation stages, whose limits come from the backend like the + // others (they used to be hard-coded zeros). Sized off the advertised limit for the same + // reason as above: one image over it must fail and name the limit, and exactly at it must + // link whenever the limit is non-zero. + TEST_F(ProgramInterfaceTest, TessellationImageUniformsAreCheckedAgainstTheirOwnLimit) { + GLint maxTesImages = -1; + GetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &maxTesImages); + ASSERT_EQ(TakeError(), GL_NO_ERROR); + ASSERT_GE(maxTesImages, 0); + + const auto linkWithTesImages = [](GLint count) { + const std::string n = std::to_string(count); + const std::string tcs = R"(#version 430 +layout(vertices = 3) out; +void main() { + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + gl_TessLevelOuter[0] = 1.0; gl_TessLevelOuter[1] = 1.0; gl_TessLevelOuter[2] = 1.0; + gl_TessLevelInner[0] = 1.0; +} +)"; + const std::string tes = std::string(R"(#version 430 +layout(triangles) in; +layout(r32i) uniform iimage2D u_tesImage[)") + n + R"(]; +void main() { + int value = 1; + for (int i = 0; i < )" + n + R"(; ++i) { + value = imageAtomicAdd(u_tesImage[i], ivec2(0), value); + } + gl_Position = gl_in[0].gl_Position * float(value); +} +)"; + const GLuint program = CreateProgram(); + const auto attach = [program](GLenum stage, const char* source) { + const GLuint sh = CreateShader(stage); + ShaderSource(sh, 1, &source, nullptr); + CompileShader(sh); + GLint compiled = 0; + GetShaderiv(sh, GL_COMPILE_STATUS, &compiled); + EXPECT_EQ(compiled, GL_TRUE) << "stage " << stage << " must compile"; + AttachShader(program, sh); + }; + attach(GL_VERTEX_SHADER, kSimpleVs); + attach(GL_TESS_CONTROL_SHADER, tcs.c_str()); + attach(GL_TESS_EVALUATION_SHADER, tes.c_str()); + attach(GL_FRAGMENT_SHADER, kSimpleFs); + LinkProgram(program); + return program; + }; + + const GLuint over = linkWithTesImages(maxTesImages + 1); + GLint status = -1; + GetProgramiv(over, GL_LINK_STATUS, &status); + EXPECT_EQ(status, GL_FALSE); + char log[4096] = ""; + GetProgramInfoLog(over, sizeof(log), nullptr, log); + EXPECT_NE(std::string(log).find("GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS"), std::string::npos) + << "info log was: " << log; + ClearErrors(); + + if (maxTesImages > 0) { + ExpectLinked(linkWithTesImages(maxTesImages)); + ClearErrors(); + } + } + // Uniform blocks are counted the same way, and the synthesized block that carries the // default-block uniforms is not one of them. TEST_F(ProgramInterfaceTest, UniformBlocksOverAStageLimitFailToLink) { diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index 2d806cab..d96e4aec 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -664,6 +664,8 @@ TEST(DirectVulkanSanity, GatesPerStageImageUniformLimitsOnPhysicalDeviceFeatures const auto& withoutGeometry = backend.GetDynamicParameters(); EXPECT_EQ(withoutGeometry.MaxVertexImageUniforms, 10); + EXPECT_EQ(withoutGeometry.MaxTessControlImageUniforms, 10); + EXPECT_EQ(withoutGeometry.MaxTessEvaluationImageUniforms, 10); EXPECT_EQ(withoutGeometry.MaxGeometryImageUniforms, 0); EXPECT_EQ(withoutGeometry.MaxFragmentImageUniforms, 10); EXPECT_EQ(withoutGeometry.MaxComputeImageUniforms, 9); @@ -676,6 +678,8 @@ TEST(DirectVulkanSanity, GatesPerStageImageUniformLimitsOnPhysicalDeviceFeatures caps.SupportsFragmentStoresAndAtomics = false; backend.ApplyVulkanCapabilitiesForTesting(caps); EXPECT_EQ(backend.GetDynamicParameters().MaxVertexImageUniforms, 0); + EXPECT_EQ(backend.GetDynamicParameters().MaxTessControlImageUniforms, 0); + EXPECT_EQ(backend.GetDynamicParameters().MaxTessEvaluationImageUniforms, 0); EXPECT_EQ(backend.GetDynamicParameters().MaxGeometryImageUniforms, 0); EXPECT_EQ(backend.GetDynamicParameters().MaxFragmentImageUniforms, 0); EXPECT_EQ(backend.GetDynamicParameters().MaxComputeImageUniforms, 9); @@ -689,6 +693,8 @@ TEST(DirectGLESSanity, PreservesHostPerStageImageUniformLimits) { caps.MaxImageUnits = 8; caps.MaxCombinedImageUniforms = 16; caps.MaxVertexImageUniforms = 2; + caps.MaxTessControlImageUniforms = 6; + caps.MaxTessEvaluationImageUniforms = 7; caps.MaxGeometryImageUniforms = 3; caps.MaxFragmentImageUniforms = 4; caps.MaxComputeImageUniforms = 5; @@ -696,6 +702,8 @@ TEST(DirectGLESSanity, PreservesHostPerStageImageUniformLimits) { const auto& params = backend.GetDynamicParameters(); EXPECT_EQ(params.MaxVertexImageUniforms, 2); + EXPECT_EQ(params.MaxTessControlImageUniforms, 6); + EXPECT_EQ(params.MaxTessEvaluationImageUniforms, 7); EXPECT_EQ(params.MaxGeometryImageUniforms, 3); EXPECT_EQ(params.MaxFragmentImageUniforms, 4); EXPECT_EQ(params.MaxComputeImageUniforms, 5); @@ -1095,6 +1103,8 @@ TEST(GetterSanity, PerStageImageUniformQueriesMatchShaderCompilerLimits) { params.MaxImageUnits = 8; params.MaxCombinedImageUniforms = 8; params.MaxVertexImageUniforms = 1; + params.MaxTessControlImageUniforms = 5; + params.MaxTessEvaluationImageUniforms = 6; params.MaxGeometryImageUniforms = 2; params.MaxFragmentImageUniforms = 3; params.MaxComputeImageUniforms = 4; @@ -1103,6 +1113,10 @@ TEST(GetterSanity, PerStageImageUniformQueriesMatchShaderCompilerLimits) { GLint reported = -1; MG_Impl::GLImpl::GetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &reported); EXPECT_EQ(reported, 1); + MG_Impl::GLImpl::GetIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &reported); + EXPECT_EQ(reported, 5); + MG_Impl::GLImpl::GetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &reported); + EXPECT_EQ(reported, 6); MG_Impl::GLImpl::GetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &reported); EXPECT_EQ(reported, 2); MG_Impl::GLImpl::GetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &reported); @@ -1308,8 +1322,17 @@ TEST(GetterSanity, EveryLimitWithABuiltinAgreesWithItsQuery) { auto previousContext = MG_State::pGLContext.Take(); auto previousBackend = Move(MG_Backend::pActiveBackendObject); MG_State::pGLContext = MakeUnique(); - MG_Backend::pActiveBackendObject = - MakeUnique(MG_Backend::DynamicBackendParameters{}, BackendType::DirectGLES); + // Distinct non-zero image-uniform limits, so the per-stage pairs below cannot agree by both + // falling back to the same default. + MG_Backend::DynamicBackendParameters params; + params.MaxVertexImageUniforms = 1; + params.MaxTessControlImageUniforms = 2; + params.MaxTessEvaluationImageUniforms = 3; + params.MaxGeometryImageUniforms = 4; + params.MaxFragmentImageUniforms = 5; + params.MaxComputeImageUniforms = 6; + params.MaxCombinedImageUniforms = 7; + MG_Backend::pActiveBackendObject = MakeUnique(params, BackendType::DirectGLES); struct LimitPair { GLenum pname; @@ -1330,6 +1353,13 @@ TEST(GetterSanity, EveryLimitWithABuiltinAgreesWithItsQuery) { {GL_MAX_FRAGMENT_INPUT_COMPONENTS, "gl_MaxFragmentInputComponents"}, {GL_MAX_DRAW_BUFFERS, "gl_MaxDrawBuffers"}, {GL_MAX_IMAGE_UNITS, "gl_MaxImageUnits"}, + {GL_MAX_VERTEX_IMAGE_UNIFORMS, "gl_MaxVertexImageUniforms"}, + {GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, "gl_MaxTessControlImageUniforms"}, + {GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, "gl_MaxTessEvaluationImageUniforms"}, + {GL_MAX_GEOMETRY_IMAGE_UNIFORMS, "gl_MaxGeometryImageUniforms"}, + {GL_MAX_FRAGMENT_IMAGE_UNIFORMS, "gl_MaxFragmentImageUniforms"}, + {GL_MAX_COMPUTE_IMAGE_UNIFORMS, "gl_MaxComputeImageUniforms"}, + {GL_MAX_COMBINED_IMAGE_UNIFORMS, "gl_MaxCombinedImageUniforms"}, // The SAME token (0x8F39) under two spellings, and the two glslang fields behind them // must therefore carry the same value. {GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS, "gl_MaxCombinedImageUnitsAndFragmentOutputs"}, diff --git a/MobileGL/MG_Test/ShaderTranspiler/TranslationCacheTest.cpp b/MobileGL/MG_Test/ShaderTranspiler/TranslationCacheTest.cpp index e0859101..26707ae6 100644 --- a/MobileGL/MG_Test/ShaderTranspiler/TranslationCacheTest.cpp +++ b/MobileGL/MG_Test/ShaderTranspiler/TranslationCacheTest.cpp @@ -720,6 +720,9 @@ TEST_F(TranslationCacheTest, TheFrontendFingerprintMovesWithEveryFrontendLimit) {"params.MaxImageUnits", [](CompileEnv& e) { e.params.MaxImageUnits += 1; }}, {"params.MaxDrawBuffers", [](CompileEnv& e) { e.params.MaxDrawBuffers += 1; }}, {"params.MaxVertexImageUniforms", [](CompileEnv& e) { e.params.MaxVertexImageUniforms += 1; }}, + {"params.MaxTessControlImageUniforms", [](CompileEnv& e) { e.params.MaxTessControlImageUniforms += 1; }}, + {"params.MaxTessEvaluationImageUniforms", + [](CompileEnv& e) { e.params.MaxTessEvaluationImageUniforms += 1; }}, {"params.MaxGeometryImageUniforms", [](CompileEnv& e) { e.params.MaxGeometryImageUniforms += 1; }}, {"params.MaxFragmentImageUniforms", [](CompileEnv& e) { e.params.MaxFragmentImageUniforms += 1; }}, {"params.MaxComputeImageUniforms", [](CompileEnv& e) { e.params.MaxComputeImageUniforms += 1; }}, diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 89b6d382..927a6877 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -1398,6 +1398,8 @@ namespace MobileGL::MG_Util::BackendLoader { GLint maxImageUnits = 8; GLint maxCombinedImageUniforms = 8; GLint maxVertexImageUniforms = 0; + GLint maxTessControlImageUniforms = 0; + GLint maxTessEvaluationImageUniforms = 0; GLint maxGeometryImageUniforms = 0; GLint maxFragmentImageUniforms = 8; GLint maxComputeImageUniforms = 8; @@ -1547,12 +1549,14 @@ namespace MobileGL::MG_Util::BackendLoader { glesFuncs.glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &maxVertexImageUniforms); glesFuncs.glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &maxFragmentImageUniforms); glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms); - // Geometry shaders and their image-uniform query are core only in ES 3.2. DirectGLES - // emits ESSL 3.10 on an ES 3.1 context, so reporting zero there is both legal and an - // accurate description of what the backend compiler can consume. + // Geometry and tessellation shaders and their image-uniform queries are core only in + // ES 3.2. DirectGLES emits ESSL 3.10 on an ES 3.1 context, so reporting zero there is + // both legal and an accurate description of what the backend compiler can consume. if (caps.GLESVersion.Major > 3 || (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2)) { glesFuncs.glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &maxGeometryImageUniforms); + glesFuncs.glGetIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &maxTessControlImageUniforms); + glesFuncs.glGetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, &maxTessEvaluationImageUniforms); } // Closes the bracket opened before the run: every local above now holds either the // driver's answer or a floor, and nothing this function asked for is left in the error @@ -1864,6 +1868,8 @@ namespace MobileGL::MG_Util::BackendLoader { caps.MaxImageUnits = maxImageUnits; caps.MaxCombinedImageUniforms = maxCombinedImageUniforms; caps.MaxVertexImageUniforms = maxVertexImageUniforms; + caps.MaxTessControlImageUniforms = maxTessControlImageUniforms; + caps.MaxTessEvaluationImageUniforms = maxTessEvaluationImageUniforms; caps.MaxGeometryImageUniforms = maxGeometryImageUniforms; caps.MaxFragmentImageUniforms = maxFragmentImageUniforms; caps.MaxComputeImageUniforms = maxComputeImageUniforms; @@ -1962,6 +1968,8 @@ namespace MobileGL::MG_Util::BackendLoader { MGLOG_I(" GL_MAX_IMAGE_UNITS: %d", caps.MaxImageUnits); MGLOG_I(" GL_MAX_COMBINED_IMAGE_UNIFORMS: %d", caps.MaxCombinedImageUniforms); MGLOG_I(" GL_MAX_VERTEX_IMAGE_UNIFORMS: %d", caps.MaxVertexImageUniforms); + MGLOG_I(" GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS: %d", caps.MaxTessControlImageUniforms); + MGLOG_I(" GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS: %d", caps.MaxTessEvaluationImageUniforms); MGLOG_I(" GL_MAX_GEOMETRY_IMAGE_UNIFORMS: %d", caps.MaxGeometryImageUniforms); MGLOG_I(" GL_MAX_FRAGMENT_IMAGE_UNIFORMS: %d", caps.MaxFragmentImageUniforms); MGLOG_I(" GL_MAX_COMPUTE_IMAGE_UNIFORMS: %d", caps.MaxComputeImageUniforms); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index 3e151ac4..bf105692 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1364,6 +1364,8 @@ namespace MobileGL { Int MaxImageUnits = 8; Int MaxCombinedImageUniforms = 8; Int MaxVertexImageUniforms = 0; + Int MaxTessControlImageUniforms = 0; + Int MaxTessEvaluationImageUniforms = 0; Int MaxGeometryImageUniforms = 0; Int MaxFragmentImageUniforms = 8; Int MaxComputeImageUniforms = 8; diff --git a/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp b/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp index 80485f01..dbb417a2 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp @@ -61,6 +61,8 @@ namespace MobileGL::MG_Util::ShaderTranspiler { HashValue(state, env.params.MaxImageUnits); HashValue(state, env.params.MaxDrawBuffers); HashValue(state, env.params.MaxVertexImageUniforms); + HashValue(state, env.params.MaxTessControlImageUniforms); + HashValue(state, env.params.MaxTessEvaluationImageUniforms); HashValue(state, env.params.MaxGeometryImageUniforms); HashValue(state, env.params.MaxFragmentImageUniforms); HashValue(state, env.params.MaxComputeImageUniforms); diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 982cf7d1..1b40996d 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -212,6 +212,8 @@ namespace MobileGL { Resources.maxCombinedShaderOutputResources = Resources.maxCombinedImageUnitsAndFragmentOutputs; Resources.maxVertexImageUniforms = dynamicParameters.MaxVertexImageUniforms; + Resources.maxTessControlImageUniforms = dynamicParameters.MaxTessControlImageUniforms; + Resources.maxTessEvaluationImageUniforms = dynamicParameters.MaxTessEvaluationImageUniforms; Resources.maxGeometryImageUniforms = dynamicParameters.MaxGeometryImageUniforms; Resources.maxFragmentImageUniforms = dynamicParameters.MaxFragmentImageUniforms; Resources.maxComputeImageUniforms = dynamicParameters.MaxComputeImageUniforms;