mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Pipe, MG_Backend, MG_Remote, MG_Test] (Disaggregated): P5e wave 1 integration - one body per seam, and the stamp waits for ra
- c0e and id were implemented in parallel and each declared the two shared seams, so the merge produced five duplicate definitions the text could not collide: MGPipeBarriered, MGPipeApplierCurrentRecordIsBarriered and the three by-handle twin resolvers. Resolved per ID-99 and ID-103. - MGPipeBarriered keeps c0e's real body (the static wait-class column, the XFB escalation and the client-array escalation); id's constant-true placeholder goes. - The current-record flag keeps c0e's thread_local storage and setter and loses id's MGPipeApplierState member: it describes which record THIS THREAD is inside, not per-context state the applier owns, and the allocator guard that reads it would otherwise have to find the right context's applier first. id's test hook posts through the setter. - The three resolvers keep id's real bodies beside each registry; c0e's asserting stubs for them go. The seam stubs no package has bodied yet stay. - ApplyOne stamps `true` while kMGPipeP5eClientWaitRuleLanded is false. The predicate says what the client WILL do; until ra changes EmitAndWaitTails the client still blocks after every record, so a `false` stamp would withdraw the §4.4 exemption from a probe the client's own wait still makes safe. The predicate is computed on every record regardless, so it is exercised for the whole phase instead of first running on the day it starts deciding. - Gates on the merged tree: unit 2206/2206, integration-split 111/111, both generators --check/--self-test green, include closure 0 problems, dirty surface rc 0, doc citations one pre-existing CONTRACT-P5.md line (c0e retired another).
This commit is contained in:
@@ -13837,12 +13837,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace VertexArrayImpl {
|
||||
BackendVertexArrayObject* ResolveVaoTwin(MG_Pipe::MGPipeHandle vertexElements) {
|
||||
MGPipeP5eSeamNotLanded("VertexArrayImpl::ResolveVaoTwin", "id/vi", vertexElements);
|
||||
}
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
SharedPtr<BackendTextureObject>& SyncTextureToBackendByHandle(MG_Pipe::MGPipeHandle texture,
|
||||
Bool imageBindableStorageRequired) {
|
||||
@@ -13850,10 +13844,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MGPipeP5eSeamNotLanded("TextureImpl::SyncTextureToBackendByHandle", "tx2", texture);
|
||||
}
|
||||
|
||||
BackendTextureObject* ResolveTextureTwin(MG_Pipe::MGPipeHandle texture) {
|
||||
MGPipeP5eSeamNotLanded("TextureImpl::ResolveTextureTwin", "id/tx2", texture);
|
||||
}
|
||||
|
||||
void BackendTextureObject::SyncMipmapsToBackendByHandle(MG_Pipe::MGPipeHandle texture) {
|
||||
MGPipeP5eSeamNotLanded("BackendTextureObject::SyncMipmapsToBackendByHandle", "tx2",
|
||||
texture);
|
||||
@@ -13869,10 +13859,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
} // namespace FramebufferImpl
|
||||
|
||||
namespace PrgramImpl {
|
||||
BackendProgramObjectImpl* ResolveProgramTwin(MG_Pipe::MGPipeHandle cso) {
|
||||
MGPipeP5eSeamNotLanded("PrgramImpl::ResolveProgramTwin", "id/pg", cso);
|
||||
}
|
||||
|
||||
void BackendProgramObjectImpl::SyncToBackendByHandle(MG_Pipe::MGPipeHandle cso) {
|
||||
MGPipeP5eSeamNotLanded("BackendProgramObjectImpl::SyncToBackendByHandle", "pg", cso);
|
||||
}
|
||||
|
||||
@@ -1212,30 +1212,6 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 MGPipeApplierContextSerial() { return g_applier.ContextSerial; }
|
||||
void MGPipeApplierNoteTextureStateMoved() { ++g_applier.TextureShutterSerial; }
|
||||
|
||||
// P5e (id), CONTRACT-P5E §2.1. THE BODY ra REPLACES, and the reason it is a constant here
|
||||
// rather than absent: every caller of the predicate lands with this package, so a missing
|
||||
// body would be a link error in six worktrees at once, and a body that guessed at the wait
|
||||
// rule would change behaviour in the package whose whole claim is that it changes none.
|
||||
// True for every record IS today's rule - the client blocks after publishing every verb
|
||||
// (ClientSession.cpp's :915-959 lockstep path) - so this is a restatement, not a stub.
|
||||
Bool MGPipeBarriered(MGPWireOp op, const void* payload, const MGPipeApplierState& st) {
|
||||
(void)op;
|
||||
(void)payload;
|
||||
(void)st;
|
||||
return true;
|
||||
}
|
||||
|
||||
// §4.4's input. Outside a disaggregated build there is no ApplyOne to stamp it and no
|
||||
// client that could run ahead, so the answer is the constant the monolith has always
|
||||
// behaved as.
|
||||
Bool MGPipeApplierCurrentRecordIsBarriered() {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
return g_applier.CurrentRecordBarriered;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MGPipeSetResourceOps(const MGPipeResourceOps* ops) { g_resourceOps = ops; }
|
||||
const MGPipeResourceOps* MGPipeGetResourceOps() { return g_resourceOps; }
|
||||
|
||||
|
||||
@@ -819,22 +819,13 @@ namespace MobileGL::MG_Pipe {
|
||||
// dispatch_indirect's command buffer (MGPGridInfo::IndirectBuffer).
|
||||
MGPipeHandle VerbDispatchIndirectBuffer = kMGPipeNullHandle;
|
||||
|
||||
// ---- P5e (id, CONTRACT-P5E §2.1 / §4.4): IS THE RECORD BEING APPLIED BARRIERED? ----
|
||||
//
|
||||
// SERVER-PRIVATE and per-record: PipeApplier::ApplyOne writes it from MGPipeBarriered()
|
||||
// before the decode and nothing else may. It is what §4.4's exemption is keyed on - a
|
||||
// frontend probe inside a named scope is legal only while the CLIENT IS PARKED behind
|
||||
// this record, because the wait is the whole of what makes the client's memory stable
|
||||
// to read. An unbarriered record's apply has no such wait, so the same probe would
|
||||
// read memory the client is concurrently moving, and the scope stops exempting it.
|
||||
//
|
||||
// TRUE IS THE ONLY VALUE THIS PHASE PRODUCES, and that is deliberate: MGPipeBarriered
|
||||
// answers true for every record until ra lands the wait rule, so id changes no
|
||||
// behaviour at all (lockstep is unchanged) while every site that must one day ask the
|
||||
// question already asks it. It is also the value a MONOLITH applier keeps - nothing
|
||||
// writes it there and there is no client to run ahead - which is what keeps Magma and
|
||||
// the push-monolith build on P5C's semantics (§6.1).
|
||||
Bool CurrentRecordBarriered = true;
|
||||
// P5e: "is the record being applied barriered?" is NOT a member of this struct. It is
|
||||
// thread_local beside MGPipeApplierCurrentRecordIsBarriered() in PipeApply.cpp, because
|
||||
// it describes which record THIS THREAD is inside rather than per-context state the
|
||||
// applier owns - and because a reader (the allocator guard) that had to find the right
|
||||
// context's applier first would be asking a harder question than the one it needs
|
||||
// answered. The integrator resolved the two packages' duplicate here (ID-103).
|
||||
|
||||
|
||||
// Every verb's writer calls this FIRST and then sets its own fields, so no field ever
|
||||
// outlives the verb that wrote it and a reader can treat non-null as "this verb's".
|
||||
@@ -921,6 +912,16 @@ namespace MobileGL::MG_Pipe {
|
||||
// the call site (it is the line ra rebases onto); c0e owns the storage so both compile.
|
||||
void MGPipeApplierSetCurrentRecordBarriered(Bool barriered);
|
||||
|
||||
// THE CLIENT'S HALF OF THE WAIT RULE IS NOT LANDED YET, AND THIS CONSTANT IS THAT FACT.
|
||||
// MGPipeBarriered above describes what the client WILL do; until package ra changes
|
||||
// EmitAndWaitTails the client still blocks after every record, so the answer the server
|
||||
// must stamp is `true` whatever the wire says - the two named exemption scopes (§4.4) are
|
||||
// legal exactly while the client is parked, and it is parked behind all of them today.
|
||||
// Package ra flips this to true in the same commit that lands the wait rule; the sink's
|
||||
// stamp then follows the predicate. Computing the predicate anyway (PipeApplier::ApplyOne)
|
||||
// is deliberate: it keeps the function exercised on every record for the whole phase.
|
||||
inline constexpr Bool kMGPipeP5eClientWaitRuleLanded = false;
|
||||
|
||||
// P5c (rv): the two serials the PipeInputs texture-shutter accessors answer with under a
|
||||
// server-stamped verb (FieldOwnership.def, APPLIER_DERIVED). Free functions rather than
|
||||
// member reads so PipeInputs.h needs this header's DECLARATIONS only... and because the
|
||||
@@ -932,37 +933,6 @@ namespace MobileGL::MG_Pipe {
|
||||
// this, so the bump rule lives in exactly one place.
|
||||
void MGPipeApplierNoteTextureStateMoved();
|
||||
|
||||
// ---- P5e: the two halves of the barriered predicate (CONTRACT-P5E §2.1, §4.4) ---------
|
||||
//
|
||||
// OWNERSHIP NOTE, so the duplicate is not mistaken for a fork: package c0e owns the final
|
||||
// declaration of both names in this header. id declares them here because the identity
|
||||
// guard (§4.4) cannot compile without them and the two packages are implemented in
|
||||
// parallel; the integrator resolves the textual overlap at landing.
|
||||
//
|
||||
// MGPWireOp is declared opaquely rather than by including MGPipe.h: the enum is generated
|
||||
// into generated/PipeWire.inc, which MGPipe.h includes INSIDE this namespace, and this
|
||||
// header sits below it (PipeRoute.h includes both). An opaque enum declaration with the
|
||||
// same fixed underlying type is the whole coupling the predicate needs.
|
||||
enum class MGPWireOp : Uint16;
|
||||
|
||||
// "Does the client block after publishing this record?" - a pure function of wire-visible
|
||||
// data, computed identically by the emit table and by the sink (§2.1):
|
||||
//
|
||||
// MGPipeWaitClassFor(op) != kWaitNone // static column
|
||||
// || (MGPipeCallClassFor(op) == kCtxVerb && st.IsTransformFeedbackActive) // (i) XFB
|
||||
// || (op == DrawVbo && (payload.Flags & kDrawClientArrays)) // (ii)
|
||||
//
|
||||
// UNTIL ra LANDS THE WAIT RULE THE ANSWER IS TRUE FOR EVERY RECORD, and that is what keeps
|
||||
// this package behaviour-free: the client waits exactly where it waits today, so every
|
||||
// apply-thread frontend read that is legal today stays legal today. `op` and `payload` are
|
||||
// therefore unread by the body id ships; they are in the signature because ra rebases onto
|
||||
// it rather than onto a narrower one.
|
||||
Bool MGPipeBarriered(MGPWireOp op, const void* payload, const MGPipeApplierState& st);
|
||||
|
||||
// The record currently being applied, as ApplyOne stamped it. Server-private; `true` on a
|
||||
// server that does not publish kCapRunAheadApply and in every monolith process, which is
|
||||
// what leaves Magma (§6.1) and the push-monolith build on P5C's semantics.
|
||||
Bool MGPipeApplierCurrentRecordIsBarriered();
|
||||
|
||||
// A MAKE-CURRENT, NOT A TEARDOWN - and the distinction is the whole of this function's
|
||||
// contract. It runs on every change of the current GLContext (MGPipeTracker::Update resets
|
||||
|
||||
@@ -1062,8 +1062,17 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// reach are exactly the askers. MGPipeBarriered answers true for every record until ra
|
||||
// lands the wait rule, so this line changes nothing this phase and is the line ra
|
||||
// rebases onto rather than adds.
|
||||
MG_Pipe::MGPipeApplier().CurrentRecordBarriered = MG_Pipe::MGPipeBarriered(
|
||||
//
|
||||
// AND THE STAMP IS `true` UNTIL ra LANDS THE CLIENT'S HALF (ID-103). The predicate
|
||||
// describes what the client WILL do once EmitAndWaitTails follows the wait classes;
|
||||
// today it still blocks after every record, so a `false` stamp here would withdraw the
|
||||
// §4.4 exemptions from a probe the client's own wait still makes safe - a refusal with
|
||||
// no defect behind it. The predicate is computed on every record all the same, so it is
|
||||
// exercised for the whole phase rather than first run on the day it starts deciding.
|
||||
const Bool wireSaysBarriered = MG_Pipe::MGPipeBarriered(
|
||||
static_cast<MG_Pipe::MGPWireOp>(record.kind), record.payload, MG_Pipe::MGPipeApplier());
|
||||
MG_Pipe::MGPipeApplierSetCurrentRecordBarriered(
|
||||
MG_Pipe::kMGPipeP5eClientWaitRuleLanded ? wireSaysBarriered : true);
|
||||
// ORDER IS THE CONTRACT'S: stamp, then apply. The stamp is what makes any server-side
|
||||
// read of gPipeInputs legal at all (PipeApplier.h's block 1), so a record applied
|
||||
// before it aborts on the FIRST field inside SyncRenderState.
|
||||
|
||||
@@ -1915,8 +1915,10 @@ TEST(RemoteGuards, AnAllocatorProbeInsideAnExemptionScopeFromTheApplyThreadIsFat
|
||||
Srv::ServerLoopInstance().RunOnApplyThread(
|
||||
+[](void* self) -> MobileGLResult {
|
||||
auto& probe = *static_cast<Probe*>(self);
|
||||
// The record being applied is one the client did NOT park behind.
|
||||
MG_Pipe::MGPipeApplier().CurrentRecordBarriered = false;
|
||||
// The record being applied is one the client did NOT park behind. The stamp is
|
||||
// the thread_local the sink writes (ID-103), so setting it here puts this
|
||||
// thread in exactly the state an unbarriered apply is in.
|
||||
MG_Pipe::MGPipeApplierSetCurrentRecordBarriered(false);
|
||||
const MG_Pipe::MGPipeFrontendKeyedRegistryScope frontendKeyedRegistry;
|
||||
probe.table->HandleOf(probe.texture);
|
||||
return MOBILEGL_OK;
|
||||
|
||||
Reference in New Issue
Block a user