diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index a81d8f7a..ffa5aea2 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -977,10 +977,22 @@ namespace MobileGL::MG_Backend::DirectGLES { 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; + // Clamp the advertised sampler limits the same way the DirectVulkan backend does: per-stage + // GL_MAX_TEXTURE_IMAGE_UNITS must never exceed host-side fixed arrays sized off it (e.g. + // Minecraft's 128-entry Blaze3D GlStateManager.TEXTURES[], iterated by Iris), and the combined + // limit must stay within our texture-unit state array capacity. + m_dynamicParameters.MaxTextureImageUnits = + std::min(m_GLESCapabilities.MaxTextureImageUnits, + static_cast(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS)); + m_dynamicParameters.MaxVertexTextureImageUnits = + std::min(m_GLESCapabilities.MaxVertexTextureImageUnits, + static_cast(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS)); + m_dynamicParameters.MaxComputeTextureImageUnits = + std::min(m_GLESCapabilities.MaxComputeTextureImageUnits, + static_cast(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS)); + m_dynamicParameters.MaxCombinedTextureImageUnits = + std::min(m_GLESCapabilities.MaxCombinedTextureImageUnits, + static_cast(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS)); // Never advertise more attributes than the state layer can store: the current-value array and // the Uint32 attribute masks the draw path passes around are both bounded by MAX_VERTEX_ATTRIBS. m_dynamicParameters.MaxVertexAttribs = diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 3585eb6b..772c2a31 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -718,11 +718,21 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_dynamicParameters.MaxSampleMaskWords = m_vulkanCaps.MaxSampleMaskWords; const Int maxSupportedTextureUnits = static_cast(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS); - m_dynamicParameters.MaxTextureImageUnits = std::min(m_vulkanCaps.MaxTextureImageUnits, maxSupportedTextureUnits); + // GL_MAX_TEXTURE_IMAGE_UNITS is a *per-stage* sampler limit. Adreno/Qualcomm report a huge + // maxPerStageDescriptorSampledImages (descriptor-indexing scale), so clamping it only to our + // combined array capacity (192) still advertises 192 per stage. Host code treats this value as + // an array bound: Minecraft's Blaze3D GlStateManager.TEXTURES[] holds 128 entries and Iris + // iterates [0, GL_MAX_TEXTURE_IMAGE_UNITS) over it (CompositeRenderer.renderAll), so any value + // > 128 throws ArrayIndexOutOfBoundsException. Match desktop drivers (32) for the per-stage + // limits while keeping the combined limit at our texture-unit array capacity. + constexpr Int maxPerStageTextureUnits = + static_cast(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS); + m_dynamicParameters.MaxTextureImageUnits = + std::min(m_vulkanCaps.MaxTextureImageUnits, maxPerStageTextureUnits); m_dynamicParameters.MaxVertexTextureImageUnits = - std::min(m_vulkanCaps.MaxVertexTextureImageUnits, maxSupportedTextureUnits); + std::min(m_vulkanCaps.MaxVertexTextureImageUnits, maxPerStageTextureUnits); m_dynamicParameters.MaxComputeTextureImageUnits = - std::min(m_vulkanCaps.MaxComputeTextureImageUnits, maxSupportedTextureUnits); + std::min(m_vulkanCaps.MaxComputeTextureImageUnits, maxPerStageTextureUnits); m_dynamicParameters.MaxCombinedTextureImageUnits = std::min(m_vulkanCaps.MaxCombinedTextureImageUnits, maxSupportedTextureUnits); // Never advertise more attributes than the state layer can store: the current-value array and diff --git a/MobileGL/MG_State/GLState/TextureState/TextureState.h b/MobileGL/MG_State/GLState/TextureState/TextureState.h index 29285eff..b9ef9b68 100644 --- a/MobileGL/MG_State/GLState/TextureState/TextureState.h +++ b/MobileGL/MG_State/GLState/TextureState/TextureState.h @@ -37,7 +37,13 @@ namespace MobileGL::MG_State::GLState { class TextureState { public: + // Capacity of the combined texture-unit state arrays (indexed by glActiveTexture unit). static constexpr int MAX_TEXTURE_IMAGE_UNITS = 192; + // Per-stage sampler limit advertised through GL_MAX_TEXTURE_IMAGE_UNITS / + // GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS. Held at the desktop-driver value (32) so it never exceeds + // host-side fixed arrays sized off this query -- e.g. Minecraft's 128-entry Blaze3D + // GlStateManager.TEXTURES[], which Iris iterates over in CompositeRenderer.renderAll. + static constexpr int MAX_PER_STAGE_TEXTURE_IMAGE_UNITS = 32; TextureState(); void GenerateNames(Uint number, Vector& textures); diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index f9577142..f5196696 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -311,9 +311,13 @@ TEST(DirectVulkanSanity, ClampsAdvertisedTextureAndDrawBufferLimitsToFrontendSta backend.ApplyVulkanCapabilitiesForTesting(highCaps); const auto& highParams = backend.GetDynamicParameters(); - EXPECT_EQ(highParams.MaxTextureImageUnits, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS); - EXPECT_EQ(highParams.MaxVertexTextureImageUnits, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS); - EXPECT_EQ(highParams.MaxComputeTextureImageUnits, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS); + // Per-stage sampler limits clamp to the desktop-conventional per-stage cap (kept well under + // Blaze3D's 128-entry TEXTURES[] so Iris' unbind loop cannot index out of bounds); the combined + // limit clamps to the full texture-unit array capacity. + EXPECT_EQ(highParams.MaxTextureImageUnits, MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS); + EXPECT_EQ(highParams.MaxVertexTextureImageUnits, MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS); + EXPECT_EQ(highParams.MaxComputeTextureImageUnits, MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS); + EXPECT_LE(highParams.MaxTextureImageUnits, 128); EXPECT_EQ(highParams.MaxCombinedTextureImageUnits, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS); EXPECT_EQ(highParams.MaxDrawBuffers, MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS); EXPECT_EQ(highParams.MaxColorAttachments, MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS);