diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 9b08eeac..9e7b021b 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -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& 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); } diff --git a/MobileGL/MG_Pipe/PipeApply.cpp b/MobileGL/MG_Pipe/PipeApply.cpp index 777c7eee..52f91983 100644 --- a/MobileGL/MG_Pipe/PipeApply.cpp +++ b/MobileGL/MG_Pipe/PipeApply.cpp @@ -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; } diff --git a/MobileGL/MG_Pipe/PipeApply.h b/MobileGL/MG_Pipe/PipeApply.h index a23e8fd2..232ef337 100644 --- a/MobileGL/MG_Pipe/PipeApply.h +++ b/MobileGL/MG_Pipe/PipeApply.h @@ -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 diff --git a/MobileGL/MG_Remote/Server/PipeApplier.cpp b/MobileGL/MG_Remote/Server/PipeApplier.cpp index 55a78e5d..cfd3f735 100644 --- a/MobileGL/MG_Remote/Server/PipeApplier.cpp +++ b/MobileGL/MG_Remote/Server/PipeApplier.cpp @@ -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(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. diff --git a/MobileGL/MG_Test/Wire/RemoteClientTest.cpp b/MobileGL/MG_Test/Wire/RemoteClientTest.cpp index 301bd552..fc6699ea 100644 --- a/MobileGL/MG_Test/Wire/RemoteClientTest.cpp +++ b/MobileGL/MG_Test/Wire/RemoteClientTest.cpp @@ -1915,8 +1915,10 @@ TEST(RemoteGuards, AnAllocatorProbeInsideAnExemptionScopeFromTheApplyThreadIsFat Srv::ServerLoopInstance().RunOnApplyThread( +[](void* self) -> MobileGLResult { auto& probe = *static_cast(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;