diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index b02257ea..80bdad4b 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -2159,6 +2159,16 @@ namespace MobileGL::MG_Backend::DirectGLES { g_GLESFuncs.glTexParameterf(target, GL_TEXTURE_MAX_LOD, samplerParams.maxLod); m_cacheSamplerParameters.maxLod = samplerParams.maxLod; } + if (m_cacheSamplerParameters.maxAnisotropy != samplerParams.maxAnisotropy) { + if (g_GLESCapabilities.SupportsTextureFilterAnisotropy) { + g_GLESFuncs.glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, + samplerParams.maxAnisotropy); + } + // Unsupported GLES backends intentionally treat anisotropy as a + // frontend-only no-op; remember the observed value so the cache + // remains coherent without issuing an illegal enum every sync. + m_cacheSamplerParameters.maxAnisotropy = samplerParams.maxAnisotropy; + } DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); }); @@ -3040,6 +3050,13 @@ namespace MobileGL::MG_Backend::DirectGLES { g_GLESFuncs.glSamplerParameterf(m_backendSamplerId, GL_TEXTURE_MAX_LOD, samplerParams.maxLod); m_cacheSamplerParameters.maxLod = samplerParams.maxLod; } + if (m_cacheSamplerParameters.maxAnisotropy != samplerParams.maxAnisotropy) { + if (g_GLESCapabilities.SupportsTextureFilterAnisotropy) { + g_GLESFuncs.glSamplerParameterf(m_backendSamplerId, GL_TEXTURE_MAX_ANISOTROPY_EXT, + samplerParams.maxAnisotropy); + } + m_cacheSamplerParameters.maxAnisotropy = samplerParams.maxAnisotropy; + } #undef SYNC_SAMPLER_PARAM_IF_CHANGED m_isInitialized = true; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp index 495e75af..2d837a92 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp @@ -99,6 +99,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { XXHASH_VERIFY(XXH64_update(m_hashState, &maxLod, sizeof(maxLod))); const auto lodBias = sampler.GetLodBias(); XXHASH_VERIFY(XXH64_update(m_hashState, &lodBias, sizeof(lodBias))); + // Anisotropy is currently an accepted frontend-only state on DirectVulkan. + // Keep it out of the key so changing this no-op does not manufacture duplicate + // VkSamplers while sampler versioning still exposes the new frontend value. const auto compareMode = sampler.GetCompareMode(); XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode))); const auto compareFunc = ResolveCompareFunc(sampler, texture); @@ -125,6 +128,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { samplerInfo.addressModeV = ToVkAddressMode(sampler.GetWrapT()); samplerInfo.addressModeW = ToVkAddressMode(sampler.GetWrapR()); samplerInfo.mipLodBias = sampler.GetLodBias(); + // DirectVulkan does not yet plumb samplerAnisotropy feature/limit discovery; + // preserve the accepted frontend state without requesting an unsupported feature. samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.maxAnisotropy = 1.0f; samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE; diff --git a/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp b/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp index ceec7248..0f31503e 100644 --- a/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp @@ -14,7 +14,13 @@ namespace MobileGL::MG_Impl::GLImpl { namespace { - Bool ValidateSamplerParameterValue(GLenum pname, const void* param, Bool isFloat, Bool isInteger) { + Float ReadSamplerScalar(const void* param, Bool isFloat, Bool isUnsignedInteger) { + if (isFloat) return *(const GLfloat*)param; + if (isUnsignedInteger) return static_cast(*(const GLuint*)param); + return static_cast(*(const GLint*)param); + } + + Bool ValidateSamplerParameterValue(GLenum pname, const void* param, Bool isFloat, Bool isUnsignedInteger) { if (param == nullptr) return false; switch (pname) { @@ -22,6 +28,13 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_TEXTURE_MAX_LOD: case GL_TEXTURE_LOD_BIAS: return true; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (ReadSamplerScalar(param, isFloat, isUnsignedInteger) >= 1.0f) return true; + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "SetSamplerParam_State", + "GL_TEXTURE_MAX_ANISOTROPY_EXT must be at least 1.0.")); + return false; default: break; } @@ -29,14 +42,15 @@ namespace MobileGL::MG_Impl::GLImpl { if (isFloat) { return SamplerImpl::ValidateSamplerFloatParam(pname, *(const GLfloat*)param); } - if (isInteger) { + if (isUnsignedInteger) { return SamplerImpl::ValidateSamplerIntParam(pname, static_cast(*(const GLuint*)param)); } return SamplerImpl::ValidateSamplerIntParam(pname, *(const GLint*)param); } } // namespace - void SetSamplerParam_State(GLuint sampler, GLenum pname, const void* param, bool isFloat, bool isInteger) { + void SetSamplerParam_State(GLuint sampler, GLenum pname, const void* param, bool isFloat, + bool isUnsignedInteger) { if (param == nullptr) return; if (!SamplerImpl::ValidateSamplerName(sampler)) return; @@ -47,7 +61,7 @@ namespace MobileGL::MG_Impl::GLImpl { } auto& samplerObj = MG_State::pGLContext->GetSamplerObject(sampler); if (!SamplerImpl::ValidateSamplerObject(sampler)) return; - if (!ValidateSamplerParameterValue(pname, param, isFloat, isInteger)) return; + if (!ValidateSamplerParameterValue(pname, param, isFloat, isUnsignedInteger)) return; using namespace MG_Util; switch (pname) { @@ -76,6 +90,9 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_TEXTURE_LOD_BIAS: samplerObj->SetLodBias(*(const GLfloat*)param); break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + samplerObj->SetMaxAnisotropy(ReadSamplerScalar(param, isFloat, isUnsignedInteger)); + break; case GL_TEXTURE_COMPARE_MODE: samplerObj->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(*(const GLint*)param)); break; @@ -89,7 +106,8 @@ namespace MobileGL::MG_Impl::GLImpl { } } - void GetSamplerParam_State(GLuint sampler, GLenum pname, void* params, bool isFloat, bool isInteger) { + void GetSamplerParam_State(GLuint sampler, GLenum pname, void* params, bool isFloat, + bool isUnsignedInteger) { if (params == nullptr) return; if (!SamplerImpl::ValidateSamplerName(sampler)) return; @@ -129,6 +147,15 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_TEXTURE_LOD_BIAS: *(GLfloat*)params = samplerObj->GetLodBias(); break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (isFloat) { + *(GLfloat*)params = samplerObj->GetMaxAnisotropy(); + } else if (isUnsignedInteger) { + *(GLuint*)params = static_cast(samplerObj->GetMaxAnisotropy()); + } else { + *(GLint*)params = static_cast(samplerObj->GetMaxAnisotropy()); + } + break; case GL_TEXTURE_COMPARE_MODE: *(GLuint*)params = MG_Util::ConvertSamplerCompareModeToGLEnum(samplerObj->GetCompareMode()); break; @@ -240,7 +267,7 @@ namespace MobileGL::MG_Impl::GLImpl { } void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param) { - SetSamplerParam_State(sampler, pname, param, false, true); + SetSamplerParam_State(sampler, pname, param, false, false); } void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param) { @@ -268,7 +295,7 @@ namespace MobileGL::MG_Impl::GLImpl { } void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params) { - GetSamplerParam_State(sampler, pname, params, false, true); + GetSamplerParam_State(sampler, pname, params, false, false); } void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params) { diff --git a/MobileGL/MG_Impl/GLImpl/Sampler/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Sampler/Validators.cpp index 83934ce3..9a028d31 100644 --- a/MobileGL/MG_Impl/GLImpl/Sampler/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sampler/Validators.cpp @@ -99,6 +99,16 @@ namespace MobileGL::MG_Impl::GLImpl::SamplerImpl { case GL_TEXTURE_LOD_BIAS: return true; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (!(param >= 1.0f)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "ValidateSamplerFloatParam", + "GL_TEXTURE_MAX_ANISOTROPY_EXT must be at least 1.0.")); + return false; + } + return true; + case GL_TEXTURE_BORDER_COLOR: if (param < 0.0f || param > 1.0f) { MG_State::pGLContext->RecordError( @@ -125,6 +135,16 @@ namespace MobileGL::MG_Impl::GLImpl::SamplerImpl { } return true; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (param < 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "ValidateSamplerIntParam", + "GL_TEXTURE_MAX_ANISOTROPY_EXT must be at least 1.")); + return false; + } + return true; + default: return ValidateSamplerParam(pname, static_cast(param)); } diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 676dd0e2..fc0c852a 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -74,6 +74,16 @@ namespace MobileGL::MG_Impl::GLImpl { return true; } + Bool ValidateMaxAnisotropy(Float maxAnisotropy, const char* caller) { + if (maxAnisotropy >= 1.0f) return true; + + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, + "GL_TEXTURE_MAX_ANISOTROPY_EXT must be at least 1.0.")); + return false; + } + template void WithTemporarilyBoundNamedTexture(const SharedPtr& textureObject, Fn&& fn) { @@ -466,6 +476,9 @@ namespace MobileGL::MG_Impl::GLImpl { Bool ValidateTextureParameterForTarget(const SharedPtr& textureObject, GLenum pname, GLint param, const char* caller) { const auto target = textureObject->GetTarget(); + if (pname == GL_TEXTURE_MAX_ANISOTROPY_EXT && !ValidateMaxAnisotropy(param, caller)) { + return false; + } if ((pname == GL_TEXTURE_BASE_LEVEL || pname == GL_TEXTURE_MAX_LEVEL) && param < 0) { MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, @@ -495,7 +508,8 @@ namespace MobileGL::MG_Impl::GLImpl { (pname == GL_TEXTURE_WRAP_S || pname == GL_TEXTURE_WRAP_T || pname == GL_TEXTURE_WRAP_R || pname == GL_TEXTURE_MIN_FILTER || pname == GL_TEXTURE_MAG_FILTER || pname == GL_TEXTURE_MIN_LOD || pname == GL_TEXTURE_MAX_LOD || pname == GL_TEXTURE_LOD_BIAS || pname == GL_TEXTURE_COMPARE_MODE || - pname == GL_TEXTURE_COMPARE_FUNC || pname == GL_TEXTURE_BORDER_COLOR)) { + pname == GL_TEXTURE_COMPARE_FUNC || pname == GL_TEXTURE_BORDER_COLOR || + pname == GL_TEXTURE_MAX_ANISOTROPY_EXT)) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", caller, @@ -586,6 +600,9 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_TEXTURE_LOD_BIAS: textureObject->GetSamplerObject()->SetLodBias((GLfloat)param); break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + textureObject->GetSamplerObject()->SetMaxAnisotropy(static_cast(param)); + break; case GL_GENERATE_MIPMAP: g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != GL_FALSE); break; @@ -602,7 +619,10 @@ namespace MobileGL::MG_Impl::GLImpl { void TextureParameterObjectf_State(const SharedPtr& textureObject, GLenum pname, GLfloat param, const char* caller) { if (!textureObject) return; - if (!ValidateTextureParameterForTarget(textureObject, pname, static_cast(param), caller)) return; + if (pname == GL_TEXTURE_MAX_ANISOTROPY_EXT && !ValidateMaxAnisotropy(param, caller)) return; + const GLint validationParam = + pname == GL_TEXTURE_MAX_ANISOTROPY_EXT ? 1 : static_cast(param); + if (!ValidateTextureParameterForTarget(textureObject, pname, validationParam, caller)) return; switch (pname) { case GL_TEXTURE_MAG_FILTER: @@ -648,6 +668,9 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_TEXTURE_LOD_BIAS: textureObject->GetSamplerObject()->SetLodBias(param); break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + textureObject->GetSamplerObject()->SetMaxAnisotropy(param); + break; case GL_GENERATE_MIPMAP: g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f); break; @@ -717,6 +740,9 @@ namespace MobileGL::MG_Impl::GLImpl { *params = (GLint)MG_Util::ConvertSamplerCompareFuncToGLEnum( textureObject->GetSamplerObject()->GetSamplerCompareFunc()); break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + *params = static_cast(textureObject->GetSamplerObject()->GetMaxAnisotropy()); + break; default: MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, @@ -1105,6 +1131,10 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_TEXTURE_LOD_BIAS: textureObject->GetSamplerObject()->SetLodBias(param); break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (!ValidateMaxAnisotropy(param, __func__)) return; + textureObject->GetSamplerObject()->SetMaxAnisotropy(param); + break; case GL_GENERATE_MIPMAP: g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f); break; @@ -1912,6 +1942,11 @@ namespace MobileGL::MG_Impl::GLImpl { textureObject->GetSamplerObject()->GetSamplerCompareFunc()); } break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (params) { + *params = static_cast(textureObject->GetSamplerObject()->GetMaxAnisotropy()); + } + break; case GL_IMAGE_FORMAT_COMPATIBILITY_TYPE: if (params) { *params = GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE; @@ -2058,6 +2093,11 @@ namespace MobileGL::MG_Impl::GLImpl { textureObject->GetSamplerObject()->GetSamplerCompareFunc()); } break; + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (params) { + *params = textureObject->GetSamplerObject()->GetMaxAnisotropy(); + } + break; default: MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum, MakeUnique("MG_Impl/GLImpl", "GetTexParameterfv_State", diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp index 68569872..fad9abb7 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp @@ -78,6 +78,13 @@ namespace MobileGL { ++m_version; } + void SamplerObject::SetMaxAnisotropy(Float maxAnisotropy) { + if (maxAnisotropy == m_samplerParameters.maxAnisotropy) return; + + m_samplerParameters.maxAnisotropy = maxAnisotropy; + ++m_version; + } + void SamplerObject::SetSamplerCompareFunc(SamplerCompareFunc func) { if (func == m_samplerParameters.compareFunc) return; @@ -128,6 +135,10 @@ namespace MobileGL { return m_samplerParameters.lodBias; } + Float SamplerObject::GetMaxAnisotropy() const { + return m_samplerParameters.maxAnisotropy; + } + SamplerCompareMode SamplerObject::GetCompareMode() const { return m_samplerParameters.compareMode; } diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h index 16fda9b0..54352396 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h @@ -65,6 +65,7 @@ namespace MobileGL { Float minLod = -1000.0f; Float maxLod = 1000.0f; Float lodBias = 0.0f; + Float maxAnisotropy = 1.0f; SamplerCompareFunc compareFunc = SamplerCompareFunc::Always; SamplerCompareMode compareMode = SamplerCompareMode::None; }; @@ -83,6 +84,7 @@ namespace MobileGL { void SetMipmapMode(SamplerMipmapMode mode); void SetLodRange(Float minLod, Float maxLod); void SetLodBias(Float bias); + void SetMaxAnisotropy(Float maxAnisotropy); void SetSamplerCompareFunc(SamplerCompareFunc func); void SetCompareMode(SamplerCompareMode mode); @@ -95,6 +97,7 @@ namespace MobileGL { Float GetMinLod() const; Float GetMaxLod() const; Float GetLodBias() const; + Float GetMaxAnisotropy() const; SamplerCompareMode GetCompareMode() const; SamplerCompareFunc GetSamplerCompareFunc() const; Uint GetExternalIndex() const; diff --git a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp index ff5adb4a..8ee37032 100644 --- a/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp +++ b/MobileGL/MG_Test/BackendLoader/BackendLoaderTest.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ namespace { GLenum errorRaisedByDraw = GL_NO_ERROR; GLenum pendingError = GL_NO_ERROR; + std::vector extensions; GLuint nextBufferId = 1; GLuint nextShaderId = 1; @@ -86,7 +88,7 @@ namespace { *data = 1; break; case GL_NUM_EXTENSIONS: - *data = 0; + *data = static_cast(g_fake.extensions.size()); break; default: // Leave the caller's defaults for every other capability query. @@ -114,9 +116,10 @@ namespace { return reinterpret_cast(""); } }; - // GL_NUM_EXTENSIONS reports 0 above, so this is never reached; it exists so the - // table stays complete if the extension loop ever runs. - funcs.glGetStringi = [](GLenum, GLuint) -> const GLubyte* { return nullptr; }; + funcs.glGetStringi = [](GLenum name, GLuint index) -> const GLubyte* { + if (name != GL_EXTENSIONS || index >= g_fake.extensions.size()) return nullptr; + return reinterpret_cast(g_fake.extensions[index].c_str()); + }; funcs.glGetFloatv = [](GLenum pname, GLfloat* data) { switch (pname) { // Two-component range queries. @@ -400,3 +403,20 @@ TEST(IndirectInstanceIdProbe, FillInCapabilitiesWiresProbeResult) { EXPECT_FALSE(conformingCaps.IndirectDrawInstanceIdIncludesBaseInstance); ExpectProbeReleasedAllObjects(); } + +TEST(TextureAnisotropyCapabilities, ExtensionPresenceIsDetectedExactly) { + ResetFakeDriver(); + g_fake.maxVertexSsboBlocks = 0; + const auto funcs = MakeFakeGLESFunctions(); + + MobileGL::MG_External::GLESCapabilities absentCaps; + ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(absentCaps, funcs)); + EXPECT_FALSE(absentCaps.SupportsTextureFilterAnisotropy); + + ResetFakeDriver(); + g_fake.maxVertexSsboBlocks = 0; + g_fake.extensions.emplace_back("GL_EXT_texture_filter_anisotropic"); + MobileGL::MG_External::GLESCapabilities presentCaps; + ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(presentCaps, funcs)); + EXPECT_TRUE(presentCaps.SupportsTextureFilterAnisotropy); +} diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index e14afdac..56bed14d 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,127 @@ TEST_F(TextureTest, CreateTexturesCreatesObjectsWithoutBinding) { EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(TextureTest, TextureMaxAnisotropyDefaultsToOneAndRoundTripsWithoutRedundantVersionBumps) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture); + ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + ASSERT_NE(textureObject, nullptr); + const auto& samplerObject = textureObject->GetSamplerObject(); + ASSERT_NE(samplerObject, nullptr); + + GLfloat floatValue = 0.0f; + MG_Impl::GLImpl::GetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &floatValue); + EXPECT_FLOAT_EQ(floatValue, 1.0f); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 1.0f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + const Uint16 initialVersion = samplerObject->GetVersion(); + MG_Impl::GLImpl::TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4.0f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 4.0f); + EXPECT_EQ(samplerObject->GetVersion(), static_cast(initialVersion + 1)); + + GLint integerValue = 0; + MG_Impl::GLImpl::GetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &integerValue); + EXPECT_EQ(integerValue, 4); + MG_Impl::GLImpl::GetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &floatValue); + EXPECT_FLOAT_EQ(floatValue, 4.0f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + const Uint16 setVersion = samplerObject->GetVersion(); + MG_Impl::GLImpl::TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4.0f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_EQ(samplerObject->GetVersion(), setVersion); + + MG_Impl::GLImpl::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 8.0f); + EXPECT_EQ(samplerObject->GetVersion(), static_cast(setVersion + 1)); +} + +TEST_F(TextureTest, TextureMaxAnisotropyBelowOneIsInvalidValueAndPreservesState) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture); + ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + ASSERT_NE(textureObject, nullptr); + const auto& samplerObject = textureObject->GetSamplerObject(); + ASSERT_NE(samplerObject, nullptr); + const Uint16 initialVersion = samplerObject->GetVersion(); + + MG_Impl::GLImpl::TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0.5f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_VALUE); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 1.0f); + EXPECT_EQ(samplerObject->GetVersion(), initialVersion); + + MG_Impl::GLImpl::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_VALUE); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 1.0f); + EXPECT_EQ(samplerObject->GetVersion(), initialVersion); +} + +TEST_F(TextureTest, SamplerMaxAnisotropyUsesTheSameStateAndValidationSemantics) { + GLuint sampler = 0; + MG_Impl::GLImpl::GenSamplers(1, &sampler); + ASSERT_NE(sampler, 0u); + + GLfloat floatValue = 0.0f; + MG_Impl::GLImpl::GetSamplerParameterfv(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, &floatValue); + ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_FLOAT_EQ(floatValue, 1.0f); + + const auto& samplerObject = MG_State::pGLContext->GetSamplerObject(sampler); + ASSERT_NE(samplerObject, nullptr); + const Uint16 initialVersion = samplerObject->GetVersion(); + + MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 6.0f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 6.0f); + EXPECT_EQ(samplerObject->GetVersion(), static_cast(initialVersion + 1)); + + GLint integerValue = 0; + MG_Impl::GLImpl::GetSamplerParameteriv(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, &integerValue); + EXPECT_EQ(integerValue, 6); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + const Uint16 setVersion = samplerObject->GetVersion(); + MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 6.0f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_EQ(samplerObject->GetVersion(), setVersion); + + MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0.25f); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_VALUE); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 6.0f); + EXPECT_EQ(samplerObject->GetVersion(), setVersion); + + MG_Impl::GLImpl::SamplerParameteri(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_VALUE); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 6.0f); + EXPECT_EQ(samplerObject->GetVersion(), setVersion); + + const GLint signedInvalidValue = -1; + MG_Impl::GLImpl::SamplerParameterIiv(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, &signedInvalidValue); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_VALUE); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 6.0f); + EXPECT_EQ(samplerObject->GetVersion(), setVersion); + + const GLuint unsignedValue = 10; + MG_Impl::GLImpl::SamplerParameterIuiv(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, &unsignedValue); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + EXPECT_FLOAT_EQ(samplerObject->GetMaxAnisotropy(), 10.0f); + EXPECT_EQ(samplerObject->GetVersion(), static_cast(setVersion + 1)); + + GLuint queriedUnsignedValue = 0; + MG_Impl::GLImpl::GetSamplerParameterIuiv(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, &queriedUnsignedValue); + EXPECT_EQ(queriedUnsignedValue, unsignedValue); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(TextureTest, GenThenBindCreatesObjectForUnsizedPackedBgraSubImageUpload) { GLuint texture = 0; MG_Impl::GLImpl::GenTextures(1, &texture); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 5e6df140..9b9aa97c 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -799,6 +799,9 @@ namespace MobileGL::MG_Util::BackendLoader { if (std::strcmp(extension, "GL_EXT_texture_norm16") == 0) { caps.SupportsNorm16Texture = true; } + if (std::strcmp(extension, "GL_EXT_texture_filter_anisotropic") == 0) { + caps.SupportsTextureFilterAnisotropy = true; + } if (std::strcmp(extension, "GL_EXT_base_instance") == 0) { caps.SupportsBaseInstance = true; } diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index ef0283b7..f07f4b00 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1031,6 +1031,9 @@ namespace MobileGL { String GLESShadingLanguageVersionString; Bool SupportsPersistentMapping = false; Bool SupportsNorm16Texture = false; + // GL_EXT_texture_filter_anisotropic is present, so sampler/texture + // anisotropy may be forwarded without raising GL_INVALID_ENUM in GLES. + Bool SupportsTextureFilterAnisotropy = false; Bool SupportsBaseInstance = false; // GL_EXT_disjoint_timer_query is present in the extension string. Bool SupportsDisjointTimerQuery = false;