[Feat] (MG_Impl, MG_State, MG_Util): attach a buffer texture to a range of its buffer

glTexBufferRange, glTextureBuffer and glTextureBufferRange were all stubs, so a
buffer texture could only ever be attached through glTexBuffer -- by binding, and
always to the whole buffer.

Give the buffer texture the window it is supposed to address. The non-range forms
record it as offset 0 with a whole-buffer sentinel rather than the size the buffer
happens to have, so a later respecify keeps being followed instead of freezing the
texture at yesterday's size. All four entry points now share one attach path,
differing only in how they name the texture: by binding for the target forms, by
name for the DSA ones.

Both backends honour the window: DirectVulkan offsets and clamps the buffer view,
DirectGLES uses glTexBufferRange when the texture names a sub-range and keeps
plain glTexBuffer for the whole-buffer case, which also works on a driver without
the range entry point.

GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT reported 0 with a comment explaining that the
range entry points were stubbed. It now reports what the device actually requires
-- minTexelBufferOffsetAlignment on Vulkan, the driver's own value on GLES -- and
the range entry points enforce it. Zero was never a legal answer; the minimum is
1, and an application that trusted it would have built unaligned offsets.
This commit is contained in:
BZLZHH
2026-08-04 20:53:53 -04:00
parent f39e6eb82d
commit bb582203d9
14 changed files with 155 additions and 7 deletions
@@ -21,10 +21,31 @@ namespace MobileGL {
BindingSlot<BufferObject>& GetBufferBindingSlot(
TextureUploadTarget target = TextureUploadTarget::TextureBuffer);
// The window of the attached buffer the texture addresses. glTexBuffer attaches
// the whole buffer, which is expressed here as an offset of 0 and a size of
// kWholeBuffer so a later respecify of the buffer keeps being followed - a stored
// size would freeze the texture at the size the buffer happened to have.
static constexpr SizeT kWholeBuffer = ~static_cast<SizeT>(0);
void SetBufferRange(SizeT offset, SizeT size) {
m_bufferRangeOffset = offset;
m_bufferRangeSize = size;
}
SizeT GetBufferRangeOffset() const { return m_bufferRangeOffset; }
// Resolved against the buffer's current size, so kWholeBuffer tracks it.
SizeT GetBufferRangeSizeInBytes() const {
const auto& buffer = m_bufferBindingSlot.GetBoundObject();
const SizeT bufferSize = buffer != nullptr ? buffer->GetSize() : 0;
if (m_bufferRangeSize == kWholeBuffer) return bufferSize;
const SizeT available = bufferSize > m_bufferRangeOffset ? bufferSize - m_bufferRangeOffset : 0;
return std::min(m_bufferRangeSize, available);
}
protected:
Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override;
BindingSlot<BufferObject> m_bufferBindingSlot = BindingSlot<BufferObject>(BufferTarget::Texture);
SizeT m_bufferRangeOffset = 0;
SizeT m_bufferRangeSize = kWholeBuffer;
const Vector<TextureUploadTarget> m_uploadTargets{TextureUploadTarget::TextureBuffer};
};
} // namespace GLState