[MG_Remote, MG_Test] (Disaggregated): P5f fs - keep applier diagnostics within each role

This commit is contained in:
2026-09-19 23:49:45 -04:00
parent b420504279
commit 35ad51d8f7
3 changed files with 52 additions and 5 deletions
@@ -292,7 +292,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
// P4a (D-F3, [correction]). THE RAW-DEPTH-FETCH SUBSTITUTION STAYS ON THE SERVER, and it
// keeps constructing a frontend SamplerObject inside MG_Backend to do it. That is
// keeps constructing a frontend SamplerObject inside MG_Backend on MONOLITH to do it. That is
// deliberate and it is not this phase's to change: ARCHITECTURE.md assigns the two
// backend-specific post-processings of the resolved sampler set - this one and Magma's
// feedback-loop detection - to the server, acting ON the already-resolved set, and the
@@ -305,6 +305,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
// MG_State-type usage anywhere under MG_Backend/DirectGLES, and its owner is P3b/P4b - so
// the include-graph gate at P13 meets a known item rather than a surprise. The purity
// gates are unaffected either way: they grep MG_Backend for the pull arm's live-GLContext
// P5f fs's transport arm below uses only a native sampler with fixed values. The legacy
// pair is not constructed or consulted by the server. The purity gate's live-context
// pointer token (G13 - deliberately not spelled here, because that grep is a BARE TOKEN
// grep and a comment naming it is a hit) and for an MG_State type inside
// MGPipeResourceOps, and this is neither.
+14 -4
View File
@@ -19,6 +19,7 @@
#include "WireTables.h"
#include <MGGitHash.h>
#include <MG_Backend/MGPipe/PipeInputs.h>
#include <MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h>
#include <MG_Impl/Pipe/ResourceTracker.h>
#include <MG_Pipe/MGPipeCallbacks.h>
@@ -116,6 +117,10 @@ namespace MobileGL::MG_Remote::Client {
// thread inside the applier" - see ClientSession::InBarrierWait's note.
thread_local Bool g_inBarrierWait = false;
std::atomic<Bool> g_applyThreadInsideApplier{false};
// A split client cannot observe a server's process-local diagnostic. The
// role-local value remains useful to the applier itself; ordering between
// roles is carried by the ring watermarks, not by this instrumentation.
thread_local Bool g_roleInsideApplier = false;
struct BarrierWaitScope {
BarrierWaitScope() { g_inBarrierWait = true; }
@@ -1280,10 +1285,11 @@ namespace MobileGL::MG_Remote::Client {
// THE CLIENT'S FLAG IS THREAD-LOCAL AND THE SERVER'S IS NOT, and the asymmetry is the
// point: "am I inside a barrier wait" is a question about the calling thread, while "is the
// apply thread inside the applier" is a question the GL thread asks about a DIFFERENT
// thread - so the second has to be a shared atomic and the first must not be, or a second
// GL thread would see the first one's wait as its own.
// thread in the shared-block control. Under role split each thread instead observes only
// its own diagnostic; the client writes a different block and waits through appliedSeq.
Bool ClientSession::InBarrierWait() { return g_inBarrierWait; }
Bool ClientSession::ApplyThreadIsInsideApplier() {
if (MG_Pipe::MGPipeRoleSplitActive()) return g_roleInsideApplier;
return g_applyThreadInsideApplier.load(std::memory_order_acquire);
}
@@ -1379,10 +1385,14 @@ namespace MobileGL::MG_Remote::Client {
std::abort();
}
void ClientSession::NoteApplyThreadEnteredApplier() {
g_applyThreadInsideApplier.store(true, std::memory_order_release);
g_roleInsideApplier = true;
if (!MG_Pipe::MGPipeRoleSplitActive())
g_applyThreadInsideApplier.store(true, std::memory_order_release);
}
void ClientSession::NoteApplyThreadLeftApplier() {
g_applyThreadInsideApplier.store(false, std::memory_order_release);
g_roleInsideApplier = false;
if (!MG_Pipe::MGPipeRoleSplitActive())
g_applyThreadInsideApplier.store(false, std::memory_order_release);
}
Uint32 ClientSession::PumpControlPlane() {
@@ -7,6 +7,9 @@
#include <MG_Backend/MGPipe/PipeInputs.h>
#include <MG_Pipe/PipeApply.h>
#include <MG_State/GLState/Core.h>
#include <MG_Remote/Client/ClientSession.h>
#include <thread>
#include <atomic>
namespace MobileGL::MG_Backend::DirectGLES {
SamplerImpl::BackendSamplerObject* GetRawDepthFetchSampler();
@@ -149,3 +152,35 @@ TEST(ContextEpochTest, ServerLivenessDoesNotFollowTheClientContextOrAVerbStamp)
GTEST_SKIP() << "server liveness exists only in the disaggregated build";
#endif
}
TEST(ContextEpochTest, DualBlockApplierDiagnosticIsRoleLocalWhileSharedControlRemainsArmed) {
#if MOBILEGL_BUILD_DISAGGREGATED
DriverScope scope;
const Bool previous = MG_Config::Ipc.RoleSplitState;
using Session = MG_Remote::Client::ClientSession;
const auto observe = [&](Bool split) {
MG_Config::Ipc.RoleSplitState = split;
std::atomic<Bool> entered{false}, leave{false};
Bool serverSawItself = false;
std::thread server([&] {
Session::NoteApplyThreadEnteredApplier();
serverSawItself = Session::ApplyThreadIsInsideApplier();
entered.store(true, std::memory_order_release);
while (!leave.load(std::memory_order_acquire)) std::this_thread::yield();
Session::NoteApplyThreadLeftApplier();
});
while (!entered.load(std::memory_order_acquire)) std::this_thread::yield();
const Bool clientSawServer = Session::ApplyThreadIsInsideApplier();
leave.store(true, std::memory_order_release);
server.join();
EXPECT_TRUE(serverSawItself);
EXPECT_EQ(clientSawServer, !split);
EXPECT_FALSE(Session::ApplyThreadIsInsideApplier());
};
observe(false);
observe(true);
MG_Config::Ipc.RoleSplitState = previous;
#else
GTEST_SKIP() << "role-local observer requires the disaggregated build";
#endif
}