mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Remote, android-plugin] (P6): the device can run the split retrace, and only the app can spell the server's path
Two small pieces, so an Android spawn arm is a flag rather than a project. THE RUNNER GAINS --transport. It already had a generic env passthrough - one intent extra carries `K=V;K=V` all the way to setenv before the library is dlopened - so the transport itself costs one entry and no new plumbing in the five files between here and the device. monolith passes NOTHING rather than MOBILEGL_TRANSPORT=monolith, so the default arm's environment stays byte-identical to what it was before this option existed: a control that changed when the option landed would not be one. THE SERVER PATH CANNOT COME FROM THE HOST. Every other knob can, but this one names a file that must be EXEC-ABLE, and on Android there is exactly one such place: an APK's lib/<abi>/. W^X has forbidden the app's own data dirs since API 29, and the packager only puts a file in lib/<abi>/ if it is named lib*.so - which is why the server executable wears that name, and what P0 spike A proved on two devices. nativeLibraryDir contains an install-time hash, so no host-side script can spell the path; the process about to launch the server is the only one that can. TraceReplayActivity resolves it, and only when the caller actually asked for spawn. AN EXPLICIT MOBILEGL_IPC_SERVER_PATH STILL WINS. A caller that set one is pointing somewhere deliberately - at a pushed build, say - and silently replacing it would make that knob untestable on the one platform where it matters most. The APK must be built with -Pmobilegl.buildDisaggregated=ON or none of this is reachable: without it ConfigLoader has no transport parser, accepts the value, ignores it (CONTRACT-P5 rule 5), and the run is monolith under a name that says spawn. The --transport help says so where somebody will read it.
This commit is contained in:
+36
-3
@@ -45,7 +45,10 @@ public final class TraceReplayActivity extends Activity {
|
||||
request = TraceReplayRequest.from(
|
||||
intent,
|
||||
getFilesDir(),
|
||||
getString(top.mobilegl.plugin.R.string.mobilegl_default_backend)
|
||||
getString(top.mobilegl.plugin.R.string.mobilegl_default_backend),
|
||||
// P6: the ONE place that knows where an exec-able file lives. See
|
||||
// resolveSpawnServerPath.
|
||||
getApplicationInfo().nativeLibraryDir
|
||||
);
|
||||
statusView = new TextView(this);
|
||||
statusView.setText("Waiting for render surface\n" + request.outputDir);
|
||||
@@ -354,7 +357,37 @@ public final class TraceReplayActivity extends Activity {
|
||||
this.envOverrides = envOverrides;
|
||||
}
|
||||
|
||||
static TraceReplayRequest from(Intent intent, File filesDir, String defaultBackend) {
|
||||
// P6: MOBILEGL_TRANSPORT=spawn NEEDS AN ABSOLUTE PATH TO AN EXEC-ABLE FILE, and on
|
||||
// Android there is exactly one such place. An APK's lib/<abi>/ is the only directory an
|
||||
// untrusted_app may exec from - W^X has forbidden the app's own data dirs since API 29 -
|
||||
// and the packager only puts a file there if it is named lib*.so, which is why the server
|
||||
// executable wears that name (P0 spike A proved the arrangement on two devices).
|
||||
//
|
||||
// NOTHING OFF THE DEVICE CAN SPELL THAT PATH. nativeLibraryDir contains an
|
||||
// install-time hash, so the host-side runner cannot pass MOBILEGL_IPC_SERVER_PATH the way
|
||||
// it passes every other knob; the path has to be resolved HERE, by the process that is
|
||||
// about to launch the server. Left unresolved, LaunchServer refuses BY NAME and the arm
|
||||
// reds on "unresolvable image" - honest, but about the wrong thing.
|
||||
//
|
||||
// AN EXPLICIT VALUE WINS. A caller that set MOBILEGL_IPC_SERVER_PATH itself is pointing
|
||||
// somewhere deliberately, and silently replacing it would make that knob untestable.
|
||||
static String resolveSpawnServerPath(String envOverrides, String nativeLibraryDir) {
|
||||
if (nativeLibraryDir == null || nativeLibraryDir.isEmpty()) {
|
||||
return envOverrides;
|
||||
}
|
||||
if (!envOverrides.contains("MOBILEGL_TRANSPORT=spawn")) {
|
||||
return envOverrides;
|
||||
}
|
||||
if (envOverrides.contains("MOBILEGL_IPC_SERVER_PATH=")) {
|
||||
return envOverrides;
|
||||
}
|
||||
String serverPath = new File(nativeLibraryDir, "libMobileGLServer.so").getAbsolutePath();
|
||||
String entry = "MOBILEGL_IPC_SERVER_PATH=" + serverPath;
|
||||
return envOverrides.isEmpty() ? entry : envOverrides + ";" + entry;
|
||||
}
|
||||
|
||||
static TraceReplayRequest from(Intent intent, File filesDir, String defaultBackend,
|
||||
String nativeLibraryDir) {
|
||||
String outputDir = readString(intent, "output_dir", new File(filesDir, "trace-replay").getAbsolutePath());
|
||||
String diffPath = readString(intent, "diff_path", "");
|
||||
String benchmarkResultPath =
|
||||
@@ -389,7 +422,7 @@ public final class TraceReplayActivity extends Activity {
|
||||
intent.getIntExtra("benchmark_tail_frames", 200),
|
||||
intent.getBooleanExtra("benchmark_finish", true),
|
||||
benchmarkResultPath,
|
||||
readString(intent, "mobilegl_env", "")
|
||||
resolveSpawnServerPath(readString(intent, "mobilegl_env", ""), nativeLibraryDir)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -403,6 +403,18 @@ def parse_args():
|
||||
help="Do not glFinish at every frame boundary, so frame times measure CPU submission "
|
||||
"only instead of GPU completion.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--transport",
|
||||
choices=("monolith", "inproc", "spawn"),
|
||||
default="monolith",
|
||||
help="MOBILEGL_TRANSPORT for the replay. `spawn` runs the server role in a SECOND PROCESS "
|
||||
"on the device, launched out of the APK's lib/<abi>/ - which requires an APK built "
|
||||
"with -Pmobilegl.buildDisaggregated=ON, or ConfigLoader has no parser, accepts the "
|
||||
"value and ignores it (CONTRACT-P5 rule 5) and the run is monolith under a name that "
|
||||
"says otherwise. MOBILEGL_IPC_SERVER_PATH is NOT passed from here: nativeLibraryDir "
|
||||
"carries an install-time hash, so only the app can spell it, and "
|
||||
"TraceReplayActivity.resolveSpawnServerPath does.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--benchmark-timeout-seconds",
|
||||
type=int,
|
||||
@@ -412,6 +424,16 @@ def parse_args():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def transport_env(args):
|
||||
"""The transport knob as env overrides, or nothing at all for monolith.
|
||||
|
||||
An EMPTY list on monolith rather than MOBILEGL_TRANSPORT=monolith, so the default arm's
|
||||
environment is byte-identical to what it was before this option existed - the control arm has
|
||||
to stay a control.
|
||||
"""
|
||||
return [] if args.transport == "monolith" else [f"MOBILEGL_TRANSPORT={args.transport}"]
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
selected_backends = args.backend or list(BACKENDS)
|
||||
@@ -435,7 +457,7 @@ def main():
|
||||
failures += run_benchmark_case(case, backend, args)
|
||||
continue
|
||||
print(f"=== Android retrace: {case['name']} / {backend} ===", flush=True)
|
||||
rc = run_case(case, backend, env_overrides=args.env)
|
||||
rc = run_case(case, backend, env_overrides=transport_env(args) + list(args.env))
|
||||
try:
|
||||
render_summary()
|
||||
except Exception as error:
|
||||
|
||||
Reference in New Issue
Block a user