mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +09:00
[Test] (MG_Benchmark, MG_Util, MG_Backend, android-plugin): run the driver benchmark on a phone
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.
This commit is contained in:
@@ -2920,6 +2920,21 @@ void main() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
// The AImageReader owns the ANativeWindow the pbuffer fallback handed to the
|
||||
// WSI, so it outlives the surface and is released only here.
|
||||
if (m_fallbackImageReader != nullptr && m_platformLibrary != nullptr) {
|
||||
using AImageReaderDeleteFn = void (*)(void*);
|
||||
auto* imageReaderDelete =
|
||||
reinterpret_cast<AImageReaderDeleteFn>(dlsym(m_platformLibrary, "AImageReader_delete"));
|
||||
if (imageReaderDelete) {
|
||||
imageReaderDelete(m_fallbackImageReader);
|
||||
}
|
||||
m_fallbackImageReader = nullptr;
|
||||
m_window = 0;
|
||||
dlclose(m_platformLibrary);
|
||||
m_platformLibrary = nullptr;
|
||||
}
|
||||
if (m_debugMessenger != VK_NULL_HANDLE) {
|
||||
DestroyDebugMessenger();
|
||||
m_debugMessenger = VK_NULL_HANDLE;
|
||||
@@ -9809,6 +9824,20 @@ void main() {
|
||||
if (!m_window) {
|
||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||
exts.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||||
#elif defined VK_USE_PLATFORM_ANDROID_KHR
|
||||
m_headlessSurfaceSupported = IsExtensionSupported(m_extensions, VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||
if (m_headlessSurfaceSupported) {
|
||||
exts.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||
} else {
|
||||
// No mobile ICD seen so far implements VK_EXT_headless_surface
|
||||
// (Mali r32p1 does not), and this used to abort the process the
|
||||
// moment an application asked for a pbuffer context. CreateSurface()
|
||||
// gives the WSI an AImageReader window instead, so request the
|
||||
// Android surface extension for it.
|
||||
MGLOG_I("%s not available; falling back to an AImageReader %s surface for the pbuffer context.",
|
||||
VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
exts.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
#elif defined VK_USE_PLATFORM_XLIB_KHR
|
||||
m_headlessSurfaceSupported = IsExtensionSupported(m_extensions, VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||
if (m_headlessSurfaceSupported) {
|
||||
@@ -10646,6 +10675,54 @@ void main() {
|
||||
m_window = reinterpret_cast<NativeWindowType>(
|
||||
CreateInternalMetalLayer(m_config.SurfaceWidth, m_config.SurfaceHeight, &m_platformDisplay));
|
||||
m_platformLibrary = reinterpret_cast<void*>(m_window);
|
||||
#elif defined VK_USE_PLATFORM_ANDROID_KHR
|
||||
if (m_headlessSurfaceSupported) {
|
||||
auto* createHeadlessSurface = reinterpret_cast<PFN_vkCreateHeadlessSurfaceEXT>(
|
||||
vkGetInstanceProcAddr(m_instance, "vkCreateHeadlessSurfaceEXT"));
|
||||
MOBILEGL_ASSERT(createHeadlessSurface != nullptr,
|
||||
"VK_EXT_headless_surface is not available for DirectVulkan pbuffer surface");
|
||||
VkHeadlessSurfaceCreateInfoEXT sci{VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT};
|
||||
VK_VERIFY(createHeadlessSurface(m_instance, &sci, nullptr, &m_surface),
|
||||
"vkCreateHeadlessSurfaceEXT failed");
|
||||
return;
|
||||
}
|
||||
// Windowless context on a driver without VK_EXT_headless_surface: give
|
||||
// the WSI an AImageReader's ANativeWindow. It is a real, valid producer
|
||||
// surface that is attached to no display and whose images this code never
|
||||
// acquires, which is exactly the "drawable nobody sees" the Xlib fallback
|
||||
// below builds out of an unmapped window. libmediandk is dlopen'd rather
|
||||
// than linked so a device without it degrades to the old error instead of
|
||||
// failing to load the library at all.
|
||||
{
|
||||
void* mediaLib = dlopen("libmediandk.so", RTLD_NOW | RTLD_LOCAL);
|
||||
MOBILEGL_ASSERT(mediaLib != nullptr,
|
||||
"VK_EXT_headless_surface is unavailable and libmediandk.so could not be loaded "
|
||||
"for the pbuffer surface fallback");
|
||||
using AImageReaderNewFn = int (*)(int32_t, int32_t, int32_t, int32_t, void**);
|
||||
using AImageReaderGetWindowFn = int (*)(void*, void**);
|
||||
auto* imageReaderNew = reinterpret_cast<AImageReaderNewFn>(dlsym(mediaLib, "AImageReader_new"));
|
||||
auto* imageReaderGetWindow =
|
||||
reinterpret_cast<AImageReaderGetWindowFn>(dlsym(mediaLib, "AImageReader_getWindow"));
|
||||
MOBILEGL_ASSERT(imageReaderNew != nullptr && imageReaderGetWindow != nullptr,
|
||||
"libmediandk.so is missing AImageReader_new/AImageReader_getWindow");
|
||||
|
||||
constexpr int32_t kAndroidFormatRgba8888 = 0x1; // AIMAGE_FORMAT_RGBA_8888
|
||||
const int32_t width = static_cast<int32_t>(std::max<Uint32>(m_config.SurfaceWidth, 1));
|
||||
const int32_t height = static_cast<int32_t>(std::max<Uint32>(m_config.SurfaceHeight, 1));
|
||||
void* reader = nullptr;
|
||||
// maxImages must cover the swapchain's images; the reader never
|
||||
// acquires any, so this only sizes its buffer queue.
|
||||
const int status = imageReaderNew(width, height, kAndroidFormatRgba8888, 8, &reader);
|
||||
MOBILEGL_ASSERT(status == 0 && reader != nullptr,
|
||||
"AImageReader_new failed (%d) for the pbuffer surface fallback", status);
|
||||
void* nativeWindow = nullptr;
|
||||
const int windowStatus = imageReaderGetWindow(reader, &nativeWindow);
|
||||
MOBILEGL_ASSERT(windowStatus == 0 && nativeWindow != nullptr,
|
||||
"AImageReader_getWindow failed (%d) for the pbuffer surface fallback", windowStatus);
|
||||
m_fallbackImageReader = reader;
|
||||
m_platformLibrary = mediaLib;
|
||||
m_window = reinterpret_cast<NativeWindowType>(nativeWindow);
|
||||
}
|
||||
#elif defined VK_USE_PLATFORM_XLIB_KHR
|
||||
if (m_headlessSurfaceSupported) {
|
||||
auto* createHeadlessSurface =
|
||||
|
||||
Reference in New Issue
Block a user