mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-17 08:38:30 +09:00
[Feat] (MG_Pipe): give HasLiveHostWrites its producer, decline every persistent-map acquisition under split, and retire the verify pin that was waiting for exactly this
This commit is contained in:
@@ -702,6 +702,15 @@ namespace MobileGL::MG_Pipe {
|
|||||||
// one was emitted, so this cannot be false - but a zeroed record (null
|
// one was emitted, so this cannot be false - but a zeroed record (null
|
||||||
// handle, size 0) is not the answer if that pre-pass is ever relaxed.
|
// handle, size 0) is not the answer if that pre-pass is ever relaxed.
|
||||||
if (!MGPipeBuildSubDataRecord(handle, at, length, record, /*verbatimShadow=*/true)) return;
|
if (!MGPipeBuildSubDataRecord(handle, at, length, record, /*verbatimShadow=*/true)) return;
|
||||||
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||||
|
// P5 (b1): the live-host-writes bit rides the content record, because
|
||||||
|
// "someone may be writing these bytes without telling you" is a fact about the
|
||||||
|
// CONTENT and not about the storage. It is set from the object's PUBLISHED
|
||||||
|
// value rather than from a live IsMapped() read so that the record and the
|
||||||
|
// edge that announced it can never disagree. MGPipeBuildSubDataRecord does not
|
||||||
|
// take the object, which is why it is set here and not in the builder.
|
||||||
|
record.HasLiveHostWrites = buffer.HasLiveHostWritesForWire() ? 1 : 0;
|
||||||
|
#endif
|
||||||
MGPipeApplyResourceSubData(record, base + at);
|
MGPipeApplyResourceSubData(record, base + at);
|
||||||
});
|
});
|
||||||
if (!encodable) {
|
if (!encodable) {
|
||||||
|
|||||||
@@ -26,6 +26,13 @@
|
|||||||
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
|
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||||
|
// R-6's tier gate. One spelling, asked at the one place the decline is decided. Outside the
|
||||||
|
// MOBILEGL_PIPE_VERIFY block above on purpose: the tier is a property of the BUILD, not of the
|
||||||
|
// comparator, and a split build without the comparator still declines every acquisition.
|
||||||
|
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -835,12 +842,18 @@ namespace MobileGL::MG_Pipe {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MOBILEGL_PIPE_VERIFY
|
#if MOBILEGL_PIPE_VERIFY && !MOBILEGL_BUILD_DISAGGREGATED
|
||||||
// D-A4's pin. HasLiveHostWrites is ALWAYS false in this phase and is written by
|
// D-A4's pin, AND P5 (b1) IS THE PHASE IT WAS WAITING FOR. It said "HasLiveHostWrites
|
||||||
// nobody: it exists so the phase that pushes persistent-mapped host writes can set it
|
// is always false in this phase and is written by nobody: it exists so the phase that
|
||||||
// with no new record kind. A producer that landed under it would change what
|
// pushes persistent-mapped host writes can set it with no new record kind", and a
|
||||||
// IsBufferDrawClean answers with no other visible edit, so a verify build refuses to
|
// verify build refused to let such a producer arrive unannounced.
|
||||||
// let one arrive unannounced.
|
//
|
||||||
|
// The producer is announced: MGPSubData::HasLiveHostWrites, set by
|
||||||
|
// MGPipeEmitResourceSubData from BufferObject::HasLiveHostWritesForWire and read by
|
||||||
|
// ApplyBufferWrite below. So the pin is LIFTED FOR A SPLIT BUILD ONLY, and left
|
||||||
|
// standing everywhere else - in a monolith build nothing sets the bit and the wire is
|
||||||
|
// still the thing that would say so if something started to. That is the whole value
|
||||||
|
// of the pin and it survives the phase it was written for.
|
||||||
void PinNoLiveHostWrites(const MGPipeResourceRecord& record, MGPipeHandle res, const char* call) {
|
void PinNoLiveHostWrites(const MGPipeResourceRecord& record, MGPipeHandle res, const char* call) {
|
||||||
if (!record.HasLiveHostWrites) return;
|
if (!record.HasLiveHostWrites) return;
|
||||||
MGP_TRIP_WIRE_REPORT("MGPipe: " MGP_TRIP_WIRE_TAG("PipeLiveHostWrites")
|
MGP_TRIP_WIRE_REPORT("MGPipe: " MGP_TRIP_WIRE_TAG("PipeLiveHostWrites")
|
||||||
@@ -905,6 +918,16 @@ namespace MobileGL::MG_Pipe {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
PinNoLiveHostWrites(*stored, record.Res, call);
|
PinNoLiveHostWrites(*stored, record.Res, call);
|
||||||
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||||
|
// P5 (b1): THE PRODUCER. The record's own statement about the resource, applied
|
||||||
|
// before the serial moves so that a probe re-entered from inside the backend hook
|
||||||
|
// below already sees it. It is an assignment and not an OR: the bit is a STATE,
|
||||||
|
// and a content record emitted while nothing maps the buffer is exactly how the
|
||||||
|
// state goes back to false - which is why the falling edge pushes one block
|
||||||
|
// (BufferObject::NotePersistentMapStateChanged) rather than relying on the next
|
||||||
|
// ordinary write to arrive.
|
||||||
|
stored->HasLiveHostWrites = record.HasLiveHostWrites != 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
// THE SERIAL MOVES BEFORE THE BACKEND IS TOLD, and that order is load-bearing:
|
// THE SERIAL MOVES BEFORE THE BACKEND IS TOLD, and that order is load-bearing:
|
||||||
// the backend stamps its own synced serial from this record inside the hook, so a
|
// the backend stamps its own synced serial from this record inside the hook, so a
|
||||||
@@ -1932,6 +1955,26 @@ namespace MobileGL::MG_Pipe {
|
|||||||
// the flag exists to announce, and the wire is what refuses to let it arrive unnamed.
|
// the flag exists to announce, and the wire is what refuses to let it arrive unnamed.
|
||||||
PinNoLiveHostWrites(*record, handle.Handle, "map_persistent");
|
PinNoLiveHostWrites(*record, handle.Handle, "map_persistent");
|
||||||
|
|
||||||
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||||
|
// R-6: A SPLIT BUILD RUNS AT TIER T2 AND DECLINES EVERY ACQUISITION, ALWAYS.
|
||||||
|
//
|
||||||
|
// The mint returns a raw void* that the client stores as the store's base
|
||||||
|
// (BufferObject.cpp:238 / :603 / :658). Across a process boundary that address is
|
||||||
|
// meaningless, and under `inproc` it is WORSE than meaningless: it happens to work,
|
||||||
|
// so a lane that kept adoption alive would be green for a reason spawn cannot
|
||||||
|
// reproduce, and persistent-map-push - an exit-gate counter - would be structurally
|
||||||
|
// zero (PipeStats.cpp says so in as many words). Forcing T2 here rather than at the
|
||||||
|
// three client call sites is what keeps `mpr` identical between the arms: the
|
||||||
|
// roundtrip is COUNTED above, unconditionally, because a decline costs the same round
|
||||||
|
// trip as a mint.
|
||||||
|
//
|
||||||
|
// The frontend already tolerates a decline in all three places, and has since P3a.
|
||||||
|
if (MG_Config::Transport != MG_Config::TransportMode::Monolith &&
|
||||||
|
MG_Remote::Client::AdoptTierIsEmulate()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// NO SERIAL MOVES and NO DESCRIPTOR CHANGES: the donation re-mints the backend's own
|
// NO SERIAL MOVES and NO DESCRIPTOR CHANGES: the donation re-mints the backend's own
|
||||||
// driver object, which is a server-local event that the backend's own id generation
|
// driver object, which is a server-local event that the backend's own id generation
|
||||||
// already catches, and the client's view of the store's extent is untouched by it.
|
// already catches, and the client's view of the store's extent is untouched by it.
|
||||||
|
|||||||
@@ -219,9 +219,20 @@ namespace MobileGL::MG_Pipe {
|
|||||||
// sub-data). It is what replaces the frontend change serial the backend used to
|
// sub-data). It is what replaces the frontend change serial the backend used to
|
||||||
// mirror, and no MGPipe call may require the client to provide or know one.
|
// mirror, and no MGPipe call may require the client to provide or know one.
|
||||||
Uint64 Serial = 0;
|
Uint64 Serial = 0;
|
||||||
// ALWAYS FALSE IN P3a, AND WRITTEN BY NOBODY. It exists so the phase that pushes
|
// "DOES THIS RESOURCE HAVE A LIVE HOST WRITER RIGHT NOW?"
|
||||||
// persistent-mapped host writes can set it with zero new record kinds; a verify build
|
//
|
||||||
// pins that it is false, so that phase cannot land a silent semantic change under it.
|
// False and written by nobody through P3a and P4a; P5 (b1) is the phase it was waiting
|
||||||
|
// for and gives it its producer, with zero new record kinds exactly as planned: the
|
||||||
|
// bit rides MGPSubData::HasLiveHostWrites - a byte out of that payload's existing pad -
|
||||||
|
// and ApplyBufferWrite assigns it here. In a MONOLITH build nothing sets it and the
|
||||||
|
// MOBILEGL_PIPE_VERIFY wire (PinNoLiveHostWrites) still refuses a producer, so the pin
|
||||||
|
// survives the phase it was written for instead of being deleted by it.
|
||||||
|
//
|
||||||
|
// It is what IsBufferDrawCleanByHandle asks under split INSTEAD of the frontend
|
||||||
|
// object's IsMapped(), because a spawned server has no frontend object to ask. Getting
|
||||||
|
// that substitution wrong once already cost a silent regression - an emulated
|
||||||
|
// persistent map read draw-clean for ever - which MG_Test/SanityTest.cpp's
|
||||||
|
// DirectGLESBufferDrawProbe pair now pins from both sides.
|
||||||
Bool HasLiveHostWrites = false;
|
Bool HasLiveHostWrites = false;
|
||||||
|
|
||||||
// ---- P4a. Only a record of kind Texture ever carries these; a buffer's stay at
|
// ---- P4a. Only a record of kind Texture ever carries these; a buffer's stay at
|
||||||
|
|||||||
Reference in New Issue
Block a user