[Fix] (Texture): give glTexBuffer the sized-format check its TODO deferred and the target-taking forms their own INVALID_ENUM

This commit is contained in:
2026-08-27 05:37:18 -04:00
parent e315d9e798
commit e430e1b3be
2 changed files with 88 additions and 4 deletions
@@ -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<Int>(bindingPoints, std::max<Int>(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<Int>(env.params.MaxCombinedTextureImageUnits, 0);
limits.MaxImageBindings = std::max<Int>(env.params.MaxImageUnits, 0);
limits.MaxShaderStorageBufferBindings =
std::min<Int>(bindingPoints, std::max<Int>(env.params.MaxShaderStorageBufferBindings, 0));
return limits;
}
+78
View File
@@ -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();
}