[Fix] (MG_Impl, MG_Backend): reject incomplete cube maps in mipmap generation instead of crashing on them

Both direct_state_access.textures_generate_mipmap* cases crashed DirectVulkan.
Two causes, neither of them a broken invariant:

glGenerateMipmap and glGenerateTextureMipmap never checked cube completeness, so
an incomplete cube map went straight to the backend, which asserts that the
texture it is handed is complete. GL 4.6 core 8.14.4 makes that call
INVALID_OPERATION - there is no consistent set of faces to filter down - and both
entry points now say so through a shared check.

VulkanRenderer::GenerateMipmap asserted that the target was one of the four it
implements. 1D, 1D array and cube map array are legal GL and the front end passes
them through, so meeting one is a gap in this backend's coverage; it now logs and
declines, leaving the generated levels unwritten rather than aborting.

textures_generate_mipmap_errors passes on both backends now. textures_generate_mipmaps
stops crashing but still fails: DirectVulkan does not generate the 1D mip chain
the case checks - the frontend's storage allocation gives the levels the right
sizes, which is why the case passes when run on its own, but not the descending
content the full-run state leaves it looking for.
This commit is contained in:
BZLZHH
2026-08-05 02:55:54 -04:00
parent 765aaec6dc
commit a63699cde6
2 changed files with 32 additions and 11 deletions
@@ -7883,9 +7883,15 @@ void main() {
void VulkanRenderer::GenerateMipmap(GLenum target) {
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
MOBILEGL_ASSERT(textureTarget == TextureTarget::Texture2D || textureTarget == TextureTarget::Texture2DArray ||
textureTarget == TextureTarget::Texture3D || textureTarget == TextureTarget::TextureCubeMap,
"GenerateMipmap currently only supports GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D, and GL_TEXTURE_CUBE_MAP.");
// The other mipmappable targets - 1D, 1D array, cube map array - are legal GL and the front
// end lets them through, so reaching one here is a coverage gap in this backend, not a
// broken invariant. Declining leaves the mip chain unwritten; asserting took the process
// down with it.
if (textureTarget != TextureTarget::Texture2D && textureTarget != TextureTarget::Texture2DArray &&
textureTarget != TextureTarget::Texture3D && textureTarget != TextureTarget::TextureCubeMap) {
MGLOG_W("GenerateMipmap: unsupported target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
return;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
auto texture = textureUnit.GetBindingSlot(textureTarget).GetBoundObject();
+23 -8
View File
@@ -4309,6 +4309,24 @@ namespace MobileGL::MG_Impl::GLImpl {
bindImageTexture(unit, texture, level, layered, layer, access, format);
}
// GL 4.6 core 8.14.4: a cube map that is not cube complete has no consistent set of faces to
// filter down, so generating its mipmaps is INVALID_OPERATION. Without this the incomplete
// texture reached the backend, where DirectVulkan asserts on it and takes the process down.
Bool ValidateGenerateMipmapTexture(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
const char* caller) {
if (!textureObject) return false;
const auto target = textureObject->GetTarget();
if ((target == TextureTarget::TextureCubeMap || target == TextureTarget::TextureCubeMapArray) &&
!textureObject->IsComplete()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"Mipmap generation requires a cube complete cube map texture."));
return false;
}
return true;
}
void GenerateMipmap(GLenum target) {
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
if (!TextureImpl::ValidateTextureTarget(textureTarget)) {
@@ -4322,6 +4340,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "GenerateMipmap requires a bound texture."));
return;
}
if (!ValidateGenerateMipmapTexture(textureObject, __func__)) return;
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires mipmap texture storage.");
@@ -4331,15 +4350,11 @@ namespace MobileGL::MG_Impl::GLImpl {
void GenerateTextureMipmap(GLuint texture) {
auto textureObject = GetTextureObjectByName(texture, __func__);
if (textureObject) {
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateTextureMipmap requires mipmap texture storage.");
}
if (!ValidateGenerateMipmapTexture(textureObject, __func__)) return;
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateTextureMipmap requires mipmap texture storage.");
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
if (textureObject) {
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
EnsureGeneratedMipmapStorageAllocated(*mipmapTexture);
}
EnsureGeneratedMipmapStorageAllocated(*mipmapTexture);
GenerateMipmap_Backend(target);
});
}