mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
The Minecraft-shaped driver benchmark could only be run from a desktop shell against a desktop driver, which is the wrong machine: MobileGL exists to run on mobile GPUs, and nothing said what its translation costs there. This puts the same cases on an Android device, both in the plugin's POST screen and from a shell, and adds the native-driver baseline they have to be read against. The cases move into DriverBenchCases.inc so both harnesses run byte-identical bodies - the desktop program resolving entry points from one EGL provider, and DriverBenchJni.cpp calling MobileGL's frontend in-process. The JNI file binds every gl*/egl* name to MG_Impl by macro rather than by linkage: this library legitimately has the platform libEGL and libGLESv3 in its own lookup scope, and a benchmark that quietly measured the device driver instead of the translation layer would have looked like very good news. Frames are now closed with a fence wait instead of glFinish. MobileGL implements glFinish and glFlush as no-ops, so the old loop timed submit-plus-GPU on a native driver and submit-only on a MobileGL backend, and the two numbers did not describe the same work. To measure a device's own driver the cases needed to be expressible in GLES: ESSL 3.20 twins of the four shaders (chosen at runtime from GL_VERSION, since MobileGL is deliberately still fed desktop GLSL - translating it is the thing under test), a multi-draw hook that loops DrawElementsBaseVertex where the multi-draw entry point does not exist, and an EGL bootstrap that falls back from desktop GL to GLES 3. The binary cross-compiles for arm64 unchanged. BenchService hosts each run in its own process and exits afterwards. That is not caution: the backend is latched from MOBILEGL_BACKEND_TYPE at initialization, so Espryt and Magma can never share a process, and Espryt's teardown terminates the process-default EGL display, which would take the POST activity's own EGL objects with it. Running it found that Magma could not create a windowless context on Mali at all - CreateInstance required VK_EXT_headless_surface, which no mobile driver here exposes, and aborted the process. The Xlib path already probes and falls back to a hidden window for the same reason on NVIDIA; Android now probes too and hands the WSI an AImageReader's ANativeWindow, a real producer surface attached to no display whose images are never acquired. DriverPost reports the extension's absence as a WARN so the fallback is visible rather than silent. Measured on a Mali-G77 MC9 (native / Espryt / Magma, ns per operation): 5495 chunk draws 14397 / 36934 / 33763, the 26.2 per-draw uniform-range pattern 13710 / 31205 / 21252, sodium-style multi-draw 256956 / 238389 / 209527. The translation costs about 2.4x per draw here against 5-9x on the desktop, because the mobile driver's own per-call cost dwarfs it - and both backends beat the native driver on multi-draw, which it has to emulate. Desktop unit tests 421/421; the POST screen and both Run Bench buttons verified on the device.
78 lines
3.0 KiB
Java
78 lines
3.0 KiB
Java
package top.mobilegl.plugin;
|
|
|
|
import android.app.Service;
|
|
import android.content.Intent;
|
|
import android.os.IBinder;
|
|
import android.util.Log;
|
|
|
|
/**
|
|
* Hosts one driver-benchmark run in its own process ({@code android:process=":bench"})
|
|
* and exits when done.
|
|
*
|
|
* The isolation is load-bearing, not defensive: MobileGL latches its backend from
|
|
* {@code MOBILEGL_BACKEND_TYPE} on first initialization, so Espryt and Magma can never
|
|
* share a process, and Espryt's teardown terminates the process-default EGL display,
|
|
* which would take the POST activity's HWUI context down with it. A fresh process per
|
|
* tap sidesteps both, and {@link System#exit} at the end guarantees the next tap gets
|
|
* one.
|
|
*/
|
|
public final class BenchService extends Service {
|
|
private static final String TAG = "MobileGLBench";
|
|
|
|
public static final String ACTION_RESULT = "top.mobilegl.plugin.BENCH_RESULT";
|
|
public static final String EXTRA_BACKEND = "backend";
|
|
public static final String EXTRA_RESULT_JSON = "resultJson";
|
|
public static final String EXTRA_FRAMES = "frames";
|
|
public static final String EXTRA_WARMUP = "warmupFrames";
|
|
|
|
private static native String nativeRunDriverBench(String backendType, int frames, int warmupFrames);
|
|
|
|
@Override
|
|
public IBinder onBind(Intent intent) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
final String backend = intent != null ? intent.getStringExtra(EXTRA_BACKEND) : null;
|
|
final int frames = intent != null ? intent.getIntExtra(EXTRA_FRAMES, 120) : 120;
|
|
final int warmup = intent != null ? intent.getIntExtra(EXTRA_WARMUP, 30) : 30;
|
|
if (backend == null) {
|
|
stopSelf();
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
new Thread(() -> {
|
|
String result;
|
|
try {
|
|
System.loadLibrary("MobileGL");
|
|
result = nativeRunDriverBench(backend, frames, warmup);
|
|
} catch (Throwable t) {
|
|
Log.e(TAG, "bench failed", t);
|
|
result = "{\"error\":\"" + t.getClass().getSimpleName() + ": "
|
|
+ String.valueOf(t.getMessage()).replace("\\", "\\\\").replace("\"", "\\\"")
|
|
+ "\"}";
|
|
}
|
|
|
|
Intent reply = new Intent(ACTION_RESULT);
|
|
reply.setPackage(getPackageName());
|
|
reply.putExtra(EXTRA_BACKEND, backend);
|
|
reply.putExtra(EXTRA_RESULT_JSON, result);
|
|
sendBroadcast(reply);
|
|
Log.i(TAG, "bench done for " + backend + " (" + result.length() + " bytes)");
|
|
|
|
// The broadcast is already handed to system_server; give binder a
|
|
// moment, then take the whole process down so the next run starts
|
|
// from an uninitialized MobileGL.
|
|
try {
|
|
Thread.sleep(250);
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
stopSelf();
|
|
System.exit(0);
|
|
}, "MobileGLDriverBench").start();
|
|
|
|
return START_NOT_STICKY;
|
|
}
|
|
}
|