Files
MobileGL/tools/cts/README.md
T

4.0 KiB

Running the OpenGL CTS (VK-GL-CTS / KHR-GL33) against MobileGL on Android

Goal: measure how much of the OpenGL 3.3 core-profile conformance suite MobileGL passes, separately for each backend (DirectGLES, DirectVulkan).

How MobileGL is reached from a test binary

MobileGL ships its own EGL implementation alongside its desktop-GL implementation in a single libMobileGL.so. A plain arm64 ELF in /data/local/tmp can therefore drive it with no APK and no Activity:

  1. setenv("MOBILEGL_BACKEND_TYPE", "DirectGLES"|"DirectVulkan") before the library is mapped — MobileGL parses its configuration from an ELF constructor.
  2. dlopen("libMobileGL.so"), then dlsym the egl* and gl* entry points. MobileGL exports 45 EGL symbols and the desktop GL functions directly; eglGetProcAddress resolves the same set.
  3. eglBindAPI(EGL_OPENGL_API), choose a config with EGL_RENDERABLE_TYPE = EGL_OPENGL_BIT, then eglCreateContext with EGL_CONTEXT_OPENGL_PROFILE_MASK = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT and major/minor 3/3.

This yields a genuine GL 3.3 core context (GL_CONTEXT_PROFILE_MASK == 0x1).

Surface type, per backend

backend pbuffer (headless) window
DirectGLES works works
DirectVulkan unusable works

DirectVulkan's pbuffer path builds a headless VkSurfaceKHR and so requires the VK_EXT_headless_surface instance extension, which Adreno's Android driver does not expose. It fails inside eglMakeCurrent, not at surface creation.

The workaround that keeps everything in a shell process: obtain a real ANativeWindow from AImageReader (AImageReader_newWithUsage + AImageReader_getWindow). It is an ordinary BufferQueue producer, so vkCreateAndroidSurfaceKHR accepts it, and no Activity is involved. Register an onImageAvailable listener that acquires and deletes each image — otherwise the producer blocks once maxImages buffers are in flight and the next swap hangs.

Why the suite must render into an FBO

On a window surface, DirectVulkan's glReadPixels from the default framebuffer returns all zeros, with no GL error, both before and after eglSwapBuffers. DirectGLES on the identical window is correct, and readback from a user FBO is correct on both backends.

Verified on two SoCs and two drivers, so this is MobileGL's behaviour rather than a driver quirk:

device GPU driver default-FB user FBO
Xiaomi 24129PN74C Adreno 830 Vulkan 1.3.284 / 512.800.46 zeros ok
Lenovo TB321FU Adreno 750 Vulkan 1.3.128 / 512.762.28 zeros ok

dEQP verifies nearly every case through glReadPixels, so running it against the default framebuffer would score DirectVulkan near zero for a reason unrelated to conformance. The runs therefore use --deqp-surface-type=fbo, uniformly for both backends so the two numbers stay comparable.

Other constraints the harness must respect

  • eglMakeCurrent requires draw == read and rejects EGL_NO_SURFACE with EGL_BAD_MATCH. dEQP's surfaceless platform is therefore unusable, which is why this port supplies its own tcu::Platform.
  • MobileGL aborts during static teardown (FORTIFY: pthread_mutex_lock called on a destroyed mutex) after all work completes. Flush and _exit() so the exit code and the .qpa log survive.

Contents

probe/mgprobe.c        preflight gate: one backend x one surface type, checks
                       context version/profile and both readback paths
scripts/qpa_report.py  .qpa -> pass rate, status histogram, worst groups

Preflight

aarch64-linux-android26-clang -O1 -o mgprobe mgprobe.c -ldl -llog -landroid -lmediandk
adb push mgprobe libMobileGL.so /data/local/tmp/mgcts/
adb shell 'cd /data/local/tmp/mgcts && LD_LIBRARY_PATH=. ./mgprobe \
    --backend DirectVulkan --surface imagereader --lib ./libMobileGL.so'

Exit status is 0 when a 3.3 core context came up and FBO readback is correct. Default-framebuffer readback is reported but deliberately does not gate.