[Feature] (P14): state ownership - multi-session, multi-context, share groups

Breaks the one process = one server = one session = one context equation
that the whole stack's correctness used to rest on. Design and per-slice
shape: docs/Disaggregated/design/11-state-ownership.md.

S1 wire: bind_context ring record (opcode 84) announces the current
context at the make-current edge; CreateContext/DestroyContext join the
SurfaceOp control frames; control revision 5 -> 6; the server keeps a
per-session token table and refuses unknown tokens by name.

S2 server: SessionRuntime per session with thread-scoped resolution
(singletons become fallbacks), per-thread gPipeInputs, per-session latch
domains, per-thread segment resolvers; the in-process unix supervisor
serves up to MOBILEGL_IPC_INPROC_MAX_SESSIONS (16) concurrent sessions;
surface mode is declared per session and the display lease refuses a
second on-screen session by name.

S3 frontend: ShareGroupState owns the shared object stores and name
generators (buffer/texture/renderbuffer/sampler/program), GLContext keeps
per-context bindings/errors/container objects; pGLContext becomes
thread-local with a process default context preserving the single-context
world verbatim; eglCreateContext builds a real GLContext and joins the
sharing context's group.

S4 backend: native EGL tuples keyed by (session, context token);
InitDisplayAndContext's unconditional teardown is gone - creating a
second context or surface no longer destroys the first; eglCreateContext
sharing is wired to the native driver; bind_context is the native switch
point.

S5 applier: MGPipeApplierState splits into MGPipeObjectRecords (per
share group) and MGPipeWorkingState (per context), resolved through a
{session, token} registry with a thread-local cached fast path.

Tests: MultiSessionTest (incl. two sessions x two contexts interleaved
with real clear/readback witnesses), ApplierOwnershipTest (with a
null-resolver negative control), ContextStateOwnershipTest,
ServerLoopEglTest native-context liveness; 99/99 of the acceptance set
green on the host build.

Known debts (documented in the design doc): twin registries still keyed
by handle alone, no per-record ContextSlot, process-global backend
generations, MakeCurrent dedup per current binding.
This commit is contained in:
2026-10-01 19:16:41 -04:00
parent ebc552a048
commit 4ed1c9cef2
80 changed files with 4677 additions and 949 deletions
+16 -3
View File
@@ -51,10 +51,20 @@ namespace MobileGL::MG_Remote {
// cp's one schema addition, on the same append-only terms.
static_assert(static_cast<Uint8>(SurfaceControlOp::InitCapabilities) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::InitCapabilities));
// P14 S1's two appends, on the same append-only terms. The VALUES are pinned because they
// are wire ABI, and pinned SEPARATELY from the identity cast beside them: the cast would
// stay green if both enums moved together to 20/21, and a peer built before the move
// would then read the two ops as two others.
static_assert(static_cast<Uint8>(SurfaceControlOp::CreateContext) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::CreateContext));
static_assert(static_cast<Uint8>(SurfaceControlOp::DestroyContext) ==
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::DestroyContext));
static_assert(static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::SetSwapInterval) == 8 &&
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::ReleaseResources) == 9 &&
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::SetWindowHandle) == 10 &&
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::InitCapabilities) == 11);
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::InitCapabilities) == 11 &&
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::CreateContext) == 12 &&
static_cast<Uint8>(::MobileGL::Wire::SurfaceOpKind::DestroyContext) == 13);
static_assert(static_cast<Uint8>(::MobileGL::Wire::WindowKind::MetalLayer) == 6);
// P12's append, on the same terms: the value is wire ABI.
static_assert(static_cast<Uint8>(::MobileGL::Wire::WindowKind::ServerOwned) == 7);
@@ -112,7 +122,7 @@ namespace MobileGL::MG_Remote {
// Fatal{ProtocolCorruption} at ServerApplyWireSurfaceOp rather than as
// the missing row it is.
if (::flatbuffers::IsOutRange(kind, ::MobileGL::Wire::SurfaceOpKind::InitializeDisplay,
::MobileGL::Wire::SurfaceOpKind::InitCapabilities)) {
::MobileGL::Wire::SurfaceOpKind::DestroyContext)) {
return false;
}
const SurfaceControlOp op = static_cast<SurfaceControlOp>(static_cast<Uint8>(kind));
@@ -174,7 +184,8 @@ namespace MobileGL::MG_Remote {
}
const auto op = ::MobileGL::Wire::CreateSurfaceOp(
*builder, frame.seq, kind, frame.display, frame.surface, windowKind, nativeToken,
frame.width, frame.height, frame.swapInterval, frame.readSurface, frame.context);
frame.width, frame.height, frame.swapInterval, frame.readSurface, frame.context,
frame.shareGroupToken, frame.contextFlags);
const auto envelope = ::MobileGL::Wire::CreateCtrlEnvelope(*builder, ::MobileGL::Wire::CtrlMsg::SurfaceOp, op.Union());
::MobileGL::Wire::FinishCtrlEnvelopeBuffer(*builder, envelope);
return SurfaceWireError::None;
@@ -195,6 +206,8 @@ namespace MobileGL::MG_Remote {
decoded.width = op.width();
decoded.height = op.height();
decoded.swapInterval = op.swapInterval();
decoded.shareGroupToken = op.shareGroupToken();
decoded.contextFlags = op.contextFlags();
if (OpNeedsAWindowBackend(kind)) {
if (op.windowKind() == ::MobileGL::Wire::WindowKind::ServerOwned) {
// P12 (D2). Asked BEFORE the Android refusal below, which it is not: the window is