[Fix] (MG_Impl/GLImpl, MG_State/GLState, MG_Test/Texture, scripts/data): keep GENERATE_MIPMAP on the texture, not keyed by GL name

- A process-wide map keyed by GL name gave a deleted texture's flag to the
  next texture created under the recycled name.
- The flag is now texture object state, so it dies with its texture.
This commit is contained in:
2026-10-16 16:52:59 -04:00
parent 095cf2643b
commit ad7340921e
4 changed files with 36 additions and 9 deletions
@@ -59,7 +59,6 @@
namespace MobileGL::MG_Impl::GLImpl {
static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject;
static UnorderedMap<Uint, Bool> g_autoGenerateMipmapByTextureId;
namespace {
void SetTextureBorderColorFromFloats(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
@@ -1321,7 +1320,7 @@ namespace MobileGL::MG_Impl::GLImpl {
textureObject->GetSamplerObject()->SetMaxAnisotropy(static_cast<GLfloat>(param));
break;
case GL_GENERATE_MIPMAP:
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != GL_FALSE);
textureObject->SetAutoGenerateMipmap(param != GL_FALSE);
break;
case GL_DEPTH_STENCIL_TEXTURE_MODE:
if (param != GL_DEPTH_COMPONENT && param != GL_STENCIL_INDEX) {
@@ -1435,7 +1434,7 @@ namespace MobileGL::MG_Impl::GLImpl {
textureObject->GetSamplerObject()->SetMaxAnisotropy(param);
break;
case GL_GENERATE_MIPMAP:
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f);
textureObject->SetAutoGenerateMipmap(param != 0.0f);
break;
case GL_DEPTH_STENCIL_TEXTURE_MODE:
if (param != GL_DEPTH_COMPONENT && param != GL_STENCIL_INDEX) {
@@ -1599,11 +1598,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void MaybeAutoGenerateMipmap(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject, Bool isProxy,
GLint level) {
if (isProxy || level != 0 || !textureObject) {
return;
}
const auto it = g_autoGenerateMipmapByTextureId.find(textureObject->GetExternalIndex());
if (it == g_autoGenerateMipmapByTextureId.end() || !it->second) {
if (isProxy || level != 0 || !textureObject || !textureObject->GetAutoGenerateMipmap()) {
return;
}
GenerateMipmap_Backend(textureObject);
@@ -99,6 +99,9 @@ namespace MobileGL::MG_State::GLState {
// DEPTH_COMPONENT until set, and meaningless for every other format.
virtual GLenum GetDepthStencilTextureMode() const = 0;
virtual void SetDepthStencilTextureMode(GLenum mode) = 0;
// GENERATE_MIPMAP: level 0 uploads regenerate the chain. Frontend-only, so no version moves.
virtual Bool GetAutoGenerateMipmap() const = 0;
virtual void SetAutoGenerateMipmap(Bool enabled) = 0;
// ---- Texture views (ARB_texture_view / GL 4.6 core 8.18) ----
// The texture object whose immutable storage this one's texels actually live in, or
@@ -206,6 +209,8 @@ namespace MobileGL::MG_State::GLState {
// set_texture_params is addressed by resource and is independent of every binding.
PipePublishParams();
}
Bool GetAutoGenerateMipmap() const override { return m_autoGenerateMipmap; }
void SetAutoGenerateMipmap(Bool enabled) override { m_autoGenerateMipmap = enabled; }
protected:
static Uint64 AllocateLifetimeId();
@@ -290,6 +295,7 @@ namespace MobileGL::MG_State::GLState {
Uint m_viewNumLayers = 0;
Int m_samples = 0;
Bool m_fixedSampleLocations = true;
Bool m_autoGenerateMipmap = false;
};
class TextureObjectMipmap : public TextureObjectBase {
@@ -392,3 +392,29 @@ TEST_F(TextureByNameTest, MipmapGenerationChecksTheTargetOfBothForms) {
MG_Impl::GLImpl::GenerateMipmap(GL_TEXTURE_RECTANGLE);
ExpectSingleGlError(GL_INVALID_ENUM);
}
// GENERATE_MIPMAP belongs to the texture it was set on. GL names are recycled, so the next texture
// created under a deleted texture's name must start without the flag.
TEST_F(TextureByNameTest, AutoMipmapFlagDoesNotOutliveItsTexture) {
const ScopedBackendStubs stubs;
static Int generated;
generated = 0;
MG_Backend::gBackendFunctionsTable.GL.GenerateTextureMipmap =
[](const SharedPtr<MG_State::GLState::ITextureObject>&, GLenum) { ++generated; };
const Uint8 pixels[4 * 4 * 4] = {};
const GLuint deleted = CreateTexture(GL_TEXTURE_2D);
MG_Impl::GLImpl::TextureParameteri(deleted, GL_GENERATE_MIPMAP, GL_TRUE);
MG_Impl::GLImpl::TextureStorage2D(deleted, 3, GL_RGBA8, 4, 4);
MG_Impl::GLImpl::TextureSubImage2D(deleted, 0, 0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
ASSERT_EQ(generated, 1) << "the flag did not generate mipmaps on its own texture";
MG_Impl::GLImpl::DeleteTextures(1, &deleted);
const GLuint recycled = CreateTexture(GL_TEXTURE_2D);
ASSERT_EQ(recycled, deleted) << "the name was not recycled, so nothing here is tested";
MG_Impl::GLImpl::TextureStorage2D(recycled, 3, GL_RGBA8, 4, 4);
MG_Impl::GLImpl::TextureSubImage2D(recycled, 0, 0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
EXPECT_EQ(generated, 1) << "the new texture inherited the deleted texture's GENERATE_MIPMAP";
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"file_excess_lines": 21859,
"file_excess_lines": 21854,
"function_excess_lines": 15516,
"layering_violations": 63
}