mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
[Refactor] (P13 W5 S4): the persistent-map tracker moves to MG_State as MG_Record; the adoption tier's verdict stays in MG_Remote.
This commit is contained in:
+4
-1
@@ -656,7 +656,8 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
# MarkGpuWritten producers are on the server's side of the line, and the
|
||||
# block-granularity persistent-map push that tier T2 makes mandatory.
|
||||
MobileGL/MG_Remote/Client/GpuWritePending.cpp
|
||||
MobileGL/MG_Remote/Client/PersistentMapTracker.cpp
|
||||
# P13 W5: the adoption tier's verdict (the tracker itself moved to the record arm below).
|
||||
MobileGL/MG_Remote/Client/AdoptTierChoice.cpp
|
||||
MobileGL/MG_Remote/Server/ServerSession.cpp
|
||||
MobileGL/MG_Remote/Server/PipeApplier.cpp
|
||||
MobileGL/MG_Remote/Server/ServerLoop.cpp
|
||||
@@ -694,6 +695,8 @@ if (MOBILEGL_BUILD_RECORD_ARM)
|
||||
# arms and the probe that measures the first against the second, shared by the
|
||||
# renderer's bring-up and the Vulkan POST.
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.cpp
|
||||
# P5 b1: the block-granularity persistent-map push the record arm makes mandatory (T2).
|
||||
MobileGL/MG_State/GLState/BufferState/PersistentMapTracker.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1964,14 +1964,14 @@ endif()
|
||||
# healthy pull build into a link error, which is why this decision is made here.
|
||||
if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
mgl_itest_probe_for_symbol(MGL_ITEST_PERSISTENT_MAP_TRACKER
|
||||
"${MGL_ITEST_ROOT}/MobileGL/MG_Remote/Client" "IsLivePersistentMap")
|
||||
"${MGL_ITEST_ROOT}/MobileGL/MG_State/GLState/BufferState" "IsLivePersistentMap")
|
||||
if (MGL_ITEST_PERSISTENT_MAP_TRACKER)
|
||||
message(STATUS "Integration tests: package b1's persistent-map tracker is present "
|
||||
"(${MGL_ITEST_PERSISTENT_MAP_TRACKER}) - PersistentCoherentMapScenario's "
|
||||
"membership assertion is live")
|
||||
target_compile_definitions(MobileGLIntegrationTest PRIVATE -DMGITEST_PERSISTENT_MAP_TRACKER=1)
|
||||
else()
|
||||
message(STATUS "Integration tests: no source under MobileGL/MG_Remote/Client names "
|
||||
message(STATUS "Integration tests: no source under MobileGL/MG_State/GLState/BufferState names "
|
||||
"IsLivePersistentMap - package b1's tracker has not landed, so "
|
||||
"PersistentCoherentMapScenario's membership assertion SKIPS")
|
||||
endif()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
// build that never compiles MG_Remote, so keying on it would turn a healthy pull build into a
|
||||
// link error.
|
||||
#if defined(MGITEST_PERSISTENT_MAP_PEEK_LIVE) && defined(MGITEST_PERSISTENT_MAP_TRACKER)
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#define MGITEST_PERSISTENT_MAP_TRACKER_LIVE 1
|
||||
#endif
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace MGITest {
|
||||
MobileGL::MG_State::GLState::BufferObject* buffer = FrontendBuffer(bufferName);
|
||||
if (buffer == nullptr) return false;
|
||||
*outLive = static_cast<bool>(
|
||||
MobileGL::MG_Remote::Client::PersistentMapTracker::IsLivePersistentMap(*buffer));
|
||||
MobileGL::MG_Record::PersistentMapTracker::IsLivePersistentMap(*buffer));
|
||||
return true;
|
||||
#else
|
||||
(void)bufferName;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace MGITest {
|
||||
// has to be made before the compiler sees it rather than by __has_include.
|
||||
bool PersistentMapTrackerAvailable();
|
||||
|
||||
// MG_Remote::Client::PersistentMapTracker::IsLivePersistentMap() for this buffer - b1-v1.md
|
||||
// MG_Record::PersistentMapTracker::IsLivePersistentMap() for this buffer - b1-v1.md
|
||||
// 4.1 item 2. The membership set is meant to be exactly the early-out chain of
|
||||
// SyncPersistentMappedRange; asking it here is what makes a drift between the two fail in a
|
||||
// named test rather than silently stop the push.
|
||||
|
||||
@@ -36,11 +36,13 @@
|
||||
// DEFINED below, so MG_Backend names no MG_Remote symbol of its own to reach Session::Fail. P13 W5:
|
||||
// in every build, for the record arm's fail / latch half.
|
||||
#include <MG_Pipe/PipeSessionFail.h>
|
||||
#include <MG_Pipe/PipeClientSeam.h>
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// R-6's tier gate. One spelling, asked at the one place the decline is decided. Outside the
|
||||
// MOBILEGL_PIPE_VERIFY block above on purpose: the tier is a property of the BUILD, not of the
|
||||
// comparator, and a split build without the comparator still declines every acquisition.
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_Remote/Client/AdoptTierChoice.h>
|
||||
// P5c (ct), CONTRACT-P5C.md §6 layer 2: MGPipeApplierReset's role guard asks
|
||||
// ServerLoop::OnApplyThread() - the one predicate that tells the GL thread from the thread
|
||||
// that owns g_applier under an active transport - and ClientSession::Active(), which is what
|
||||
@@ -182,6 +184,14 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeRecordLatchHook MGPipeRecordLatchHookInstalled() { return g_recordLatchHook; }
|
||||
MGPipeRecordLatchedHook MGPipeRecordLatchedHookInstalled() { return g_recordLatchedHook; }
|
||||
|
||||
// P13 W5: the client session's device-loss answer (PipeClientSeam.h). Constant-initialized for
|
||||
// the same static-init reason as the hooks above.
|
||||
namespace {
|
||||
constinit MGPipeClientDeviceLostProbe g_clientDeviceLostProbe = nullptr;
|
||||
} // namespace
|
||||
void MGPipeSetClientDeviceLostProbe(MGPipeClientDeviceLostProbe probe) { g_clientDeviceLostProbe = probe; }
|
||||
Bool MGPipeClientDeviceLost() { return g_clientDeviceLostProbe != nullptr && g_clientDeviceLostProbe(); }
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// ----------------------------------------------------------------------------
|
||||
// THE BACKEND-SIDE SESSION-FAIL SEAM (P7 wave 0). PipeSessionFail.h holds the argument;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// MobileGL - MobileGL/MG_Pipe/PipeClientSeam.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
// P13 W5: THE CLIENT SESSION'S ANSWERS THE RECORD ARM ASKS FOR, WITHOUT NAMING MG_Remote. The record
|
||||
// arm's client half (the persistent-map tracker, the verb port) runs in a library without a
|
||||
// transport too; there the answers are the monolith's (no session, so nothing was lost). With
|
||||
// MG_Remote linked, ClientSession.cpp installs the real probe at static init.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Includes.h>
|
||||
|
||||
namespace MobileGL::MG_Pipe {
|
||||
|
||||
// Has the calling process's client session latched a lost device (ClientSession::DeviceLost)?
|
||||
using MGPipeClientDeviceLostProbe = Bool (*)();
|
||||
void MGPipeSetClientDeviceLostProbe(MGPipeClientDeviceLostProbe probe);
|
||||
Bool MGPipeClientDeviceLost();
|
||||
|
||||
} // namespace MobileGL::MG_Pipe
|
||||
@@ -0,0 +1,49 @@
|
||||
// MobileGL - MobileGL/MG_Remote/Client/AdoptTierChoice.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#include "AdoptTierChoice.h"
|
||||
|
||||
#include <Config.h>
|
||||
#include <MG_Remote/FatalFunnel.h>
|
||||
#include <MG_Remote/Transport/AdoptTier.h>
|
||||
|
||||
namespace MobileGL::MG_Remote::Client {
|
||||
Bool AdoptTierIsEmulate() {
|
||||
const Uint32 tier = MG_Config::Ipc.AdoptTier;
|
||||
if (tier == 2) return true;
|
||||
// P11 B2: UNSET (T0 by default) reaching the T2 path - settled or not - is T2, quietly: a
|
||||
// session-free caller (a unit case, a harness) runs the P5 default it always ran, and a
|
||||
// settled session already counted its fallback at the handshake or its first map_persistent.
|
||||
if (tier == MG_Config::kAdoptTierUnset) return true;
|
||||
// P11 A1 (CONTRACT-P11 §1): THE VERDICT IS THE HANDSHAKE'S, NOT THIS CALL'S. Both roles
|
||||
// settle the tier from the knob and the data plane before the first record
|
||||
// (Transport/AdoptTier.cpp): a stream refuses T0/T1 by name and runs T2, a client over
|
||||
// shared segments dies by name there, and a server over shared segments serves the T2
|
||||
// its client settled. So a settled session reaching this with 0/1 is a T2 session and
|
||||
// says nothing more - the one line was the handshake's.
|
||||
if (tier <= 1 && Transport::AdoptTierSettledAtHandshake()) return true;
|
||||
// A map_persistent NO handshake settled (a session-free caller) is still a NAMED
|
||||
// refusal rather than a silent T2: falling back would make `MOBILEGL_IPC_ADOPT_TIER=0`
|
||||
// look like a working T0 run and silently produce pmap bytes it must not produce.
|
||||
// Anything above 2 is not a tier at all.
|
||||
// `dl` (CONTRACT-P6 5.2): the family word every death here carries.
|
||||
if (tier <= 1) {
|
||||
SessionFail(MGFatalFamily::UnimplementedAdoptTier,
|
||||
"MGPipe: Fatal{UnimplementedAdoptTier, \"T%u\"} - MOBILEGL_IPC_ADOPT_TIER=%u "
|
||||
"reached a map_persistent that no handshake settled; T0 and T1 are not "
|
||||
"implemented and only T2 (emulate) runs.",
|
||||
static_cast<unsigned>(tier), static_cast<unsigned>(tier));
|
||||
} else {
|
||||
SessionFail(MGFatalFamily::UnimplementedAdoptTier,
|
||||
"MGPipe: Fatal{UnimplementedAdoptTier, \"%u\"} - MOBILEGL_IPC_ADOPT_TIER=%u is "
|
||||
"not an adoption tier; the only values are 0 and 1 (P11) and 2 (emulate, the "
|
||||
"P5 default).",
|
||||
static_cast<unsigned>(tier), static_cast<unsigned>(tier));
|
||||
}
|
||||
}
|
||||
} // namespace MobileGL::MG_Remote::Client
|
||||
@@ -0,0 +1,30 @@
|
||||
// MobileGL - MobileGL/MG_Remote/Client/AdoptTierChoice.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
// P13 W5: the adoption tier's verdict at map_persistent, split out of PersistentMapTracker when the
|
||||
// tracker left MG_Remote for the record arm (it runs without a transport; this does not).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Includes.h>
|
||||
|
||||
namespace MobileGL::MG_Remote::Client {
|
||||
// R-6's tier gate, read at use. True for MOBILEGL_IPC_ADOPT_TIER=2, the only implemented
|
||||
// tier. The tiers are design/07's: T0 = the client allocates an AHardwareBuffer and the
|
||||
// server imports it (P11 package B), T1 = the server exports an opaque fd (closed), T2 =
|
||||
// decline and push. 0 and 1 are SETTLED AT THE HANDSHAKE (P11 A1, Transport/AdoptTier.h):
|
||||
// a stream refuses them by name and runs T2, a client over shared segments dies by name
|
||||
// there. So a settled session answers true here; only a map_persistent no handshake
|
||||
// settled is still a named refusal. It is asked
|
||||
// by MGPipeApplyMapPersistent, which is where the decline is decided, so the client's
|
||||
// three adoption call sites keep their existing "null means declined" branch and the
|
||||
// map-persistent-roundtrips counter keeps counting ATTEMPTS in both arms (E3(c) asserts
|
||||
// mpr is equal between the monolith and the split arm, which is only true if the decline
|
||||
// happens after the count, on the applier's side of the emission).
|
||||
Bool AdoptTierIsEmulate();
|
||||
} // namespace MobileGL::MG_Remote::Client
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "ClientSession.h"
|
||||
#include <MG_Remote/FatalFunnel.h>
|
||||
#include <MG_Pipe/PipeClientSeam.h>
|
||||
#include <MG_Remote/Handshake.h>
|
||||
#include <MG_Remote/Transport/LinkMetrics.h>
|
||||
|
||||
@@ -24,7 +25,7 @@
|
||||
#include "../Transport/InProcessTransport.h"
|
||||
#include "WireTables.h"
|
||||
#include "EmitTables.h"
|
||||
#include "PersistentMapTracker.h"
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include "ServerProbe.h"
|
||||
|
||||
#include <MGGitHash.h>
|
||||
@@ -646,6 +647,16 @@ namespace MobileGL::MG_Remote::Client {
|
||||
return context != nullptr && context->IsFromEndedWireSession();
|
||||
}
|
||||
|
||||
// P13 W5: the record arm's client half (the persistent-map tracker) asks this through MG_Pipe's
|
||||
// client seam, which has no MG_Remote to name. Registered at static init: the tracker may ask
|
||||
// before any session starts, and must get this answer, not the seam's monolith default.
|
||||
namespace {
|
||||
[[maybe_unused]] const bool g_deviceLostProbeRegistered = [] {
|
||||
MG_Pipe::MGPipeSetClientDeviceLostProbe(&ClientSession::DeviceLost);
|
||||
return true;
|
||||
}();
|
||||
} // namespace
|
||||
|
||||
String ClientSession::LastLossReason() {
|
||||
const std::lock_guard<std::mutex> lock(LossReasonMutex());
|
||||
return LossReason();
|
||||
@@ -757,7 +768,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
"the reset serial, persistent maps) start over",
|
||||
static_cast<unsigned long long>(SessionsStarted()));
|
||||
ForgetWireTablesOfEndedSession();
|
||||
PersistentMapTracker::Instance().ForgetEndedSession();
|
||||
MG_Record::PersistentMapTracker::Instance().ForgetEndedSession();
|
||||
MG_Pipe::MGPipeForgetEndedSession();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "ClientSession.h"
|
||||
#include "GpuWritePending.h"
|
||||
#include "PersistentMapTracker.h"
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
|
||||
#include "../Server/ServerLoop.h"
|
||||
#include "../Server/PipeApplier.h"
|
||||
@@ -237,7 +237,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// push still has to run - a coherent map is read by the GPU on any of them - but there
|
||||
// is no shader that could write one, so no mark walk.
|
||||
void BeforeReadOnlyVerb() {
|
||||
PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
MG_Pipe::MGPipeDrainDeferredDestroys();
|
||||
}
|
||||
|
||||
@@ -717,7 +717,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
const void* clientIndices, Uint64 clientIndexBytes,
|
||||
const MG_Pipe::MGPDrawIndirect* indirect) {
|
||||
VerbChannel session = ChannelFor(slot);
|
||||
PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MG_Record::PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MG_Pipe::MGPipeDrainDeferredDestroys();
|
||||
// Snapshot before marking THIS draw's potential GPU writes: resolving
|
||||
// a GPU-produced EBO here must not clear its pending mark for this draw.
|
||||
@@ -1491,7 +1491,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// slot would inherit a call site that was already correct.
|
||||
void EmitDispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) {
|
||||
VerbChannel session = ChannelFor("DispatchCompute");
|
||||
PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MG_Record::PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MarkGpuWritesForDispatch();
|
||||
|
||||
MG_Pipe::MGPGridInfo record{};
|
||||
@@ -1510,7 +1510,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
|
||||
void EmitDispatchComputeIndirect(GLintptr indirect) {
|
||||
VerbChannel session = ChannelFor("DispatchComputeIndirect");
|
||||
PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MG_Record::PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MarkGpuWritesForDispatch();
|
||||
|
||||
MG_Pipe::MGPGridInfo record{};
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// ---------------------------------------------------------------------------------
|
||||
//
|
||||
// OnApplyThread() is the predicate under EVERY role guard on both hot paths -
|
||||
// RefuseLegacyBufferArmFromApplyThread in BufferObject's accessors, PersistentMapTracker::
|
||||
// RefuseLegacyBufferArmFromApplyThread in BufferObject's accessors, MG_Record::PersistentMapTracker::
|
||||
// OnServerRole, ClientSession::RefusePipeInputsTouchWhileApplierOwnsIt, RunsAsTheServerRole,
|
||||
// MGPipeRefuseAllocatorFromApplyThread, RefuseLegacyTextureArmFromApplyThread. simpleperf at
|
||||
// head 56a77348 (FCL + Minecraft, VD12, Adreno 830) measured it at 2.66% self / 3.64%
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
#include <MG_Pipe/PipeApply.h>
|
||||
// R-6's tier gate, and the ONE spelling of it (b1's file, unchanged by this package): the
|
||||
// MapPersistent arm below asks it the same question MGPipeApplyMapPersistent asks.
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_Remote/Client/AdoptTierChoice.h>
|
||||
#include <MG_Remote/Protocol/generated/protocol_generated.h>
|
||||
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
#include <atomic>
|
||||
#include <MG_Pipe/PipeMutation.h>
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// The client role's persistent-map tracker. MG_State reaching into MG_Remote/Client is the
|
||||
// layering ARCHITECTURE.md:575 names - the CLIENT role IS MG_State plus MG_Impl - and the
|
||||
// edge exists only in a build that has the transport at all.
|
||||
// MG_State reaching into MG_Remote/Client is the layering ARCHITECTURE.md:575 names - the CLIENT
|
||||
// role IS MG_State plus MG_Impl - and the edge exists only in a build that has the transport at all.
|
||||
#include <MG_Remote/Client/GpuWritePending.h>
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// The client role's persistent-map tracker (P13 W5: the record arm's, so beside this file).
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -60,8 +62,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
// of MG_Config::Transport and OnServerRole() is now one relaxed load of the apply
|
||||
// thread's key (ServerLoop.h), so the monolith answer costs a load each.
|
||||
void RefuseLegacyBufferArmFromApplyThread(const char* accessor) {
|
||||
if (!MG_Remote::Client::PersistentMapTracker::PushIsArmed()) return;
|
||||
if (!MG_Remote::Client::PersistentMapTracker::OnServerRole()) return;
|
||||
if (!MG_Record::PersistentMapTracker::PushIsArmed()) return;
|
||||
if (!MG_Record::PersistentMapTracker::OnServerRole()) return;
|
||||
FatalLegacyBufferArmFromApplyThread(accessor);
|
||||
}
|
||||
#endif
|
||||
@@ -97,7 +99,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// tracker is a leaked singleton whose entries are raw pointers, and an entry that
|
||||
// outlives its object is the one failure this set must not have. Forget is a no-op
|
||||
// for a buffer that was never a member.
|
||||
MG_Remote::Client::PersistentMapTracker::Instance().Forget(*this);
|
||||
MG_Record::PersistentMapTracker::Instance().Forget(*this);
|
||||
#endif
|
||||
// P3a D-L: the buffer's death crosses as resource_destroy, which is the catalogue
|
||||
// call for it - no seventh NotifyStateObjectDestroyed raiser is added, because that
|
||||
@@ -419,11 +421,11 @@ namespace MobileGL::MG_State::GLState {
|
||||
// overwrite the server shadow through the monolith adapter without crossing the wire -
|
||||
// and "do nothing" is spelled Fatal, not `return`, because a server-side caller here
|
||||
// is a role violation and not a shape the design tolerates.
|
||||
if (MG_Remote::Client::PersistentMapTracker::PushIsArmed()) {
|
||||
if (MG_Remote::Client::PersistentMapTracker::OnServerRole()) {
|
||||
if (MG_Record::PersistentMapTracker::PushIsArmed()) {
|
||||
if (MG_Record::PersistentMapTracker::OnServerRole()) {
|
||||
FatalLegacyBufferArmFromApplyThread("SyncPersistentMappedRange");
|
||||
}
|
||||
MG_Remote::Client::PersistentMapTracker::Instance().PushBlocksFor(*this);
|
||||
MG_Record::PersistentMapTracker::Instance().PushBlocksFor(*this);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -447,7 +449,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// push does, and a second route to the same record is a second thing to keep in step.
|
||||
if (size == 0) return;
|
||||
// Assert ownership at the final producer entry too, before serial/aggregate updates.
|
||||
if (MG_Remote::Client::PersistentMapTracker::OnServerRole()) {
|
||||
if (MG_Record::PersistentMapTracker::OnServerRole()) {
|
||||
MGLOG_F("MGPipe: Fatal{RoleViolation, \"PushMappedSpanBlock\"} - the server role (the apply "
|
||||
"thread) reached the CLIENT persistent-map producer for buffer lifetime %llu "
|
||||
"[%zu, +%zu). Under an active transport the server's staged copy is the draw's "
|
||||
@@ -474,10 +476,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
void BufferObject::NotePersistentMapStateChanged() {
|
||||
if (!MG_Remote::Client::PersistentMapTracker::PushIsArmed()) return;
|
||||
MG_Remote::Client::PersistentMapTracker::Instance().NoteMapStateChanged(*this);
|
||||
if (!MG_Record::PersistentMapTracker::PushIsArmed()) return;
|
||||
MG_Record::PersistentMapTracker::Instance().NoteMapStateChanged(*this);
|
||||
// Teardown must retire membership, but only the client publishes live-host-write state.
|
||||
if (MG_Remote::Client::PersistentMapTracker::OnServerRole()) return;
|
||||
if (MG_Record::PersistentMapTracker::OnServerRole()) return;
|
||||
|
||||
// THE LIVE-HOST-WRITES BIT (ARCHITECTURE.md 12, CONTRACT-P5.md section 3). A live
|
||||
// WRITE map - persistent or not - mutates the shadow with no call, no serial and no
|
||||
@@ -511,7 +513,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// carries are real, current and the cheapest honest ones there are. A zero-length
|
||||
// record would have been the alternative and it is illegal by contract rule A.
|
||||
if (m_size == 0) return;
|
||||
const Uint64 blockBytes = MG_Remote::Client::PersistentMapTracker::BlockBytes();
|
||||
const Uint64 blockBytes = MG_Record::PersistentMapTracker::BlockBytes();
|
||||
// MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0 TURNS THE WHOLE MECHANISM OFF, STATE RECORD
|
||||
// INCLUDED (E3(a)). An earlier cut skipped the blocks and still shipped a whole buffer
|
||||
// here, which meant an unmap delivered the bytes the control exists to withhold - and
|
||||
@@ -930,7 +932,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
// P11 B2 made this reachable (a T0 adoption), so the tracker lets go of the
|
||||
// shadow range WHILE THE SHADOW IS STILL OURS: AdoptPersistentMap frees it, and
|
||||
// an untrack after that would mprotect pages the buffer no longer owns.
|
||||
MG_Remote::Client::PersistentMapTracker::Instance().Forget(*this);
|
||||
MG_Record::PersistentMapTracker::Instance().Forget(*this);
|
||||
#endif
|
||||
m_resource.AdoptPersistentMap(pushedBase);
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace MobileGL {
|
||||
// the private NotifySubData - the same serial, the same defined-content flag, the
|
||||
// same record - so the split arm and the monolith arm differ in HOW the span is
|
||||
// cut and in nothing else. Called only by
|
||||
// MG_Remote::Client::PersistentMapTracker, which owns the cutting.
|
||||
// MG_Record::PersistentMapTracker, which owns the cutting.
|
||||
void PushMappedSpanBlock(SizeT offset, SizeT size);
|
||||
|
||||
// Called on every event that can move the tracker's membership predicate: map,
|
||||
|
||||
+13
-45
@@ -1,4 +1,4 @@
|
||||
// MobileGL - MobileGL/MG_Remote/Client/PersistentMapTracker.cpp
|
||||
// MobileGL - MobileGL/MG_State/GLState/BufferState/PersistentMapTracker.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
@@ -7,9 +7,8 @@
|
||||
// End of Source File Header
|
||||
|
||||
#include "PersistentMapTracker.h"
|
||||
#include "ClientSession.h"
|
||||
#include <MG_Remote/FatalFunnel.h>
|
||||
#include <MG_Remote/Transport/AdoptTier.h>
|
||||
#include <MG_Pipe/PipeSessionFail.h>
|
||||
#include <MG_Pipe/PipeClientSeam.h>
|
||||
|
||||
#include <MG_Pipe/MGPipeTypes.h>
|
||||
#include <MG_State/GLState/BufferState/BufferObject.h>
|
||||
@@ -17,7 +16,7 @@
|
||||
#include <MG_State/GLState/TextureState/TextureObjectBuffer.h>
|
||||
#include <MG_State/GLState/VertexArrayState/VertexArrayObject.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
#include <MG_Remote/Server/ServerLoop.h>
|
||||
#include <MG_Backend/MGPipe/PipeInputs.h>
|
||||
|
||||
#include <xxhash.h>
|
||||
|
||||
@@ -30,7 +29,7 @@
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace MobileGL::MG_Remote::Client {
|
||||
namespace MobileGL::MG_Record {
|
||||
|
||||
using MG_State::GLState::BufferObject;
|
||||
|
||||
@@ -714,7 +713,9 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// guard, two acquire loads and an out-of-line thread::id comparison. It stays out of line
|
||||
// here for WireTables.cpp's reason beside RunsAsTheServerRole: inlining it would put a
|
||||
// server header inside PersistentMapTracker.h, which MG_State's BufferObject.cpp includes.
|
||||
Bool PersistentMapTracker::OnServerRole() { return Server::ServerLoop::OnApplyThread(); }
|
||||
// P13 W5: the apply-thread question through MG_Pipe's role probe (MG_Remote installs it; a library
|
||||
// without a transport has no server role, so it is false there).
|
||||
Bool PersistentMapTracker::OnServerRole() { return MG_Pipe::MGPipeServerArm(); }
|
||||
|
||||
// SyncPersistentMappedRange's early-out chain (BufferObject.cpp:341-353), in its order,
|
||||
// read as a membership test. Every line here has a line there; if one of them moves, the
|
||||
@@ -757,7 +758,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// A buffer mapped on a LOST context (a thread still on one, or a session that is lost)
|
||||
// never becomes a member: the push before every verb would carry its bytes to whichever
|
||||
// session is current, and a fresh one never created the buffer.
|
||||
if (IsLivePersistentMap(buffer) && !ClientSession::DeviceLost()) {
|
||||
if (IsLivePersistentMap(buffer) && !MG_Pipe::MGPipeClientDeviceLost()) {
|
||||
const auto range = buffer.GetMappedRange();
|
||||
// The mprotect arm registers beside the hash arm: tracked buffers are pushed
|
||||
// from the fault bitmap, everything else keeps the content scan. A failed
|
||||
@@ -862,7 +863,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
void PersistentMapTracker::PushBlocksFor(BufferObject& buffer) {
|
||||
if (!PushIsArmed()) return;
|
||||
if (OnServerRole()) {
|
||||
SessionFail(MGFatalFamily::RoleViolation, "MGPipe: Fatal{RoleViolation, \"PushBlocksFor\"} - the persistent-map "
|
||||
MG_Pipe::MGPipeRecordFail(MG_Pipe::MGFatalFamily::RoleViolation, "MGPipe: Fatal{RoleViolation, \"PushBlocksFor\"} - the persistent-map "
|
||||
"producer belongs to the client; the server consumes transported bytes");
|
||||
}
|
||||
PushBlocksForChecked(buffer);
|
||||
@@ -1084,7 +1085,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
void PersistentMapTracker::PushAllMembers() {
|
||||
if (!PushIsArmed()) return;
|
||||
if (OnServerRole()) {
|
||||
SessionFail(MGFatalFamily::RoleViolation, "MGPipe: Fatal{RoleViolation, \"PushAllMembers\"} - the persistent-map "
|
||||
MG_Pipe::MGPipeRecordFail(MG_Pipe::MGFatalFamily::RoleViolation, "MGPipe: Fatal{RoleViolation, \"PushAllMembers\"} - the persistent-map "
|
||||
"producer belongs to the client; the server consumes transported bytes");
|
||||
}
|
||||
if (m_livePersistentMaps.empty()) return;
|
||||
@@ -1101,7 +1102,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
void PersistentMapTracker::PushDrawConsumers() {
|
||||
if (!PushIsArmed()) return;
|
||||
if (OnServerRole()) {
|
||||
SessionFail(MGFatalFamily::RoleViolation, "MGPipe: Fatal{RoleViolation, \"PushDrawConsumers\"} - the persistent-map "
|
||||
MG_Pipe::MGPipeRecordFail(MG_Pipe::MGFatalFamily::RoleViolation, "MGPipe: Fatal{RoleViolation, \"PushDrawConsumers\"} - the persistent-map "
|
||||
"producer belongs to the client; the server consumes transported bytes");
|
||||
}
|
||||
if (m_livePersistentMaps.empty()) return;
|
||||
@@ -1258,38 +1259,5 @@ namespace MobileGL::MG_Remote::Client {
|
||||
PersistentMapTracker::Instance().PushAllMembers();
|
||||
}
|
||||
|
||||
Bool AdoptTierIsEmulate() {
|
||||
const Uint32 tier = MG_Config::Ipc.AdoptTier;
|
||||
if (tier == 2) return true;
|
||||
// P11 B2: UNSET (T0 by default) reaching the T2 path - settled or not - is T2, quietly: a
|
||||
// session-free caller (a unit case, a harness) runs the P5 default it always ran, and a
|
||||
// settled session already counted its fallback at the handshake or its first map_persistent.
|
||||
if (tier == MG_Config::kAdoptTierUnset) return true;
|
||||
// P11 A1 (CONTRACT-P11 §1): THE VERDICT IS THE HANDSHAKE'S, NOT THIS CALL'S. Both roles
|
||||
// settle the tier from the knob and the data plane before the first record
|
||||
// (Transport/AdoptTier.cpp): a stream refuses T0/T1 by name and runs T2, a client over
|
||||
// shared segments dies by name there, and a server over shared segments serves the T2
|
||||
// its client settled. So a settled session reaching this with 0/1 is a T2 session and
|
||||
// says nothing more - the one line was the handshake's.
|
||||
if (tier <= 1 && Transport::AdoptTierSettledAtHandshake()) return true;
|
||||
// A map_persistent NO handshake settled (a session-free caller) is still a NAMED
|
||||
// refusal rather than a silent T2: falling back would make `MOBILEGL_IPC_ADOPT_TIER=0`
|
||||
// look like a working T0 run and silently produce pmap bytes it must not produce.
|
||||
// Anything above 2 is not a tier at all.
|
||||
// `dl` (CONTRACT-P6 5.2): the family word every death here carries.
|
||||
if (tier <= 1) {
|
||||
SessionFail(MGFatalFamily::UnimplementedAdoptTier,
|
||||
"MGPipe: Fatal{UnimplementedAdoptTier, \"T%u\"} - MOBILEGL_IPC_ADOPT_TIER=%u "
|
||||
"reached a map_persistent that no handshake settled; T0 and T1 are not "
|
||||
"implemented and only T2 (emulate) runs.",
|
||||
static_cast<unsigned>(tier), static_cast<unsigned>(tier));
|
||||
} else {
|
||||
SessionFail(MGFatalFamily::UnimplementedAdoptTier,
|
||||
"MGPipe: Fatal{UnimplementedAdoptTier, \"%u\"} - MOBILEGL_IPC_ADOPT_TIER=%u is "
|
||||
"not an adoption tier; the only values are 0 and 1 (P11) and 2 (emulate, the "
|
||||
"P5 default).",
|
||||
static_cast<unsigned>(tier), static_cast<unsigned>(tier));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MobileGL::MG_Remote::Client
|
||||
} // namespace MobileGL::MG_Record
|
||||
+3
-16
@@ -1,4 +1,4 @@
|
||||
// MobileGL - MobileGL/MG_Remote/Client/PersistentMapTracker.h
|
||||
// MobileGL - MobileGL/MG_State/GLState/BufferState/PersistentMapTracker.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
@@ -61,7 +61,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
class BufferObject;
|
||||
}
|
||||
|
||||
namespace MobileGL::MG_Remote::Client {
|
||||
namespace MobileGL::MG_Record {
|
||||
|
||||
// The mprotect tracker's per-map slot, declared at namespace scope rather than in the
|
||||
// .cpp's anonymous namespace so PersistentMapTracker's membership map can CACHE a
|
||||
@@ -270,18 +270,5 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// draw-path binding walks move to the client.
|
||||
void PushPersistentMapsBeforeVerb();
|
||||
|
||||
// R-6's tier gate, read at use. True for MOBILEGL_IPC_ADOPT_TIER=2, the only implemented
|
||||
// tier. The tiers are design/07's: T0 = the client allocates an AHardwareBuffer and the
|
||||
// server imports it (P11 package B), T1 = the server exports an opaque fd (closed), T2 =
|
||||
// decline and push. 0 and 1 are SETTLED AT THE HANDSHAKE (P11 A1, Transport/AdoptTier.h):
|
||||
// a stream refuses them by name and runs T2, a client over shared segments dies by name
|
||||
// there. So a settled session answers true here; only a map_persistent no handshake
|
||||
// settled is still a named refusal. It is asked
|
||||
// by MGPipeApplyMapPersistent, which is where the decline is decided, so the client's
|
||||
// three adoption call sites keep their existing "null means declined" branch and the
|
||||
// map-persistent-roundtrips counter keeps counting ATTEMPTS in both arms (E3(c) asserts
|
||||
// mpr is equal between the monolith and the split arm, which is only true if the decline
|
||||
// happens after the count, on the applier's side of the emission).
|
||||
Bool AdoptTierIsEmulate();
|
||||
|
||||
} // namespace MobileGL::MG_Remote::Client
|
||||
} // namespace MobileGL::MG_Record
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Remote/Client/GpuWritePending.h>
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_Remote/Client/AdoptTierChoice.h>
|
||||
#include <MG_Remote/Transport/AdoptTier.h>
|
||||
#endif
|
||||
|
||||
@@ -38,7 +39,7 @@ namespace {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
using MG_State::GLState::BufferObject;
|
||||
using MG_Remote::Client::GpuWriteProducer;
|
||||
using MG_Remote::Client::PersistentMapTracker;
|
||||
using MG_Record::PersistentMapTracker;
|
||||
|
||||
// A backend that MINTS a persistent mapping, for the one case that needs the adopted arm
|
||||
// (TheAdoptedArmIsNotAMemberAndItIsTheChainRowThatSaysSo). Only AcquirePersistentMap is
|
||||
@@ -390,7 +391,7 @@ TEST_F(SplitBufferSet, TheAdoptedArmIsNotAMemberAndItIsTheChainRowThatSaysSo) {
|
||||
|
||||
// ...and the consequence, which is the one that would actually corrupt something.
|
||||
const Uint64 before = MG_Util::PipeStats::TotalBytes(MG_Util::PipeStats::ByteClass::PersistentMapPush);
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
EXPECT_EQ(MG_Util::PipeStats::TotalBytes(MG_Util::PipeStats::ByteClass::PersistentMapPush) - before,
|
||||
static_cast<Uint64>(kSize))
|
||||
<< "the declined twin's 4096 bytes and nothing else: the adopted buffer must contribute no "
|
||||
@@ -414,7 +415,7 @@ TEST_F(SplitBufferSet, ThePushCutsTheMappedSpanIntoBlocksAndMovesPmap) {
|
||||
ASSERT_TRUE(PersistentMapTracker::IsLivePersistentMap(*buffer));
|
||||
|
||||
const Uint64 before = MG_Util::PipeStats::TotalBytes(MG_Util::PipeStats::ByteClass::PersistentMapPush);
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BlocksPushed(), 4u)
|
||||
<< "a 256 KiB span at a 64 KiB block size is four records, not one";
|
||||
@@ -433,7 +434,7 @@ TEST_F(SplitBufferSet, TheLastBlockIsTheRemainderAndNotAWholeBlock) {
|
||||
auto buffer = MakeBuffer(22u, kSize);
|
||||
buffer->AcquireMemoryRange(Range1D{0, kSize},
|
||||
BufferMappingAccessBit::Write | BufferMappingAccessBit::Persistent);
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BlocksPushed(), 2u);
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BytesPushed(), static_cast<Uint64>(kSize));
|
||||
buffer->ReleaseMemory(false);
|
||||
@@ -482,7 +483,7 @@ TEST_F(SplitBufferSet, AZeroBlockSizeTurnsThePushOffRatherThanMakingItUnlimited)
|
||||
<< "the rising-edge state record went out with the push disabled";
|
||||
|
||||
const Uint64 before = MG_Util::PipeStats::TotalBytes(MG_Util::PipeStats::ByteClass::PersistentMapPush);
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BlocksPushed(), 0u);
|
||||
EXPECT_EQ(MG_Util::PipeStats::TotalBytes(MG_Util::PipeStats::ByteClass::PersistentMapPush), before)
|
||||
<< "MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0 must ship nothing, so that "
|
||||
@@ -621,10 +622,10 @@ TEST_F(SplitBufferSet, ASubPageMapIsOneProtectedPageAndAWriteThroughItFaults) {
|
||||
|
||||
// Fresh state ships everything once, then re-arms; with nothing written a second push
|
||||
// ships nothing (and, with no edges, hashes nothing).
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BlocksPushed(), 1u);
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BytesPushed(), static_cast<Uint64>(kSize));
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BlocksPushed(), 1u);
|
||||
|
||||
// The one page IS protected: a write through the application's pointer faults into the
|
||||
@@ -634,7 +635,7 @@ TEST_F(SplitBufferSet, ASubPageMapIsOneProtectedPageAndAWriteThroughItFaults) {
|
||||
mapped[kSize - 1] = 0x5A;
|
||||
EXPECT_GT(PersistentMapTracker::FaultEpochForTest(), epochBefore)
|
||||
<< "the sub-page map's page was not protected: the write did not fault";
|
||||
MG_Remote::Client::PushPersistentMapsBeforeVerb();
|
||||
MG_Record::PushPersistentMapsBeforeVerb();
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BlocksPushed(), 2u);
|
||||
EXPECT_EQ(PersistentMapTracker::Instance().BytesPushed(), static_cast<Uint64>(2 * kSize))
|
||||
<< "the push is clamped to [begin, end) even though the protected page is wider";
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
// Neither revert can be hidden by the other, which is the property R-16 asks for.
|
||||
|
||||
#include <Config.h>
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PipeResource.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace {
|
||||
using MobileGL::SizeT;
|
||||
using MobileGL::Uint64;
|
||||
using MobileGL::Uint8;
|
||||
using MobileGL::MG_Remote::Client::PersistentMapTracker;
|
||||
using MobileGL::MG_Record::PersistentMapTracker;
|
||||
using MobileGL::MG_State::GLState::MapAlignedData;
|
||||
using MobileGL::MG_State::GLState::ShadowAllocationBytesFor;
|
||||
|
||||
|
||||
@@ -638,7 +638,7 @@ TEST(RemoteClientControls, PersistentMapProducerRefusesTheApplyThread) {
|
||||
const auto child = RunInChild([] {
|
||||
StartControlSession();
|
||||
Srv::ServerLoopInstance().RunProbeOnApplyThreadForTesting([](void*) {
|
||||
PersistentMapTracker::Instance().PushAllMembers();
|
||||
MG_Record::PersistentMapTracker::Instance().PushAllMembers();
|
||||
return MOBILEGL_OK;
|
||||
}, nullptr);
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#include <MG_Remote/Client/EmitTables.h>
|
||||
#include <MG_Remote/Client/WireTables.h>
|
||||
#include <MG_Remote/Client/ClientSession.h>
|
||||
#include <MG_Remote/Client/PersistentMapTracker.h>
|
||||
#include <MG_State/GLState/BufferState/PersistentMapTracker.h>
|
||||
#include <MG_Remote/Client/BackendObject_Remote.h>
|
||||
#include <MG_Remote/Server/ServerLoop.h>
|
||||
#include <MG_Remote/Server/ServerSession.h>
|
||||
|
||||
Reference in New Issue
Block a user