diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index f723c3e4..d58e2d0f 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -722,10 +722,14 @@ namespace MobileGL::MG_Impl::GLImpl { *data = 0; return; } + // GL 4.6 core table 23.4/23.5: *_BUFFER_SIZE reports the size glBindBufferRange + // was ASKED for, verbatim. It is not clamped to the buffer's storage, and it does + // not follow the buffer when a later glBufferData resizes it - a range may legally + // name bytes the buffer does not have yet. Clamping it here answered 0 for the + // common conformance shape of binding a range on a buffer that has no storage + // yet (KHR-GL43.shader_storage_buffer_object.basic-binding). const Range1D range = bindingPoint.GetRange(); - const auto start = std::min(range.start, bufferObject->GetSize()); - const auto end = std::min(range.end, bufferObject->GetSize()); - *data = static_cast(end - start); + *data = static_cast(range.end - range.start); return; } default: @@ -951,9 +955,8 @@ namespace MobileGL::MG_Impl::GLImpl { *data = 0; return; } - const auto start = std::min(range.start, bufferObject->GetSize()); - const auto end = std::min(range.end, bufferObject->GetSize()); - *data = static_cast(end - start); + // Verbatim, unclamped - see the GetIntegeri_v arm. + *data = static_cast(range.end - range.start); return; } default: diff --git a/MobileGL/MG_Test/Buffer/BufferTest.cpp b/MobileGL/MG_Test/Buffer/BufferTest.cpp index b62bdc9e..04084a20 100644 --- a/MobileGL/MG_Test/Buffer/BufferTest.cpp +++ b/MobileGL/MG_Test/Buffer/BufferTest.cpp @@ -480,6 +480,58 @@ TEST_F(BufferTest, BindBufferRangeZeroUnbindsBindingPoint) { EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +// GL 4.6 core tables 23.4/23.5: *_BUFFER_START and *_BUFFER_SIZE report the (offset, size) pair +// glBindBufferRange was ASKED for. They are not clamped to the buffer's storage - a range may +// legally name bytes the buffer does not have, and glBufferData may resize the buffer afterwards +// without the binding's reported window moving. The size arm used to intersect the recorded range +// with the buffer's current size, so binding a range on a still-empty buffer (glGenBuffers with no +// glBufferData - exactly what KHR-GL43.shader_storage_buffer_object.basic-binding does) answered 0 +// while START still answered the offset, an internally inconsistent pair no driver reports. +TEST_F(BufferTest, IndexedBufferSizeQueryReportsTheRequestedSizeNotTheBuffersStorage) { + GLint ssboAlignment = 0; + MobileGL::MG_Impl::GLImpl::GetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &ssboAlignment); + ASSERT_GT(ssboAlignment, 0); + const GLintptr offset = ssboAlignment; + const GLsizeiptr size = 512; + + GLuint buffer = 0; + MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer); + // Deliberately no glBufferData: the name exists, the storage does not. + MobileGL::MG_Impl::GLImpl::BindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, buffer, offset, size); + ASSERT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + GLint start32 = 0; + GLint size32 = 0; + GLint64 start64 = 0; + GLint64 size64 = 0; + MobileGL::MG_Impl::GLImpl::GetIntegeri_v(GL_SHADER_STORAGE_BUFFER_START, 1, &start32); + MobileGL::MG_Impl::GLImpl::GetIntegeri_v(GL_SHADER_STORAGE_BUFFER_SIZE, 1, &size32); + MobileGL::MG_Impl::GLImpl::GetInteger64i_v(GL_SHADER_STORAGE_BUFFER_START, 1, &start64); + MobileGL::MG_Impl::GLImpl::GetInteger64i_v(GL_SHADER_STORAGE_BUFFER_SIZE, 1, &size64); + EXPECT_EQ(start32, static_cast(offset)); + EXPECT_EQ(size32, static_cast(size)); + EXPECT_EQ(start64, static_cast(offset)); + EXPECT_EQ(size64, static_cast(size)); + + // Giving the buffer storage afterwards does not move the window either way. + MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); + MobileGL::MG_Impl::GLImpl::BufferData(GL_SHADER_STORAGE_BUFFER, offset + size, nullptr, GL_DYNAMIC_DRAW); + MobileGL::MG_Impl::GLImpl::GetIntegeri_v(GL_SHADER_STORAGE_BUFFER_SIZE, 1, &size32); + EXPECT_EQ(size32, static_cast(size)); + + // glBindBufferBase binds the whole buffer and reports (0, 0), not the buffer's size. + MobileGL::MG_Impl::GLImpl::BindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffer); + MobileGL::MG_Impl::GLImpl::GetIntegeri_v(GL_SHADER_STORAGE_BUFFER_START, 1, &start32); + MobileGL::MG_Impl::GLImpl::GetIntegeri_v(GL_SHADER_STORAGE_BUFFER_SIZE, 1, &size32); + EXPECT_EQ(start32, 0); + EXPECT_EQ(size32, 0); + + MobileGL::MG_Impl::GLImpl::BindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0); + MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(BufferTest, GetInteger64vMaxShaderStorageBlockSize) { GLint64 maxSsboBlockSize = 0; MobileGL::MG_Impl::GLImpl::GetInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxSsboBlockSize);