From 5545d31c37849846ccb5be2ccfe8b872a8443a35 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Wed, 5 Aug 2026 06:58:00 -0400 Subject: [PATCH] [Feat] (MG_Backend, MG_Util): give a cube map array real storage on DirectGLES TextureCubeMapArray was missing from every storage and upload switch in the DirectGLES texture sync, so a cube map array reached the driver with no storage at all - and from the glFramebufferTextureLayer branch, so attaching one of its layers fell through to glFramebufferTexture2D and raised INVALID_ENUM. Every GL_TEXTURE_CUBE_MAP_ARRAY colour check in direct_state_access.framebuffers_texture_layer_attachment read nothing. ES 3.2 has GL_TEXTURE_CUBE_MAP_ARRAY natively and it stores exactly like a 2D array whose depth is six times the cube count, so each switch gains the case beside Texture2DArray and nothing else changes. 1D arrays join the layer branch for the same reason - their backend image is a 2D array. Per the POST rule the new GLES dependency gets a capability (SupportsTextureCubeMapArray, ES 3.2 core or EXT/OES_texture_cube_map_array) and a DriverPost row saying what a user loses without it. Takes framebuffers_texture_layer_attachment from failing to passing on Espryt. It still fails on DirectVulkan, which declines a layered attachment outright. --- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 13 ++++++++++++- MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp | 5 +++++ MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h | 2 ++ MobileGL/MG_Util/SelfTest/DriverPost.cpp | 10 ++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index a2eb6732..5607126e 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -1888,6 +1888,9 @@ namespace MobileGL::MG_Backend::DirectGLES { break; case TextureTarget::Texture3D: case TextureTarget::Texture2DArray: + // ES 3.2 has GL_TEXTURE_CUBE_MAP_ARRAY natively and it stores exactly + // like a 2D array whose depth is 6 * the cube count. + case TextureTarget::TextureCubeMapArray: g_GLESFuncs.glTexImage3D( glUploadTarget, static_cast(level), (GLint)glInternalFormat, static_cast(uploadSize.x()), static_cast(uploadSize.y()), @@ -1965,6 +1968,7 @@ namespace MobileGL::MG_Backend::DirectGLES { break; case TextureTarget::Texture3D: case TextureTarget::Texture2DArray: + case TextureTarget::TextureCubeMapArray: g_GLESFuncs.glTexStorage3D(target, static_cast(mipmapCount), glInternalFormat, static_cast(storageSize.x()), static_cast(storageSize.y()), @@ -2017,6 +2021,7 @@ namespace MobileGL::MG_Backend::DirectGLES { break; case TextureTarget::Texture3D: case TextureTarget::Texture2DArray: + case TextureTarget::TextureCubeMapArray: g_GLESFuncs.glTexSubImage3D( glUploadTarget, static_cast(level), 0, 0, 0, static_cast(uploadSize.x()), @@ -2080,7 +2085,8 @@ namespace MobileGL::MG_Backend::DirectGLES { break; } case TextureTarget::Texture3D: - case TextureTarget::Texture2DArray: { + case TextureTarget::Texture2DArray: + case TextureTarget::TextureCubeMapArray: { g_GLESFuncs.glTexImage3D( glUploadTarget, static_cast(level), (GLint)glInternalFormat, static_cast(uploadSize.x()), @@ -2180,6 +2186,9 @@ namespace MobileGL::MG_Backend::DirectGLES { break; case TextureTarget::Texture3D: case TextureTarget::Texture2DArray: + // ES 3.2 has GL_TEXTURE_CUBE_MAP_ARRAY natively and it stores exactly + // like a 2D array whose depth is 6 * the cube count. + case TextureTarget::TextureCubeMapArray: g_GLESFuncs.glTexSubImage3D(glUploadTarget, static_cast(level), 0, 0, 0, static_cast(uploadSize.x()), static_cast(uploadSize.y()), @@ -2630,6 +2639,8 @@ namespace MobileGL::MG_Backend::DirectGLES { } else if (const auto uploadTarget = attachmentObject.GetTextureUploadTarget(); uploadTarget == TextureUploadTarget::Texture3D || uploadTarget == TextureUploadTarget::Texture2DArray || + uploadTarget == TextureUploadTarget::Texture1DArray || + uploadTarget == TextureUploadTarget::CubeMapArray || uploadTarget == TextureUploadTarget::Texture2DMultisampleArray) { // Single slice/layer of a 3D or array texture: ES has no // glFramebufferTexture3D, layers attach via glFramebufferTextureLayer. diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 6da11c3e..4251fbe5 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -837,6 +837,10 @@ namespace MobileGL::MG_Util::BackendLoader { std::strcmp(extension, "GL_OES_texture_border_clamp") == 0) { caps.SupportsTextureBorderClamp = true; } + if (std::strcmp(extension, "GL_EXT_texture_cube_map_array") == 0 || + std::strcmp(extension, "GL_OES_texture_cube_map_array") == 0) { + caps.SupportsTextureCubeMapArray = true; + } if (std::strcmp(extension, "GL_EXT_base_instance") == 0) { caps.SupportsBaseInstance = true; } @@ -1021,6 +1025,7 @@ namespace MobileGL::MG_Util::BackendLoader { // Core from ES 3.2 on, whatever the extension string says. if (caps.GLESVersion.Major > 3 || (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2)) { caps.SupportsTextureBorderClamp = true; + caps.SupportsTextureCubeMapArray = true; } if (caps.SupportsTextureFilterAnisotropy) { GLfloat maxTextureMaxAnisotropy = 1.0f; diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index 35f2d650..abbf9757 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1045,6 +1045,8 @@ namespace MobileGL { // EXT/OES_texture_border_clamp before that. Without it every border-colour parameter // raises INVALID_ENUM on the driver, so the syncs have to be gated on it. Bool SupportsTextureBorderClamp = false; + // GL_TEXTURE_CUBE_MAP_ARRAY: ES 3.2 core, or EXT/OES_texture_cube_map_array before it. + Bool SupportsTextureCubeMapArray = false; // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT of the host driver; only queried when the // extension above is present, and left at 1.0 (no anisotropy) otherwise. Float MaxTextureMaxAnisotropy = 1.0f; diff --git a/MobileGL/MG_Util/SelfTest/DriverPost.cpp b/MobileGL/MG_Util/SelfTest/DriverPost.cpp index 2872bdd5..c52077d7 100644 --- a/MobileGL/MG_Util/SelfTest/DriverPost.cpp +++ b/MobileGL/MG_Util/SelfTest/DriverPost.cpp @@ -309,6 +309,16 @@ namespace MobileGL::MG_Util::SelfTest { "sampling outside a GL_CLAMP_TO_BORDER texture reads the driver's default " "border instead of the requested colour"); } + if (caps.SupportsTextureCubeMapArray) { + builder.Pass("Texture cube map array", + "supported (GL_TEXTURE_CUBE_MAP_ARRAY textures get real storage and can be " + "attached to a framebuffer)"); + } else { + builder.Warn("Texture cube map array", + "not supported (pre-ES 3.2 without GL_EXT/OES_texture_cube_map_array); a cube " + "map array texture gets no driver storage at all, so sampling one reads nothing " + "and rendering to one does not reach the screen"); + } if (glesFuncs.glPatchParameteri != nullptr) { builder.Pass("Tessellation patch parameters", "glPatchParameteri present (GL_PATCH_VERTICES reaches the driver)");