mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
F1. `MG_State::Init()` runs before `MG_Backend::Init()`, and the second is what
starts the session - so `GLContext`'s `TextureState` constructor materialised its
eleven default texture objects, each of them emitted `resource_create` through
`FamilyIsLive`, and `CapsMirror::ServerConsumes(0x400)` answered every one of them
from a generation-0 placeholder with `callMask=0`. All eleven records were dropped
and the whole event was one WARN line:
MG_Remote client: the server does not consume MGPipe subsystem 0x400 - this
family emits NOTHING and the legacy pull path runs for it (R-8). callMask=0x0,
caps generation 0
It is NOT a race. The line reproduces byte-for-byte on lavapipe and on the Adreno
830, because the two Init calls are two statements on one thread. And "the legacy
pull path runs for it" is false on the client under split: there is no pull path
on this side of the wire, so withholding is not a fallback, it is a drop that only
a later respecify's self-healing create ever undoes.
Two of the three options the brief offered do not apply, and both facts are worth
recording. (b) "extend the P6.5 fix to inproc" is already the tree: `FinishStartup`
step 7 is the one funnel every transport reaches and it will not return without a
real snapshot. (a) "a bounded wait in Consumes()" cannot work alone and would
deadlock: at `MG_State::Init()` no handshake is in flight, and the one that will
land is started later BY THE SAME THREAD.
So the fix is the ORDER, with the rule behind it as the gate:
* `MobileGL::Initialize` calls `MG_Backend::InitSplitRolesBeforeState()` before
`MG_State::Init()`. `Init()`'s split arm becomes one body, `InitSplitBackend()`,
shared with the direct callers that still reach `Init()`; the monolith arm keeps
its place after `MG_State::Init()`, because moving it would move pull `.text`.
* `CapsMirror::ServerConsumes()` refuses to answer from a placeholder. It waits,
bounded, when a bring-up is genuinely in flight on ANOTHER thread, and otherwise
dies as `Fatal{CapsBeforeFirstSnapshot, "<family>"}` through `SessionFail` -
a new family word, one row in `FatalFamilies.def`, no new abort site.
* Two arms are exempt and say why in place: the server arm (`MGPipeServerArm()` -
the apply thread and the spawn server, where the client's mirror is the wrong
object rather than a late one), and a process whose own configuration never
asked for a split transport (`MG_Config::SplitTransportRequestedByConfig`, set
by `MG_ConfigLoader::InitTransport`, which is what tells a real run apart from a
fixture that assigns `MG_Config::Transport` by hand).
The read accessors keep P5's ruling untouched: `LogBackendInfo` reads
`GetRendererInfo()` during `MG_Backend::Init()` and an abort there is a process that
cannot start. Only the DECISION changed. The warning was not made quieter and the
callMask was not defaulted to all-ones.
`MOBILEGL_TEST_DELAY_FIRST_CAPS_MS` (server side, disaggregated builds, inert unless
set) holds back the FIRST CapsSnapshot. On spawn/tcp that is a genuinely late
snapshot in another process and it exercises the client's step-7 wait; on inproc it
only makes `Start` slower, which is itself the proof that the only window a client
can read a placeholder in is the one before `Start` is called.
G1: `.text` 0xa52203 and the 30570-symbol list identical to p7-before/pull-syms.txt.
Census: 79 abort sites unchanged, 44 family words (was 43). Link ratchet: 186,
unchanged.
60 lines
3.3 KiB
C++
60 lines
3.3 KiB
C++
// MobileGL - MobileGL/MG_Backend/BackendObjects.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
|
|
|
|
#pragma once
|
|
#include <Includes.h>
|
|
#include "BackendObject.h"
|
|
#include "DirectGLES/BackendObject_DirectGLES.h"
|
|
#include "DirectVulkan/BackendObject_DirectVulkan.h"
|
|
|
|
namespace MobileGL::MG_Backend {
|
|
extern UniquePtr<BackendObject>& pActiveBackendObject;
|
|
extern GlobalBackendFunctionsTable gBackendFunctionsTable;
|
|
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// P5 v1. The counterpart of Init()'s single split hook, and a NO-OP in every run that is
|
|
// not split. It exists ONLY in a split build: G1 admits no new symbol in the pull build,
|
|
// so MobileGL/Init.cpp's call sits under the same #if rather than calling a no-op.
|
|
//
|
|
// IT MUST RUN FIRST, BEFORE ANYTHING ELSE IN DestroyImpl. ARCHITECTURE.md:537's order is
|
|
// publish -> the server drains and acks -> stop the apply thread (Kill, then join) -> close
|
|
// the transport -> the client drains the compile pool -> MobileGL::Destroy() -> release the
|
|
// sync/query handles; DestroyImpl IS MobileGL::Destroy, so everything before that arrow has
|
|
// to happen at its top. Two consequences are load-bearing rather than tidy:
|
|
//
|
|
// * the apply thread is JOINED before PipeStats::Shutdown() dumps, so nothing is writing
|
|
// a counter while the final line is produced;
|
|
// * the apply thread is joined before pActiveBackendObject.reset(), so the server's own
|
|
// BackendObject - which is NOT that global (table 3) - is destroyed on the thread that
|
|
// owns the context, by ServerLoop::Stop, rather than on the app thread.
|
|
//
|
|
// The sync/query registries stay where they are, drained at MobileGL/Init.cpp:62 and :67
|
|
// BEFORE pActiveBackendObject.reset(). ARCHITECTURE.md:537 puts them after
|
|
// MobileGL::Destroy(); the two are only reconcilable if a split sync handle is
|
|
// client-minted and needs no backend call, which is P10's. CONTRACT-P5 4 flags this as a
|
|
// KNOWN OPEN ITEM and asks v1 to record which way it went: P5 keeps today's order.
|
|
void ShutdownSplitRoles();
|
|
|
|
// F1 (P7 wave 2). Init()'s SPLIT ARM, run BEFORE MG_State::Init() rather than after it.
|
|
//
|
|
// Returns true when this process took the split path - whether the bring-up succeeded or
|
|
// was refused by name - and the caller must then NOT call Init() a second time. Returns
|
|
// false under MG_Config::TransportMode::Monolith, where there is nothing to bring up and
|
|
// Init() keeps its own place after MG_State::Init().
|
|
//
|
|
// WHY IT EXISTS AT ALL, rather than MobileGL::Initialize simply calling Init() earlier:
|
|
// the monolith arm of Init() must keep running after MG_State::Init(), because
|
|
// BackendObject_DirectGLES::Initialize() is the tree's own order for it and moving it is a
|
|
// pull-build change G1 forbids. So the split arm - and only the split arm - moves.
|
|
//
|
|
// It exists ONLY in a split build, for the reason InitServerRoleForSpawn states: a
|
|
// definition guarded only on the inside still emits a symbol, and G1 measured that.
|
|
Bool InitSplitRolesBeforeState();
|
|
#endif
|
|
} // namespace MobileGL::MG_Backend
|