mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
[Refactor] (P13 W5 S5): the record arm's three knobs move from IpcTable to RecordArmTable, which every record-arm build parses.
This commit is contained in:
+31
-22
@@ -462,6 +462,37 @@ namespace MobileGL::MG_Config {
|
||||
NamedPipe = 4, // P6: Windows named pipe (Endpoint = <name>)
|
||||
};
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W5: THE RECORD ARM'S OWN KNOBS. They kept their MOBILEGL_IPC_* names (logs, CI lanes and
|
||||
// notes spell them so), but they tune the record arm - the persistent-map push and Magma's wire
|
||||
// stores - which runs in a library without a transport too, so they are not IpcTable's.
|
||||
struct RecordArmTable {
|
||||
// MOBILEGL_IPC_WIRE_DEFERRED_MB (P7 wave 4 M2, ID-P7-32): the SERVER's budget, in MiB,
|
||||
// for orphaned wire buffer stores - the old VkBuffer every glBufferData that crosses the
|
||||
// wire leaves behind - that a GPU command recorded but not yet retired may still name.
|
||||
// Stores no command names are destroyed at once and stores whose last submission has
|
||||
// retired are destroyed at the next park; this bounds the REST. When the parked bytes
|
||||
// exceed it after a sweep, the server flushes what it has recorded and waits for it
|
||||
// (WaitForWireBufferHostAccess's sync point, mid-frame), which retires every one. It is
|
||||
// not a frame count because a frame is not bounded: a snapshot-exiting pbuffer replay
|
||||
// delivers one present for 1.3 M calls. The same sync point also fires above a fixed
|
||||
// 1024 parked stores (VkBufferManager::kWireDeferredCountCeiling), because small
|
||||
// orphans never reach a byte budget. 0 IS THE NEGATIVE CONTROL for both, not "unlimited
|
||||
// by design": no forced sync, so a one-frame respecify-and-draw loop grows without bound
|
||||
// and MagmaWireReclaimScenario's watermark cases must go red.
|
||||
Uint32 WireDeferredMb = 64;
|
||||
// MOBILEGL_IPC_PERSISTENT_BLOCK_KB: block granularity of the persistent-map push.
|
||||
// 0 IS A NEGATIVE CONTROL, NOT "unlimited": it disables the push, and
|
||||
// PersistentCoherentMapScenario must go RED under it (exit gate E3(a)).
|
||||
Uint32 PersistentBlockKb = 64;
|
||||
// MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS: 1 = the persistent-map push ships only
|
||||
// blocks whose xxHash64 changed since the last push, instead of the whole mapped
|
||||
// range every verb. 0 restores the whole-range push (A/B control).
|
||||
Uint32 PersistentHashSuppress = 1;
|
||||
};
|
||||
extern RecordArmTable RecordArm;
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// Parsed once by MG_ConfigLoader::Init(). Defaults to Monolith even here: building the
|
||||
// transport in is not the same as using it, and every existing lane of a build-split
|
||||
@@ -520,20 +551,6 @@ namespace MobileGL::MG_Config {
|
||||
// stage chunk budget (MGPipeStageChunkBytes, a quarter of this), and a record type with
|
||||
// no cut is Fatal{RingOverrun, "SEG_STAGE"} rather than allowed to exceed it.
|
||||
Uint32 StageMb = 32;
|
||||
// MOBILEGL_IPC_WIRE_DEFERRED_MB (P7 wave 4 M2, ID-P7-32): the SERVER's budget, in MiB,
|
||||
// for orphaned wire buffer stores - the old VkBuffer every glBufferData that crosses the
|
||||
// wire leaves behind - that a GPU command recorded but not yet retired may still name.
|
||||
// Stores no command names are destroyed at once and stores whose last submission has
|
||||
// retired are destroyed at the next park; this bounds the REST. When the parked bytes
|
||||
// exceed it after a sweep, the server flushes what it has recorded and waits for it
|
||||
// (WaitForWireBufferHostAccess's sync point, mid-frame), which retires every one. It is
|
||||
// not a frame count because a frame is not bounded: a snapshot-exiting pbuffer replay
|
||||
// delivers one present for 1.3 M calls. The same sync point also fires above a fixed
|
||||
// 1024 parked stores (VkBufferManager::kWireDeferredCountCeiling), because small
|
||||
// orphans never reach a byte budget. 0 IS THE NEGATIVE CONTROL for both, not "unlimited
|
||||
// by design": no forced sync, so a one-frame respecify-and-draw loop grows without bound
|
||||
// and MagmaWireReclaimScenario's watermark cases must go red.
|
||||
Uint32 WireDeferredMb = 64;
|
||||
// MOBILEGL_IPC_SPIN_US: spin before parking on a doorbell, either direction.
|
||||
Uint32 SpinUs = 50;
|
||||
// MOBILEGL_IPC_EVENT_WAIT_MS (PH-6, ID-P7-2): the SERVER's patience, in ms, for ONE
|
||||
@@ -551,14 +568,6 @@ namespace MobileGL::MG_Config {
|
||||
// spent twice. Read by the server only (ServerSpawn.cpp passes it to a spawned child);
|
||||
// the lockstep arm (no kCapRunAheadApply, i.e. Magma) never waits and keeps P5C's Fatal.
|
||||
Uint32 EventWaitMs = 2000;
|
||||
// MOBILEGL_IPC_PERSISTENT_BLOCK_KB: block granularity of the persistent-map push.
|
||||
// 0 IS A NEGATIVE CONTROL, NOT "unlimited": it disables the push, and
|
||||
// PersistentCoherentMapScenario must go RED under it (exit gate E3(a)).
|
||||
Uint32 PersistentBlockKb = 64;
|
||||
// MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS: 1 = the persistent-map push ships only
|
||||
// blocks whose xxHash64 changed since the last push, instead of the whole mapped
|
||||
// range every verb. 0 restores the whole-range push (A/B control).
|
||||
Uint32 PersistentHashSuppress = 1;
|
||||
// MOBILEGL_IPC_BATCH_WAITS: 1 = value-class records (kCtxState / kCtxCso / kCtxObject
|
||||
// with no reply slot) are published without waiting for their own apply; the barrier
|
||||
// is taken at the next pull-reading verb (kCtxVerb syncs, queries, screen rows, and
|
||||
|
||||
+24
-11
@@ -40,6 +40,9 @@ namespace MobileGL::MG_Config {
|
||||
IpcTable Ipc;
|
||||
Bool MonolithTakesRecordArm = false;
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
RecordArmTable RecordArm;
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Config
|
||||
|
||||
namespace MobileGL::MG_ConfigLoader {
|
||||
@@ -562,20 +565,11 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
// are sanity, not policy.
|
||||
ipc.RingMb = QueryEnvUint32("MOBILEGL_IPC_RING_MB", 8, 1, 1024);
|
||||
ipc.StageMb = QueryEnvUint32("MOBILEGL_IPC_STAGE_MB", 32, 1, 4096);
|
||||
// P7 wave 4 M2 (Config.h has the semantics). 0 is admitted ON PURPOSE as the negative
|
||||
// control: no forced sync, and MagmaWireReclaimScenario's watermark case must go red.
|
||||
ipc.WireDeferredMb = QueryEnvUint32("MOBILEGL_IPC_WIRE_DEFERRED_MB", 64, 0, 65536);
|
||||
ipc.SpinUs = QueryEnvUint32("MOBILEGL_IPC_SPIN_US", 50, 0, 1000000);
|
||||
// PH-6 (ID-P7-2; Config.h has the semantics). 0 is NOT admitted: a server with no
|
||||
// patience at all would forfeit a healthy run-ahead client the first time its ring
|
||||
// filled between two of the client's drains, which is an ordinary backlog.
|
||||
ipc.EventWaitMs = QueryEnvUint32("MOBILEGL_IPC_EVENT_WAIT_MS", 2000, 1, 600000);
|
||||
// 0 is admitted ON PURPOSE and is the negative control of exit gate E3(a): it turns
|
||||
// the persistent-map push OFF, and PersistentCoherentMapScenario must go red.
|
||||
ipc.PersistentBlockKb = QueryEnvUint32("MOBILEGL_IPC_PERSISTENT_BLOCK_KB", 64, 0, 65536);
|
||||
// Whole-range push is the 0 arm; with it on (default) only blocks whose
|
||||
// xxHash64 changed since the last push are shipped.
|
||||
ipc.PersistentHashSuppress = QueryEnvUint32("MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS", 1, 0, 1);
|
||||
ipc.BatchWaits = QueryEnvUint32("MOBILEGL_IPC_BATCH_WAITS", 1, 0, 1);
|
||||
// The create window. DEFAULT 6, and it works because a link that declares RetainsReplies
|
||||
// keeps a declared answer until its reader takes it - Config.h carries the three reasons it
|
||||
@@ -699,8 +693,8 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
"persistent-block=%uKiB create-window=%u pbo-readback-sync=%u poll-escalate=%u "
|
||||
"adopt-tier=%s allow-t0=%u verb-barrier=%u run-ahead=%u present-credit=%u control-timeout=%ums "
|
||||
"cold-start=%ums strict=%d audit=%d role-split-state=%d affinity='%s' surface=%s",
|
||||
ipc.RingMb, ipc.StageMb, ipc.WireDeferredMb, ipc.SpinUs, ipc.EventWaitMs,
|
||||
ipc.PersistentBlockKb, ipc.CreateWindow, ipc.PboReadbackSync, ipc.PollEscalate,
|
||||
ipc.RingMb, ipc.StageMb, MG_Config::RecordArm.WireDeferredMb, ipc.SpinUs, ipc.EventWaitMs,
|
||||
MG_Config::RecordArm.PersistentBlockKb, ipc.CreateWindow, ipc.PboReadbackSync, ipc.PollEscalate,
|
||||
ipc.AdoptTier == 0 ? "0" : ipc.AdoptTier == 1 ? "1" : ipc.AdoptTier == 2 ? "2" : "unset(T0)",
|
||||
ipc.AllowAdoptT0,
|
||||
ipc.VerbBarrier, ipc.RunAhead, ipc.PresentCredit, ipc.ControlTimeoutMs, ipc.ColdStartMs,
|
||||
@@ -712,6 +706,22 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
"expected to fail: the client still pulls 31 of 63 PipeInputs fields from a "
|
||||
"live GLContext, so an unbarriered queue lets the server read future values");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// P13 W5: the record arm's knobs (Config.h RecordArmTable), in every build that has the arm.
|
||||
inline void InitRecordArm() {
|
||||
MG_Config::RecordArmTable& ipc = MG_Config::RecordArm;
|
||||
// P7 wave 4 M2 (Config.h has the semantics). 0 is admitted ON PURPOSE as the negative
|
||||
// control: no forced sync, and MagmaWireReclaimScenario's watermark case must go red.
|
||||
ipc.WireDeferredMb = QueryEnvUint32("MOBILEGL_IPC_WIRE_DEFERRED_MB", 64, 0, 65536);
|
||||
// 0 is admitted ON PURPOSE and is the negative control of exit gate E3(a): it turns
|
||||
// the persistent-map push OFF, and PersistentCoherentMapScenario must go red.
|
||||
ipc.PersistentBlockKb = QueryEnvUint32("MOBILEGL_IPC_PERSISTENT_BLOCK_KB", 64, 0, 65536);
|
||||
// Whole-range push is the 0 arm; with it on (default) only blocks whose
|
||||
// xxHash64 changed since the last push are shipped.
|
||||
ipc.PersistentHashSuppress = QueryEnvUint32("MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS", 1, 0, 1);
|
||||
if (ipc.WireDeferredMb == 0) {
|
||||
MGLOG_W("Config: MOBILEGL_IPC_WIRE_DEFERRED_MB=0 is the M2 NEGATIVE CONTROL: the "
|
||||
"server never forces a sync for orphaned wire buffer stores, so a long frame "
|
||||
@@ -730,6 +740,9 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
|
||||
InitBackendType();
|
||||
InitFeatures();
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
InitRecordArm();
|
||||
#endif
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// After InitFeatures, so the one line InitIpc logs is the last word on this run's
|
||||
// configuration, and before the accepted-env map is destroyed just below.
|
||||
|
||||
@@ -1111,7 +1111,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VkBufferManager::EnforceWireDeferredWatermark() {
|
||||
const Uint64 budget = static_cast<Uint64>(MG_Config::Ipc.WireDeferredMb) * 1024u * 1024u;
|
||||
const Uint64 budget = static_cast<Uint64>(MG_Config::RecordArm.WireDeferredMb) * 1024u * 1024u;
|
||||
// TWO TRIGGERS, ONE SWITCH. Bytes are what the knob names, but a VkBuffer costs per
|
||||
// OBJECT as well as per byte, and small orphans never reach a byte budget: measured on
|
||||
// bsl-esc-menu-854 (spawn, lavapipe), one stretch with no submission parked 12,498 stores
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// back, and none was persistently mapped, in between - so a memo of resolved
|
||||
// slices needs no per-buffer re-check. See AcquirePersistentMap for the mapping half.
|
||||
Uint64 GetSliceEpochCounter() const { return m_sliceEpochCounter; }
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
// Bumped every time this manager destroys a WIRE store's VkBuffer (see
|
||||
// m_wireStoreDestroyEpoch). Unchanged since a memo was taken means no VkBuffer handle
|
||||
// that memo names can have been freed and re-minted in between, which is the one
|
||||
@@ -408,7 +408,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Vector<Vector<VkBufferObject>> m_deferredBufferReleases;
|
||||
Vector<Vector<SharedPtr<VkBufferResource>>> m_deferredResourceReleases;
|
||||
Vector<WeakPtr<VkBufferResource>> m_liveResources;
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#if MOBILEGL_BUILD_RECORD_ARM
|
||||
std::unordered_map<Uint64, WireBufferResource> m_wireBuffers;
|
||||
// See DeferredWireRelease. ONE FLAT LIST rather than the per-frame-slot buckets above:
|
||||
// the whole point is that these entries do not wait for a frame slot to come round.
|
||||
|
||||
@@ -700,7 +700,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
}
|
||||
|
||||
Uint64 PersistentMapTracker::BlockBytes() {
|
||||
return static_cast<Uint64>(MG_Config::Ipc.PersistentBlockKb) * 1024ull;
|
||||
return static_cast<Uint64>(MG_Config::RecordArm.PersistentBlockKb) * 1024ull;
|
||||
}
|
||||
|
||||
Bool PersistentMapTracker::PushIsArmed() {
|
||||
@@ -1049,7 +1049,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// in both directions.
|
||||
BlockHashState* state = nullptr;
|
||||
const Uint8* shadow = nullptr;
|
||||
if (MG_Config::Ipc.PersistentHashSuppress != 0) {
|
||||
if (MG_Config::RecordArm.PersistentHashSuppress != 0) {
|
||||
shadow = buffer.MappedData();
|
||||
if (shadow != nullptr) {
|
||||
state = &m_blockHashes[buffer.GetLifetimeId()];
|
||||
|
||||
@@ -64,13 +64,13 @@ namespace {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
m_transport = MG_Config::Transport;
|
||||
m_blockKb = MG_Config::Ipc.PersistentBlockKb;
|
||||
m_blockKb = MG_Config::RecordArm.PersistentBlockKb;
|
||||
m_adoptTier = MG_Config::Ipc.AdoptTier;
|
||||
m_pipeStats = MG_Config::Features.PipeStats;
|
||||
m_context = Move(MG_State::pGLContext);
|
||||
MG_State::pGLContext = MakeUnique<MG_State::GLState::GLContext>();
|
||||
MG_Config::Transport = MG_Config::TransportMode::InProcess;
|
||||
MG_Config::Ipc.PersistentBlockKb = 64;
|
||||
MG_Config::RecordArm.PersistentBlockKb = 64;
|
||||
MG_Config::Ipc.AdoptTier = 2;
|
||||
MG_Config::Features.PipeStats = true;
|
||||
MG_Util::PipeStats::Init();
|
||||
@@ -87,7 +87,7 @@ namespace {
|
||||
PersistentMapTracker::Instance().ClearForTest();
|
||||
MG_State::pGLContext = Move(m_context);
|
||||
MG_Config::Transport = m_transport;
|
||||
MG_Config::Ipc.PersistentBlockKb = m_blockKb;
|
||||
MG_Config::RecordArm.PersistentBlockKb = m_blockKb;
|
||||
MG_Config::Ipc.AdoptTier = m_adoptTier;
|
||||
MG_Config::Features.PipeStats = m_pipeStats;
|
||||
MG_Util::PipeStats::Init();
|
||||
@@ -470,7 +470,7 @@ TEST_F(SplitBufferSet, BothEdgesOfAWriteMapPublishOneStateRecord) {
|
||||
// whole buffer at unmap, so a scenario that unmapped before reading back went green and the
|
||||
// control was dead.
|
||||
TEST_F(SplitBufferSet, AZeroBlockSizeTurnsThePushOffRatherThanMakingItUnlimited) {
|
||||
MG_Config::Ipc.PersistentBlockKb = 0;
|
||||
MG_Config::RecordArm.PersistentBlockKb = 0;
|
||||
constexpr SizeT kSize = 128u * 1024u;
|
||||
auto buffer = MakeBuffer(23u, kSize);
|
||||
|
||||
|
||||
@@ -4592,12 +4592,12 @@ TEST(DirectGLESBufferDrawProbe, UnderSplitTheRecordAloneAnswersTheLiveHostMapQue
|
||||
// case reaches is optional.
|
||||
const Uint64 previousPush = MG_Config::Features.PipePush;
|
||||
const auto previousTransport = MG_Config::Transport;
|
||||
const Uint32 previousBlockKb = MG_Config::Ipc.PersistentBlockKb;
|
||||
const Uint32 previousBlockKb = MG_Config::RecordArm.PersistentBlockKb;
|
||||
MG_Pipe::MGPipeResourceOps ops{};
|
||||
MG_Config::Features.PipePush |= MG_Pipe::kMGPipeSubsystemResources;
|
||||
MG_Pipe::MGPipeSetResourceOps(&ops);
|
||||
MG_Config::Transport = MG_Config::TransportMode::InProcess;
|
||||
MG_Config::Ipc.PersistentBlockKb = 64;
|
||||
MG_Config::RecordArm.PersistentBlockKb = 64;
|
||||
// P5 c1 / R-8: UNDER SPLIT THE OP TABLE IS NO LONGER THE ARMING CONDITION, and this case is
|
||||
// the first place that shows. `MGPipeSetResourceOps(&ops)` is the SERVER's registration; the
|
||||
// client's liveness gate now reads the caps mirror's consumer mask instead, because under a
|
||||
@@ -4691,7 +4691,7 @@ TEST(DirectGLESBufferDrawProbe, UnderSplitTheRecordAloneAnswersTheLiveHostMapQue
|
||||
BufferImpl::g_backendBufferResources.ReleaseByHandle(res);
|
||||
}
|
||||
|
||||
MG_Config::Ipc.PersistentBlockKb = previousBlockKb;
|
||||
MG_Config::RecordArm.PersistentBlockKb = previousBlockKb;
|
||||
MG_Config::Transport = previousTransport;
|
||||
MG_Pipe::MGPipeSetResourceOps(nullptr);
|
||||
MG_Config::Features.PipePush = previousPush;
|
||||
|
||||
Reference in New Issue
Block a user