Reword the permanent client-window refusal text and drop the stale P12 tag.

This commit is contained in:
2026-10-09 14:23:32 -04:00
parent 9f61016776
commit 4e055e4686
15 changed files with 44 additions and 37 deletions
+4 -3
View File
@@ -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
+1 -1
View File
@@ -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") \
+11 -7
View File
@@ -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<unsigned long long>(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 "
+5 -5
View File
@@ -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
@@ -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
@@ -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<Uint8>(::MobileGL::Wire::SurfaceOpKind::CreateWindowSurface),
static_cast<Uint8>(::MobileGL::Wire::WindowKind::MetalLayer), 0x1234);
}
+4 -4
View File
@@ -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<Uint8>(::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<Uint8>(::MobileGL::Wire::WindowKind::MetalLayer));
@@ -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);
}
}
@@ -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
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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"}
@@ -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(
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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 |