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.
- the revision is a hand-bumped integer mixed into wireFingerprint and nothing tied it to protocol.fbs's content; scripts/ci/protocol_revision_pin.py holds {revision -> sha256(protocol.fbs)} in protocol_revision_pins.json and refuses a schema whose digest is not the current revision's, or a revision with no row
- registered as ProtocolSchema.RevisionPinsDigest (label unit, MG_Test/Wire/CMakeLists.txt, --self-test proves both perturbations red before it says green) and as a step of CI's flatc-check job, the one that regenerates the header on every schema edit
- --write records a bump's row and refuses to re-pin an existing revision to a different digest, which is what a schema edit without a bump looks like
- mg_protocol_base.h says where the pin lives beside the revision it pins
- red-once: protocol.fbs touched with one trailing comment, revision left at 1 -> "protocol.fbs has sha256 a7b5c7fb... but MOBILEGL_PROTOCOL_CONTROL_REVISION is still 1, which is pinned to 7fc859f7..." rc=1; restored, green