From 3019c6894591896bf4aad61a38d2cc79fc5c108e Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 1 Aug 2026 12:30:46 -0400 Subject: [PATCH] [Fix] (MG_Util): glBindBufferBase must not freeze the buffer's size BindBufferBase_State stored Range1D(0, bufferObject->GetSize()) as the binding point's range, so the range reflected whatever size the buffer happened to have at bind time. Binding an empty buffer and giving it storage afterwards is ordinary application code - glGenBuffers / glBindBufferBase / glBufferData is exactly the order KHR-GL3{0,2,3}.clip_distance.coverage uses - and the binding then stayed frozen at [0, 0). Every backend consumer reads GetRange() as the range the binding actually covers, so the stale window meant the capture buffer was bound with glBindBufferRange(..., 0, 0) instead of glBindBufferBase, transform feedback captured nothing, and the test read back its pre-draw zeros. The same stale range also under-counted the CPU-side transform feedback capacity accounting. GL resolves a whole-buffer binding against the object's size at every use; only glBindBufferRange pins a fixed window, and the binding point already tracked which of the two it was for the glGetIntegeri_v START/SIZE queries. GetRange() now resolves the non-explicit case dynamically. Fixes KHR-GL3{0,2,3}.clip_distance.coverage on Espryt; transform_feedback stays 21/21 on all four versions, and DirectVulkan (lavapipe) re-verified unaffected. --- MobileGL/MG_Util/Types.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Util/Types.h b/MobileGL/MG_Util/Types.h index 1b4621b5..dfeb0621 100644 --- a/MobileGL/MG_Util/Types.h +++ b/MobileGL/MG_Util/Types.h @@ -181,7 +181,19 @@ namespace MobileGL { explicit BindingSlotRange1D(TargetEnum target, const Range1D& range = Range1D()) : BindingSlot(target), m_range(range) {} - Range1D GetRange() const { return m_range; } + // The range the binding actually covers right now. A whole-buffer binding + // (glBindBufferBase) does not freeze anything: GL resolves it against the + // object's size at every use, so a glBufferData issued after the bind has to + // be visible here - binding an empty buffer and giving it storage afterwards + // is ordinary application code. Only glBindBufferRange pins a fixed window. + Range1D GetRange() const { + if (!m_hasExplicitRange) { + if (const auto& object = this->GetBoundObject()) { + return Range1D(0, object->GetSize()); + } + } + return m_range; + } Bool HasExplicitRange() const { return m_hasExplicitRange; }