From 4e055e468616756fa8732ff52b71b747c062e1d2 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 9 Oct 2026 14:23:32 -0400 Subject: [PATCH] Reword the permanent client-window refusal text and drop the stale P12 tag. --- .github/workflows/apk.yml | 7 ++++--- MobileGL/MG_Pipe/FatalFamilies.def | 2 +- MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp | 18 +++++++++++------- MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h | 10 +++++----- .../MG_Remote/Server/SurfaceControlFrame.h | 5 +++-- MobileGL/MG_Test/Wire/InProcessServerTest.cpp | 2 +- MobileGL/MG_Test/Wire/PeerLatchTest.cpp | 8 ++++---- .../MG_Test/Wire/SurfaceControlFrameTest.cpp | 6 +++--- .../app/src/trace/cpp/trace_replay_jni.cpp | 6 +++--- android-plugin/trace-replay-ci.sh | 5 +++-- tools/device_bench/p6/README.md | 2 +- tools/device_bench/p6/ab_session.py | 2 +- .../trace_replay/run_android_retrace_local.py | 4 ++-- userguide/android-plugin.md | 2 +- userguide/troubleshooting.md | 2 +- 15 files changed, 44 insertions(+), 37 deletions(-) diff --git a/.github/workflows/apk.yml b/.github/workflows/apk.yml index 42f56a5d..05b9f874 100644 --- a/.github/workflows/apk.yml +++ b/.github/workflows/apk.yml @@ -532,10 +532,11 @@ jobs: # MOBILEGL_IPC_SERVER_PATH IS DELIBERATELY ABSENT. nativeLibraryDir carries an # install-time hash, so nothing off the device can spell it; TraceReplayActivity # resolves it from getApplicationInfo() when the env asks for spawn, and an explicit - # value would override that. --use-pbuffer is NOT optional here: until P12 a spawned - # server cannot be handed a window at all, because an ANativeWindow* is a pointer + # value would override that. --use-pbuffer is NOT optional here: a spawned + # server cannot be handed the client's window, because an ANativeWindow* is a pointer # into the CLIENT's process and SetWindowHandle is refused on the way across with - # Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} (Rule H). + # Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"} (Rule H, permanent; + # a windowed client uses MOBILEGL_IPC_SURFACE=server instead). extra_retrace_args+=( --env "MOBILEGL_TRANSPORT=spawn;MOBILEGL_IPC_ROLE_SPLIT_STATE=1;MOBILEGL_IPC_STRICT_ERRORS=1;MOBILEGL_IPC_RUN_AHEAD=1;MOBILEGL_GPU_HANG_BUDGET_MS=${MOBILEGL_GPU_HANG_BUDGET_MS}" --use-pbuffer diff --git a/MobileGL/MG_Pipe/FatalFamilies.def b/MobileGL/MG_Pipe/FatalFamilies.def index 36250d42..b8c55323 100644 --- a/MobileGL/MG_Pipe/FatalFamilies.def +++ b/MobileGL/MG_Pipe/FatalFamilies.def @@ -73,7 +73,7 @@ #define MGL_FATAL_FAMILY_LIST(X) \ X(ProtocolCorruption, ProtocolCorruption, "a record's bounds or self-describing length was violated") \ X(UnmigratedVerb, ProtocolCorruption, "a wire verb slot P5/P6 does not implement was reached") \ - X(UnmigratedSurface, ProtocolCorruption, "a surface op named a client-process handle that means nothing here (P12)") \ + X(UnmigratedSurface, ProtocolCorruption, "a surface op named a client-process handle that means nothing here (a windowed client uses MOBILEGL_IPC_SURFACE=server)") \ X(UnmigratedPipeInput, ProtocolCorruption, "a PipeInputs field with no field-ownership row was read across the boundary") \ X(UnmigratedEmulation, ProtocolCorruption, "an emulation path the applier has no migrated arm for was reached") \ X(RoleViolation, ProtocolCorruption, "a call ran on the wrong role/thread - an invariant the two roles must hold") \ diff --git a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp index 12770443..b99e33a9 100644 --- a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp +++ b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.cpp @@ -228,8 +228,9 @@ namespace MobileGL::MG_Remote { } if (op.windowKind() == ::MobileGL::Wire::WindowKind::AndroidNativeWindow) { // The one refusal that is not corruption: the window kind is LEGAL and the token - // is an ANativeWindow*, which names memory in the CLIENT's process. Real window - // arrival is P12; until then this is refused by name at ServerApplyWireSurfaceOp. + // is an ANativeWindow*, which names memory in the CLIENT's process. This is a + // permanent rule (Rule H): such a handle can never cross, so it is refused by name + // at ServerApplyWireSurfaceOp. A windowed client uses MOBILEGL_IPC_SURFACE=server. return SurfaceWireError::AndroidNativeWindowArrived; } if (op.windowKind() == ::MobileGL::Wire::WindowKind::MetalLayer) { @@ -315,16 +316,19 @@ namespace MobileGL::MG_Remote { static_cast(op.nativeToken())); } else if (error == SurfaceWireError::AndroidNativeWindowArrived) { (void)SessionLatch(MGFatalFamily::UnmigratedSurface, - "MGPipe: Fatal{UnmigratedSurface, \"AndroidNativeWindow@P12\"} - a wire " + "MGPipe: Fatal{UnmigratedSurface, \"AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server\"} - a wire " "SurfaceOp (%s) named an ANativeWindow*, which is a pointer into the " - "CLIENT's process and means nothing here. Real window arrival is P12; " - "until then the spawn surface path is pbuffer/surfaceless only", + "CLIENT's process and means nothing here. A client-process window " + "handle can never cross processes: a windowed out-of-process client must " + "use MOBILEGL_IPC_SURFACE=server (the server owns the window); " + "otherwise it is pbuffer/surfaceless only", ::MobileGL::Wire::EnumNameSurfaceOpKind(op.kind())); } else if (error == SurfaceWireError::MetalLayerArrived) { (void)SessionLatch(MGFatalFamily::UnmigratedSurface, - "MGPipe: Fatal{UnmigratedSurface, \"MetalLayer@P12\"} - a wire " + "MGPipe: Fatal{UnmigratedSurface, \"MetalLayer:use-MOBILEGL_IPC_SURFACE=server\"} - a wire " "SurfaceOp named a CAMetalLayer* in the CLIENT's process. " - "Real window arrival is P12"); + "A client-process window handle can never cross processes: use " + "MOBILEGL_IPC_SURFACE=server (the server owns the window)"); } else { (void)SessionLatch(MGFatalFamily::ProtocolCorruption, "MGPipe: Fatal{ProtocolCorruption, \"SurfaceOp\"} - a wire surface op " diff --git a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h index 0796e24e..23401742 100644 --- a/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h +++ b/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h @@ -25,11 +25,11 @@ // ERROR SEMANTICS. Decode is pure: it answers a SurfaceWireError. The FATALS live at the server // entry point, ServerApplyWireSurfaceOp, because "a wire op the server cannot honour" is a // contract violation, not a data problem: AndroidNativeWindowArrived dies -// Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} (the ANativeWindow* means nothing in the -// server's process; real window arrival is P12) and everything else dies +// Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"} (the ANativeWindow* means nothing in the +// server's process, permanently - a windowed client uses MOBILEGL_IPC_SURFACE=server) and everything else dies // Fatal{ProtocolCorruption, "SurfaceOp"}. P6's control pump is the intended caller; P5f's unit // tests drive it directly. MetalLayerArrived likewise dies -// Fatal{UnmigratedSurface, "MetalLayer@P12"}: CAMetalLayer* is also process-local. +// Fatal{UnmigratedSurface, "MetalLayer:use-MOBILEGL_IPC_SURFACE=server"}: CAMetalLayer* is also process-local. // // P12 (on-screen server window). WindowKind::ServerOwned is the one window kind whose window is // NOT the client's: the server substitutes its own (ServerLoop.cpp's ServerOwned arm). The codec @@ -62,8 +62,8 @@ namespace MobileGL::MG_Remote { InprocOnlyOpOnTheWire, // a kind that may ride the inproc channel but never the wire WindowKindNamesNoBackend,// Surfaceless/Pbuffer where a window backend is required UnknownWindowKind, // a WindowKind the mapping table does not know - MetalLayerArrived, // -> Fatal{UnmigratedSurface, "MetalLayer@P12"} - AndroidNativeWindowArrived, // -> Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} + MetalLayerArrived, // -> Fatal{UnmigratedSurface, "MetalLayer:use-MOBILEGL_IPC_SURFACE=server"} + AndroidNativeWindowArrived, // -> Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"} // P12 (on-screen server window), D2. WindowKind::ServerOwned is legal on // CreateWindowSurface only, and only with nativeToken 0: ServerOwnedTokenNotZero, // -> Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched diff --git a/MobileGL/MG_Remote/Server/SurfaceControlFrame.h b/MobileGL/MG_Remote/Server/SurfaceControlFrame.h index 2c21514b..e9a0787e 100644 --- a/MobileGL/MG_Remote/Server/SurfaceControlFrame.h +++ b/MobileGL/MG_Remote/Server/SurfaceControlFrame.h @@ -30,8 +30,9 @@ // // PROCESS-LOCAL WINDOW OBJECTS ARE REFUSED BY NAME on the wire: ANativeWindow* and // CAMetalLayer* mean nothing in the server process. SurfaceOpCodec names their refusals -// AndroidNativeWindow@P12 and MetalLayer@P12, and they stay (Rule H): P12 does not carry a -// client's window across, it gives the SERVER a window of its own (kServerOwnedWindowBackend +// AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server and MetalLayer:use-MOBILEGL_IPC_SURFACE=server, +// and they are permanent (Rule H): a client's window never crosses, a windowed client uses +// MOBILEGL_IPC_SURFACE=server, which gives the SERVER a window of its own (kServerOwnedWindowBackend // below, WindowKind::ServerOwned on the wire). #pragma once diff --git a/MobileGL/MG_Test/Wire/InProcessServerTest.cpp b/MobileGL/MG_Test/Wire/InProcessServerTest.cpp index a0aa2754..d54a8c29 100644 --- a/MobileGL/MG_Test/Wire/InProcessServerTest.cpp +++ b/MobileGL/MG_Test/Wire/InProcessServerTest.cpp @@ -126,7 +126,7 @@ namespace { } void LatchTheSession(Remote::Client::ClientSession& client, PeerReport& r) { - // A CAMetalLayer* on the wire: Fatal{UnmigratedSurface, "MetalLayer@P12"}, latched. + // A CAMetalLayer* on the wire: Fatal{UnmigratedSurface, "MetalLayer:use-MOBILEGL_IPC_SURFACE=server"}, latched. (void)SendSurfaceOp(client, r, static_cast(::MobileGL::Wire::SurfaceOpKind::CreateWindowSurface), static_cast(::MobileGL::Wire::WindowKind::MetalLayer), 0x1234); } diff --git a/MobileGL/MG_Test/Wire/PeerLatchTest.cpp b/MobileGL/MG_Test/Wire/PeerLatchTest.cpp index 18eb7990..31cc3d4d 100644 --- a/MobileGL/MG_Test/Wire/PeerLatchTest.cpp +++ b/MobileGL/MG_Test/Wire/PeerLatchTest.cpp @@ -1298,14 +1298,14 @@ namespace { [](Transport::RingRecordHeader& h) { h.size = 12; }); }}, // ===== SurfaceOpCodec.cpp: the three wire refusals - {"SurfaceOpAndroidNativeWindow", "SurfaceOpCodec.cpp", "AndroidNativeWindow@P12", Outcome::Latched, - "Fatal{UnmigratedSurface, \"AndroidNativeWindow@P12\"}", false, + {"SurfaceOpAndroidNativeWindow", "SurfaceOpCodec.cpp", "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server", Outcome::Latched, + "Fatal{UnmigratedSurface, \"AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server\"}", false, [](Client::ClientSession& c, PeerReport& r) { return SendSurfaceOp(c, r, kCreateWindowSurface, static_cast(::MobileGL::Wire::WindowKind::AndroidNativeWindow)); }}, - {"SurfaceOpMetalLayer", "SurfaceOpCodec.cpp", "MetalLayer@P12", Outcome::Latched, - "Fatal{UnmigratedSurface, \"MetalLayer@P12\"}", false, + {"SurfaceOpMetalLayer", "SurfaceOpCodec.cpp", "MetalLayer:use-MOBILEGL_IPC_SURFACE=server", Outcome::Latched, + "Fatal{UnmigratedSurface, \"MetalLayer:use-MOBILEGL_IPC_SURFACE=server\"}", false, [](Client::ClientSession& c, PeerReport& r) { return SendSurfaceOp(c, r, kCreateWindowSurface, static_cast(::MobileGL::Wire::WindowKind::MetalLayer)); diff --git a/MobileGL/MG_Test/Wire/SurfaceControlFrameTest.cpp b/MobileGL/MG_Test/Wire/SurfaceControlFrameTest.cpp index 52a46ec5..6585e81e 100644 --- a/MobileGL/MG_Test/Wire/SurfaceControlFrameTest.cpp +++ b/MobileGL/MG_Test/Wire/SurfaceControlFrameTest.cpp @@ -16,7 +16,7 @@ // agreement with the schema is pinned, not assumed; // * every framed op round-trips through a REAL CtrlEnvelope{SurfaceOp} buffer byte-for-byte; // * the wire entry refuses by NAME: an ANativeWindow* arriving over the wire dies -// Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} (real windows are P12), and a kind the +// Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"} (a client window never crosses), and a kind the // schema does not define dies Fatal{ProtocolCorruption, "SurfaceOp"}. // // The inproc blocking path itself - posting, parking, the reply half coming back - is gated by @@ -446,7 +446,7 @@ TEST(SurfaceControlFrameTest, AnAndroidNativeWindowOnTheWireIsRefusedByName) { EXPECT_EXIT(ServerApplyWireSurfaceOp(*wireOp, nullptr), ::testing::KilledBySignal(SIGABRT), ".*"); const std::string log = ReadLog(); - EXPECT_NE(log.find("Fatal{UnmigratedSurface, \"AndroidNativeWindow@P12\"}"), std::string::npos) + EXPECT_NE(log.find("Fatal{UnmigratedSurface, \"AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server\"}"), std::string::npos) << "the abort happened but not for this rule's reason; the log says: " << log; } @@ -468,7 +468,7 @@ TEST(SurfaceControlFrameTest, AMetalLayerOnTheWireIsRefusedByName) { SurfaceControlFrame frame; EXPECT_EQ(DecodeWireSurfaceOp(*wireOp, &frame), SurfaceWireError::MetalLayerArrived); EXPECT_EXIT(ServerApplyWireSurfaceOp(*wireOp, nullptr), ::testing::KilledBySignal(SIGABRT), ".*"); - EXPECT_NE(ReadLog().find("Fatal{UnmigratedSurface, \"MetalLayer@P12\"}"), std::string::npos); + EXPECT_NE(ReadLog().find("Fatal{UnmigratedSurface, \"MetalLayer:use-MOBILEGL_IPC_SURFACE=server\"}"), std::string::npos); } } diff --git a/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp b/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp index f77a6dc1..aa4e5377 100644 --- a/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp +++ b/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp @@ -176,10 +176,10 @@ Java_top_mobilegl_plugin_trace_TraceReplayActivity_nativeRunTraceReplay(JNIEnv* // (apitrace_glws_android.cpp's createSurface), so handing Magma a window was the same as // asking for one. // - // P6 needs the pbuffer path on BOTH backends. Until P12 a spawned server cannot be given a - // window at all - an ANativeWindow* is a pointer into the CLIENT's process, and + // P6 needs the pbuffer path on BOTH backends. A spawned server cannot be given the client's + // window - an ANativeWindow* is a pointer into the CLIENT's process, and // SetWindowHandle is refused on the way across with Fatal{UnmigratedSurface, - // "AndroidNativeWindow@P12"} (Rule H). Measured: the DirectVulkan spawn arm died on exactly + // "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"} (Rule H, permanent; a windowed client uses MOBILEGL_IPC_SURFACE=server). Measured: the DirectVulkan spawn arm died on exactly // that, one op after the server had come up green, while DirectGLES with the same flag passed. // // Every existing caller is unaffected: nobody asks for a Vulkan pbuffer today, and diff --git a/android-plugin/trace-replay-ci.sh b/android-plugin/trace-replay-ci.sh index f685da5b..a4505e50 100644 --- a/android-plugin/trace-replay-ci.sh +++ b/android-plugin/trace-replay-ci.sh @@ -451,9 +451,10 @@ run_retrace() { fi # PBUFFER IS A SURFACE SHAPE, NOT A BACKEND PROPERTY, and the DirectGLES condition that used to # guard this line made it one. It was harmless while the only caller was the ANGLE lane; P6 needs - # it on BOTH backends, because until P12 the spawn arm can only use pbuffer/surfaceless - an + # it on BOTH backends, because the spawn arm can only use pbuffer/surfaceless (a windowed client + # uses MOBILEGL_IPC_SURFACE=server) - an # ANativeWindow* is a pointer into the CLIENT's process and SetWindowHandle is refused by name - # with Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} on the way across. Magma creates an + # with Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"} on the way across. Magma creates an # EGL pbuffer exactly as Espryt does. if [ "${use_pbuffer}" -eq 1 ]; then set -- "$@" --ez use_pbuffer true diff --git a/tools/device_bench/p6/README.md b/tools/device_bench/p6/README.md index ec06a02c..bf43c180 100644 --- a/tools/device_bench/p6/README.md +++ b/tools/device_bench/p6/README.md @@ -116,7 +116,7 @@ indistinguishable from a run that produced none. as an explicit `--env MOBILEGL_TRANSPORT=…` and verifies it from the library's log. - **`MOBILEGL_RETRACE_USE_PBUFFER` must be in the CI script's environment**, not passed as `--env` — `trace-replay-ci.sh` reads it itself to decide the surface shape. Without it the `spawn` arm dies - with `Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}`, because an `ANativeWindow*` means + with `Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"}`, because an `ANativeWindow*` means nothing in the server's process (Rule H). - **`MOBILEGL_TRACE_SKIP_INSTALL=1`** keeps the script's own `adb install` out of the window. The session installs once itself and waits for `dex2oat` to exit first: an `am_kill … due to diff --git a/tools/device_bench/p6/ab_session.py b/tools/device_bench/p6/ab_session.py index f4110b9d..244b6a88 100644 --- a/tools/device_bench/p6/ab_session.py +++ b/tools/device_bench/p6/ab_session.py @@ -491,7 +491,7 @@ class Arm: That is fatal on the spawn arm and only there: an ANativeWindow* is a pointer into the CLIENT's process and the wire refuses it by name (Rule H). Measured on this tree - the spawn - arm died with `Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}` published as a + arm died with `Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"}` published as a SessionFault, and the same run passes as soon as the variable is in the script's environment. """ return {"MOBILEGL_RETRACE_USE_PBUFFER": "1"} diff --git a/tools/trace_replay/run_android_retrace_local.py b/tools/trace_replay/run_android_retrace_local.py index 307f72f3..37092e16 100644 --- a/tools/trace_replay/run_android_retrace_local.py +++ b/tools/trace_replay/run_android_retrace_local.py @@ -605,9 +605,9 @@ def parse_args(): "--use-pbuffer", action="store_true", help="Render into a pbuffer instead of the Activity's window surface. REQUIRED with " - "--transport spawn until P12: an ANativeWindow* is a pointer into the CLIENT's " + "--transport spawn (a windowed client uses MOBILEGL_IPC_SURFACE=server instead): an ANativeWindow* is a pointer into the CLIENT's " "process and means nothing in the server's, so SetWindowHandle is refused by name " - "with Fatal{UnmigratedSurface, \"AndroidNativeWindow@P12\"} (Rule H). The desktop " + "with Fatal{UnmigratedSurface, \"AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server\"} (Rule H). The desktop " "retrace has always run pbuffer and matches the same goldens.", ) parser.add_argument( diff --git a/userguide/android-plugin.md b/userguide/android-plugin.md index 6669e092..354b021f 100644 --- a/userguide/android-plugin.md +++ b/userguide/android-plugin.md @@ -121,7 +121,7 @@ a window the **server** owns: the plugin's on-screen display server. Without that, a `spawn` client that creates a window surface fails. With the default `MOBILEGL_IPC_SURFACE=offscreen`, the server rejects the window with -`Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}`. With `MOBILEGL_IPC_SURFACE=server` and an +`Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"}`. With `MOBILEGL_IPC_SURFACE=server` and an offscreen server, the client logs `Refuse ServerOwned (NoServerDisplay)`. Steps (client in FCL, server in the plugin, same device): diff --git a/userguide/troubleshooting.md b/userguide/troubleshooting.md index 93fbc50e..0905dec8 100644 --- a/userguide/troubleshooting.md +++ b/userguide/troubleshooting.md @@ -59,7 +59,7 @@ The renderer string does not mention Espryt or Magma. | Message | Cause | Fix | |---|---|---| -| `Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} ... until then the spawn surface path is pbuffer/surfaceless only` | A `spawn` client created a window surface with `MOBILEGL_IPC_SURFACE=offscreen`. An Android window cannot cross processes. (The text mentions a development phase; the limitation itself is permanent.) | Use the on-screen display server and `MOBILEGL_IPC_SURFACE=server` ([android-plugin.md](android-plugin.md#out-of-process-rendering-with-a-window)), or use `inproc` | +| `Fatal{UnmigratedSurface, "AndroidNativeWindow:use-MOBILEGL_IPC_SURFACE=server"}` (a `MetalLayer:use-MOBILEGL_IPC_SURFACE=server` twin exists for macOS) | A `spawn` client created a window surface with `MOBILEGL_IPC_SURFACE=offscreen`. An Android window cannot cross processes. The limitation is permanent. (Servers older than this fix print `AndroidNativeWindow@P12` and mention a development phase; it means the same thing.) | Use the on-screen display server and `MOBILEGL_IPC_SURFACE=server` ([android-plugin.md](android-plugin.md#out-of-process-rendering-with-a-window)), or use `inproc` | | `Refuse ServerOwned (NoServerDisplay)` | `MOBILEGL_IPC_SURFACE=server`, but the server is offscreen (the plugin's service, a spawned server, any Linux server) | Start the plugin's server with **On-screen window** ticked | | `Refuse ServerOwned (NoServerWindow) ... is the display's surface visible?` | The display server's window was not visible when the client created its window surface | Bring the MobileGL display window to the front, then start the game | | `SurfaceModeMismatch` | The session's first surface was a pbuffer, so the session is offscreen; a later server window surface is refused | The application must create its window surface first |