Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions .github/workflows/build-linux-turnip.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Build WN Linux Turnip Drivers

on:
workflow_call:
inputs:
source_ref:
type: string
default: feature/linux-drivers
publish:
type: boolean
default: false
schedule:
- cron: '0 12 * * 3'
push:
branches: [feature/linux-drivers]
paths: ['linux/**', 'patches/**', 'build_linux_turnip.sh', '.github/workflows/build-linux-turnip.yml']
pull_request:
paths: ['linux/**', 'patches/**', 'build_linux_turnip.sh', '.github/workflows/build-linux-turnip.yml']
workflow_dispatch:
inputs:
publish:
description: Publish the Linux driver release
type: boolean
default: false

concurrency:
group: linux-turnip-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'release' }}
cancel-in-progress: false

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-24.04-arm
timeout-minutes: 45
outputs:
version: ${{ steps.version.outputs.version }}
commit: ${{ steps.mesa.outputs.commit }}
mesa_version: ${{ steps.mesa.outputs.version }}
recipe_commit: ${{ steps.version.outputs.recipe_commit }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.source_ref || github.sha }}
persist-credentials: false
- name: Version
id: version
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
python3 -m unittest discover -s linux -p 'test_*.py'
gh api --paginate --slurp "repos/${GITHUB_REPOSITORY}/releases?per_page=100" > releases.json
echo "version=$(python3 linux/release_version.py < releases.json)" >> "$GITHUB_OUTPUT"
echo "recipe_commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends build-essential git python3-pip ninja-build pkg-config \
flex bison zip libdrm-dev libwayland-dev wayland-protocols libx11-xcb-dev libxcb-dri3-dev \
libxcb-present-dev libxcb-randr0-dev libxcb-sync-dev libxcb-xfixes0-dev libxshmfence-dev \
libxrandr-dev libxcb-keysyms1-dev libxcb-shm0-dev libzstd-dev zlib1g-dev libexpat1-dev glslang-tools
python3 -m pip install --break-system-packages 'meson>=1.5' mako pyyaml packaging
- name: Build balanced and performance
env:
BUILD_VERSION: ${{ steps.version.outputs.version }}
BUILD_JOBS: '4'
run: |
set -euo pipefail
./build_linux_turnip.sh 2>&1 | tee linux-build.log
- name: Mesa provenance
id: mesa
run: |
echo "commit=$(cat linux_work/mesa_commit.txt)" >> "$GITHUB_OUTPUT"
echo "version=$(cat linux_work/mesa_version.txt)" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: WN-Linux-Turnip-${{ steps.version.outputs.version }}-b_Axxx
path: WN-Linux-Turnip-${{ steps.version.outputs.version }}-b_Axxx.zip
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: WN-Linux-Turnip-${{ steps.version.outputs.version }}-p_Axxx
path: WN-Linux-Turnip-${{ steps.version.outputs.version }}-p_Axxx.zip
if-no-files-found: error
- uses: actions/upload-artifact@v4
if: always()
with:
name: linux-build-provenance-${{ steps.version.outputs.version }}
path: |
linux-build.log
linux_work/patch-*.log
linux_work/mesa*.txt
linux_work/mesa-*.patch
linux_work/package-*/meta.json
if-no-files-found: warn

release:
needs: build
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: WN-Linux-Turnip-${{ needs.build.outputs.version }}-*_Axxx
merge-multiple: true
- name: Release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.build.outputs.version }}
MESA_COMMIT: ${{ needs.build.outputs.commit }}
MESA_VERSION: ${{ needs.build.outputs.mesa_version }}
RECIPE_COMMIT: ${{ needs.build.outputs.recipe_commit }}
PUBLISH: ${{ github.event_name == 'schedule' || inputs.publish }}
run: |
set -euo pipefail
cat > notes.md <<EOF
Linux ARM64/glibc Turnip for WinNative GameScope/Wayland. Install from Settings → Drivers → Linux.

Mesa ${MESA_VERSION}: https://gitlab.freedesktop.org/mesa/mesa/-/commit/${MESA_COMMIT}
Recipe: https://github.com/${GITHUB_REPOSITORY}/commit/${RECIPE_COMMIT}

Both variants use the same Mesa commit. Balanced (-b) adjusts the GMEM autotuner; performance (-p) adds KGSL PWR_MAX requests, refreshed every 1000 submissions.

These packages use glibc and Wayland/X11 WSI. Android Adrenotools drivers remain separate.
EOF
options=(--draft)
if [[ "$PUBLISH" == true ]]; then options=(--latest=false); fi
gh api --paginate --slurp "repos/${GITHUB_REPOSITORY}/releases?per_page=100" > releases.json
existing=$(jq -r --arg tag "linux-v${VERSION}" '[.[][] | select(.tag_name == $tag)] | if length == 0 then "absent" elif .[0].draft then "draft" else "published" end' releases.json)
if [[ "$existing" == published ]]; then
echo "Refusing to replace published linux-v${VERSION}" >&2
exit 1
fi
if [[ "$existing" == draft ]]; then
gh release delete "linux-v${VERSION}" --repo "$GITHUB_REPOSITORY" --yes
fi
gh release create "linux-v${VERSION}" WN-Linux-Turnip-*.zip --repo "$GITHUB_REPOSITORY" \
--target "$RECIPE_COMMIT" --title "WN Linux Turnip ${VERSION}" --notes-file notes.md "${options[@]}"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ build_full_*.log
# mesa reference checkout
mesa_ref/
mesa_version.txt

linux_work/
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,7 @@ implemented and device-confirmed in [`Leb-Sun/Drivers`](https://github.com/Leb-S
## License

MIT — see `LICENSE`.

## Linux GameScope/Wayland drivers

The `feature/linux-drivers` branch adds glibc ARM64 Turnip packages alongside the existing Android builds. See [linux/README.md](linux/README.md) for the build, package format, validation and scheduling requirements. Android build scripts and releases keep their existing behavior.
86 changes: 86 additions & 0 deletions build_linux_turnip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -euo pipefail
here=$(cd "$(dirname "$0")" && pwd)
cd "$here"
version=${BUILD_VERSION:-0.1.0}
[[ "$version" =~ ^[a-zA-Z0-9._-]+$ ]]
work="$here/linux_work"
mkdir -p "$work"
if [[ -z "${MESA_LOCAL_SRC:-}" ]]; then
rm -rf "$work/upstream"
git clone --depth=1 --branch main https://gitlab.freedesktop.org/mesa/mesa.git "$work/upstream"
MESA_LOCAL_SRC="$work/upstream"
fi
source_dir=$(cd "$MESA_LOCAL_SRC" && pwd)
commit=$(git -C "$source_dir" rev-parse HEAD)
mesa_version=$(tr -d '[:space:]' < "$source_dir/VERSION")
printf '%s\n' "$commit" > "$work/mesa_commit.txt"
printf '%s\n' "$mesa_version" > "$work/mesa_version.txt"
read -ra variants <<< "${BUILD_VARIANTS:-b p}"
for variant in "${variants[@]}"; do
[[ "$variant" == b || "$variant" == p ]]
src="$work/mesa-$variant"
rm -rf "$src" "$work/build-$variant" "$work/package-$variant"
git clone --no-hardlinks --shared "$source_dir" "$src"
git -C "$src" checkout --detach "$commit"
(
cd "$src"
for script in fix_a8xx_dev_info apply_a8xx_gpus apply_a7xx_gen1_quirks apply_a7xx_gen2_ubwc_hint; do
python3 "$here/patches/$script.py"
done
if [[ "$variant" == b ]]; then
python3 "$here/patches/apply_balance_variant.py"
else
BUILD_VARIANT=p python3 "$here/patches/apply_perf_variant.py"
fi
python3 "$here/linux/patch_mesa.py"
for patch in "$here"/linux/patches/*.patch; do
git apply "$patch"
echo "Applied $(basename "$patch")"
done
) 2>&1 | tee "$work/patch-$variant.log"
python3 "$here/linux/verify_patches.py" "$src" "$variant" "$work/patch-$variant.log"
git -C "$src" diff --binary > "$work/mesa-$variant.patch"
options=()
if [[ -n "${LINUX_SYSROOT:-}" ]]; then
rootfs=$(cd "$LINUX_SYSROOT" && pwd)
cat > "$work/cross.ini" <<EOF
[binaries]
c = 'aarch64-linux-gnu-gcc'
cpp = 'aarch64-linux-gnu-g++'
ar = 'aarch64-linux-gnu-ar'
strip = 'aarch64-linux-gnu-strip'
pkg-config = 'pkg-config'
wayland-scanner = '/usr/bin/wayland-scanner'
[properties]
sys_root = '$rootfs'
pkg_config_libdir = '$rootfs/usr/lib/pkgconfig:$rootfs/usr/share/pkgconfig'
[built-in options]
c_args = ['--sysroot=$rootfs']
cpp_args = ['--sysroot=$rootfs']
c_link_args = ['--sysroot=$rootfs', '-L$rootfs/usr/lib']
cpp_link_args = ['--sysroot=$rootfs', '-L$rootfs/usr/lib']
[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'
EOF
options+=(--cross-file "$work/cross.ini")
else
[[ $(uname -m) == aarch64 ]]
fi
meson setup "$work/build-$variant" "$src" "${options[@]}" --buildtype release \
-Dvulkan-drivers=freedreno -Dfreedreno-kmds=msm,kgsl -Dgallium-drivers= -Dplatforms=wayland,x11 \
-Dopengl=false -Dgbm=disabled -Dglx=disabled -Degl=disabled -Dllvm=disabled -Dvulkan-layers= -Dtools= -Dvideo-codecs= \
-Dxmlconfig=disabled
ninja -j "${BUILD_JOBS:-4}" -C "$work/build-$variant" src/freedreno/vulkan/libvulkan_freedreno.so
package="$work/package-$variant"
mkdir -p "$package"
strip_command=strip
[[ -z "${LINUX_SYSROOT:-}" ]] || strip_command=aarch64-linux-gnu-strip
"$strip_command" -o "$package/libvulkan_freedreno.so" "$work/build-$variant/src/freedreno/vulkan/libvulkan_freedreno.so"
python3 "$here/linux/package.py" "$package" "$version" "$variant" "$commit" "$mesa_version"
(cd "$package" && zip -9 "$here/WN-Linux-Turnip-$version-${variant}_Axxx.zip" meta.json libvulkan_freedreno.so)
done
python3 "$here/linux/verify_packages.py" "${variants[@]/#/$here/WN-Linux-Turnip-$version-}"
55 changes: 55 additions & 0 deletions linux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Linux ARM64 Turnip

Builds Mesa main for WinNative's glibc Linux runtime, with KGSL and Wayland/X11 presentation. This is separate from Android/bionic Adrenotools Turnip. It does not rebuild GameScope, Gallium, Zink or the rootfs's other Mesa components.

```sh
./build_linux_turnip.sh
```

On an ARM64 Linux host, install the dependencies listed in `.github/workflows/build-linux-turnip.yml`. Meson >=1.5, Python Mako/PyYAML, Ninja, C/C++ compilers, binutils, pkg-config, zip, flex, bison, glslang, and the Wayland/XCB/libdrm development packages are required. Mesa's checked-in wrap downloads verified Wayland protocols if the distro version is too old.

For a cross build using the existing WinNative Linux rootfs:

```sh
LINUX_SYSROOT=/absolute/path/to/rootfs ./build_linux_turnip.sh
```

The cross build additionally needs `aarch64-linux-gnu-gcc/g++`, ARM64 binutils and a host `wayland-scanner`. Rootfs development headers and pkg-config files must be available. `MESA_LOCAL_SRC` optionally supplies a Git checkout; both variants always use its HEAD. `BUILD_VERSION`, `BUILD_VARIANTS="b p"` and `BUILD_JOBS` control version, variants and parallelism. Build output is under `linux_work/`; distributable ZIPs are in the repository root.

Balanced (`-b`) uses the WN GPU fixes and GMEM bandwidth multiplier 10. Performance (`-p`) also requests KGSL PWR_MAX, sets the context/submission power-constraint flags, and refreshes the request every 1000 submissions. The upstream draw-call threshold was restructured; that retired tweak is deliberately skipped. Android gralloc/IMapper/AHB patches are excluded because this build uses glibc and Linux WSI.

`patch_mesa.py` ports the two WinNative `tools/linuxfs/turnip` fixes: KGSL dma-buf feedback device reporting, and disabling unsupported calibrated timestamps/present timing. It fails when its source anchors change. `verify_patches.py` also rejects warnings/missing patch anchors, aside from the retired draw-call threshold. Update and re-review patches when upstream changes; do not suppress failures to produce a release.

`linux/patches/` holds Mesa commits applied with `git apply` after the scripts, in name order. They are needed by DX12 Ultimate games such as FINAL FANTASY VII REBIRTH:

- `0001` implements `VK_EXT_mesh_shader` by running task and mesh shaders as compute into a device ring that a generated vertex shader draws. Render passes with mesh draws use sysmem rendering.
- `0002` supports a required subgroup size of 32 on A8XX's 64-wide waves, so vkd3d-proton accepts `WaveSize(32)` shaders. Each half of a wave acts as one subgroup.
- `0003` sanitizes cube map sampling directions on A8XX. A NaN or zero direction can hang the GPU.
- `0004` invalidates bindless descriptors through A8XX's dedicated registers, because A8XX's `SP_UPDATE_CNTL` has no bindless bits.
- `0005` fetches A8XX command streams through a KGSL virtual BO alias that is unbound before the memory is freed. Freeing command stream memory directly leads to GPU hangs.

A patch that no longer applies fails the build. Rebase it in a Mesa checkout, regenerate it with `git format-patch`, and re-run the `dEQP-VK.mesh_shader.ext.*`, `dEQP-VK.subgroups.*` and `dEQP-VK.texture.*cube*` groups on an Adreno device. Changes to `0005` also need a long game session on A8XX, since the hangs it prevents take minutes to appear.

Displayed names start at **WN Linux Turnip 0.1.0-b** and **WN Linux Turnip 0.1.0-p**, packaged as `WN-Linux-Turnip-0.1.0-b_Axxx.zip` and `WN-Linux-Turnip-0.1.0-p_Axxx.zip`. Linux has its own semantic version series. CI increments the patch component after the highest published stable Linux release; previews and drafts reuse the next unpublished version. A repeated draft build replaces that draft, while published releases cannot be replaced. Local builds default to `0.1.0`; set `BUILD_VERSION` explicitly for another version.

## ZIP contract

Each `WN-Linux-Turnip-<version>-{b,p}_Axxx.zip` contains a flat `meta.json` and `libvulkan_freedreno.so`. Metadata identifies `platform=linux`, `architecture=aarch64`, `libc=glibc`, `variant`, Mesa repository/SHA/version, build-recipe commit and SHA-256 of the library. The app creates its own ICD manifest after installing, so no device-specific absolute paths are distributed.

The `WN-Linux-` prefix reserves a separate catalog namespace. Linux releases use `linux-v...` tags and do not change the repository's latest Android release. Neither the Android mirror nor Android release notes are modified.

The app detects actual library ABI through ELF dependencies and corrects an Android/Linux destination mistake. Packages with contradictory platform metadata or an incorrect digest are rejected. New installs select that Linux driver; Settings can select another or return to bundled Mesa. The old Components/Linux Client driver remains separately available.

## Validation and provenance

The script verifies that all variants use one Mesa SHA, their binaries differ, they are ARM64/glibc libraries with Wayland and X11 entry points, and only `-p` contains the power-constraint code. CI uploads source diffs, patch logs, metadata and build logs along with separate variant artifacts. The app's package installation and UI tests run on AVD; rendering and KGSL power behavior require physical Adreno hardware.

Initial local validation built Mesa `5ff61a7646d29b54c324af0a60aa3bfb5cdd24d1`, version `26.3.0-devel`, against the existing Arch Linux ARM sysroot. Both variants passed package validation, and the Linux loader resolved the performance build's dependencies against that rootfs. Existing bundled Mesa 26.2.2 is preserved as fallback in the app.

## Release workflow

`build-linux-turnip.yml` builds both variants on Ubuntu 24.04 ARM64. Branch pushes build preview artifacts. Manual runs create a draft unless `publish` is true; scheduled runs publish. The cron is the same as the Android workflow: Wednesday 12:00 UTC (`0 12 * * 3`).

GitHub executes scheduled workflows only from the default branch. The feature branch's cron is not active by itself. To activate weekly builds, merge this workflow into the default branch or add a default-branch entrypoint that checks out the Linux branch. Preserve the Android workflow and its existing schedule. After the push registers the workflow, manual dispatch with `--ref feature/linux-drivers` works; this was verified through the GitHub API. Push builds work on this branch immediately.

A ready default-branch entrypoint is provided at `linux/scheduler/schedule-linux-turnip.yml`. Install that file as `.github/workflows/schedule-linux-turnip.yml` on `main` to call the reusable build from `feature/linux-drivers`. Its manual trigger defaults to a draft, while weekly runs publish. The reusable workflow checks out the Linux branch and records its actual recipe commit for release tags, even when the caller runs from main. Do not install both this scheduler and a separately scheduled copy of the full workflow on main.
27 changes: 27 additions & 0 deletions linux/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
import hashlib
import json
from pathlib import Path
import subprocess
import sys

output, version, variant, commit, mesa_version = sys.argv[1:]
path = Path(output)
meta = {
'schemaVersion': 1,
'name': f'WN Linux Turnip {version}-{variant}',
'description': 'Turnip for the WinNative glibc ARM64 GameScope/Wayland runtime',
'author': 'WinNative',
'platform': 'linux',
'architecture': 'aarch64',
'libc': 'glibc',
'driverVersion': f'{version}-{variant}',
'libraryName': 'libvulkan_freedreno.so',
'variant': variant,
'mesaVersion': mesa_version,
'mesaCommit': commit,
'mesaSource': 'https://gitlab.freedesktop.org/mesa/mesa',
'buildCommit': subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip(),
'librarySha256': hashlib.sha256((path / 'libvulkan_freedreno.so').read_bytes()).hexdigest(),
}
(path / 'meta.json').write_text(json.dumps(meta, indent=2) + '\n')
Loading
Loading