8.9 KiB
Trace fixture authoring
Variables
export REPO="$PWD"
export WORK="$PWD/.trace-work"
export CASE="case-name"
export WIDTH=854
export HEIGHT=480
export TARGET_FRAME=0
export TARGET_CALL=0
Prerequisites
git clone --recursive <mobilegl-repo-url> MobileGL
cd MobileGL
git lfs install
git lfs pull
Install:
- CMake
- Ninja
- C++ compiler
- Python 3
- Mesa OpenGL/EGL runtime
- Vulkan loader and ICD for
DirectVulkan - Pillow or ImageMagick for alpha cleanup
- Android SDK, Android NDK, JDK, Gradle, and
adbfor Android replay
Build apitrace
cmake -S "$REPO/3rdparty/apitrace" -B "$WORK/build-apitrace" -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_GUI=OFF
cmake --build "$WORK/build-apitrace" --target apitrace glretrace --parallel
export APITRACE="$(find "$WORK/build-apitrace" -type f -name apitrace -perm -111 | head -n 1)"
export GLRETRACE="$(find "$WORK/build-apitrace" -type f -name glretrace -perm -111 | head -n 1)"
test -n "$APITRACE"
test -n "$GLRETRACE"
On Windows, set APITRACE and GLRETRACE to the corresponding .exe files.
Prepare the capture
- Set the target window size to
WIDTHxHEIGHT. - Disable unintended overlays, frame counters, notifications, and launcher UI.
- Fix language, resource packs, mods, shader pack, world seed, time, weather, player position, camera direction, FOV, GUI scale, and render distance.
- Trace the final OpenGL process, not the launcher.
- For Minecraft, document version, mod loader, mods, shader pack, language, world, time, and camera setup.
Capture
mkdir -p "$WORK/$CASE"
"$APITRACE" trace --api=gl \
--output "$WORK/$CASE/full.trace" \
-- <application-command> <application-args>
For Java:
"$APITRACE" trace --api=gl \
--output "$WORK/$CASE/full.trace" \
-- "$JAVA_EXE" <jvm-args> <main-class-or-jar> <game-args>
Keep full.trace until both backends are validated.
Select target frame
Fixture selection must be frame-based. Do not trim the fixture from a full trace
by filtering arbitrary call ranges or single full-trace calls. Pick a rendered
frame, then trim with gltrim -f.
"$APITRACE" dump --calls=frame "$WORK/$CASE/full.trace" \
> "$WORK/$CASE/frames.txt"
Inspect frames.txt, identify the frame that contains the intended visual
state, and set:
export TARGET_FRAME=<chosen-frame-number>
Trim and package
"$APITRACE" gltrim \
-f "$TARGET_FRAME" \
--output "$WORK/$CASE/trace.trace" \
"$WORK/$CASE/full.trace"
Generate golden
Generate frame snapshots from the trimmed trace, then choose the snapshot that matches the selected frame. The target call used by replay registration must come from the trimmed trace, not from a call-filtered full-trace selection.
mkdir -p "$WORK/$CASE/golden"
"$APITRACE" replay --headless \
--snapshot-prefix "$WORK/$CASE/golden/$CASE." \
--call-nos \
"$WORK/$CASE/trace.trace"
Set TARGET_CALL to the call number in the chosen trimmed-trace snapshot
filename:
export TARGET_CALL=<chosen-trimmed-trace-snapshot-call>
GOLDEN_SRC="$WORK/$CASE/golden/$CASE.$(printf '%010d' "$TARGET_CALL").png"
Remove unintended alpha:
python3 - "$GOLDEN_SRC" "$WORK/$CASE/$CASE.$(printf '%010d' "$TARGET_CALL").png" <<'PY'
import sys
from PIL import Image
src, dst = sys.argv[1], sys.argv[2]
img = Image.open(src).convert("RGBA")
bg = Image.new("RGBA", img.size, (0, 0, 0, 255))
bg.alpha_composite(img)
bg.convert("RGB").save(dst)
PY
Or copy directly:
cp "$GOLDEN_SRC" "$WORK/$CASE/$CASE.$(printf '%010d' "$TARGET_CALL").png"
Package:
mkdir -p "$WORK/$CASE/archive"
cp "$WORK/$CASE/trace.trace" "$WORK/$CASE/archive/trace.trace"
tar -czf "$REPO/tools/trace_replay/fixtures/$CASE.tgz" \
-C "$WORK/$CASE/archive" trace.trace
cp "$WORK/$CASE/$CASE.$(printf '%010d' "$TARGET_CALL").png" \
"$REPO/tools/trace_replay/fixtures/"
Check the final archive size. The committed fixture archive should be less than 20 MiB, and should preferably be less than 10 MiB. If it is larger, recapture with a shorter run or trim a smaller frame-only fixture instead of adding call-based filtering.
du -h "$REPO/tools/trace_replay/fixtures/$CASE.tgz"
tar -tzf "$REPO/tools/trace_replay/fixtures/$CASE.tgz"
Track with Git LFS:
git lfs track "tools/trace_replay/fixtures/*.tgz"
git lfs track "tools/trace_replay/fixtures/*.png"
git add .gitattributes tools/trace_replay/fixtures/$CASE.tgz \
tools/trace_replay/fixtures/$CASE.$(printf '%010d' "$TARGET_CALL").png
git lfs status
Register Linux replay
Edit tools/trace_replay/CMakeLists.txt:
add_trace_replay_test_for_backends(case-name
TRACE_ARCHIVE ${MOBILEGL_TRACE_ROOT}/fixtures/case-name.tgz
TRACE_FILE trace.trace
GOLDEN ${MOBILEGL_TRACE_ROOT}/fixtures/case-name.0000000000.png
TARGET_CALL 0
WIDTH 854
HEIGHT 480
SSIM_THRESHOLD 0.99)
Optional crop:
CROP_X 1
CROP_Y 1
CROP_WIDTH 852
CROP_HEIGHT 478
Register Android replay
Edit .github/workflows/apk.yml TRACE_REPLAY_CASES:
case-name|tools/trace_replay/fixtures/case-name.tgz|trace.trace|tools/trace_replay/fixtures/case-name.0000000000.png|0|854|480|0.99|0|0|0|0|900
Field order:
case|trace archive|trace file|golden|target call|width|height|ssim threshold|crop x|crop y|crop width|crop height|timeout seconds
Update tools/trace_replay/README.md with one fixture sentence and one golden
image link.
Validate on Linux
cmake -S "$REPO" -B "$WORK/build-linux" -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DMOBILEGL_BUILD_TEST=ON \
-DMOBILEGL_BUILD_BENCHMARK=OFF \
-DMOBILEGL_BUILD_TRACE_REPLAY=ON \
-DMOBILEGL_LOG_ACTIVE_LEVEL=MOBILEGL_LOG_LEVEL_INFO
cmake --build "$WORK/build-linux" --target mobilegl_trace_replay --parallel
Run the registered case:
ctest --test-dir "$WORK/build-linux/tools/trace_replay" -V \
-R "MobileGLTraceReplay\\.$CASE\\."
Run one backend manually:
cmake \
-DTRACE_REPLAY_EXE="$WORK/build-linux/tools/trace_replay/mobilegl_trace_replay" \
-DMOBILEGL_LIBRARY="$WORK/build-linux/libMobileGL.so" \
-DTRACE_CASE_NAME="$CASE" \
-DTRACE_ARCHIVE="$REPO/tools/trace_replay/fixtures/$CASE.tgz" \
-DTRACE_FILE=trace.trace \
-DTRACE_GOLDEN="$REPO/tools/trace_replay/fixtures/$CASE.$(printf '%010d' "$TARGET_CALL").png" \
-DTRACE_BACKEND=DirectGLES \
-DTRACE_TARGET_CALL="$TARGET_CALL" \
-DTRACE_WIDTH="$WIDTH" \
-DTRACE_HEIGHT="$HEIGHT" \
-DTRACE_SSIM_THRESHOLD=0.99 \
-DTRACE_CROP_X=0 \
-DTRACE_CROP_Y=0 \
-DTRACE_CROP_WIDTH=0 \
-DTRACE_CROP_HEIGHT=0 \
-DTRACE_OUTPUT_DIR="$WORK/$CASE/linux-DirectGLES" \
-DTRACE_ARTIFACT_DIR="$WORK/$CASE/linux-artifacts" \
-P "$REPO/tools/trace_replay/run_trace_case.cmake"
DirectGLES CI env:
export EGL_PLATFORM=surfaceless
export LIBGL_ALWAYS_SOFTWARE=1
export MESA_GL_VERSION_OVERRIDE=3.3
export MESA_GLSL_VERSION_OVERRIDE=330
DirectVulkan check:
vulkaninfo | grep -E 'deviceName|VK_EXT_headless_surface'
Validate on Android
Build trace APKs:
gradle --no-daemon -p "$REPO/android-plugin" \
:app:assembleEsprytTraceRelease \
:app:assembleMagmaTraceRelease \
-Pmobilegl.abis=all \
-Pmobilegl.debuggableRelease=true \
-Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO \
--parallel
Run DirectGLES:
sh "$REPO/android-plugin/trace-replay-ci.sh" \
--apk-file "$REPO/android-plugin/app/build/outputs/apk/esprytTrace/release/MobileGL-EsprytTrace-release.apk" \
--package top.mobilegl.plugin.espryt.trace \
--backend DirectGLES \
--result-root "$WORK/$CASE/android-result" \
--fixture-root "$WORK/$CASE/android-fixture" \
--case "$CASE" \
--trace-archive "$REPO/tools/trace_replay/fixtures/$CASE.tgz" \
--trace-file trace.trace \
--golden "$REPO/tools/trace_replay/fixtures/$CASE.$(printf '%010d' "$TARGET_CALL").png" \
--target-call "$TARGET_CALL" \
--width "$WIDTH" \
--height "$HEIGHT" \
--ssim-threshold 0.99 \
--crop-x 0 \
--crop-y 0 \
--crop-width 0 \
--crop-height 0 \
--timeout-seconds 900
Run DirectVulkan with:
- APK:
MobileGL-MagmaTrace-release.apk - package:
top.mobilegl.plugin.magma.trace - backend:
DirectVulkan
Inspect:
$WORK/$CASE/android-result/$CASE-DirectGLES/result.json$WORK/$CASE/android-result/$CASE-DirectGLES/$CASE-DirectGLES-actual.png$WORK/$CASE/android-result/$CASE-DirectGLES/$CASE-DirectGLES-diff.png$WORK/$CASE/android-result/$CASE-DirectGLES/retrace.log$WORK/$CASE/android-result/$CASE-DirectGLES/logcat.txt
Checklist
- Golden matches the committed trace and target call.
- Archive contains only
trace.trace. - Both Linux backends pass before Linux CI registration.
- Both Android backends pass before APK CI registration.
actual.pngandcase-diff.pngare inspected.- Fixture
.tgzand.pngfiles are tracked by Git LFS. - No build output, extracted trace directory, temporary report, or debug text is staged.