mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix, Test] (GLImpl, MG_IntegrationTest): answer glGetTexLevelParameter for buffer textures instead of erroring
This commit is contained in:
@@ -661,21 +661,42 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
"Compressed texture formats are not supported."));
|
"Compressed texture formats are not supported."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// glGetTexLevelParameter{i,f}v answers WIDTH/HEIGHT/DEPTH out of the mipmap chain. The only
|
// GL_TEXTURE_WIDTH of a buffer texture: how many texels of the texture's internal format fit
|
||||||
// other storage type the state layer knows is GL_TEXTURE_BUFFER (TextureStorageType is
|
// in the buffer range it addresses, CLAMPED to GL_MAX_TEXTURE_BUFFER_SIZE. Attaching a larger
|
||||||
// {Mipmap, Buffer}), whose level geometry this stack does not track yet. Report that instead
|
// buffer is legal (GL 4.6 core 8.9) - the texture simply addresses the first
|
||||||
// of throwing: THROW_UNIMPL_EXCEPTION unwinds a C++ exception through the C GL ABI and takes
|
// MAX_TEXTURE_BUFFER_SIZE texels of it, and that clamped count is what WIDTH reports.
|
||||||
// the process down, which is never an acceptable answer to a query - see the same reasoning
|
//
|
||||||
// above for the compressed-format path.
|
// GL_TEXTURE_BUFFER_SIZE is deliberately NOT clamped the same way: it reports the range in
|
||||||
|
// basic machine units exactly as glTexBuffer/glTexBufferRange were given it. Swapping the two
|
||||||
|
// fails KHR-GL43.texture_buffer.texture_buffer_max_size in the opposite direction.
|
||||||
|
GLint GetBufferTextureTexelWidth(const MG_State::GLState::ITextureObject* textureObject) {
|
||||||
|
const SizeT texelByteSize = MG_Util::GetSizedInternalFormatSizeInBytes(textureObject->GetFormat());
|
||||||
|
// A format with no known footprint has no texel count to report; answering 0 beats
|
||||||
|
// dividing by it.
|
||||||
|
if (texelByteSize == 0) return 0;
|
||||||
|
const auto* bufferTextureObject =
|
||||||
|
static_cast<const MG_State::GLState::TextureObjectBuffer*>(textureObject);
|
||||||
|
const SizeT texelCount = bufferTextureObject->GetBufferRangeSizeInBytes() / texelByteSize;
|
||||||
|
const SizeT maxTexelCount = static_cast<SizeT>(
|
||||||
|
std::max(0, MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxTextureBufferSize));
|
||||||
|
return static_cast<GLint>(std::min(texelCount, maxTexelCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// glGetTexLevelParameter{i,f}v answers WIDTH/HEIGHT/DEPTH out of the mipmap chain, and (since
|
||||||
|
// the buffer-texture arms above) out of the attached buffer range for GL_TEXTURE_BUFFER. This
|
||||||
|
// is what is left: a storage class with no level geometry at all. Report it instead of
|
||||||
|
// throwing - THROW_UNIMPL_EXCEPTION unwinds a C++ exception through the C GL ABI and takes the
|
||||||
|
// process down, which is never an acceptable answer to a query - see the same reasoning above
|
||||||
|
// for the compressed-format path.
|
||||||
void RecordUnsupportedLevelQueryStorage(const char* caller, GLenum pname) {
|
void RecordUnsupportedLevelQueryStorage(const char* caller, GLenum pname) {
|
||||||
MGLOG_W_ONCE("%s: glGetTexLevelParameter(pname=%s) is not implemented for texture-buffer "
|
MGLOG_W_ONCE("%s: glGetTexLevelParameter(pname=%s) is not implemented for this texture's "
|
||||||
"storage; recording GL_INVALID_OPERATION instead of terminating",
|
"storage class; recording GL_INVALID_OPERATION instead of terminating",
|
||||||
caller, MG_Util::ConvertGLEnumToString(pname).c_str());
|
caller, MG_Util::ConvertGLEnumToString(pname).c_str());
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidOperation,
|
ErrorCode::InvalidOperation,
|
||||||
MakeUnique<GenericErrorInfo>(
|
MakeUnique<GenericErrorInfo>(
|
||||||
"MG_Impl/GLImpl", caller,
|
"MG_Impl/GLImpl", caller,
|
||||||
"Level queries are not supported for texture-buffer storage."));
|
"Level queries are not supported for this texture's storage class."));
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -3117,6 +3138,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x();
|
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureStorageType::Buffer:
|
||||||
|
*params = GetBufferTextureTexelWidth(textureObject.get());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
RecordUnsupportedLevelQueryStorage("GetTexLevelParameteriv_State", pname);
|
RecordUnsupportedLevelQueryStorage("GetTexLevelParameteriv_State", pname);
|
||||||
break;
|
break;
|
||||||
@@ -3132,6 +3156,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y();
|
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureStorageType::Buffer:
|
||||||
|
*params = 1; // a buffer texture is one-dimensional
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
RecordUnsupportedLevelQueryStorage("GetTexLevelParameteriv_State", pname);
|
RecordUnsupportedLevelQueryStorage("GetTexLevelParameteriv_State", pname);
|
||||||
break;
|
break;
|
||||||
@@ -3147,6 +3174,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z();
|
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureStorageType::Buffer:
|
||||||
|
*params = 1; // a buffer texture is one-dimensional
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
RecordUnsupportedLevelQueryStorage("GetTexLevelParameteriv_State", pname);
|
RecordUnsupportedLevelQueryStorage("GetTexLevelParameteriv_State", pname);
|
||||||
break;
|
break;
|
||||||
@@ -3216,6 +3246,31 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case GL_TEXTURE_BUFFER_SIZE:
|
||||||
|
case GL_TEXTURE_BUFFER_OFFSET: {
|
||||||
|
// GL 4.6 core 8.9: both describe the window of the attached buffer a GL_TEXTURE_BUFFER
|
||||||
|
// texture addresses, so there is nothing to report for any other storage - which is
|
||||||
|
// INVALID_OPERATION, the same shape GL_TEXTURE_COMPRESSED_IMAGE_SIZE guards itself with
|
||||||
|
// above.
|
||||||
|
if (textureObject->GetStorageType() != TextureStorageType::Buffer) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", "GetTexLevelParameteriv_State",
|
||||||
|
"GL_TEXTURE_BUFFER_SIZE / GL_TEXTURE_BUFFER_OFFSET need a buffer texture."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (params) {
|
||||||
|
const auto* bufferTextureObject =
|
||||||
|
static_cast<MG_State::GLState::TextureObjectBuffer*>(textureObject.get());
|
||||||
|
// Basic machine units, and UNCLAMPED - see GetBufferTextureTexelWidth for why this
|
||||||
|
// half does not take the GL_MAX_TEXTURE_BUFFER_SIZE clamp that WIDTH does.
|
||||||
|
*params = static_cast<GLint>(pname == GL_TEXTURE_BUFFER_SIZE
|
||||||
|
? bufferTextureObject->GetBufferRangeSizeInBytes()
|
||||||
|
: bufferTextureObject->GetBufferRangeOffset());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexLevelParameteriv_State",
|
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexLevelParameteriv_State",
|
||||||
@@ -3255,6 +3310,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x();
|
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureStorageType::Buffer:
|
||||||
|
*params = (GLfloat)GetBufferTextureTexelWidth(textureObject.get());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
RecordUnsupportedLevelQueryStorage("GetTexLevelParameterfv_State", pname);
|
RecordUnsupportedLevelQueryStorage("GetTexLevelParameterfv_State", pname);
|
||||||
break;
|
break;
|
||||||
@@ -3270,6 +3328,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y();
|
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureStorageType::Buffer:
|
||||||
|
*params = 1.0f; // a buffer texture is one-dimensional
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
RecordUnsupportedLevelQueryStorage("GetTexLevelParameterfv_State", pname);
|
RecordUnsupportedLevelQueryStorage("GetTexLevelParameterfv_State", pname);
|
||||||
break;
|
break;
|
||||||
@@ -3285,6 +3346,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z();
|
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TextureStorageType::Buffer:
|
||||||
|
*params = 1.0f; // a buffer texture is one-dimensional
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
RecordUnsupportedLevelQueryStorage("GetTexLevelParameterfv_State", pname);
|
RecordUnsupportedLevelQueryStorage("GetTexLevelParameterfv_State", pname);
|
||||||
break;
|
break;
|
||||||
@@ -3352,6 +3416,27 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case GL_TEXTURE_BUFFER_SIZE:
|
||||||
|
case GL_TEXTURE_BUFFER_OFFSET: {
|
||||||
|
// See GetTexLevelParameteriv_State: both describe the attached buffer range of a
|
||||||
|
// GL_TEXTURE_BUFFER texture, so any other storage makes the query INVALID_OPERATION.
|
||||||
|
if (textureObject->GetStorageType() != TextureStorageType::Buffer) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", "GetTexLevelParameterfv_State",
|
||||||
|
"GL_TEXTURE_BUFFER_SIZE / GL_TEXTURE_BUFFER_OFFSET need a buffer texture."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (params) {
|
||||||
|
const auto* bufferTextureObject =
|
||||||
|
static_cast<MG_State::GLState::TextureObjectBuffer*>(textureObject.get());
|
||||||
|
*params = static_cast<GLfloat>(pname == GL_TEXTURE_BUFFER_SIZE
|
||||||
|
? bufferTextureObject->GetBufferRangeSizeInBytes()
|
||||||
|
: bufferTextureObject->GetBufferRangeOffset());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexLevelParameterfv_State",
|
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexLevelParameterfv_State",
|
||||||
|
|||||||
@@ -299,4 +299,99 @@ void main() {
|
|||||||
EXPECT_EQ(FirstGLError(), 0u);
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// glGetTexLevelParameter used to refuse EVERY pname on a buffer texture: WIDTH/HEIGHT/DEPTH
|
||||||
|
// fell out of a mipmap-only switch as GL_INVALID_OPERATION, and GL_TEXTURE_BUFFER_SIZE /
|
||||||
|
// GL_TEXTURE_BUFFER_OFFSET were not in the switch at all, so they came back GL_INVALID_ENUM.
|
||||||
|
// KHR-GL43.texture_buffer wraps both queries in GLU_EXPECT_NO_ERROR, so the error alone fails
|
||||||
|
// the case before any value is compared.
|
||||||
|
//
|
||||||
|
// The two halves report DIFFERENT units and only one of them is clamped, which is the thing
|
||||||
|
// easiest to get backwards: WIDTH is a TEXEL count clamped to GL_MAX_TEXTURE_BUFFER_SIZE,
|
||||||
|
// BUFFER_SIZE is the range in basic machine units exactly as it was given.
|
||||||
|
TEST_F(BufferTextureScenario, LevelQueriesDescribeTheAttachedBufferRange) {
|
||||||
|
if (!Ready()) return;
|
||||||
|
FirstGLError();
|
||||||
|
|
||||||
|
GLint offsetAlignment = 1;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
|
||||||
|
if (offsetAlignment < 1) offsetAlignment = 1;
|
||||||
|
GLint maxTexels = 0;
|
||||||
|
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexels);
|
||||||
|
ASSERT_EQ(FirstGLError(), 0u);
|
||||||
|
ASSERT_GT(maxTexels, 0) << "an OpenGL 4.x context may not advertise a zero buffer-texture limit";
|
||||||
|
|
||||||
|
constexpr GLint kTexelBytes = 4; // GL_RGBA8
|
||||||
|
const GLsizeiptr rangeOffset = static_cast<GLsizeiptr>(offsetAlignment);
|
||||||
|
const GLsizeiptr rangeBytes = 32 * kTexelBytes;
|
||||||
|
// Deliberately bigger than the range, so a getter that answered out of the BUFFER rather
|
||||||
|
// than out of the texture's window would be caught.
|
||||||
|
const GLsizeiptr bufferBytes = rangeOffset + rangeBytes + 16 * kTexelBytes;
|
||||||
|
|
||||||
|
const std::vector<GLubyte> zeros(static_cast<size_t>(bufferBytes), 0);
|
||||||
|
GLuint buffer = 0;
|
||||||
|
glGenBuffers(1, &buffer);
|
||||||
|
glBindBuffer(GL_TEXTURE_BUFFER, buffer);
|
||||||
|
glBufferData(GL_TEXTURE_BUFFER, bufferBytes, zeros.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
GLuint texture = 0;
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, texture);
|
||||||
|
glTexBufferRange(GL_TEXTURE_BUFFER, GL_RGBA8, buffer, rangeOffset, rangeBytes);
|
||||||
|
ASSERT_EQ(FirstGLError(), 0u) << "glTexBufferRange(GL_RGBA8) was refused";
|
||||||
|
|
||||||
|
const auto levelQuery = [](GLenum pname) {
|
||||||
|
GLint value = -1;
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_BUFFER, 0, pname, &value);
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
const auto levelQueryF = [](GLenum pname) {
|
||||||
|
GLfloat value = -1.0f;
|
||||||
|
glGetTexLevelParameterfv(GL_TEXTURE_BUFFER, 0, pname, &value);
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_WIDTH), static_cast<GLint>(rangeBytes / kTexelBytes))
|
||||||
|
<< "GL_TEXTURE_WIDTH is a texel count over the attached RANGE";
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_HEIGHT), 1);
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_DEPTH), 1);
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_BUFFER_SIZE), static_cast<GLint>(rangeBytes))
|
||||||
|
<< "GL_TEXTURE_BUFFER_SIZE reports basic machine units, not texels";
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_BUFFER_OFFSET), static_cast<GLint>(rangeOffset));
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u) << "a buffer-texture level query raised an error";
|
||||||
|
EXPECT_LE(levelQuery(GL_TEXTURE_WIDTH), maxTexels)
|
||||||
|
<< "GL_TEXTURE_WIDTH must stay clamped to GL_MAX_TEXTURE_BUFFER_SIZE";
|
||||||
|
|
||||||
|
// The float getter is a separate switch and has drifted from the integer one before.
|
||||||
|
EXPECT_FLOAT_EQ(levelQueryF(GL_TEXTURE_WIDTH), static_cast<GLfloat>(rangeBytes / kTexelBytes));
|
||||||
|
EXPECT_FLOAT_EQ(levelQueryF(GL_TEXTURE_HEIGHT), 1.0f);
|
||||||
|
EXPECT_FLOAT_EQ(levelQueryF(GL_TEXTURE_BUFFER_SIZE), static_cast<GLfloat>(rangeBytes));
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u) << "the float form of a buffer-texture level query raised an error";
|
||||||
|
|
||||||
|
// The whole-buffer form follows the buffer's current size instead of freezing a window.
|
||||||
|
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, buffer);
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_BUFFER_OFFSET), 0);
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_BUFFER_SIZE), static_cast<GLint>(bufferBytes));
|
||||||
|
EXPECT_EQ(levelQuery(GL_TEXTURE_WIDTH), static_cast<GLint>(bufferBytes / kTexelBytes));
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
|
|
||||||
|
// Both buffer pnames belong to buffer textures alone; anything else is INVALID_OPERATION,
|
||||||
|
// the same shape GL_TEXTURE_COMPRESSED_IMAGE_SIZE uses for an uncompressed image.
|
||||||
|
GLuint plainTexture = 0;
|
||||||
|
glGenTextures(1, &plainTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, plainTexture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
|
GLint unused = -1;
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BUFFER_SIZE, &unused);
|
||||||
|
EXPECT_EQ(FirstGLError(), static_cast<unsigned int>(GL_INVALID_OPERATION));
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER, 0);
|
||||||
|
glBindBuffer(GL_TEXTURE_BUFFER, 0);
|
||||||
|
glDeleteTextures(1, &plainTexture);
|
||||||
|
glDeleteTextures(1, &texture);
|
||||||
|
glDeleteBuffers(1, &buffer);
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MGITest
|
} // namespace MGITest
|
||||||
|
|||||||
Reference in New Issue
Block a user