mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
177 lines
8.2 KiB
Markdown
177 lines
8.2 KiB
Markdown
# Running the OpenGL CTS against MobileGL
|
|
|
|
This directory contains three supported paths:
|
|
|
|
- Android arm64 / MobileGL EGL: the KHR-GL33 workflow documented below and in
|
|
`skills/gl-cts-on-mobilegl/SKILL.md`.
|
|
- Windows x64 / MobileGL WGL: the GL30-GL46 pipeline in
|
|
`scripts/wgl_glcts_pipeline.py`, documented by
|
|
`skills/wgl-gl-cts-on-mobilegl/SKILL.md`.
|
|
- Desktop Linux x64 / MobileGL EGL: `scripts/run_cts_local.py` against the
|
|
`mobilegl-desktop` VK-GL-CTS target, documented in "Desktop Linux workflow"
|
|
below and in `skills/linux-gl-cts-on-mobilegl/SKILL.md`. This is the path that
|
|
needs no device and no GPU.
|
|
|
|
Windows prerequisites are Git, Python 3.9+, CMake, Visual Studio 2022's Desktop
|
|
C++ workload, and a Vulkan SDK visible to CMake. DirectVulkan also needs a
|
|
working Vulkan loader plus a GPU-vendor ICD and driver; the SDK alone is not a
|
|
GPU driver.
|
|
|
|
For Windows, start with:
|
|
|
|
```powershell
|
|
python tools\cts\scripts\wgl_glcts_pipeline.py --help
|
|
```
|
|
|
|
The pipeline builds MobileGL as a drop-in `opengl32.dll`, builds or reuses
|
|
`glcts.exe`, checks that WGL loaded MobileGL rather than the system driver,
|
|
resumes individual suites after crashes/timeouts, and writes Markdown plus JSON
|
|
reports below the printed `runs/<first-16-of-run-fingerprint>` directory. Its
|
|
manifest records provenance and the runner settings used to validate a resume.
|
|
|
|
## Desktop Linux workflow
|
|
|
|
The `mobilegl-desktop` target builds `glcts` as an ordinary host executable that
|
|
reaches OpenGL only through `libMobileGL.so`. Both backends run headless with no
|
|
GPU at all, which makes this the cheapest way to measure a single test group
|
|
while working on it.
|
|
|
|
Apt packages: `mesa-vulkan-drivers` (lavapipe, for DirectVulkan),
|
|
`libegl1-mesa-dev` and `libgles2-mesa-dev` (the system EGL/ES that DirectGLES
|
|
drives), `libvulkan-dev`, `ninja-build`.
|
|
|
|
```sh
|
|
cmake -S . -B build-linux -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
|
-DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF
|
|
cmake --build build-linux --parallel "$(nproc)"
|
|
|
|
python tools/cts/scripts/sync_to_cts.py "$CTS"
|
|
git -C "$CTS" apply <path-to>/tools/cts/patches/0001-fbo-color-texture-attachment.patch
|
|
cmake -S "$CTS" -B "$CTS/build-cts" -G Ninja -DDEQP_TARGET=mobilegl-desktop \
|
|
-DCMAKE_BUILD_TYPE=Release
|
|
ninja -C "$CTS/build-cts" glcts
|
|
|
|
python tools/cts/scripts/run_cts_local.py --backend DirectGLES \
|
|
--glcts "$CTS/build-cts/external/openglcts/modules/glcts" \
|
|
--lib build-linux/libMobileGL.so \
|
|
--caselist cases.txt --outdir runs/gles --env EGL_PLATFORM=surfaceless
|
|
python tools/cts/scripts/qpa_report.py runs/gles --label DirectGLES
|
|
```
|
|
|
|
`EGL_PLATFORM=surfaceless` is not optional for DirectGLES: without a `/dev/dri`
|
|
node Mesa's EGL fails `eglInitialize` on the default display, and MobileGL
|
|
reports that as `EGL_BAD_ALLOC` out of `eglCreatePbufferSurface`. DirectVulkan
|
|
needs nothing extra - lavapipe exposes `VK_EXT_headless_surface`, which is what
|
|
the desktop platform port's pbuffer path requires.
|
|
|
|
Two known differences from a real GPU, both MobileGL's rather than the harness's:
|
|
DirectVulkan reads back zeros from the **default** framebuffer (a user FBO,
|
|
renderbuffer- or texture-attached, is correct on both backends), and lavapipe
|
|
supports renderbuffer formats that Adreno reports as unsupported, so the Android
|
|
runs see `NotSupported` where these do not.
|
|
|
|
### Reference results: KHR-GL45.direct_state_access
|
|
|
|
`opengl-cts-4.6.8.1`, the 371 `direct_state_access` cases of the `gl45-main`
|
|
mustpass list, lavapipe / Mesa 25.2.8, `--deqp-surface-type=fbo`.
|
|
|
|
| backend | conformance | strict Pass | Fail | InternalError | Crash |
|
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
| DirectGLES | **82.48%** | 74.93% | 57 | 8 | 0 |
|
|
| DirectVulkan | **73.58%** | 73.32% | 87 | 8 | 3 |
|
|
|
|
`textures_storage_multisample_*` is most of what remains, and the two backends
|
|
are there for different reasons. Espryt passes the whole `2d` half and fails only
|
|
the `3d` one, which attaches a multisample **array** texture with
|
|
`glFramebufferTextureLayer` - layered attachments are not something the
|
|
framebuffer attachment model represents yet. Magma fails both halves: it cannot
|
|
sample a multisample texture at all, so the second pass reads back nothing.
|
|
`program_pipelines_*` needs ARB_separate_shader_objects, which is stubbed
|
|
throughout.
|
|
|
|
## Android KHR-GL33 workflow
|
|
|
|
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.
|