mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
iterationRP's Program 203 declares shared vec2 prefixSumCache[32] for a 512-invocation workgroup indexed by gl_SubgroupID; any device narrower than 16 lanes partitions into more than 32 subgroups and the pack writes shared memory out of bounds (heap corruption on lavapipe's CPU rasterizer, ssim 0.028 on the CI retrace). Fix it where the fault lies - in the fixture - and keep the GL contract sound everywhere else: - FixIterationRPSubgroupScratchPass: fingerprint-gated SPIR-V pass that grows exactly that array to ceil(invocations/width) entries on sub-16-lane devices; every other module passes through byte-identical. - DeriveNumSubgroupsPass stays default-on for the Adreno topology bug and is made spec-sound: pipelines request REQUIRE_FULL_SUBGROUPS whenever the workgroup shape makes the flag legal (computeFullSubgroups enabled, local_size_x a multiple of the native width, subgroup count within maxComputeWorkgroupSubgroups). - EmulateSubgroupsPass: 32-lane virtual-subgroup lowering kept in-tree as a last resort, enabled only by MOBILEGL_MAGMA_EMULATE_SUBGROUP=1 on devices with no native subgroup support; fails closed on extended subgroup instructions and on modules whose added scratch would exceed maxComputeSharedMemorySize. - IterationRPFirstReductionScenario skips gracefully outside the pack's 16..256-lane source domain; the new IterationRPScratchFixScenario runs the fixture-shaped reduction on any width and asserts the exact width-independent total. DriverPost keeps reporting FAIL on out-of-domain devices. - Program203 -> IterationRP rename throughout; the per-trace num_subgroups_quirk plumbing is removed from the trace replayer, JNI chain, and CI workflows.
658 lines
26 KiB
YAML
658 lines
26 KiB
YAML
name: MobileGL APK
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- dev
|
|
- Feat/Backend-Direct-GLES
|
|
- Feat/Backend-Direct-Vulkan
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
env:
|
|
CCACHE_BASEDIR: ${{ github.workspace }}
|
|
CCACHE_COMPRESS: "true"
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 4G
|
|
CCACHE_NOHASHDIR: "true"
|
|
MOBILEGL_CMAKE_COMPILER_LAUNCHER: ccache
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Set artifact metadata
|
|
run: |
|
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
|
|
|
- name: Set up JDK
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: zulu
|
|
java-version: '17'
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v6
|
|
with:
|
|
gradle-version: 8.10.2
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: .ccache
|
|
key: ${{ runner.os }}-apk-${{ github.job }}-ccache-v1
|
|
restore-keys: |
|
|
${{ runner.os }}-apk-${{ github.job }}-ccache-
|
|
|
|
- name: Install ccache
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y ccache
|
|
ccache --version
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Install Android NDK
|
|
run: |
|
|
sdkmanager "ndk;27.3.13750724"
|
|
echo "ndk.dir=$ANDROID_HOME/ndk/27.3.13750724" >> android-plugin/local.properties
|
|
|
|
- name: Update glslang external sources
|
|
working-directory: 3rdparty/glslang
|
|
run: python update_glslang_sources.py
|
|
|
|
- name: Build plugin APK
|
|
run: gradle --no-daemon -p android-plugin :app:assemblePluginRelease -Pmobilegl.apkSuffix="${GITHUB_SHA}" -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)"
|
|
env:
|
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
|
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
|
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
|
|
|
- name: Download ANGLE x86_64 libraries
|
|
run: |
|
|
angle_dir="android-plugin/app/src/trace/jniLibs/x86_64"
|
|
rm -rf "${angle_dir}"
|
|
mkdir -p "${angle_dir}"
|
|
|
|
package_angle_variant() {
|
|
variant="$1"
|
|
commit="$2"
|
|
egl_sha="$3"
|
|
gles_sha="$4"
|
|
source_dir="${RUNNER_TEMP}/mobilegl-angle-${variant}"
|
|
base="https://raw.githubusercontent.com/FCL-Team/FoldCraftLauncher/${commit}/FCLauncher/src/main/jniLibs/x86_64"
|
|
mkdir -p "${source_dir}"
|
|
curl -L --fail --retry 3 -o "${source_dir}/libEGL_angle.so" "${base}/libEGL_angle.so"
|
|
curl -L --fail --retry 3 -o "${source_dir}/libGLESv2_angle.so" "${base}/libGLESv2_angle.so"
|
|
echo "${egl_sha} ${source_dir}/libEGL_angle.so" | sha256sum -c -
|
|
echo "${gles_sha} ${source_dir}/libGLESv2_angle.so" | sha256sum -c -
|
|
for library in libEGL_angle libGLESv2_angle; do
|
|
filename="${library}_${variant}.so"
|
|
cp "${source_dir}/${library}.so" "${angle_dir}/${filename}"
|
|
done
|
|
}
|
|
|
|
package_angle_variant \
|
|
ec889e6ea831 \
|
|
f2a3d510dffd8f6540a52e1a7d0c5787d151075b \
|
|
c41828768d089899fa058ec0bee711a91be88347f29bdb935223da6be1149c40 \
|
|
e4f820d99f94365c66df868c7740fef142fe5c0cd7c941790a9e30638857ca4d
|
|
package_angle_variant \
|
|
90a62123d794 \
|
|
bdcc96ac11c79001018ae4375eb73cb54a9f682f \
|
|
d0f4298ccc770cc801fc52e21733521646161e8a4adb3bd0052d9a1b57ee0ca8 \
|
|
66fdc867e552192d553d59095ea2e3cef4829de65c356f1fd826027b1905972e
|
|
|
|
- name: Build retrace APK
|
|
run: gradle --no-daemon -p android-plugin :app:assembleTraceRelease -Pmobilegl.apkSuffix="${GITHUB_SHA}" -Pmobilegl.abis=all -Pmobilegl.debuggableRelease=true -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)"
|
|
env:
|
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
|
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
|
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
|
|
|
- name: Show ccache stats
|
|
if: always()
|
|
run: ccache --show-stats
|
|
|
|
# Rewrite one rolling entry per job on the default branch. The upload stays
|
|
# cumulative - it carries every object restored at the top of this run plus
|
|
# the few TUs that actually changed - but Actions cache keys are immutable,
|
|
# so the superseded blob has to be released before the same key can be
|
|
# re-uploaded. Running after the build means a failed build leaves the
|
|
# existing entry untouched. The other trigger branches restore this entry
|
|
# rather than each writing a ~4 GB one of their own.
|
|
- name: Release superseded ccache entry
|
|
if: github.ref_name == github.event.repository.default_branch
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
CACHE_KEY: ${{ runner.os }}-apk-${{ github.job }}-ccache-v1
|
|
run: gh cache delete "${CACHE_KEY}" || true
|
|
|
|
- name: Save ccache
|
|
if: github.ref_name == github.event.repository.default_branch
|
|
continue-on-error: true
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: .ccache
|
|
key: ${{ runner.os }}-apk-${{ github.job }}-ccache-v1
|
|
|
|
- name: Verify APK metadata and packaging
|
|
run: |
|
|
AAPT2="$(find "$ANDROID_HOME/build-tools" -name aapt2 -type f | sort -V | tail -n 1)"
|
|
plugin_apk="android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-${GITHUB_SHA}.apk"
|
|
trace_apk="android-plugin/app/build/outputs/apk/trace/release/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk"
|
|
test -f "${plugin_apk}"
|
|
test -f "${trace_apk}"
|
|
bash .github/scripts/validate-plugin-apks.sh "$AAPT2" "$plugin_apk" "$trace_apk"
|
|
|
|
- name: Verify signed APKs
|
|
run: |
|
|
APKSIGNER="$(find "$ANDROID_HOME/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
|
|
mapfile -t APKS < <(printf '%s\n' \
|
|
"android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-${GITHUB_SHA}.apk" \
|
|
"android-plugin/app/build/outputs/apk/trace/release/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk")
|
|
for APK in "${APKS[@]}"; do
|
|
if [[ ! -f "$APK" ]]; then
|
|
echo "::error::Expected release APK was not produced: $APK"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
for APK in "${APKS[@]}"; do
|
|
if [[ "$APK" == *-unsigned.apk ]]; then
|
|
echo "::error::Unsigned release APK produced: $APK"
|
|
exit 1
|
|
fi
|
|
"$APKSIGNER" verify --verbose "$APK"
|
|
done
|
|
|
|
- name: Upload plugin APK
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-plugin-${{ env.date_today }}-${{ github.sha }}
|
|
path: android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-${{ github.sha }}.apk
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
- name: Upload retrace APK
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-retrace-apk-${{ env.date_today }}-${{ github.sha }}
|
|
path: android-plugin/app/build/outputs/apk/trace/release/MobileGL-plugin-trace-release-${{ github.sha }}.apk
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
trace-cases:
|
|
name: trace case matrix
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
outputs:
|
|
android: ${{ steps.trace-cases.outputs.android }}
|
|
names: ${{ steps.trace-cases.outputs.names }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Load trace cases
|
|
id: trace-cases
|
|
run: |
|
|
echo "android=$(python3 tools/trace_replay/trace_cases.py --ci --format github-apk-matrix)" >> "$GITHUB_OUTPUT"
|
|
echo "names=$(python3 tools/trace_replay/trace_cases.py --ci --format names)" >> "$GITHUB_OUTPUT"
|
|
|
|
trace-fixtures:
|
|
name: trace fixture (${{ matrix.case }})
|
|
runs-on: ubuntu-latest
|
|
needs: trace-cases
|
|
strategy:
|
|
fail-fast: false
|
|
max-parallel: 4
|
|
matrix:
|
|
case: ${{ fromJSON(needs.trace-cases.outputs.names) }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Derive trace fixture cache key
|
|
id: fixture-key
|
|
run: bash .github/scripts/trace-fixture-cache.sh key '${{ matrix.case }}'
|
|
|
|
- name: Restore trace fixture cache
|
|
id: fixture-cache
|
|
if: steps.fixture-key.outputs.cacheable == 'true'
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ${{ steps.fixture-key.outputs.paths }}
|
|
key: ${{ steps.fixture-key.outputs.key }}
|
|
|
|
- name: Verify restored trace fixture
|
|
id: fixture-verify
|
|
if: steps.fixture-cache.outputs.cache-hit == 'true'
|
|
run: |
|
|
if bash .github/scripts/trace-fixture-cache.sh verify '${{ matrix.case }}'; then
|
|
echo "ok=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "ok=false" >> "$GITHUB_OUTPUT"
|
|
echo "::warning::Cached fixture for ${{ matrix.case }} failed verification; falling back to the download path"
|
|
bash .github/scripts/trace-fixture-cache.sh reset '${{ matrix.case }}'
|
|
fi
|
|
|
|
- name: Fetch trace fixture
|
|
if: steps.fixture-verify.outputs.ok != 'true'
|
|
run: bash .github/scripts/fetch-trace-fixture-lfs.sh '${{ matrix.case }}'
|
|
|
|
- name: Save trace fixture cache
|
|
if: steps.fixture-key.outputs.cacheable == 'true' && steps.fixture-cache.outputs.cache-hit != 'true'
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: ${{ steps.fixture-key.outputs.paths }}
|
|
key: ${{ steps.fixture-key.outputs.key }}
|
|
|
|
- name: Stage trace fixture
|
|
run: |
|
|
safe_case="$(printf '%s' '${{ matrix.case }}' | sed 's/[^A-Za-z0-9._-]/_/g')"
|
|
stage_dir="trace-fixtures/${safe_case}"
|
|
mkdir -p "${stage_dir}"
|
|
python3 tools/trace_replay/trace_cases.py --format fixture-files --case '${{ matrix.case }}' |
|
|
while IFS= read -r file; do
|
|
cp "${file}" "${stage_dir}/"
|
|
done
|
|
|
|
- name: Upload trace fixture
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-trace-fixture-${{ matrix.case }}
|
|
path: trace-fixtures/**
|
|
if-no-files-found: error
|
|
|
|
android-avd:
|
|
name: android avd image
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
AVD_NAME: mobilegl-ci
|
|
ANDROID_AVD_HOME: ${{ github.workspace }}/.android/avd
|
|
ANDROID_HOME: ${{ github.workspace }}/.android/sdk
|
|
ANDROID_SDK_ROOT: ${{ github.workspace }}/.android/sdk
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Restore Android AVD cache
|
|
id: android-avd-cache
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
${{ env.ANDROID_AVD_HOME }}
|
|
${{ env.ANDROID_SDK_ROOT }}/emulator
|
|
${{ env.ANDROID_SDK_ROOT }}/platform-tools
|
|
${{ env.ANDROID_SDK_ROOT }}/platforms/android-35
|
|
${{ env.ANDROID_SDK_ROOT }}/system-images/android-35/google_apis/x86_64
|
|
key: ${{ runner.os }}-mobilegl-avd-api35-google_apis-x86_64-pixel_6-v2-${{ hashFiles('android-plugin/run-avd-ci.sh') }}
|
|
|
|
- name: Create AVD
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh create \
|
|
--api-level 35 \
|
|
--target google_apis \
|
|
--arch x86_64 \
|
|
--profile pixel_6 \
|
|
--avd-name "${AVD_NAME}"
|
|
|
|
retrace:
|
|
name: retrace (${{ matrix.backend.name }}, ${{ matrix.case.name }})
|
|
runs-on: ubuntu-latest
|
|
needs:
|
|
- build
|
|
- android-avd
|
|
- trace-cases
|
|
- trace-fixtures
|
|
if: ${{ always() && needs.build.result == 'success' && needs.android-avd.result == 'success' && needs.trace-cases.result == 'success' }}
|
|
timeout-minutes: 75
|
|
env:
|
|
AVD_NAME: mobilegl-ci
|
|
ANDROID_AVD_HOME: ${{ github.workspace }}/.android/avd
|
|
ANDROID_HOME: ${{ github.workspace }}/.android/sdk
|
|
ANDROID_SDK_ROOT: ${{ github.workspace }}/.android/sdk
|
|
strategy:
|
|
fail-fast: false
|
|
max-parallel: 4
|
|
matrix: ${{ fromJSON(needs.trace-cases.outputs.android) }}
|
|
steps:
|
|
- name: Set Swap Space
|
|
uses: pierotofy/set-swap-space@v1.0
|
|
with:
|
|
swap-size-gb: 8
|
|
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download trace fixture
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: MobileGL-trace-fixture-${{ matrix.case.name }}
|
|
path: trace-fixture-download
|
|
|
|
- name: Install trace fixture
|
|
run: |
|
|
mkdir -p tools/trace_replay/fixtures
|
|
find trace-fixture-download -type f -exec cp {} tools/trace_replay/fixtures/ \;
|
|
|
|
- name: Set artifact metadata
|
|
run: |
|
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
|
echo "EMULATOR_LOG=${RUNNER_TEMP}/mobilegl-emulator.log" >> "$GITHUB_ENV"
|
|
echo "EMULATOR_PID_FILE=${RUNNER_TEMP}/mobilegl-emulator.pid" >> "$GITHUB_ENV"
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Restore Android AVD cache
|
|
id: android-avd-cache
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: |
|
|
${{ env.ANDROID_AVD_HOME }}
|
|
${{ env.ANDROID_SDK_ROOT }}/emulator
|
|
${{ env.ANDROID_SDK_ROOT }}/platform-tools
|
|
${{ env.ANDROID_SDK_ROOT }}/platforms/android-35
|
|
${{ env.ANDROID_SDK_ROOT }}/system-images/android-35/google_apis/x86_64
|
|
key: ${{ runner.os }}-mobilegl-avd-api35-google_apis-x86_64-pixel_6-v2-${{ hashFiles('android-plugin/run-avd-ci.sh') }}
|
|
|
|
- name: Download retrace APK
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: MobileGL-plugin-trace-release-${{ github.sha }}.apk
|
|
path: android-retrace-apks
|
|
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules
|
|
sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Create AVD
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh create \
|
|
--api-level 35 \
|
|
--target google_apis \
|
|
--arch x86_64 \
|
|
--profile pixel_6 \
|
|
--avd-name "${AVD_NAME}"
|
|
|
|
- name: Launch Emulator
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh start \
|
|
--avd-name "${AVD_NAME}" \
|
|
--gpu "${{ matrix.backend.gpu }}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}" \
|
|
--boot-timeout 300
|
|
|
|
- name: Retrace and validate
|
|
env:
|
|
MOBILEGL_USE_ANGLE: ${{ matrix.backend.name == 'DirectGLES' && '1' || '0' }}
|
|
MOBILEGL_TRACE_ANGLE_VARIANT: ${{ matrix.case.name == 'minecraft-1.21.4-fabric-iris-bliss-in-world' && '90a62123d794' || 'ec889e6ea831' }}
|
|
MOBILEGL_MAGMA_R11G11B10F_FALLBACK: ${{ matrix.backend.name == 'DirectVulkan' && '1' || '0' }}
|
|
run: |
|
|
apk_file="android-retrace-apks/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk"
|
|
test -f "${apk_file}"
|
|
extra_retrace_args=()
|
|
# Bliss needs the newer signed ANGLE variant plus sampler mipmap
|
|
# min-filter downgrading on ANGLE llvmpipe.
|
|
if [ "${{ matrix.backend.name }}" = "DirectGLES" ] && [ "${{ matrix.case.name }}" = "minecraft-1.21.4-fabric-iris-bliss-in-world" ]; then
|
|
extra_retrace_args+=(--avoid-angle-llvmpipe-sampler-mipmap-min-filter)
|
|
fi
|
|
if [ "${{ matrix.backend.name }}" = "DirectGLES" ] && [ "${{ matrix.case.avoid_angle_llvmpipe_explicit_lod_bias || false }}" = "true" ]; then
|
|
extra_retrace_args+=(--avoid-angle-llvmpipe-explicit-lod-bias)
|
|
fi
|
|
if [ "${{ matrix.case.coherent_as_flush || false }}" = "true" ]; then
|
|
extra_retrace_args+=(--coherent-as-flush)
|
|
fi
|
|
|
|
run_retrace() {
|
|
timeout "$(( ${{ matrix.case.timeout_seconds }} + 300 ))" sh android-plugin/trace-replay-ci.sh \
|
|
--apk-file "${apk_file}" \
|
|
--package top.mobilegl.plugin.trace \
|
|
--backend "${{ matrix.backend.name }}" \
|
|
--result-root android-retrace-result \
|
|
--fixture-root android-retrace-fixture \
|
|
--case "${{ matrix.case.name }}" \
|
|
--trace-archive "${{ matrix.case.trace_archive }}" \
|
|
--trace-file "${{ matrix.case.trace_file }}" \
|
|
--golden "${{ matrix.case.golden }}" \
|
|
--alternate-golden "${{ matrix.case.alternate_golden || '' }}" \
|
|
--target-call "${{ matrix.case.target_call }}" \
|
|
--width "${{ matrix.case.width }}" \
|
|
--height "${{ matrix.case.height }}" \
|
|
--ssim-threshold "${{ matrix.case.ssim_threshold || '0.99' }}" \
|
|
--crop-x "${{ matrix.case.crop_x }}" \
|
|
--crop-y "${{ matrix.case.crop_y }}" \
|
|
--crop-width "${{ matrix.case.crop_width }}" \
|
|
--crop-height "${{ matrix.case.crop_height }}" \
|
|
--timeout-seconds "${{ matrix.case.timeout_seconds }}" \
|
|
"${extra_retrace_args[@]}"
|
|
}
|
|
|
|
retrace_status=0
|
|
run_retrace || retrace_status=$?
|
|
if [ "${retrace_status}" -eq 75 ]; then
|
|
echo "::warning::Android emulator infrastructure failed; restarting it and retrying this retrace once."
|
|
# Surface-lost is retried rather than failed, so it would otherwise
|
|
# be invisible. Report it per job - a healthy run prints nothing and
|
|
# a rate spike shows up as a row per affected case.
|
|
reason_file="android-retrace-result/infrastructure-failure-reason.txt"
|
|
surface_lost_retries=0
|
|
if [ -f "${reason_file}" ]; then
|
|
surface_lost_retries="$(grep -c 'angle-surface-lost' "${reason_file}" || true)"
|
|
fi
|
|
if [ "${surface_lost_retries}" -gt 0 ]; then
|
|
echo "surface-lost retries: ${surface_lost_retries} (${{ matrix.backend.name }}, ${{ matrix.case.name }})" \
|
|
>> "${GITHUB_STEP_SUMMARY}"
|
|
fi
|
|
# The restart truncates EMULATOR_LOG, and the attempt that lost the
|
|
# emulator is the one worth reading - the retry usually only shows
|
|
# the wreckage. Keep the first attempt's log before it is clobbered.
|
|
if [ -f "${EMULATOR_LOG}" ]; then
|
|
cp "${EMULATOR_LOG}" "${EMULATOR_LOG}.first-attempt" || true
|
|
fi
|
|
sh android-plugin/run-avd-ci.sh stop \
|
|
--avd-name "${AVD_NAME}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}"
|
|
adb kill-server || true
|
|
sleep 2
|
|
sh android-plugin/run-avd-ci.sh start \
|
|
--avd-name "${AVD_NAME}" \
|
|
--gpu "${{ matrix.backend.gpu }}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}" \
|
|
--boot-timeout 300
|
|
run_retrace
|
|
elif [ "${retrace_status}" -ne 0 ]; then
|
|
exit "${retrace_status}"
|
|
fi
|
|
|
|
- name: Collect retrace summary inputs
|
|
if: always()
|
|
run: |
|
|
safe_case="$(printf '%s' '${{ matrix.case.name }}' | sed 's/[^A-Za-z0-9._-]/_/g')"
|
|
result_dir="android-retrace-result/${safe_case}-${{ matrix.backend.name }}"
|
|
mkdir -p "${result_dir}"
|
|
if [ -s "${{ matrix.case.golden }}" ]; then
|
|
cp "${{ matrix.case.golden }}" "${result_dir}/${safe_case}-${{ matrix.backend.name }}-golden.png"
|
|
fi
|
|
if [ -n "${{ matrix.case.alternate_golden || '' }}" ] && [ -s "${{ matrix.case.alternate_golden || '' }}" ]; then
|
|
cp "${{ matrix.case.alternate_golden || '' }}" "${result_dir}/${safe_case}-${{ matrix.backend.name }}-alternate-golden.png"
|
|
fi
|
|
|
|
- name: Collect emulator diagnostics
|
|
if: always()
|
|
run: |
|
|
mkdir -p android-retrace-result/diagnostics
|
|
adb devices -l > android-retrace-result/diagnostics/adb-devices.txt || true
|
|
timeout 30 adb logcat -d -t 1000 > android-retrace-result/diagnostics/logcat.txt || true
|
|
if [ -f "${EMULATOR_LOG}" ]; then
|
|
cp "${EMULATOR_LOG}" android-retrace-result/diagnostics/emulator.log
|
|
fi
|
|
if [ -f "${EMULATOR_LOG}.first-attempt" ]; then
|
|
cp "${EMULATOR_LOG}.first-attempt" android-retrace-result/diagnostics/emulator-first-attempt.log
|
|
fi
|
|
# A vanished emulator looks identical whether the host OOM killer took
|
|
# qemu or the renderer faulted. These two say which.
|
|
free -h > android-retrace-result/diagnostics/host-memory.txt 2>&1 || true
|
|
sudo dmesg -T 2>/dev/null | tail -300 > android-retrace-result/diagnostics/host-dmesg.txt || true
|
|
|
|
- name: Stop Emulator
|
|
if: always()
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh stop \
|
|
--avd-name "${AVD_NAME}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}"
|
|
|
|
- name: Upload Android retrace result
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-android-retrace-result-${{ env.date_today }}-${{ github.sha }}-${{ matrix.backend.name }}-${{ matrix.case.name }}
|
|
path: android-retrace-result/**
|
|
if-no-files-found: warn
|
|
|
|
retrace-summary:
|
|
name: retrace summary
|
|
runs-on: ubuntu-latest
|
|
needs: retrace
|
|
if: always()
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Set artifact metadata
|
|
run: |
|
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Download Android retrace results
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: MobileGL-android-retrace-result-*
|
|
path: retrace-artifacts
|
|
|
|
- name: Render retrace summary
|
|
run: |
|
|
node tools/trace_replay/render_retrace_summary.mjs \
|
|
--input retrace-artifacts \
|
|
--output-dir android-retrace-summary \
|
|
--title "MobileGL Android retrace overview" \
|
|
--group-label "Android Emulator" \
|
|
--html mobilegl-android-retrace-overview.html
|
|
|
|
- name: Upload Android retrace summary
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
path: android-retrace-summary/mobilegl-android-retrace-overview.html
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
remove-artifact-clutter:
|
|
name: remove artifact clutter
|
|
runs-on: ubuntu-latest
|
|
needs: retrace-summary
|
|
if: always()
|
|
permissions:
|
|
actions: write
|
|
steps:
|
|
- name: Delete intermediate Android retrace artifacts
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
declare -A failed_cases=()
|
|
while IFS= read -r job_name; do
|
|
case_name="${job_name#retrace (*, }"
|
|
case_name="${case_name%)}"
|
|
failed_cases["${case_name}"]=1
|
|
done < <(
|
|
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100" \
|
|
--jq '.jobs[] | select(.name | startswith("retrace (")) | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out" or .conclusion == "action_required") | .name'
|
|
)
|
|
|
|
if ((${#failed_cases[@]})); then
|
|
echo "Retaining fixtures and results for failed retrace case(s):"
|
|
printf ' %s\n' "${!failed_cases[@]}"
|
|
else
|
|
echo "All retrace jobs succeeded; nothing needs to be retained."
|
|
fi
|
|
|
|
deleted=0
|
|
retained=0
|
|
while IFS=$'\t' read -r artifact_id artifact_name; do
|
|
keep=0
|
|
if [[ "${artifact_name}" == MobileGL-trace-fixture-* ]]; then
|
|
case_name="${artifact_name#MobileGL-trace-fixture-}"
|
|
if [[ -v "failed_cases[${case_name}]" ]]; then
|
|
keep=1
|
|
fi
|
|
elif [[ "${artifact_name}" == MobileGL-android-retrace-result-* ]]; then
|
|
# The result artifact carries mobilegl.log, retrace.log, logcat,
|
|
# the emulator log and the actual/diff images - the only record of
|
|
# why a retrace failed. Its name ends in -<backend>-<case>, so a
|
|
# suffix match on the case name keeps both backends' results for a
|
|
# case that failed on either of them, which is what a comparison
|
|
# needs. The match is anchored at the end, so a case name that is a
|
|
# prefix of a longer one does not retain the longer one's results.
|
|
for case_name in "${!failed_cases[@]}"; do
|
|
if [[ "${artifact_name}" == *-"${case_name}" ]]; then
|
|
keep=1
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if ((keep)); then
|
|
echo "Retaining ${artifact_name} (${artifact_id}) for failed retrace."
|
|
((retained += 1))
|
|
continue
|
|
fi
|
|
|
|
echo "Deleting ${artifact_name} (${artifact_id})"
|
|
gh api --method DELETE "repos/${GITHUB_REPOSITORY}/actions/artifacts/${artifact_id}"
|
|
((deleted += 1))
|
|
done < <(
|
|
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \
|
|
--jq '.artifacts[] | select(.name | startswith("MobileGL-trace-fixture-") or startswith("MobileGL-android-retrace-result-") or startswith("trace-fixture-") or startswith("retrace-result-")) | [.id, .name] | @tsv'
|
|
)
|
|
|
|
echo "Deleted ${deleted} intermediate Android artifact(s); retained ${retained} failed-retrace fixture(s)."
|