[Fix] (Sampler): convert sampler parameters set through the other scalar type

This commit is contained in:
2026-10-02 11:22:00 -04:00
parent 5a7b23f661
commit 51c37bcbc9
2 changed files with 79 additions and 12 deletions
+29 -12
View File
@@ -23,6 +23,17 @@ namespace MobileGL::MG_Impl::GLImpl {
return static_cast<Float>(*(const GLint*)param);
}
// An enum-valued pname through whichever scalar spelling the caller used. GL 4.6 core 8.10
// lets every scalar sampler pname be set through either type and converts the value; reading
// a glSamplerParameterf argument as a GLint took the IEEE bits of, say, 9728.0f for the enum,
// and the parameter silently became garbage. Truncation, as the validator
// (ValidateSamplerFloatParam) and the texture-side float setter convert.
GLint ReadSamplerEnum(const void* param, Bool isFloat, Bool isUnsignedInteger) {
if (isFloat) return static_cast<GLint>(*(const GLfloat*)param);
if (isUnsignedInteger) return static_cast<GLint>(*(const GLuint*)param);
return *(const GLint*)param;
}
// GL_TEXTURE_BORDER_COLOR is the only sampler parameter with more than one component, and it
// is also the only one whose meaning depends on WHICH entry point wrote it. Everything else
// reads exactly one component and does not care.
@@ -122,41 +133,47 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!SamplerImpl::ValidateSamplerObject(sampler)) return;
if (!ValidateSamplerParameterValue(pname, param, isFloat, isUnsignedInteger)) return;
// Every scalar pname reads the caller's own type and converts it (GL 4.6 core 8.10);
// GL_TEXTURE_BORDER_COLOR alone reads its array, below.
const Bool isScalar = !IsVectorOnlySamplerPname(pname);
const GLint enumParam = isScalar ? ReadSamplerEnum(param, isFloat, isUnsignedInteger) : 0;
const Float scalarParam = isScalar ? ReadSamplerScalar(param, isFloat, isUnsignedInteger) : 0.0f;
using namespace MG_Util;
switch (pname) {
case GL_TEXTURE_WRAP_S:
samplerObj->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
samplerObj->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(enumParam));
break;
case GL_TEXTURE_WRAP_T:
samplerObj->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
samplerObj->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(enumParam));
break;
case GL_TEXTURE_WRAP_R:
samplerObj->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
samplerObj->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(enumParam));
break;
case GL_TEXTURE_MIN_FILTER:
samplerObj->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(*(const GLint*)param));
samplerObj->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(*(const GLint*)param));
samplerObj->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(enumParam));
samplerObj->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(enumParam));
break;
case GL_TEXTURE_MAG_FILTER:
samplerObj->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(*(const GLint*)param));
samplerObj->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(enumParam));
break;
case GL_TEXTURE_MIN_LOD:
samplerObj->SetLodRange(*(const GLfloat*)param, samplerObj->GetMaxLod());
samplerObj->SetLodRange(scalarParam, samplerObj->GetMaxLod());
break;
case GL_TEXTURE_MAX_LOD:
samplerObj->SetLodRange(samplerObj->GetMinLod(), *(const GLfloat*)param);
samplerObj->SetLodRange(samplerObj->GetMinLod(), scalarParam);
break;
case GL_TEXTURE_LOD_BIAS:
samplerObj->SetLodBias(*(const GLfloat*)param);
samplerObj->SetLodBias(scalarParam);
break;
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
samplerObj->SetMaxAnisotropy(ReadSamplerScalar(param, isFloat, isUnsignedInteger));
samplerObj->SetMaxAnisotropy(scalarParam);
break;
case GL_TEXTURE_COMPARE_MODE:
samplerObj->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(*(const GLint*)param));
samplerObj->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(enumParam));
break;
case GL_TEXTURE_COMPARE_FUNC:
samplerObj->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(*(const GLint*)param));
samplerObj->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(enumParam));
break;
case GL_TEXTURE_BORDER_COLOR:
// The only four-component sampler parameter: the caller's form decides which
+50
View File
@@ -731,6 +731,56 @@ TEST_F(TextureTest, BorderColorFormChangeBumpsTheVersionEvenWhenTheNumbersDoNotM
DrainPendingGlErrors();
}
// GL 4.6 core 8.10: every sampler pname may be set through EITHER scalar type, and the value is
// converted - an enum through glSamplerParameterf, a LOD through glSamplerParameteri. The setter used
// to read the caller's bytes as the pname's own type: glSamplerParameterf(GL_TEXTURE_MIN_FILTER,
// GL_NEAREST) handed the converter the IEEE bits of 9728.0f, the filter became garbage and the sampler
// kept a mipmap filter, so a single-level mutable texture sampled through it was incomplete and read
// as opaque black. That is how a translation layer that funnels every sampler parameter through the
// float entry point (enums cast to float) drew solid black icons.
TEST_F(TextureTest, SamplerParametersSetThroughTheOtherScalarTypeAreConverted) {
GLuint sampler = 0;
MG_Impl::GLImpl::GenSamplers(1, &sampler);
ASSERT_NE(sampler, 0u);
const auto& samplerObject = MG_State::pGLContext->GetSamplerObject(sampler);
ASSERT_NE(samplerObject, nullptr);
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_MIN_FILTER, static_cast<GLfloat>(GL_NEAREST));
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_MAG_FILTER, static_cast<GLfloat>(GL_NEAREST));
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_WRAP_S, static_cast<GLfloat>(GL_CLAMP_TO_EDGE));
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_WRAP_T, static_cast<GLfloat>(GL_MIRRORED_REPEAT));
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_WRAP_R, static_cast<GLfloat>(GL_CLAMP_TO_EDGE));
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_COMPARE_MODE,
static_cast<GLfloat>(GL_COMPARE_REF_TO_TEXTURE));
MG_Impl::GLImpl::SamplerParameterf(sampler, GL_TEXTURE_COMPARE_FUNC, static_cast<GLfloat>(GL_GREATER));
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
EXPECT_EQ(samplerObject->GetMinFilter(), SamplerFilterMode::Nearest);
EXPECT_EQ(samplerObject->GetMipmapMode(), SamplerMipmapMode::None)
<< "a NEAREST min filter set through the float spelling kept a mipmap mode";
EXPECT_EQ(samplerObject->GetMagFilter(), SamplerFilterMode::Nearest);
EXPECT_EQ(samplerObject->GetWrapS(), SamplerWrapMode::ClampToEdge);
EXPECT_EQ(samplerObject->GetWrapT(), SamplerWrapMode::MirroredRepeat);
EXPECT_EQ(samplerObject->GetWrapR(), SamplerWrapMode::ClampToEdge);
EXPECT_EQ(samplerObject->GetCompareMode(), SamplerCompareMode::CompareToTexture);
EXPECT_EQ(samplerObject->GetSamplerCompareFunc(), SamplerCompareFunc::Greater);
GLint minFilter = 0;
MG_Impl::GLImpl::GetSamplerParameteriv(sampler, GL_TEXTURE_MIN_FILTER, &minFilter);
EXPECT_EQ(minFilter, GL_NEAREST);
// And the other direction: a LOD written through the integer spellings is a number, not bits.
MG_Impl::GLImpl::SamplerParameteri(sampler, GL_TEXTURE_MIN_LOD, -2);
MG_Impl::GLImpl::SamplerParameteri(sampler, GL_TEXTURE_MAX_LOD, 5);
const GLuint bias = 3;
MG_Impl::GLImpl::SamplerParameterIuiv(sampler, GL_TEXTURE_LOD_BIAS, &bias);
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
EXPECT_FLOAT_EQ(samplerObject->GetMinLod(), -2.0f);
EXPECT_FLOAT_EQ(samplerObject->GetMaxLod(), 5.0f);
EXPECT_FLOAT_EQ(samplerObject->GetLodBias(), 3.0f);
MG_Impl::GLImpl::DeleteSamplers(1, &sampler);
DrainPendingGlErrors();
}
// GL 4.6 core 8.10: the scalar sampler setters take "the value of pname", so a four-component pname is
// INVALID_ENUM there. Taking the address of the by-value argument and handing it to the vector path -
// which is what these used to do - both lost the error and read twelve bytes past a stack scalar.