7.3 KiB
name, description
| name | description |
|---|---|
| linux-gl-cts-on-mobilegl | Run the Khronos OpenGL CTS (VK-GL-CTS glcts) against MobileGL on desktop Linux with no GPU and no device, and report a per-backend conformance rate for Espryt (DirectGLES) and Magma (DirectVulkan). Use when measuring or iterating on the conformance of one test group, when a fix needs a before/after number, or when neither an Android device nor a Windows GPU box is available. |
OpenGL CTS on MobileGL (desktop Linux)
Overview
glcts is built as an ordinary x86-64 host executable that reaches OpenGL only
through libMobileGL.so, using the mobilegl-desktop VK-GL-CTS target and the
tcu::Platform port in tools/cts/platform/. Nothing links libGL or libEGL,
so a result is unambiguously MobileGL's.
Both backends run headless on software rendering, so this needs no GPU at all:
- Espryt (
DirectGLES) drives Mesa's OpenGL ES through the system EGL. - Magma (
DirectVulkan) runs on lavapipe, whoseVK_EXT_headless_surfaceis what the desktop platform port's pbuffer path requires.
Always report the two backends separately. They are different implementations of the same front end, they fail different cases, and a single combined number hides which one a change moved.
Prerequisites
sudo apt-get install -y ninja-build cmake libvulkan-dev \
libegl1-mesa-dev libgles2-mesa-dev mesa-vulkan-drivers
mesa-vulkan-drivers is what installs lavapipe; without it DirectVulkan has no
ICD and eglInitialize fails inside the backend. A C++23 toolchain is required
(GCC 13+, or Clang 20+ — Clang 18 defines __cpp_concepts as 201907L, which
switches libstdc++'s <expected> off and the build fails in
MG_Util/ShaderTranspiler/Types.h).
export MG=<path-to-MobileGL-worktree>
export CTS=<path-to-VK-GL-CTS-checkout>
Step 1 — build libMobileGL.so
git -C "$MG" submodule update --init --recursive
python3 "$MG/3rdparty/glslang/update_glslang_sources.py" # SPIRV-Tools; ENABLE_OPT is forced on
cmake -S "$MG" -B "$MG/build-linux" -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF \
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build "$MG/build-linux" --parallel "$(nproc)"
Add -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=FALSE when iterating. The
Release configuration turns LTO on, which makes every relink cost minutes for no
behavioural difference; a conformance number is identical either way.
Step 2 — get VK-GL-CTS and build glcts
Use a release tag so the mustpass list, and therefore the reported rate, is citable.
git -C "$CTS" checkout opengl-cts-4.6.8.1
python3 "$CTS/external/fetch_sources.py"
python3 "$MG/tools/cts/scripts/sync_to_cts.py" "$CTS"
git -C "$CTS" apply "$MG/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 \
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
ninja -C "$CTS/build-cts" glcts
Confirm the configure output says *** Using MobileGL desktop target. Budget a
couple of hours for the glcts link on a small machine; it is a one-time cost
that ccache makes cheap afterwards.
If fetch_sources.py dies with HTTP Error 403 it is an egress policy blocking
the GitHub archive downloads (zlib, libpng), not a broken checkout: git clone
still works, so clone the package at the tag the script pins into
external/<pkg>/src by hand — for libpng also copy
scripts/pnglibconf.h.prebuilt to src/pnglibconf.h, which is what the
script's post-extract step does.
Step 3 — run, once per backend
cd "$MG"
for BACKEND in DirectGLES DirectVulkan; do
python3 tools/cts/scripts/run_cts_local.py --backend "$BACKEND" \
--glcts "$CTS/build-cts/external/openglcts/modules/glcts" \
--lib build-linux/libMobileGL.so \
--caselist cases.txt --outdir "runs/${BACKEND}" \
--env EGL_PLATFORM=surfaceless
python3 tools/cts/scripts/qpa_report.py "runs/${BACKEND}" --label "$BACKEND"
done
cases.txt is any subset of a mustpass list. For one group, filter the list
rather than running the whole suite:
grep direct_state_access \
"$CTS"/external/openglcts/data/gl_cts/data/mustpass/gl/khronos_mustpass/main/gl45-main.txt \
> cases.txt
run_cts_local.py re-invokes glcts with only the cases that have no result
yet, so a crash costs one case rather than the run, and it records the case that
was open when the process died as Crash. qpa_report.py scores Pass,
NotSupported and the warning statuses as non-failures, the way Khronos scores
a submission, and reports the strict Pass-only rate alongside.
Required flags, and why
| Flag | Why it is not optional |
|---|---|
--env EGL_PLATFORM=surfaceless |
For DirectGLES. With no /dev/dri node Mesa's EGL fails eglInitialize on the default display, and MobileGL surfaces that as EGL_BAD_ALLOC out of eglCreatePbufferSurface — which points at the wrong call entirely. Harmless for DirectVulkan, so pass it to both. |
--deqp-surface-type=fbo (the runner's default) |
DirectVulkan reads back zeros from the default framebuffer. dEQP verifies nearly everything through glReadPixels, so rendering to the surface scores Magma near zero for a reason unrelated to conformance. Use it for both backends so the two numbers stay comparable. |
--deqp-terminate-on-device-lost=disable (supplied by the runner) |
Its default calls glGetGraphicsResetStatus() after every case. That is GL 4.5 / KHR_robustness, absent from what MobileGL exports, so the pointer is null and the process segfaults on the first case. |
What this environment does and does not tell you
Reproducible here, and MobileGL's own rather than a driver quirk:
- DirectVulkan's default-framebuffer readback returns zeros; a user FBO, renderbuffer- or texture-attached, is correct on both backends. This is the same defect the Android runs work around, so it can be debugged without a phone.
Different from a real GPU, so do not read conformance into it:
- lavapipe supports renderbuffer formats Adreno reports as unsupported, so the
Android runs see
NotSupportedwhere these do not, and vice versa. - Backend limits differ.
GL_TEXTURE_BUFFER_OFFSET_ALIGNMENTis 16 on llvmpipe and on lavapipe; a device that reports 1 will not exercise the same paths. - Everything is a software rasterizer, so a case that fails only under real timing or real tiling will not fail here.
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, Mesa 25.2.8, --deqp-surface-type=fbo.
| backend | renderer | conformance | strict Pass | Fail | InternalError | Crash |
|---|---|---|---|---|---|---|
| DirectGLES | Espryt | 82.48% | 74.93% | 57 | 8 | 0 |
| DirectVulkan | Magma | 73.58% | 73.32% | 87 | 8 | 3 |
Contents
platform/tcuMobileGLPlatform.{cpp,hpp} dEQP tcu::Platform for MobileGL
targets/mobilegl-desktop.cmake VK-GL-CTS target (-DDEQP_TARGET=mobilegl-desktop)
scripts/sync_to_cts.py inject the port into a CTS checkout
scripts/run_cts_local.py crash-resuming local-host runner
scripts/qpa_report.py .qpa -> conformance rate