From 07f91f352abbc2ac8e8e5869f8a25689f7e33a98 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sun, 20 Sep 2026 18:45:29 -0400 Subject: [PATCH] [MGPipe] carry no initial bytes for an empty store glBufferData(target, 0, NULL, usage) DEFINES its store - that is not the orphaning idiom - so HasDefinedContent is set, and MappedData() answers a non-null pointer for it because the shadow reserves one byte whatever the size (PipeResource.h:140-143). Reading MappedData() alone therefore put the call on the split arm's respecify(nullptr)-plus-follow-up shape, whose walk emits nothing for a zero-length range (ResourceTracker.h:253), and the InitialBytesNotCarried self-check aborted by name on the first such call in the bsl-esc-menu trace. The predicate is 'HasDefinedContent && GetSize() > 0': there are bytes here, not merely a defined store. The split arm's own condition is untouched - moving it instead would send the zero-byte case to the UncarriedInitialBytes refusal in the wire escape. PipeFill.cpp. --- MobileGL/MG_Impl/Pipe/PipeFill.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Impl/Pipe/PipeFill.cpp b/MobileGL/MG_Impl/Pipe/PipeFill.cpp index 21ca9b20..1fb94051 100644 --- a/MobileGL/MG_Impl/Pipe/PipeFill.cpp +++ b/MobileGL/MG_Impl/Pipe/PipeFill.cpp @@ -924,7 +924,19 @@ namespace MobileGL::MG_Pipe { // initialBytes is the client's own shadow base - zero copy, and null is a real answer // for the orphaning idiom (a NULL-data respecify leaves the store undefined and the // backend must not upload the stale bytes). - const void* initialBytes = desc.HasDefinedContent != 0 ? buffer.MappedData() : nullptr; + // + // THE SIZE TEST IS NOT REDUNDANT. A zero-byte store is DEFINED content - glBufferData's + // `size == 0` arm sets HasDefinedContent (BufferObject.cpp:242) because the store exists + // and is empty - and MappedData() answers a NON-NULL pointer for it, since the shadow + // reserves one byte whatever the size (PipeResource.h:140-143). Reading MappedData() + // alone therefore answered "bytes to carry" for a store that has none, which sent the + // split branch below down the respecify(nullptr)-plus-follow-up shape; the follow-up + // walk emits nothing for a zero-length range (ResourceTracker.h:253), so the + // InitialBytesNotCarried self-check aborted by name on the first + // glBufferData(target, 0, NULL, usage) of the bsl-esc-menu trace. `GetSize() > 0` is + // what makes the answer mean "there are bytes here" rather than "the store is defined". + const void* initialBytes = + (desc.HasDefinedContent != 0 && buffer.GetSize() > 0) ? buffer.MappedData() : nullptr; // kNeedsAck rides on the CALL and MGPipeResourceRespecifyNeedsAck(desc) decides per // record: only an immutable store (a glBufferStorage*) is a real synchronous // allocation and only it is allowed one. In monolith the acknowledgement is