From e430e1b3bec356997684b8bf5e2612bced3b2dd2 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 27 Aug 2026 04:57:16 -0400 Subject: [PATCH] [Fix] (Texture): give glTexBuffer the sized-format check its TODO deferred and the target-taking forms their own INVALID_ENUM --- .../GLState/ProgramState/ShaderCompileTask.h | 14 +++- MobileGL/MG_Test/Texture/TextureTest.cpp | 78 +++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_State/GLState/ProgramState/ShaderCompileTask.h b/MobileGL/MG_State/GLState/ProgramState/ShaderCompileTask.h index 161452e0..cefc8d66 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ShaderCompileTask.h +++ b/MobileGL/MG_State/GLState/ProgramState/ShaderCompileTask.h @@ -40,15 +40,21 @@ namespace MobileGL::MG_State::GLState { // binding array in the getter and its floor (the GL 4.5 core minimum of 84) is that same // array's width, so the backend's own number never moves it. limits.MaxUniformBufferBindings = bindingPoints; + // The storage-buffer ceiling has the same shape as GetIndexedBufferQueryPointCount's: the + // backend's count capped by the array, and the array alone when there is no backend. That + // "no backend" arm is not a detail - it is what the GPU-free test binary runs under, and + // it has to keep matching what glGetIntegerv answers there. + limits.MaxShaderStorageBufferBindings = + env.HasBackend() + ? std::min(bindingPoints, std::max(env.params.MaxShaderStorageBufferBindings, 0)) + : bindingPoints; if (!env.HasBackend()) { - // No backend: the two backend-derived ceilings have nothing to be measured against, - // and zero means "do not enforce this kind" rather than "reject everything". + // The two genuinely per-DEVICE ceilings have nothing to be measured against here, and + // zero means "do not enforce this kind" rather than "reject everything". return limits; } limits.MaxSamplerBindings = std::max(env.params.MaxCombinedTextureImageUnits, 0); limits.MaxImageBindings = std::max(env.params.MaxImageUnits, 0); - limits.MaxShaderStorageBufferBindings = - std::min(bindingPoints, std::max(env.params.MaxShaderStorageBufferBindings, 0)); return limits; } diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index 995c1f2f..7f65a0f3 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -5697,3 +5697,81 @@ TEST_F(TextureTest, CopyTexSubImage1DRejectsAnythingButTexture1D) { MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, 0); } + +// --------------------------------------------------------------------------------------------- +// The buffer-texture entry points' error taxonomy (GL 4.6 core 8.9 / GL_EXT_texture_buffer). +// esextcTextureBufferErrors walks every OTHER texture target through glTexBuffer and +// glTexBufferRange and reads the code back each time, then does the same for a format a buffer +// texture cannot take. glTexBuffer carried `// TODO: make sure internalformat is in one of +// supported format for TexBuffer` and never checked, and the wrong-target code came out of a +// deeper "the bound object is not a buffer texture" arm whose code depends on which entry point +// reached it - GL_INVALID_OPERATION, which belongs only to the name-taking DSA forms. +// --------------------------------------------------------------------------------------------- + +TEST_F(TextureTest, TexBufferAndTexBufferRangeRejectANonBufferTargetWithInvalidEnum) { + GLuint buffer = 0; + MG_Impl::GLImpl::GenBuffers(1, &buffer); + MG_Impl::GLImpl::BindBuffer(GL_TEXTURE_BUFFER, buffer); + MG_Impl::GLImpl::BufferData(GL_TEXTURE_BUFFER, 64, nullptr, GL_STATIC_DRAW); + DrainPendingGlErrors(); + + static constexpr GLenum kWrongTargets[] = { + GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY, + }; + for (const GLenum target : kWrongTargets) { + MG_Impl::GLImpl::TexBuffer(target, GL_RGBA32I, buffer); + ExpectSingleGlError(GL_INVALID_ENUM); + MG_Impl::GLImpl::TexBufferRange(target, GL_RGBA32I, buffer, 0, 64); + ExpectSingleGlError(GL_INVALID_ENUM); + } + + MG_Impl::GLImpl::BindBuffer(GL_TEXTURE_BUFFER, 0); + MG_Impl::GLImpl::DeleteBuffers(1, &buffer); + DrainPendingGlErrors(); +} + +TEST_F(TextureTest, TexBufferRejectsAnInternalFormatABufferTextureCannotTake) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_BUFFER, texture); + GLuint buffer = 0; + MG_Impl::GLImpl::GenBuffers(1, &buffer); + MG_Impl::GLImpl::BindBuffer(GL_TEXTURE_BUFFER, buffer); + MG_Impl::GLImpl::BufferData(GL_TEXTURE_BUFFER, 64, nullptr, GL_STATIC_DRAW); + DrainPendingGlErrors(); + + // GL_DEPTH_COMPONENT32F is the one the conformance suite passes: a sized format, just not one + // of the sized formats table 8.15 lists for a buffer texture. + MG_Impl::GLImpl::TexBuffer(GL_TEXTURE_BUFFER, GL_DEPTH_COMPONENT32F, buffer); + ExpectSingleGlError(GL_INVALID_ENUM); + MG_Impl::GLImpl::TexBufferRange(GL_TEXTURE_BUFFER, GL_DEPTH_COMPONENT32F, buffer, 0, 64); + ExpectSingleGlError(GL_INVALID_ENUM); + + // A format the table DOES list still goes through. + MG_Impl::GLImpl::TexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32I, buffer); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_BUFFER, 0); + MG_Impl::GLImpl::BindBuffer(GL_TEXTURE_BUFFER, 0); + MG_Impl::GLImpl::DeleteBuffers(1, &buffer); + DrainPendingGlErrors(); +} + +// The DSA form keeps its own, DIFFERENT code for the corresponding shape: a texture that is not a +// buffer texture is a wrong OBJECT, not a wrong token. The two must not be unified. +TEST_F(TextureTest, TextureBufferKeepsInvalidOperationForANonBufferTexture) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + GLuint buffer = 0; + MG_Impl::GLImpl::GenBuffers(1, &buffer); + MG_Impl::GLImpl::BindBuffer(GL_TEXTURE_BUFFER, buffer); + MG_Impl::GLImpl::BufferData(GL_TEXTURE_BUFFER, 64, nullptr, GL_STATIC_DRAW); + DrainPendingGlErrors(); + + MG_Impl::GLImpl::TextureBuffer(texture, GL_RGBA32I, buffer); + ExpectSingleGlError(GL_INVALID_OPERATION); + + MG_Impl::GLImpl::BindBuffer(GL_TEXTURE_BUFFER, 0); + MG_Impl::GLImpl::DeleteBuffers(1, &buffer); + DrainPendingGlErrors(); +}