mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Docs] (MG_Backend/DirectGLES, MG_Backend/MGPipe, MG_Backend/Record, MG_Impl/Pipe, MG_IntegrationTest, MG_IntegrationTest/Harness, MG_IntegrationTest/Scenarios, MG_Pipe, MG_Pipe/generated, MG_Remote, MG_Remote/Client, MG_Remote/Server, MG_Remote/Transport, MG_Remote/Wire, MG_State/GLState, MG_Test/Pipe, MG_Test/Wire, MG_Util/Metrics, MobileGL, scripts, scripts/ci): cite symbols, not line numbers, in the shortened files
This commit is contained in:
+1
-1
@@ -163,7 +163,7 @@ namespace MobileGL {
|
||||
// lavapipe and on an Adreno 830, because the two Init calls are two statements in one
|
||||
// thread. And "the legacy pull path runs for it" is false under split - the client has
|
||||
// no pull path to fall back to - so the records are simply lost, and only a later
|
||||
// respecify's self-healing create (TextureEmit.h:872) ever brings one back.
|
||||
// respecify's self-healing create (EmitResourceRespecify in TextureEmit.h) ever brings one back.
|
||||
//
|
||||
// A BOUNDED WAIT INSIDE ServerConsumes CANNOT FIX THIS AND WOULD DEADLOCK: during
|
||||
// MG_State::Init() no handshake is in flight, and the one that will land is started
|
||||
|
||||
@@ -3078,7 +3078,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static_cast<Uint>(std::max(g_GLESCapabilities.MaxImageUnits, 0)));
|
||||
// P4a e3 (D-G2, D-J2), CORRECTED BY THE REVIEW'S MAJOR-1.
|
||||
//
|
||||
// THE RECORD'S WINDOW IS NOT THIS SWEEP'S MEMBERSHIP. D-J2 and PipeApply.h:461-464
|
||||
// THE RECORD'S WINDOW IS NOT THIS SWEEP'S MEMBERSHIP. D-J2 and PipeApply.h's MGPipeWorkingState
|
||||
// are a rule about RECORD RETENTION - entries outside the var-tail window are not
|
||||
// cleared in the applier - and say nothing about which DRIVER units a re-issue
|
||||
// sweep has to visit. The membership this loop needs is "every unit the driver
|
||||
@@ -3100,7 +3100,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// Start is as undescribed as one above Start + Count).
|
||||
//
|
||||
// A8, which the review adds to this package's assumption list: NOTHING IN THE
|
||||
// CONTRACT PINS THE WINDOW'S MEMBERSHIP. PipeApply.h:461-464 says only that the
|
||||
// CONTRACT PINS THE WINDOW'S MEMBERSHIP. PipeApply.h's MGPipeWorkingState says only that the
|
||||
// record is the last set as received, and that Start + Count above the bound is
|
||||
// Fatal{ProtocolCorruption}. Until package C documents in ImageEmit.h that the
|
||||
// window covers every unit the driver holds, this union is load-bearing; the
|
||||
|
||||
@@ -4555,7 +4555,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return PipeRecordAt(MG_Pipe::MGPipeApplier().ShaderCsos, cso);
|
||||
}
|
||||
|
||||
// N-7: D KEYS ON THE UPLOAD HALF; WIRE (AccumulatePendingUpload, PipeApply.cpp:814) KEYS ON
|
||||
// N-7: D KEYS ON THE UPLOAD HALF; WIRE (AccumulatePendingUpload, PipeApply.cpp) KEYS ON
|
||||
// THE WHOLE PACKED FIELD. That asymmetry is resolved HERE, in D's favour and with the
|
||||
// reason, rather than by widening the two comparisons: D's question is per (uploadTarget,
|
||||
// level) because that is the granularity its upload loop iterates - it is handed a
|
||||
@@ -4567,10 +4567,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
//
|
||||
// So the low byte is CROSS-CHECKED instead of ignored. The comparand is the resource's own
|
||||
// descriptor Target, which B fills from exactly the expression the sub-data's low byte
|
||||
// comes from (TextureEmit.h:223 / :517 against :1037), so the two are carried and equal by
|
||||
// construction and any disagreement is a seam defect, not a shape to learn. This is also
|
||||
// MGPResourceDesc::Target's first reader in this package (esprytobj-v2.md §2(6) recorded it
|
||||
// as having none).
|
||||
// comes from (TextureEmit.h's MGPipeBuildTextureResourceDesc and EmitResourceCreate against
|
||||
// EmitOneLevel), so the two are carried and equal by construction and any disagreement is a
|
||||
// seam defect, not a shape to learn. This is also MGPResourceDesc::Target's first reader in
|
||||
// this package (esprytobj-v2.md §2(6) recorded it as having none).
|
||||
static void NotePipeSubDataTargetLowByte(const MG_Pipe::MGPipeResourceRecord& record, Uint16 packedTarget,
|
||||
Uint16 level) {
|
||||
const Uint8 carried = MG_Pipe::MGPipeSubDataResourceTargetOf(packedTarget);
|
||||
@@ -4932,8 +4932,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// THE KEY IS THE ATTRIBUTE INDEX, NOT MGPVertexAttribWire::BindingIndex, and the two
|
||||
// live in different spaces. Espryt consumes RESOLVED attributes, so the client emits
|
||||
// set_vertex_buffers as one entry per attribute slot with
|
||||
// MGPVertexBuffer::BindingIndex == the attribute index (VertexInputEmit.h:190-193,
|
||||
// :229 - "Espryt consumes RESOLVED attributes, so the set is one entry per attribute
|
||||
// MGPVertexBuffer::BindingIndex == the attribute index (EmitVertexBuffers in VertexInputEmit.h -
|
||||
// "Espryt consumes RESOLVED attributes, so the set is one entry per attribute
|
||||
// slot with BindingIndex == the attribute index"), each carrying that attribute's
|
||||
// already-folded buffer, stride and divisor. MGPVertexAttribWire::BindingIndex is the
|
||||
// OTHER thing: the GL binding POINT the attribute was attached to by
|
||||
|
||||
@@ -3239,9 +3239,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// BoundSamplerStates[unit])` (the record arm of BindCurrentUnitSamplers, E's S4) could
|
||||
// never find a twin, the record arm bound nothing on every draw, and every glBindSampler
|
||||
// reached the driver only through the pre-handle program pass - S-1's confusion one
|
||||
// loop over, and exactly what SamplerEmit.h:201-205 forbids ("a backend must NOT key a
|
||||
// loop over, and exactly what ARCHITECTURE.md §2.3.1 forbids (a backend must NOT key a
|
||||
// sampler twin on a SamplerObject's lifetime id; the twin's life is
|
||||
// create_sampler_state -> delete_sampler_state").
|
||||
// create_sampler_state -> delete_sampler_state).
|
||||
//
|
||||
// This is the twin keyed the way the record is: resolved-or-created AT THE CSO HANDLE
|
||||
// (GetOrCreateByHandle, the same slot table, a slot the identity family can never hold)
|
||||
|
||||
@@ -540,7 +540,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// GetOrCreate from a twin destructor cannot resurrect it.
|
||||
//
|
||||
// The slot itself is NOT freed here: it belongs to the kind, and for a handle-keyed
|
||||
// kind the CLIENT frees it after the destroy call returns (SlotAllocator.h:60 - the
|
||||
// kind the CLIENT frees it after the destroy call returns (MGPipeSlotAllocator::Free - the
|
||||
// Gen bump rides the next handout, so a double free cannot skip a generation). An
|
||||
// entry whose Gen no longer matches is a twin of the slot's previous owner and is
|
||||
// left alone: the successor's own GetOrCreate resets it.
|
||||
|
||||
@@ -387,9 +387,9 @@ namespace MobileGL::MG_Pipe {
|
||||
}
|
||||
// THE ONE FIELD TABLE 2 NARROWS BY ARGUMENT. m_pixelStore[2] is one array indexed by
|
||||
// this accessor's own argument, exactly as m_bufferBindingSlot[15] is indexed by a
|
||||
// BufferTarget, and Coverage.def:62-69 already rules that such a field stays ONE row.
|
||||
// BufferTarget, and Coverage.def's GetBufferBindingSlot row already rules that such a field stays ONE row.
|
||||
// Only [0] (pack) has a carrier - set_pixel_pack_state, which the applier writes
|
||||
// (PipeApply.cpp:1373) - so the field is APPLIER-DERIVED and the UNPACK half is FATAL:
|
||||
// (MGPipeApplySetPixelPackState) - so the field is APPLIER-DERIVED and the UNPACK half is FATAL:
|
||||
// every gPipeInputs->GetPixelStoreParameters site in the tree passes false
|
||||
// (DirectGLES.cpp's PackStateFromContext, StoreReadbackRowsToClient and ReadPixels, Utils.cpp's
|
||||
// ReadbackImpl::StoreClientRows, RecordVerbSink::ReadBoundFramebufferTight and
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// StagedShadow.h (R-11), held to its four rulings (CONTRACT-P5C.md §2.1).
|
||||
//
|
||||
// THE DEFECT THIS ENDS (T1/T5). resource_subdata's texture half stages the level bytes into
|
||||
// SEG_STAGE and ApplyTextureUpload (PipeApply.cpp:989-1036) drops the pointer after the gate,
|
||||
// SEG_STAGE and ApplyTextureUpload (PipeApply.cpp) drops the pointer after the gate,
|
||||
// the accumulation and the serial. Espryt then re-reads the client's MipmapStorage at sync
|
||||
// time (Managers.cpp:6940, :7129, :7198, :7315), reads per-level shape off the client object
|
||||
// (:6928, :6936, :7314, :7332, DirectGLES.cpp:8502-8507, :8528-8529), and Magma WRITES the
|
||||
|
||||
@@ -819,7 +819,7 @@ namespace MobileGL::MG_Pipe {
|
||||
// R-8 (c1). THE SECOND CONJUNCT MOVES UNDER SPLIT, AND ONLY UNDER SPLIT.
|
||||
//
|
||||
// `MGPipeGetResourceOps() != nullptr` asks "has a backend registered the consumer". That
|
||||
// table is the SERVER's registration and it is a PROCESS-WIDE global (PipeApply.cpp:402):
|
||||
// table is the SERVER's registration and it is a PROCESS-WIDE global (g_resourceOps in PipeApply.cpp):
|
||||
// under inproc a client reading it answers correctly BY ACCIDENT, and under spawn the
|
||||
// client process has no backend at all, so the read answers null and five record families
|
||||
// stop emitting - silently, while the emitters go on clearing their per-level dirty flags
|
||||
@@ -956,8 +956,8 @@ namespace MobileGL::MG_Pipe {
|
||||
// - and DeviceLost() is false.
|
||||
if (MG_Remote::Client::ClientWireRecordsEmitted() == before &&
|
||||
!MG_Remote::Client::ClientSession::DeviceLost()) {
|
||||
// UncarriedInitialBytes, not a second word for the same family: WireTables.cpp:418
|
||||
// already dies of exactly this - a respecify that crossed with no initial bytes -
|
||||
// UncarriedInitialBytes, not a second word for the same family: Wire_Escape_ResourceRespecify
|
||||
// (WireTables.cpp) already dies of exactly this - a respecify that crossed with no initial bytes -
|
||||
// under that name, and two words for one family is the vocabulary drift a6
|
||||
// censused and 5.2's .def exists to bound (P7 wave 0).
|
||||
MGLOG_F("MGPipe: Fatal{UncarriedInitialBytes, \"resource_respecify\"} - the "
|
||||
@@ -976,7 +976,7 @@ namespace MobileGL::MG_Pipe {
|
||||
// THE CAP THE TWO CONTENT WALKS BELOW CUT A RANGE AT. The record's own bound (2^32-1) is not
|
||||
// the one a real upload meets first: one piece's bytes are staged WHOLE in SEG_STAGE, a
|
||||
// linear arena, and a blob larger than that arena is Fatal{RingOverrun, "SEG_STAGE"} at the
|
||||
// encoder rather than a split (PipeWireCodec.cpp:856-864). Measured on the CI traces: a
|
||||
// encoder rather than a split (PipeWireEncoder::StageAllocate). Measured on the CI traces: a
|
||||
// 128 MiB arena's whole-buffer follow-up against a 32 MiB segment aborted there, which is
|
||||
// the RingOverrun this walk exists to prevent.
|
||||
//
|
||||
@@ -2008,7 +2008,7 @@ namespace MobileGL::MG_Pipe {
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
|
||||
// DELETED with an active transport (CONTRACT-P5C §4.2): R-12's caps
|
||||
// re-publication already invalidates the CLIENT's compile environment when the
|
||||
// second snapshot arrives (CapsMirror.cpp:78-80), and this forward is a write
|
||||
// second snapshot arrives (CapsMirror::Adopt), and this forward is a write
|
||||
// into the frontend with no wire shape. A caller that still needs it under a
|
||||
// transport is a defect to fix, not a pull to serve.
|
||||
return;
|
||||
@@ -3338,8 +3338,8 @@ namespace MobileGL::MG_Pipe {
|
||||
|
||||
// P3a's vertex segment, in the order the design fixes: vertex elements, then the
|
||||
// vertex buffers that fill them, then the index binding. All three are LIVE now (m1):
|
||||
// bits 5 / 9 / 10 map onto kMGPipeSubsystemVertexInput in Tracker.h:145-148 and all
|
||||
// three emitters have bodies, so `wants()` answers true whenever bit 8 is in the push
|
||||
// bits 5 / 9 / 10 map onto kMGPipeSubsystemVertexInput in Tracker.h's MGPipeSubsystemForDirty and
|
||||
// all three emitters have bodies, so `wants()` answers true whenever bit 8 is in the push
|
||||
// mask - which the phase default 0x1ff sets, on every backend. The sentence that used
|
||||
// to stand here ("all three still resolve to false today - their dirty bits map to no
|
||||
// subsystem") was the contract commit's and stopped being true when the client landed.
|
||||
|
||||
@@ -560,7 +560,7 @@ namespace MobileGL::MG_Record {
|
||||
info.RestartIndex = 0;
|
||||
info.DrawIdOffset = 0;
|
||||
info.IndexResource = MG_Pipe::kMGPipeNullHandle;
|
||||
info.MinIndex = ~0u; // "unknown", MGPipeTypes.h:1330
|
||||
info.MinIndex = ~0u; // "unknown", MGPDrawInfo::MinIndex in MGPipeTypes.h
|
||||
info.MaxIndex = ~0u;
|
||||
info.XfbCpuCapturedVertices = 0;
|
||||
info.NumDraws = 1;
|
||||
|
||||
@@ -534,7 +534,7 @@ function(mgl_itest_probe_for_two_symbols outVar directory symbolRegexA symbolReg
|
||||
endfunction()
|
||||
|
||||
# DirectGLES' Track H arm, probed by the subsystem bit it is gated on rather than by
|
||||
# SlotTables.h existing: the bit is declared in the contract (MG_Pipe/MGPipe.h:77) and the
|
||||
# SlotTables.h existing: the bit is declared in the contract (kMGPipeSubsystemEsprytSlots, MG_Pipe/MGPipe.h) and the
|
||||
# backend has to name it to honour MOBILEGL_PIPE_PUSH's default mask, whatever files package C
|
||||
# spreads the slot tables across.
|
||||
mgl_itest_probe_for_symbol(MGL_ITEST_ESPRYT_SLOTS
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
// MOBILEGL_TRANSPORT=inproc` (review finding M-5).
|
||||
// 2. `ClientSession::Active() != nullptr`. c0 made this one deliberately return null rather
|
||||
// than Fatal, because "does a session exist" has a legitimate "no" - it is the monolith
|
||||
// answer (ClientSession.cpp:31). So it is exactly "a client session exists in this process",
|
||||
// answer (ClientSession::Active's definition). So it is exactly "a client session exists in this process",
|
||||
// and no amount of editing stub MESSAGES makes a null pointer non-null.
|
||||
// 3. `ImplementedVerbCount() > 0`. c0's stub returns 0; the contract gives this function the
|
||||
// job of making "a table that silently lost an emitter" distinguishable from "a table that
|
||||
|
||||
@@ -819,12 +819,11 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
|
||||
EXPECT_EQ(liveAfter, liveBefore)
|
||||
<< kChurn << " " << kindName << SpaceSuffix(space)
|
||||
<< " objects were created, drawn with and destroyed and "
|
||||
<< (liveAfter - liveBefore)
|
||||
<< " objects were created, drawn with and destroyed and " << (liveAfter - liveBefore)
|
||||
<< " slots never came back. Each one holds a SlotState, a lifetime-id map node "
|
||||
"and the applier's record for the life of the process, and past the kind's "
|
||||
"slot bound every create trips Fatal{ProtocolCorruption} for good "
|
||||
"(PipeApply.h:97-113). This is P3a's C-1 defect, which is why every P4a kind "
|
||||
"(PipeApply.h's slot bounds). This is P3a's C-1 defect, which is why every P4a kind "
|
||||
"frees its slot from the frontend destructor through one client-side helper "
|
||||
"(D-I1) rather than from a backend death table. Backend "
|
||||
<< Gl().BackendName();
|
||||
|
||||
@@ -290,7 +290,7 @@ void main() { oColor = vec4(vColor, 1.0); }
|
||||
// undefined and this function returned at the line above before asserting anything;
|
||||
// the split build's monolith lane, where the tracker IS compiled, adopts
|
||||
// (MGPipeApplyMapPersistent's R-6 decline is ANDed with `Transport != Monolith`,
|
||||
// PipeApply.cpp:2005), and seven entries went red the first time the assertion ran at
|
||||
// PipeApply.cpp), and seven entries went red the first time the assertion ran at
|
||||
// all.
|
||||
//
|
||||
// So the expectation is `live == (arm == emulated)`, taken from the SAME peek
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
// P3a migrates two subsystems: kMGPipeSubsystemResources (bit 7, the resource_* family) and
|
||||
// kMGPipeSubsystemVertexInput (bit 8, vertex elements / buffers / index). The push build's default
|
||||
// mask becomes kMGPipeSubsystemsMigratedAtP3a = 0x1ff, and P2's 0x7f survives as the control that
|
||||
// clears exactly those two bits - MGPipe.h:79's rule that every phase's constant keeps meaning what
|
||||
// it meant, so an operator's recorded mask is still readable a phase later.
|
||||
// clears exactly those two bits - the rule in MGPipe.h's MOBILEGL_PIPE_PUSH bitmask section that every
|
||||
// phase's constant keeps meaning what it meant, so an operator's recorded mask is still readable a phase later.
|
||||
//
|
||||
// That A/B is what every "push vs pull" number in MEASUREMENTS.md is taken against, and it has one
|
||||
// characteristic failure mode: the bits stop steering anything, both arms run the same code, and
|
||||
|
||||
@@ -122,12 +122,12 @@
|
||||
// reviewer's head and so that the generator's completeness check has something to accept.
|
||||
#define MGP_VERB_OP_EXEMPT_LIST(X) \
|
||||
X(Present, \
|
||||
"FillPoints.def:21 - Present and SetSwapInterval go through BackendObject virtuals and " \
|
||||
"Present and SetSwapInterval go through BackendObject virtuals and " \
|
||||
"read no frontend state, so they are not verbs here. There is no MGPipeVerb::Present, " \
|
||||
"MGPipeValidateForVerb is never called for it, and stamping would retire the previous " \
|
||||
"verb's answers with nothing to put in their place. It is class B all the same.") \
|
||||
X(SetSwapInterval, \
|
||||
"the same sentence of FillPoints.def:21, and it is class C besides") \
|
||||
"not a verb for the same reason as Present, and it is class C besides") \
|
||||
X(Flush, \
|
||||
"not a verb at all: the verb census (BRIEF §12 C-3) found Flush is not a GLFunctionsTable " \
|
||||
"slot and glFlush/glFinish are empty function bodies (Definitions.cpp:111-112), so there " \
|
||||
|
||||
@@ -360,9 +360,9 @@ namespace MobileGL::MG_Pipe {
|
||||
// of a content hash (section 4.2.1). Widened from the plan's two bytes, which
|
||||
// cannot hold one.
|
||||
Uint32 GlNameForDiag;
|
||||
// The pair HasRespecifiedLevel above gates. Byte for byte the two members of
|
||||
// MGPRespecifiedLevel (PipeApply.h:792-795), in that order, so the applier's existing
|
||||
// struct and this carrier are one layout - but spelled as two Uint16s rather than as
|
||||
// The pair HasRespecifiedLevel above gates. Byte for byte the first two members of
|
||||
// MGPRespecifiedLevel (PipeApply.h), in that order, so the applier's existing
|
||||
// struct and this carrier share that pair's layout - but spelled as two Uint16s rather than as
|
||||
// that type, because PipeApply.h includes THIS header and not the other way round.
|
||||
Uint16 RespecifiedUploadTarget;
|
||||
Uint16 RespecifiedLevel;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
/* as resource_subdata records IMMEDIATELY AFTER this one. Reuses a path that is */ \
|
||||
/* already chunked (MGPipeForEachSubDataRecordRange) and already acceptance-gated; the */ \
|
||||
/* cost is one extra record. NOTE the SECOND uncarried companion on this call, the */ \
|
||||
/* MGPRespecifiedLevel* scope (PipeApply.h:792) - it is NOT bytes and NOT covered by */ \
|
||||
/* MGPRespecifiedLevel* scope (PipeApply.h) - it is NOT bytes and NOT covered by */ \
|
||||
/* this ruling; table 1 row 19 gives it the two pad fields of MGPResourceDesc. */ \
|
||||
X(ResourceRespecify, MGPResourceDesc, kScreen, kNeedsAck|kReplySlot, kWaitReply) \
|
||||
X(ResourceDestroy, MGPHandleOnly, kScreen, kNone, kWaitNone) \
|
||||
|
||||
@@ -206,10 +206,9 @@ inline constexpr SizeT kMGPipeFieldOwnershipRowCount =
|
||||
static_assert(kMGPipeFieldOwnershipRowCount == 70, "table 2's row count moved");
|
||||
|
||||
// An ARGUMENT-KEYED narrowing of one field. The field keeps its single row above; this
|
||||
// says that one argument value of it belongs to a different class. Coverage.def:62-69 already
|
||||
// rules the shape for GetBufferBindingSlot - "THE ROW STAYS ONE ROW ... the field is ONE array
|
||||
// that a second row of the same name could only duplicate" - and m_pixelStore[2] is the same
|
||||
// shape indexed by its own isUnpack argument.
|
||||
// says that one argument value of it belongs to a different class. Coverage.def's comment on the
|
||||
// GetBufferBindingSlot row already rules the shape - "The field is one array, so it stays one
|
||||
// row" - and m_pixelStore[2] is the same shape indexed by its own isUnpack argument.
|
||||
struct MGPipeFieldArgumentOwnership {
|
||||
MGPipeInputField Field;
|
||||
Uint32 Arg0;
|
||||
@@ -265,8 +264,8 @@ static_assert(MGPipeEveryFieldIsClassified(),
|
||||
// mask and name, so a field inside that mask would read FRESH while holding the previous
|
||||
// verb's value - the one silent failure this table has. The exemptions:
|
||||
// Flush not a verb at all: the verb census (BRIEF §12 C-3) found Flush is not a GLFunctionsTable
|
||||
// Present FillPoints.def:21 - Present and SetSwapInterval go through BackendObject virtuals and
|
||||
// SetSwapInterval the same sentence of FillPoints.def:21, and it is class C besides
|
||||
// Present Present and SetSwapInterval go through BackendObject virtuals and
|
||||
// SetSwapInterval not a verb for the same reason as Present, and it is class C besides
|
||||
constexpr MGPipeVerb MGPipeVerbForWireOp(MGPWireOp op) {
|
||||
switch (op) {
|
||||
case MGPWireOp::Clear: return MGPipeVerb::Clear;
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
// The MGPCaps snapshot's serializers, and the CallMask encoding. Owner: package w1 (the two
|
||||
// blob codecs) and s1 (the handshake asserts). Signatures by c0.
|
||||
//
|
||||
// MGPCaps (MG_Pipe/MGPipeTypes.h:126-139) has four members. Two are flat and cross by memcpy
|
||||
// MGPCaps (MG_Pipe/MGPipeTypes.h) has four members. Two are flat and cross by memcpy
|
||||
// (DynamicBackendParameters Dynamic, Uint64 CallMask); two are MGPBlobRefs over containers
|
||||
// and HAVE NO SERIALIZER IN THE TREE - the header says so itself at MGPipeTypes.h:134-136,
|
||||
// and HAVE NO SERIALIZER IN THE TREE - the header says so itself above MGPCaps::FormatCapabilities,
|
||||
// "Their serializers land with the transport (P5)". They are:
|
||||
//
|
||||
// FormatCapabilities -> FormatCapabilityCache (MG_Backend/BackendObject.h:93-98):
|
||||
|
||||
@@ -504,8 +504,8 @@ namespace MobileGL::MG_Remote::Client {
|
||||
static_cast<unsigned>(head->RangeCount),
|
||||
static_cast<unsigned long long>(tail));
|
||||
}
|
||||
// EventRange and MGPRange are the same two Uint64s (EventRing.h:61-66
|
||||
// asserts it), so the tail is handed over as-is rather than copied into
|
||||
// EventRange and MGPRange are the same two Uint64s (EventRing.h asserts it next
|
||||
// to EventRange), so the tail is handed over as-is rather than copied into
|
||||
// a second array a later reader could get out of step with.
|
||||
const auto* ranges = reinterpret_cast<const MG_Pipe::MGPRange*>(
|
||||
static_cast<const Uint8*>(view.payload) + sizeof(*head));
|
||||
@@ -1989,8 +1989,8 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// 1. publish and let the server drain. Bounded: a lost record must be a red lane, not
|
||||
// a hung exit.
|
||||
if (m_link->Attached()) {
|
||||
// The PRODUCER's own last-published seq, not RingControl::submittedSeq. Ring.h:72-77
|
||||
// permits submittedSeq to be published lazily and Ring.h:243 encourages batching
|
||||
// The PRODUCER's own last-published seq, not RingControl::submittedSeq. Ring.h's header comment
|
||||
// permits submittedSeq to be published lazily and RingProducer::Publish encourages batching
|
||||
// the publish, so the shared watermark is allowed to lag the emitter - and a drain
|
||||
// that waited for `appliedSeq >= submittedSeq` would then under-wait and free an
|
||||
// emitter-owned var-tail while a record still names it. With the verb barrier armed
|
||||
@@ -2677,9 +2677,9 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// (P5d round 3, package D). Every line below is a pure predicate whose only effect is
|
||||
// "no violation, go home"; the Fatal fires on the AND of all of them, so reordering
|
||||
// cannot change which touches abort. It changes only what a touch that does NOT abort
|
||||
// pays, and this guard sits on the per-verb fill path (PipeFill.cpp:542, :1951, :2789
|
||||
// - MGPipeValidateForVerb is 4.11% self / 10.9% inclusive of the client thread), so
|
||||
// "what it costs to answer no" is the whole of its cost in a healthy process.
|
||||
// pays, and this guard sits on the per-verb fill path (PipeFill.cpp's MGPipeNoteFrontendMutation,
|
||||
// MGPipeLeaveVerb and MGPipeValidateForVerb - the last is 4.11% self / 10.9% inclusive of the
|
||||
// client thread), so "what it costs to answer no" is the whole of its cost in a healthy process.
|
||||
//
|
||||
// The two config reads come first because they are the two answers that are constant
|
||||
// for the life of the process and true for nearly every process that runs this code:
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// catalogue's reply rule, and it exists for exactly one row - resource_subdata's
|
||||
// BUFFER half, whose Bool the only caller discards (PipeFill.cpp's
|
||||
// MGPipeEmitResourceSubData). The row keeps kReplySlot on the wire (the server still
|
||||
// posts; an unread slot is harmless, ReplySlot.h:16, 105), so nothing about the
|
||||
// posts; an unread slot is harmless, ReplySlotPool's seq-stamped slots), so nothing about the
|
||||
// protocol moves - what moves is that under run-ahead this client neither waits for
|
||||
// the answer nor reads it, which is what makes the 64 KB persistent-map push
|
||||
// fire-and-forget instead of a hidden round trip per block. It is NOT a licence to
|
||||
@@ -302,7 +302,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// direction - it only adds waits - so it is allowed.
|
||||
Bool RunAheadArmed() const;
|
||||
|
||||
// §2.3: the four O-class SharedPtr rows of gPipeInputs (PipeInputs.h:801-809 - the
|
||||
// §2.3: the four O-class SharedPtr rows of gPipeInputs (PipeInputs' SharedPtr members - the
|
||||
// bound VAO and the three programs), dropped on the GL thread once a barriered apply
|
||||
// has returned. Under lockstep the next fill overwrites them within a verb; under
|
||||
// run-ahead the next fill may be a frame away, and a pinned VAO whose last owner ends
|
||||
@@ -508,13 +508,13 @@ namespace MobileGL::MG_Remote::Client {
|
||||
|
||||
// SEG_EVENT's ring capacity, exposed so the readback path can slice a writeback
|
||||
// request into records that always fit (RingProducer::MaxRecordBytes ==
|
||||
// capacity/2, Ring.h:288).
|
||||
// capacity/2, Ring.h).
|
||||
Uint64 EventRingCapacityBytes() const;
|
||||
|
||||
// SEG_STAGE's capacity, exposed so a content emitter can cut a range into records that
|
||||
// always stage whole: the staging area is a linear arena of exactly these bytes (w1),
|
||||
// and one blob larger than it is Fatal{RingOverrun, "SEG_STAGE"} at the encoder rather
|
||||
// than a split (PipeWireCodec.cpp:856-864).
|
||||
// than a split (PipeWireEncoder::StageAllocate in PipeWireCodec.cpp).
|
||||
Uint64 StageCapacityBytes() const;
|
||||
|
||||
// The CLIENT's own segment table (P5c ev, CONTRACT-P5C §4.3): the writeback
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// avoided.
|
||||
const Uint64 quarter = stageCapacityBytes / 4;
|
||||
// A floor so a pathologically small operator-supplied segment cannot make the piece
|
||||
// width zero, which the walk reads as "refuse" (ResourceTracker.h:254); such a segment
|
||||
// width zero, which the walk reads as "refuse" (MGPipeForEachSubDataRecordRange); such a segment
|
||||
// is broken anyway, and the producer's Fatal{RingOverrun} names it on the first stage.
|
||||
const Uint64 chunk = quarter < 4096 ? 4096 : quarter;
|
||||
return static_cast<SizeT>(chunk > stageCapacityBytes ? stageCapacityBytes : chunk);
|
||||
|
||||
@@ -39,11 +39,10 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// 0 with no session, where every writeback is synchronous and needs no ordering.
|
||||
Uint64 GpuWriteOrderSeq();
|
||||
|
||||
|
||||
// The slice a whole-buffer readback is cut into so one writeback event always fits
|
||||
// SEG_EVENT. The writeback's bytes travel INLINE in the event record (P5c ev, CONTRACT-P5C
|
||||
// §4.2), one record must fit the ring (RingProducer::MaxRecordBytes == capacity/2,
|
||||
// Ring.h:288), and a 24 MiB arena's single shot cannot (measured: Fatal{EventRingOverflow}
|
||||
// Ring.h), and a 24 MiB arena's single shot cannot (measured: Fatal{EventRingOverflow}
|
||||
// on LargeArenaAdoptionScenario.GpuWriteIntoTheArenaIsReadBack). 0 when no session is
|
||||
// active - the synchronous arm never slices.
|
||||
SizeT BufferWritebackSliceBytes();
|
||||
@@ -52,7 +51,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// before the range has to be cut. SEG_STAGE is a linear arena, one record's blob is
|
||||
// allocated from it whole, and a blob larger than the arena is
|
||||
// Fatal{RingOverrun, "SEG_STAGE"} at the encoder rather than a split
|
||||
// (PipeWireCodec.cpp:856-864) - measured on the CI traces, where a 128 MiB arena's
|
||||
// (PipeWireCodec.cpp's PipeWireEncoder::StageAllocate) - measured on the CI traces, where a 128 MiB arena's
|
||||
// whole-buffer follow-up against a 32 MiB segment aborted. 0 means "do not cut", i.e. the
|
||||
// record's own bound (MGPipeForEachSubDataRecordRange's default), which is the answer for
|
||||
// monolith, for the server role's own uploads (they run the monolith adapter) and for a
|
||||
@@ -61,7 +60,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
|
||||
// The clamp itself, a pure function of the segment's size so that a unit gate can drive the
|
||||
// splitter at exactly the value a live session produces. A quarter of the segment, never
|
||||
// below 4096 (a zero-width piece is a refusal, ResourceTracker.h:254) and never above the
|
||||
// below 4096 (a zero-width piece is a refusal, MGPipeForEachSubDataRecordRange) and never above the
|
||||
// segment (a larger cap would stage the very blob the arena refuses).
|
||||
SizeT MGPipeStageChunkBytesFor(Uint64 stageCapacityBytes);
|
||||
|
||||
|
||||
@@ -227,14 +227,14 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// -- the rows that fit none of the three shapes --------------------------------
|
||||
|
||||
// set_dynamic_state. AN OPTIONAL BLOB, NOT A MANDATORY ONE (round-2 regression, the
|
||||
// census's Fatal{BlobMissing, "SetDynamicState"}). EmitRenderState (PipeFill.cpp:2405-
|
||||
// 2421) sends a set_dynamic_state whenever the RenderState VERSION moved, and when the
|
||||
// census's Fatal{BlobMissing, "SetDynamicState"}). EmitRenderState (PipeFill.cpp) sends a
|
||||
// set_dynamic_state whenever the RenderState VERSION moved, and when the
|
||||
// chunk-level suppressor finds NO dynamic chunk changed it sends the 32-byte header with
|
||||
// ChunkMask == 0 and blobByteCount == 0 - a version-only update, which the comment there
|
||||
// calls out as deliberate. The generic MGP_WIRE_BLOB wrapper stages through StageRequired,
|
||||
// which Fatals on a zero count, so a legal header-only update aborted the process with a
|
||||
// blob-shape complaint that had nothing to do with the actual state of the pipe. The
|
||||
// DECODER already handles it: PipeWireCodec.cpp:1571-1587 takes the `ChunkMask == 0` arm
|
||||
// DECODER already handles it: PipeWireDecoder::ApplyChecked takes the `ChunkMask == 0` arm
|
||||
// (CheckBlobIsHonest, no blob resolved) and applies the header alone. So the emitter is
|
||||
// the only side that was wrong; it stages OPTIONALLY, exactly like the two sub-data rows.
|
||||
// A non-empty mask still stages (blobByteCount > 0), so the reduced-path scenarios, whose
|
||||
@@ -504,9 +504,9 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// not - the shared pool, whose slot is seq % slotCount and is reused - the answer the
|
||||
// drain comes back for may already be gone, and there is NO recovery: retrying wedges
|
||||
// the row for the rest of the process, and re-latching the object by sending another
|
||||
// create wipes the server's record along with its PendingUploads (PipeApply.cpp:2033,
|
||||
// PipeApply.h:332) while the client cleared its dirty flags at emission
|
||||
// (PipeApply.h:298-304), so nobody re-sends those texels. So it takes the blocking
|
||||
// create wipes the server's record along with its PendingUploads (MGPipeApplyResourceCreate,
|
||||
// MGPipeResourceRecord::PendingUploads) while the client cleared its dirty flags at emission
|
||||
// (PipeApply.h's PendingUpload comment), so nobody re-sends those texels. So it takes the blocking
|
||||
// path instead, which is the pre-window shape exactly.
|
||||
const Uint32 window = session.LinkRetainsReplies() ? CreateWindowEffective() : 1u;
|
||||
// P15: UNDER RUN-AHEAD A CREATE IS FIRE-AND-FORGET. The handle is the client's own
|
||||
@@ -651,7 +651,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// the only caller discards it (PipeFill.cpp's MGPipeEmitResourceSubData), so
|
||||
// "accepted" is not a re-derivation of a server decision - it is the absence of a
|
||||
// question. The server still posts the real answer into the slot; nothing reads
|
||||
// it, which ReplySlot.h:16, 105 makes legal. A row whose acceptance a caller USES
|
||||
// it, which ReplySlotPool's seq-stamped slots make legal. A row whose acceptance a caller USES
|
||||
// may never take this path - R-5 has not moved.
|
||||
const Bool wantReply = MG_Pipe::MGPipeSubDataWantsItsReply(record);
|
||||
Int32 status = 0;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// moves for the class-B verbs too. So an armed Clear-only lane is green on EmitSeq while this
|
||||
// counter stays 0. Making the harness read THIS instead is t1's file (SplitRuntimePeek), so
|
||||
// the honest statement is the one here: c1 counts the routed ordinal at the only place that
|
||||
// can, PipeFill's InitialBytesNotCarried self-check reads it (PipeFill.cpp:755), and the
|
||||
// can, PipeFill's InitialBytesNotCarried self-check reads it (MGPipeEmitResourceRespecify), and the
|
||||
// lane's own arming remains EmitSeq until t1 re-points it. c1-v3.md M7 has the full note.
|
||||
Uint64 ClientWireRecordsEmitted();
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
}
|
||||
++m_applierResetSerial;
|
||||
// THE WHOLE POINT OF THE RECORD: the reset runs HERE, on the apply thread, against
|
||||
// the g_applier this role owns (PipeApply.cpp:409). The layer-2 guard inside
|
||||
// the applier this role owns (MGPipeApplier() in PipeApply.h). The layer-2 guard inside
|
||||
// MGPipeApplierReset passes because this IS the apply thread; the GL-thread direct
|
||||
// call it replaced is the Fatal arm.
|
||||
MG_Pipe::MGPipeApplierReset();
|
||||
@@ -754,10 +754,10 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// calling it unconditionally on every record is both correct and cheap. Four ops stamp:
|
||||
// Clear -> Clear, DrawVbo -> DrawArrays, ReadPixels -> ReadPixels, Blit -> BlitFramebuffer.
|
||||
//
|
||||
// PRESENT IS DELIBERATELY NOT ONE, although contract 7 puts it in class B: FillPoints.def:21
|
||||
// says Present and SetSwapInterval "go through BackendObject virtuals and read no frontend
|
||||
// state, so they are not verbs here". There is no MGPipeVerb::Present, and stamping there
|
||||
// would retire the previous verb's answers with nothing to put in their place.
|
||||
// PRESENT IS DELIBERATELY NOT ONE, although contract 7 puts it in class B: Present and
|
||||
// SetSwapInterval sit outside GLFunctionsTable (BackendObject.h) and read no frontend state,
|
||||
// so FillPoints.def's MGP_FILL_VERB_LIST has no row for them. There is no MGPipeVerb::Present,
|
||||
// and stamping there would retire the previous verb's answers with nothing to put in their place.
|
||||
void PipeApplier::StampVerbBoundary(MG_Pipe::MGPWireOp op) {
|
||||
const MG_Pipe::MGPipeVerb verb = MG_Pipe::MGPipeVerbForWireOp(op);
|
||||
if (verb == MG_Pipe::MGPipeVerb::kVerbCount) return; // not a verb boundary: stamp nothing
|
||||
|
||||
@@ -420,7 +420,7 @@ int RunSession(std::unique_ptr<SocketTransport> control, std::vector<std::uint8_
|
||||
}
|
||||
DelaySessionHandshakeForTest();
|
||||
// THE FIRST FRAME, CLASSIFIED BY THE SAME FUNCTION THE TCP SUPERVISOR USES (plan §1.1, fuzz
|
||||
// arm 1 row: "ServerMain.cpp:193 CtrlEnvelopeBufferHasIdentifier"). The identifier is asked
|
||||
// arm 1 row: add CtrlEnvelopeBufferHasIdentifier to this first-frame read). The identifier is asked
|
||||
// first, and every malformed shape is refused BY NAME - this read used to `_exit(67)` on all
|
||||
// of them, so a peer that sent garbage got a bare close and the supervisor counted a fault.
|
||||
const auto shape = Server::ClassifyFirstFrame(helloFrame.data(), helloFrame.size());
|
||||
@@ -840,7 +840,7 @@ bool ReapActiveSession(pid_t& active, unsigned long& sessionsFaulted) {
|
||||
if (::waitpid(active, &status, WNOHANG) != active) return false;
|
||||
const int reaped = static_cast<int>(active);
|
||||
active = -1;
|
||||
// WIFEXITED / WIFSIGNALED / WTERMSIG, the same three questions ServerSpawn.cpp:289 asks of
|
||||
// WIFEXITED / WIFSIGNALED / WTERMSIG, the same three questions ReapServer (ServerSpawn.cpp) asks of
|
||||
// the same kind of child - but logged rather than folded into one signed integer, because
|
||||
// this side has nobody to return the answer to.
|
||||
if (WIFEXITED(status)) {
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
|
||||
// THE THREE FLAG-SPACE COLLISIONS, AS TRIPWIRES RATHER THAN AS A COMMENT.
|
||||
//
|
||||
// MGPipeCallFlags (MG_Pipe/MGPipe.h:42-54) and RingRecordFlags (Ring.h) are separate spaces
|
||||
// MGPipeCallFlags (MG_Pipe/MGPipe.h) and RingRecordFlags (Ring.h) are separate spaces
|
||||
// that overlap, and three bits mean DIFFERENT things in each. Ring.h's enum carries the
|
||||
// table; these are the assertions that break the build if either enum is renumbered, so the
|
||||
// collision can never become news again. They live here because this is the nearest .cpp
|
||||
@@ -105,7 +105,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
|
||||
namespace {
|
||||
|
||||
// A control-plane frame is small by construction (ITransport.h:56-58: bulk bytes
|
||||
// A control-plane frame is small by construction (ITransport::SendFrame: bulk bytes
|
||||
// belong in shm, never here), so one stack-free vector sized from PeekFrameSize is
|
||||
// the whole reader. The BUFFER_TOO_SMALL half of ReceiveFrame's contract is what
|
||||
// makes the two-step safe: a short buffer leaves the message queued.
|
||||
@@ -452,8 +452,8 @@ namespace MobileGL::MG_Remote::Server {
|
||||
head.RangeCount = static_cast<std::uint32_t>(rangeCount);
|
||||
std::memcpy(slot, &head, sizeof(head));
|
||||
if (tailBytes != 0) {
|
||||
// EventRange and MGPRange are the same two Uint64s (EventRing.h:61-66 asserts
|
||||
// it), so the tail is a plain copy rather than a per-element conversion.
|
||||
// EventRange and MGPRange are the same two Uint64s (EventRing.h asserts it at
|
||||
// EventRange's declaration), so the tail is a plain copy rather than a per-element conversion.
|
||||
std::memcpy(static_cast<Uint8*>(slot) + sizeof(head), ranges,
|
||||
static_cast<SizeT>(tailBytes));
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// which is how DirectGLES - which has no OnCapsInvalidated producer - tells the client
|
||||
// its InitCapabilities re-ran, without any dev-shaped backend edit. It is also the
|
||||
// re-open signal for "the server's ES context died and its rings were dropped", the
|
||||
// event MGPipeCallbacks has no eleventh slot for (MGPipeCallbacks.h:56-58).
|
||||
// event MGPipeCallbacks has no slot for (the count is pinned by kMGPipeCallbackCount).
|
||||
MobileGLResult PublishCapsSnapshot();
|
||||
|
||||
Transport::RingConsumer& CommandRing();
|
||||
|
||||
@@ -36,14 +36,14 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
kEventFenceSignaled = 5, // P10: a client fence the server saw signaled (CONTRACT-P10 §1)
|
||||
};
|
||||
|
||||
// The 8-byte {slot, gen} pair, mirrored (MGPipeHandles.h:54-65).
|
||||
// The 8-byte {slot, gen} pair, mirrored (MGPipeHandle in MGPipeHandles.h).
|
||||
struct EventHandle {
|
||||
std::uint32_t Slot;
|
||||
std::uint32_t Gen;
|
||||
};
|
||||
static_assert(sizeof(EventHandle) == 8, "the handle is the 8-byte {slot, gen} pair");
|
||||
|
||||
// MGPRange, mirrored (MGPipeTypes.h:63-67).
|
||||
// MGPRange, mirrored (MGPipeTypes.h).
|
||||
struct EventRange {
|
||||
std::uint64_t Offset;
|
||||
std::uint64_t Size;
|
||||
@@ -79,7 +79,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
};
|
||||
static_assert(sizeof(EventGpuWrittenHead) == 16, "wire shape");
|
||||
|
||||
// OnSurfaceChanged(const MGPSurfaceInfo*), mirrored (MGPipeTypes.h:1394-1401).
|
||||
// OnSurfaceChanged(const MGPSurfaceInfo*), mirrored (MGPipeTypes.h).
|
||||
struct EventSurfaceChangedHead {
|
||||
std::uint32_t Width;
|
||||
std::uint32_t Height;
|
||||
@@ -135,7 +135,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
void* Reserve(EventKind kind, std::uint64_t payloadBytes) {
|
||||
void* slot = m_producer.Reserve(static_cast<std::uint16_t>(kind), kRecNone, payloadBytes);
|
||||
if (slot == nullptr && m_cmdControl != nullptr) {
|
||||
// "SEG_EVENT full, server stopped applying" - Ring.h:123. Latched
|
||||
// "SEG_EVENT full, server stopped applying" - RingControl::eventRingFull in Ring.h. Latched
|
||||
// here, cleared by the consumer once it has drained.
|
||||
m_cmdControl->eventRingFull.store(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// THESE TWO RETIRE CONTRACT-P5 §3.9. That ruling kept the doorbell
|
||||
// accessors on the session "so that this stays one switch in one file
|
||||
// rather than two virtuals every transport has to invent a home for"
|
||||
// (ServerSession.cpp:709-724) - and the switch it produced aborts for
|
||||
// (ServerSession::ConsumerDoorbell) - and the switch it produced aborts for
|
||||
// every role but InProcess, so a spawn apply thread dies in
|
||||
// ConsumerDoorbell before it pops its first record. The seam is the
|
||||
// home the ruling said did not exist. lk deletes both accessors rather
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// makes every later advance a no-op and every WaitForApplied on
|
||||
// kWaitForever - the verb barrier and every reply wait - block for
|
||||
// ever. A hang with one ERROR line in the log is strictly worse than
|
||||
// an abort at the instruction that caused it, and Ring.h:181-185
|
||||
// an abort at the instruction that caused it, and RingCursorsValid (Ring.h)
|
||||
// already rules the same way for the cursor invariants on this page.
|
||||
// "Early" can only be caught at the call site, which is why every
|
||||
// advance below has exactly one caller and a named unit case.
|
||||
@@ -449,7 +449,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// RingCursorSet::Stage is driven by nobody (Ring.h's stage triple).
|
||||
m_cmd->Publish();
|
||||
// Kept locally as well as on the shared page: the shared watermark is
|
||||
// allowed to lag (Ring.h:72-77), and teardown's drain must not.
|
||||
// allowed to lag (Ring.h's header comment: late, never early), and teardown's drain must not.
|
||||
//
|
||||
// Clamped UP, not passed through. A caller that republishes an older
|
||||
// bound - teardown does exactly that, and so does any batched publisher
|
||||
@@ -638,7 +638,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// into BOTH tails, i.e. it hands back every byte the consumer has popped
|
||||
// whether or not a record among them was borrowed into the GPU timeline.
|
||||
// That would defeat the whole reason the ring carries two tails
|
||||
// (Ring.h:24-27) and would let the producer overwrite a slot the GPU is
|
||||
// (Ring.h's header comment) and would let the producer overwrite a slot the GPU is
|
||||
// still reading. m_retirableCursor stops at the first borrowed record.
|
||||
m_cmd->PublishApplied();
|
||||
m_cmd->PublishRetiredUpTo(m_retirableCursor);
|
||||
|
||||
@@ -262,8 +262,8 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
void PublishAndNotify(std::uint64_t submittedSeq);
|
||||
|
||||
// The last seq THIS producer published, kept locally rather than read back
|
||||
// out of RingControl::submittedSeq. Teardown's drain needs it: Ring.h:72-77
|
||||
// explicitly permits submittedSeq to be published LAZILY and Ring.h:243
|
||||
// out of RingControl::submittedSeq. Teardown's drain needs it: ARCHITECTURE.md §11.2.1
|
||||
// explicitly permits submittedSeq to be published LAZILY and Ring.h's RingProducer::Publish
|
||||
// encourages batching the publish, so the shared watermark may lag the
|
||||
// emitter - and a drain that waits for `appliedSeq >= submittedSeq` would
|
||||
// then under-wait and free an emitter's var-tail while a record still
|
||||
@@ -295,7 +295,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// True when the peer latched "SEG_EVENT is full and I stopped producing".
|
||||
bool EventRingIsFull() const;
|
||||
// Back-pressure when Reserve returned nullptr. NEVER call this when
|
||||
// FreeBytes() is already >= the record: Ring.h:226-233 - a nullptr with
|
||||
// FreeBytes() is already >= the record: RingProducer::Reserve (Ring.h) - a nullptr with
|
||||
// enough free bytes can only mean "too big, chunk", and waiting on it
|
||||
// stalls forever.
|
||||
SessionWait WaitForCmdSpace(std::uint64_t bytes, std::uint32_t timeoutMs);
|
||||
@@ -401,7 +401,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// THE BYTE CURSOR RetireThrough MAY RECLAIM TO, which is NOT simply
|
||||
// "everything popped". A record carrying kRecBorrowSlot has been lent
|
||||
// into the GPU timeline and its slot can only be recycled after
|
||||
// completedFrameSerial (Ring.h:24-27), so the reclaimable cursor stops
|
||||
// completedFrameSerial (Ring.h's header comment), so the reclaimable cursor stops
|
||||
// AT the first borrowed record and does not move again until
|
||||
// RetireBorrowedUpTo releases it. Nothing sets kRecBorrowSlot yet;
|
||||
// this is here so that the day something does, the producer does not
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// closes what has already been created: Close only walks m_owned when
|
||||
// m_owns is true, and setting it afterwards left a failure at segment 3
|
||||
// holding segments 0-2's descriptors and mappings open with Valid()
|
||||
// false, against ShmSegment.h:66's "unmaps and releases the descriptor".
|
||||
// false, against ShmSegment::Close's "unmaps and releases the descriptor".
|
||||
m_owns = true;
|
||||
|
||||
struct Spec {
|
||||
|
||||
@@ -663,7 +663,7 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
outServer.reset();
|
||||
|
||||
int stream[2] = {-1, -1};
|
||||
// SOCK_STREAM, not SOCK_SEQPACKET: Doorbell.h:390-398 already chose a
|
||||
// SOCK_STREAM, not SOCK_SEQPACKET: SocketDoorbell (Doorbell.h) already chose a
|
||||
// stream socket for the bell so that a closed peer surfaces as
|
||||
// POLLIN|POLLHUP with recv()==0 rather than as a hang, and the control
|
||||
// plane must not disagree with the bell about what death looks like.
|
||||
|
||||
@@ -702,7 +702,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
}
|
||||
case MGPWireOp::SetShaderBuffers: {
|
||||
// TWO TAILS. HostSpanCount is 0 or Count and NEVER anything else
|
||||
// (MGPipeTypes.h:820-823), because the two arrays stay index-aligned; a third
|
||||
// (MGPShaderBuffers in MGPipeTypes.h), because the two arrays stay index-aligned; a third
|
||||
// value would let a record describe spans for ranges it does not have.
|
||||
const auto& p = *static_cast<const MGPShaderBuffers*>(payload);
|
||||
// P5e (sb, CONTRACT-P5E.md §1): the class and the window, bounded HERE as well as
|
||||
@@ -1750,7 +1750,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
const auto& desc = *static_cast<const MGPResourceDesc*>(payload);
|
||||
// R-13.3: `initialBytes` IS ALWAYS NULL UNDER SPLIT. Initial content arrives as
|
||||
// resource_subdata records immediately after this one, which is what the texture
|
||||
// path already does (TextureEmit.h:1137).
|
||||
// path already does (ApplyRespecify in TextureEmit.h).
|
||||
//
|
||||
// The scope and exact mutable-level extent are one carrier value. Read each part
|
||||
// through the helpers; forgetting the presence byte would turn a whole-resource
|
||||
@@ -1886,7 +1886,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
case MGPWireOp::CreateVertexElements: {
|
||||
const auto& desc = *static_cast<const MGPVertexElements*>(payload);
|
||||
// The blob's length cross-check against the two counts is the applier's already
|
||||
// (PipeApply.cpp:1990-1999) and is the model every other row copies; the decoder
|
||||
// (MGPipeApplyCreateVertexElements in PipeApply.cpp) and is the model every other row copies; the decoder
|
||||
// adds only what the applier cannot see, which is that the bytes exist at all.
|
||||
const void* blob = ResolveOrFatal(op, desc.Blob);
|
||||
if (blob == nullptr) return false;
|
||||
@@ -1907,7 +1907,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
// R-13.1 gave this call its kHasBlob. The serialization IS the memcpy - a POD -
|
||||
// and borderColorForm must survive BYTE FOR BYTE, because all three colour
|
||||
// representations are always numerically populated and it is the only thing that
|
||||
// says which one the backend must use (MGPipeTypes.h:425-428).
|
||||
// says which one the backend must use (MGPSamplerDesc in MGPipeTypes.h).
|
||||
if (!RequireDeclaredBlob(op, desc.Parameters, *m_segments)) return false;
|
||||
if (desc.Parameters.Size != sizeof(MobileGL::SamplerParameters)) {
|
||||
return WireProtocolLatchAt("CreateSamplerState.Parameters", desc.Parameters.Size,
|
||||
@@ -2146,7 +2146,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
|
||||
case MGPWireOp::SetGlobalConstants: {
|
||||
const auto& rec = *static_cast<const MGPGlobalConstants*>(payload);
|
||||
// The length cross-check is the applier's (PipeApply.cpp:2784, against the
|
||||
// The length cross-check is the applier's (PipeApply.cpp's MGPipeApplySetGlobalConstants, against the
|
||||
// program's GlobalUboSize, which this record does not carry). Under rule A it
|
||||
// stops being inert for the first time, which is the whole point of arming Size.
|
||||
const void* bytes = ResolveOrFatal(op, rec.Blob);
|
||||
@@ -2201,8 +2201,8 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
Uint64(rec.UnionBox.Z) + rec.UnionBox.D > rec.LevelDepth))
|
||||
return WireProtocolLatch("ResourceSubData.LevelExtent", "invalid full image extent or dirty box");
|
||||
// Rule A arms both halves. The buffer half already declared a real size in
|
||||
// monolith and is cross-checked at PipeApply.cpp:702; THE TEXTURE HALF DECLARED 0
|
||||
// (TextureEmit.h:1265-1267, on the grounds that the byte count was "the server's
|
||||
// monolith and is cross-checked by PipeApply.cpp's SubDataBoxFault; THE TEXTURE HALF DECLARED 0
|
||||
// (TextureEmit.h's EmitOneLevel, on the grounds that the byte count was "the server's
|
||||
// to compute") and under split it must declare too - a length the reader computes
|
||||
// from the record it is checking is not a bounds check.
|
||||
const Bool namesABuffer = rec.Target == kMGPipeResourceTargetBuffer;
|
||||
@@ -2263,7 +2263,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
// drives rewrites its range from the AUTHORITATIVE SHADOW, which rule C makes
|
||||
// server-owned, so resource_subdata is already the only way bytes reach it and a
|
||||
// second way would be a forgeable one. AccessFlags cross verbatim, never
|
||||
// normalised (PipeApply.h:902-903).
|
||||
// normalised (MGPipeApplyResourceFlushRange in PipeApply.h).
|
||||
MGPipeApplyResourceFlushRange(*static_cast<const MGPFlushRange*>(payload), nullptr);
|
||||
return true;
|
||||
|
||||
@@ -2485,7 +2485,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
return true;
|
||||
|
||||
case MGPWireOp::SetSwapInterval:
|
||||
// Class C, wave 3 (census-classC.md "static cross"); not a verb (FillPoints.def:21).
|
||||
// Class C, wave 3 (census-classC.md "static cross"); not a verb (no MGP_FILL_VERB_LIST row).
|
||||
return false;
|
||||
|
||||
case MGPWireOp::Flush:
|
||||
|
||||
@@ -590,7 +590,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// here; under R-1's verb barrier the block is nearly free, since the client is already
|
||||
// waiting for appliedSeq to reach the record it just emitted.
|
||||
//
|
||||
// NO NARROWING (ResourceTracker.h:587-592's rangeCount == 1 assertion stays): this
|
||||
// NO NARROWING (MGPipeClientOnGpuWritten's rangeCount == 1 assertion stays): this
|
||||
// phase only gives the client a conservative set, and a zero-range announcement stays
|
||||
// illegal until P8/P9 make it mean "fully narrowed - nothing is dirty".
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// End of Source File Header
|
||||
|
||||
#pragma once
|
||||
#include <Includes.h> // String/Vector/Array/UnorderedMap/SharedPtr + GL enums. DEBT NOTE (MGPipeTypes.h:24-31 style):
|
||||
#include <Includes.h> // String/Vector/Array/UnorderedMap/SharedPtr + GL enums. DEBT NOTE (MGPipeTypes.h header style):
|
||||
// Includes.h:80-84 still pulls glslang and :59 spirv_cross_c.h; this header is glslang-free BY
|
||||
// SYMBOL (what P7's `nm -D | grep glslang` measures), not by preprocessed text. A textually
|
||||
// glslang-free closure needs MG_Util/Types.h split off Includes.h (Types.h:11 includes it
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
#include "Includes.h"
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
#include "Init.h"
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
|
||||
@@ -333,8 +333,8 @@ TEST_F(FieldOwnershipTest, VerbBoundaryOpsCoverEveryVerbShapedCall) {
|
||||
EXPECT_EQ(kMGPipeVerbBoundaryExemptCount, SizeT{3});
|
||||
|
||||
// Present is class B (it is emitted in P5) and is STILL not a verb boundary:
|
||||
// FillPoints.def:21 - "Present and SetSwapInterval go through BackendObject virtuals and
|
||||
// read no frontend state, so they are not verbs here". Stamping there would retire the
|
||||
// Present and SetSwapInterval go through BackendObject virtuals and read no frontend state,
|
||||
// so FillPoints.def's MGP_FILL_VERB_LIST has no row for them. Stamping there would retire the
|
||||
// previous verb's answers with nothing to put in their place.
|
||||
EXPECT_EQ(MGPipeVerbForWireOp(MGPWireOp::Present), MGPipeVerb::kVerbCount);
|
||||
EXPECT_EQ(MGPipeVerbForWireOp(MGPWireOp::SetSwapInterval), MGPipeVerb::kVerbCount);
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
#include "Includes.h"
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#include "Includes.h"
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
#include "Init.h"
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
|
||||
@@ -1333,8 +1333,8 @@ TEST(PipeCatalogue, ResourceRespecifyAcksOnlyImmutableStorage) {
|
||||
|
||||
// And the four by name, because a count alone would let a row lose the flag while another
|
||||
// gained one. These are exactly the MGPipeApply* entry points that return Bool
|
||||
// (PipeApply.h:820, :868, :897, :1023); map_persistent's void* is the fifth answer and was
|
||||
// already declared.
|
||||
// (ResourceCreate, ResourceRespecify, ResourceSubData, SetTextureParams in PipeApply.h);
|
||||
// map_persistent's void* is the fifth answer and was already declared.
|
||||
Uint32 acceptanceWithSlot = 0;
|
||||
#define MGP_COUNT_ACCEPTANCE_ROWS(Name, Payload, Class, Flags, Wait) \
|
||||
if ((std::strcmp(#Name, "ResourceCreate") == 0 || std::strcmp(#Name, "ResourceRespecify") == 0 || \
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
#include "Includes.h"
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
#include "Init.h"
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (its #define in PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#include "Includes.h"
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
#include <Config.h>
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
@@ -2122,7 +2122,7 @@ namespace {
|
||||
// is what PipeFill.cpp's two content walks pass - because the record's own 2^32-1 bound is
|
||||
// not the one a real upload meets first. One piece's bytes are staged WHOLE in SEG_STAGE, a
|
||||
// linear arena, and a blob larger than that arena is Fatal{RingOverrun, "SEG_STAGE"} at the
|
||||
// encoder rather than a split (PipeWireCodec.cpp:856-864); measured on the CI traces, where
|
||||
// encoder rather than a split (PipeWireEncoder::StageAllocate); measured on the CI traces, where
|
||||
// a 128 MiB arena's whole-buffer follow-up against a 32 MiB segment aborted there.
|
||||
//
|
||||
// A UNIT PROCESS HAS NO SESSION, so the live answer is 0 - "nothing to fit, keep the
|
||||
@@ -2139,7 +2139,7 @@ namespace {
|
||||
"bound rather than invent a cap";
|
||||
EXPECT_EQ(MG_Remote::Client::MGPipeStageChunkBytesFor(0), 0u);
|
||||
|
||||
// SessionRings.h:89's default segment, and the three shapes the clamp has to get right:
|
||||
// SessionSegmentSizes::StageBytes' default segment, and the three shapes the clamp has to get right:
|
||||
// an ordinary arena, a segment below the floor, and a segment equal to it.
|
||||
constexpr Uint64 kStage = 32ull * 1024ull * 1024ull;
|
||||
constexpr Uint64 kChunk = kStage / 4;
|
||||
@@ -2212,7 +2212,7 @@ namespace {
|
||||
// are bytes to carry: MappedData() answers a NON-NULL pointer for a zero-byte store
|
||||
// (PipeResource.h:140-143 reserves one byte whatever the size). Reading it alone is what
|
||||
// sent this call down the split arm's respecify(nullptr)-plus-follow-up shape, whose walk
|
||||
// emits NOTHING for a zero-length range (ResourceTracker.h:253), so the self-check that
|
||||
// emits NOTHING for a zero-length range (MGPipeForEachSubDataRecordRange), so the self-check that
|
||||
// exists to prove the content followed aborted by name:
|
||||
// Fatal{InitialBytesNotCarried, "resource_respecify"} ... 0 bytes
|
||||
// measured on the first glBufferData(target, 0, NULL, ...) of the bsl-esc-menu trace.
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "Init.h"
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
#include "Init.h"
|
||||
#include <Config.h>
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere else,
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere else,
|
||||
// so a TU that tests it without this include silently reads it as 0 - and then
|
||||
// ExpectRefusedNaming takes the in-process arm while the applier's trip wire really does
|
||||
// abort, which takes the whole binary down with it. ImageEmitTest carries the same include for
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
#include "Includes.h"
|
||||
#include <MG_Pipe/MGPipe.h>
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h:20-26) and nowhere
|
||||
// MOBILEGL_PIPE_POISON is DERIVED in the header below (PipeInputs.h) and nowhere
|
||||
// else, so a TU that tests it without this include silently reads it as 0. That is
|
||||
// invisible in a push build (where it really is 0) and in a verify build (where
|
||||
// -DMOBILEGL_PIPE_VERIFY=1 is on the command line); MOBILEGL_BUILD_DISAGGREGATED is the
|
||||
@@ -639,7 +639,7 @@ TEST(TextureEmit, ALevelTooLargeForTheStageChunkIsCutIntoSlabs) {
|
||||
#else
|
||||
EXPECT_EQ(MGPipeTextureStageChunkBytes(), 0u)
|
||||
<< "a unit process has no ClientSession, so the drain must keep the whole-level record";
|
||||
// SessionRings.h:89's default segment, and the cap is a quarter of it - the number the
|
||||
// SessionRings.h's default SessionSegmentSizes::StageBytes, and the cap is a quarter of it - the number the
|
||||
// emitter really passes to the walk.
|
||||
const SizeT cap = MG_Remote::Client::MGPipeStageChunkBytesFor(32ull * 1024ull * 1024ull);
|
||||
ASSERT_EQ(cap, static_cast<SizeT>(8ull * 1024ull * 1024ull));
|
||||
|
||||
@@ -2684,7 +2684,7 @@ TEST_F(PipeWireCodecTest, ADeclaredPerStageSpirvRunIsFatal) {
|
||||
}
|
||||
|
||||
TEST_F(PipeWireCodecTest, AHostSpanCountThatIsNeitherZeroNorCountIsFatal) {
|
||||
// MGPipeTypes.h:820-823: HostSpanCount is 0 OR Count, never anything else, so the two
|
||||
// MGPShaderBuffers (MGPipeTypes.h): HostSpanCount is 0 OR Count, never anything else, so the two
|
||||
// arrays stay index-aligned. A third value lets a record describe spans for ranges it does
|
||||
// not have - and the arrays would then be read off by one for the rest of the tail.
|
||||
const ChildResult r = RunInChild([] {
|
||||
|
||||
@@ -306,7 +306,7 @@ TEST(RemoteClientControls, LongFenceWaitBudgetStillWakesOnShutdown) {
|
||||
TEST(RemoteClientControls, ResourceRespecifyErrorIsNotADecline) {
|
||||
// THE TEXTURE HALF, WHICH IS THE HALF THAT STILL READS ITS ANSWER (P12). This case used a
|
||||
// zeroed descriptor, which is a BUFFER target - and the buffer half is now fire-and-forget
|
||||
// because both of its call sites discard the Bool (PipeFill.cpp:1105, :1142). A row that reads
|
||||
// because both of its call sites in MGPipeEmitResourceRespecify (PipeFill.cpp) discard the Bool. A row that reads
|
||||
// no answer cannot fold ERROR into a decision, so the rule this case exists for moved to the
|
||||
// half that does read one: the transport fault is caught here, at the next row that waits,
|
||||
// instead of at a record whose verdict nobody ever looked at.
|
||||
@@ -761,19 +761,19 @@ TEST(RemoteClientControls, ADrawOnASessionWithoutTheRunAheadBitStaysStampedBarri
|
||||
// =====================================================================================
|
||||
//
|
||||
// ReplySink's status space is three-valued and its own header says what the values mean:
|
||||
// 0 OK, 1 DECLINED IS A REAL ANSWER, 2 ERROR (PipeWireCodec.h:457-460). The escape rows are
|
||||
// 0 OK, 1 DECLINED IS A REAL ANSWER, 2 ERROR (MGPipeReplySink in PipeVerbSink.h). The escape rows are
|
||||
// held to the other half of the same rule - "ERROR IS NOT A DECLINE", an escape may not fold
|
||||
// 2 into `false` (WireTables.cpp:446-450). The fence and query consumers folded BOTH 1 and 2
|
||||
// 2 into `false` (Wire_Escape_ResourceRespecify, WireTables.cpp). The fence and query consumers folded BOTH 1 and 2
|
||||
// into a single abort (EmitTables.cpp ReadFenceReply, QueryEmit.inc ReadQueryReply), so a
|
||||
// client that had latched device-lost - where every verb is DECLINED by design,
|
||||
// ClientSession.cpp:1837 - died of Fatal{ProtocolCorruption, "Fence.reply"} on its next
|
||||
// ClientSession::EmitAndWaitTails - died of Fatal{ProtocolCorruption, "Fence.reply"} on its next
|
||||
// glClientWaitSync instead of going quiet the way the design promises ("GL calls become
|
||||
// no-ops", design/08-runtime-and-platform.md:33).
|
||||
//
|
||||
// THE PEER BELOW IS THE SERVER'S OWN DECLINE, not a substitution of the status byte: these
|
||||
// verbs answer false when the server cannot answer them, and the codec posts exactly that as
|
||||
// DECLINED with a zero-length payload (PipeWireCodec.cpp:1820-1830 for the two fence rows,
|
||||
// :1841-1861 for the three query rows). The CLIENT's own decline is pinned separately, by
|
||||
// DECLINED with a zero-length payload (PipeWireDecoder::ApplyChecked in PipeWireCodec.cpp, for the
|
||||
// two fence rows and the three query rows). The CLIENT's own decline is pinned separately, by
|
||||
// LongFenceWaitBudgetStillWakesOnShutdown above.
|
||||
//
|
||||
// THE RED, RUN ONCE: take the `if (status == Wire::ReplySink::kStatusDeclined)` arm out of
|
||||
@@ -810,7 +810,7 @@ TEST(RemoteClientControls, DeclinedFenceReplyAnswersSignaledInsteadOfAborting) {
|
||||
auto* sync = RemoteEmitTable().GL.FenceSync();
|
||||
if (sync == nullptr) ::_exit(131);
|
||||
// glClientWaitSync on a fence nobody can wait on: signaled now, as every fallback in
|
||||
// this tree already answers (GL_Sync.cpp:103-106, PipeApplier.cpp:175-177).
|
||||
// this tree already answers (ClientWaitSync in GL_Sync.cpp, ServerVerbSink::OnFenceWait).
|
||||
if (RemoteEmitTable().GL.ClientWaitSync(sync, 0, 0) != GL_ALREADY_SIGNALED) ::_exit(132);
|
||||
// GL_SYNC_STATUS: the same answer, in the boolean the row carries.
|
||||
if (!RemoteEmitTable().GL.GetSyncStatus(sync)) ::_exit(133);
|
||||
@@ -919,8 +919,8 @@ TEST(RemoteClientControls, DeviceLostRespecifyDoesNotDieOfTheUncarriedBytesCheck
|
||||
// P12 exit gate (a): killing the server must give a CLEAN device-lost latch. With the
|
||||
// latch set every verb is DECLINED, so the resource_subdata that R-13.3 owes emits
|
||||
// nothing - which is the latch working, not bytes the client forgot to carry. The check
|
||||
// at PipeFill.cpp:1115 compared client record ordinals only, so the next glBufferData
|
||||
// with data aborted the client by name instead.
|
||||
// in MGPipeEmitResourceRespecify (PipeFill.cpp) compared client record ordinals only, so
|
||||
// the next glBufferData with data aborted the client by name instead.
|
||||
const auto child = RunInChild([] {
|
||||
StartControlSession();
|
||||
// Taken through the production entry point, the way the barrier and the encoder take
|
||||
@@ -1260,12 +1260,12 @@ TEST(RemoteClientControls, AParameterChangeForAConfirmedObjectTakesNoReply) {
|
||||
// =====================================================================================
|
||||
//
|
||||
// The two halves of resource_respecify are not the same row. The buffer half's Bool is discarded
|
||||
// at BOTH call sites (PipeFill.cpp:1105, :1142), so its wait bought a value that was thrown away -
|
||||
// at BOTH call sites in MGPipeEmitResourceRespecify (PipeFill.cpp), so its wait bought a value that was thrown away -
|
||||
// fire-and-forget costs nothing and saves a round trip per glBufferData, which is the shape a
|
||||
// chunk uploader produces thousands of times per frame. The texture half's Bool is load-bearing:
|
||||
// it triggers the self-heal AND gates the acceptance mirror, and advancing that mirror on a
|
||||
// provisional accept is ID-18 M3 (TextureEmit.h:885-888). So this case asserts BOTH directions -
|
||||
// the asymmetry is the design, not an oversight.
|
||||
// provisional accept is ID-18 M3 (EmitResourceRespecify in TextureEmit.h). So this case asserts
|
||||
// BOTH directions - the asymmetry is the design, not an oversight.
|
||||
//
|
||||
// THE RED, RUN ONCE: take the bufferHalf branch out of Wire_Escape_ResourceRespecify and exit 155
|
||||
// fires (the buffer respecify starts waiting again).
|
||||
|
||||
@@ -392,7 +392,7 @@ TEST(SessionTest, TwoThreadsMoveTwentyThousandRecordsAndAgreeOnEveryOne) {
|
||||
void* payload = nullptr;
|
||||
while ((payload = session.cmdProducer.Reserve(1, kRecNone, sizeof(std::uint32_t))) ==
|
||||
nullptr) {
|
||||
// Never "wait" on a nullptr with enough free bytes - Ring.h:226-233 says that can
|
||||
// Never "wait" on a nullptr with enough free bytes - RingProducer::Reserve (Ring.h) says that can
|
||||
// only mean "too big, chunk", and a producer that waited there would stall for ever.
|
||||
ASSERT_LT(session.cmdProducer.FreeBytes(), 16u);
|
||||
if (session.producer.WaitForCmdSpace(16, 5000) != SessionWait::Reached) {
|
||||
@@ -1219,7 +1219,7 @@ TEST(SessionTest, TheInprocPeerGetsItsOwnMappingOfTheSameSharedMemory) {
|
||||
// M-5. RetireThrough must NOT hand back a slot borrowed into the GPU timeline. The first
|
||||
// version called RingConsumer::PublishRetired(), which stores the local tail into BOTH tails
|
||||
// and therefore frees every popped byte regardless of kRecBorrowSlot - defeating the reason the
|
||||
// ring carries two tails at all (Ring.h:24-27) and letting the producer overwrite a slot the
|
||||
// ring carries two tails at all (Ring.h's header comment) and letting the producer overwrite a slot the
|
||||
// GPU is still reading. Nothing sets kRecBorrowSlot yet; this is the case that has to be true
|
||||
// on the day something does.
|
||||
TEST(SessionTest, ABorrowedSlotIsNotHandedBackUntilItIsReleased) {
|
||||
@@ -1269,7 +1269,7 @@ TEST(SessionTest, ABorrowedSlotIsNotHandedBackUntilItIsReleased) {
|
||||
}
|
||||
|
||||
// q-1. Teardown's drain keys on the PRODUCER's own last-published seq, not on
|
||||
// RingControl::submittedSeq: Ring.h:72-77 permits that watermark to be published lazily, so a
|
||||
// RingControl::submittedSeq: Ring.h's header comment permits that watermark to be published lazily, so a
|
||||
// drain that trusted it would under-wait and free an emitter-owned var-tail while a record
|
||||
// still names it. With the verb barrier armed the two are equal; under
|
||||
// MOBILEGL_IPC_VERB_BARRIER=0 - R-1's negative control, which the phase must run once - they
|
||||
|
||||
@@ -440,7 +440,7 @@ TEST(SocketTransportTest, BlockedReceiverWakesOnSendAndOnShutdown) {
|
||||
std::uint64_t size = 0;
|
||||
MobileGLMutableByteSpan span{buffer.data(), buffer.size()};
|
||||
// The wake is a real POLLHUP here, not a condvar notify: this is the
|
||||
// mechanism Doorbell.h:390-398 chose a stream socket for, and the reason
|
||||
// mechanism SocketDoorbell (Doorbell.h) chose a stream socket for, and the reason
|
||||
// death is an event rather than a hang.
|
||||
EXPECT_EQ(pair.server->ReceiveFrame(span, &size, kWaitForever), MOBILEGL_ERR_TRANSPORT_CLOSED);
|
||||
closer.join();
|
||||
|
||||
@@ -1004,7 +1004,7 @@ TEST(StagedTextureProductionTest, TextureSubDataThroughTheRealOpsTableCopiesAndS
|
||||
rec.UnionBox = {0, 0, 0, 4, 4, 1};
|
||||
rec.RegionCount = 0;
|
||||
// Under split the codec declares the run's length: "the bytes this record declares ARE
|
||||
// the level shadow" (TextureEmit.h:1285). The adoption reads exactly this field.
|
||||
// the level shadow" (TextureEmit.h's EmitOneLevel). The adoption reads exactly this field.
|
||||
rec.Blob.Size = src.size();
|
||||
|
||||
// THE PRODUCTION CALL. Not StagedTextureStore::Adopt directly - the whole point of the
|
||||
|
||||
@@ -230,7 +230,7 @@ namespace {
|
||||
std::atomic<std::uint64_t> answered{0};
|
||||
void SetUp() override {
|
||||
StreamClientPublication::SetUp();
|
||||
// `Start()` does this on the real path (ClientSession.cpp:1615); this harness enters
|
||||
// `Start()` does this on the real path; this harness enters
|
||||
// through FinishStartup, so the routed rows have to be installed by hand or
|
||||
// MGPipeRouteResourceCreate would reach the monolith screen.
|
||||
Client::InstallClientWireTables();
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace MobileGL::MG_Util::PipeStats {
|
||||
//
|
||||
// COUNTED AT THE ALLOCATOR, WHICH IS THE ONE CHOKE POINT. Every writer that puts bytes
|
||||
// into SEG_STAGE reaches them through PipeWireEncoder::StageBytes and that function's
|
||||
// only allocation call is StageAllocate (PipeWireCodec.h:376), so a single site here
|
||||
// only allocation call is PipeWireEncoder::StageAllocate (PipeWireCodec.h), so a single site here
|
||||
// covers the buffer content walks, the texture slabs, the CSO archives and the
|
||||
// storage-block names alike. Summing at the CALLERS instead would be a hand-kept list of
|
||||
// the paths that happen to exist today, and the next producer added would be missing
|
||||
|
||||
@@ -140,7 +140,7 @@ PROBES = [
|
||||
"Forbidden": ["MobileGL/Includes.h"],
|
||||
"Allow": [],
|
||||
"TextLimits": {},
|
||||
"Why": "WireLog.h:9-24 states the rule: nothing about a byte pipe needs the GL "
|
||||
"Why": "ITransport.h's file comment states the rule: nothing about a byte pipe needs the GL "
|
||||
"frontend's umbrella header. Green today - it is the gate's own canary.",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -210,10 +210,10 @@ ENUM_VALUE = re.compile(r"^\s*([A-Za-z][A-Za-z0-9]*)\s*=", re.MULTILINE)
|
||||
# change and not a wave-0 instrument's to make. Recorded for the integrator.
|
||||
LOCAL_REFUSAL_WORDS = {
|
||||
"ProtocolMismatch":
|
||||
"ClientSession.cpp:700/:718 - the CLIENT refusing a control/data transport pair locally, "
|
||||
"ClientSession.cpp's StartSpawned - the CLIENT refusing a control/data transport pair locally, "
|
||||
"before any Hello is sent; there is no peer to send a Refuse frame to",
|
||||
"InitialCapsSnapshot":
|
||||
"ClientSession.cpp:1292/:1302 - the CLIENT refusing a Welcome that carried no initial "
|
||||
"ClientSession.cpp's FinishStartup - the CLIENT refusing a Welcome that carried no initial "
|
||||
"capabilities; the handshake has already been answered, so the decline is local",
|
||||
"AdoptTierOnStream":
|
||||
"Transport/AdoptTier.cpp (P11 A1) - either side refusing its OWN MOBILEGL_IPC_ADOPT_TIER=0/1 "
|
||||
|
||||
@@ -576,10 +576,9 @@ inline constexpr const char* kMGPipeFieldOwnershipNames[] = {
|
||||
add("static_assert(kMGPipeFieldOwnershipRowCount == 70, \"table 2's row count moved\");\n")
|
||||
|
||||
add("""// An ARGUMENT-KEYED narrowing of one field. The field keeps its single row above; this
|
||||
// says that one argument value of it belongs to a different class. Coverage.def:62-69 already
|
||||
// rules the shape for GetBufferBindingSlot - "THE ROW STAYS ONE ROW ... the field is ONE array
|
||||
// that a second row of the same name could only duplicate" - and m_pixelStore[2] is the same
|
||||
// shape indexed by its own isUnpack argument.
|
||||
// says that one argument value of it belongs to a different class. Coverage.def's comment on the
|
||||
// GetBufferBindingSlot row already rules the shape - "The field is one array, so it stays one
|
||||
// row" - and m_pixelStore[2] is the same shape indexed by its own isUnpack argument.
|
||||
struct MGPipeFieldArgumentOwnership {
|
||||
MGPipeInputField Field;
|
||||
Uint32 Arg0;
|
||||
|
||||
Reference in New Issue
Block a user