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
58 changes: 19 additions & 39 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: Build WN-Turnip Drivers

on:
schedule:
# Weekly on Wednesday 12:00 UTC (first run 2026-07-08).
- cron: '0 12 * * 3'
pull_request:
branches: [main]
push:
Expand All @@ -12,18 +15,10 @@ on:
required: false
default: main

# Releases happen ONLY on manual workflow_dispatch. PRs and main pushes
# build artifacts (preview the next version label) but never tag, never
# release, never advance the version.
#
# - PR: per-PR concurrency group, cancel-in-progress so only the latest
# push to a PR branch is built.
# - Push to main: per-run concurrency (each push runs independently,
# no eviction risk because no shared state is mutated).
# - workflow_dispatch: serialize on `build-release` so two manual
# dispatches can't race on tag creation.
# schedule + workflow_dispatch release (tag + GitHub release, version bumped);
# PR and push only build a preview. Release events serialize on build-release.
concurrency:
group: ${{ github.event_name == 'pull_request' && format('build-pr-{0}', github.event.pull_request.number) || github.event_name == 'workflow_dispatch' && 'build-release' || format('build-push-{0}', github.run_id) }}
group: ${{ github.event_name == 'pull_request' && format('build-pr-{0}', github.event.pull_request.number) || (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && 'build-release' || format('build-push-{0}', github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
Expand All @@ -45,52 +40,37 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || inputs.ref || github.sha }}
persist-credentials: false

- name: Resolve build version from tags
- name: Resolve build version
id: ver
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git fetch --tags --quiet

# Pin the SHA we actually checked out (not github.sha, which
# differs under workflow_dispatch + inputs.ref).
head_sha=$(git rev-parse HEAD)
echo "head_sha=${head_sha}" >> "$GITHUB_OUTPUT"

# Match exactly vMAJOR.MINOR (no patch suffix, no prerelease).
latest=$(git tag --list 'v*' | grep -E '^v[0-9]+\.[0-9]+$' | sort -V | tail -n1 | sed 's/^v//' || true)
# Next version = latest PUBLISHED v1.NN release + 1 (zero-padded, floor 1.03).
latest=$(gh api "repos/${GITHUB_REPOSITORY}/releases?per_page=100" \
--jq '.[] | select(.draft==false and .prerelease==false) | .tag_name' 2>/dev/null \
| grep -E '^v1\.[0-9]{2}$' | sort -V | tail -n1 | sed 's/^v//' || true)
if [ -z "${latest:-}" ]; then
next_base="1.0"
next_base="1.03"
else
major="${latest%.*}"
minor="${latest#*.}"
minor=$((minor + 1))
if [ "$minor" -ge 10 ]; then
major=$((major + 1))
minor=0
fi
next_base="${major}.${minor}"
n=$((10#${latest#1.}))
next_base=$(printf '1.%02d' "$((n + 1))")
fi

case "${{ github.event_name }}" in
pull_request)
# PR preview build — never releases, never tags.
build_ver="${next_base}-PR${{ github.event.pull_request.number }}"
echo "release=false" >> "$GITHUB_OUTPUT"
;;
push)
# Auto build on merge — preview the next version label only.
# Never tags, never releases, never advances the version.
build_ver="${next_base}"
echo "release=false" >> "$GITHUB_OUTPUT"
;;
workflow_dispatch)
# Manual release build — refuse to re-release a commit that
# already carries a vMAJOR.MINOR tag.
existing=$(git tag --points-at "${head_sha}" --list 'v*' | grep -E '^v[0-9]+\.[0-9]+$' | head -n1 || true)
if [ -n "${existing:-}" ]; then
echo "::error::Commit ${head_sha} is already tagged ${existing}; refusing duplicate release."
exit 1
fi
schedule|workflow_dispatch)
if git rev-parse "v${next_base}" >/dev/null 2>&1; then
echo "::error::Tag v${next_base} already exists; refusing to release the same version twice."
exit 1
Expand All @@ -101,7 +81,7 @@ jobs:
;;
esac
echo "build_version=${build_ver}" >> "$GITHUB_OUTPUT"
echo "Resolved build version: ${build_ver}"
echo "Resolved build version: ${build_ver} (event=${{ github.event_name }})"

- name: Install host build dependencies
run: |
Expand Down Expand Up @@ -145,7 +125,7 @@ jobs:

release:
needs: build
if: github.event_name == 'workflow_dispatch' && needs.build.outputs.release == 'true'
if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && needs.build.outputs.release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ __pycache__/
# os junk
.DS_Store
Thumbs.db
build_full_*.log
71 changes: 71 additions & 0 deletions MAINTENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Maintenance — patch review & upstream tracking

This repo always builds from **upstream mesa main**, then applies the idempotent
Python scripts in `patches/`. Because upstream moves, every patch must be able to
tell three states apart and act accordingly:

1. **Already applied** (our change is present) → log "already …", do nothing.
2. **Anchor present** (upstream unchanged) → apply the change.
3. **Anchor absent** (upstream refactored/absorbed it) → log a clear "anchor absent /
upstream absorbed — skipping" line and exit 0. **Never** silently no-op, and
**never** `sys.exit(1)` for an absorbed change (that would break the build).

All `patches/*.py` are idempotent and safe to re-run.

## Patch status — verified against mesa `43094891c9b` (Mesa 26.2.0-devel, 2026-07-01)

| Script | Target / anchor | Status | Notes |
|--------|-----------------|--------|-------|
| `fix_gralloc_flushall.py` | `u_gralloc_fallback.c` gmsm block | **needed** | UBWC detection for newer Qualcomm gralloc. Anchor present. |
| `fix_a8xx_dev_info.py` | `freedreno_dev_info.h` `disable_gmem` prop + `tu_cmd_buffer.cc` no_gmem check | **needed** | Upstream has render-pass-scoped `disable_gmem`, but **no per-GPU** flag. Anchor `bool has_image_processing;` present. |
| `apply_a8xx_gpus.py` | `freedreno_devices.py` A810 / A829 / A825 | **needed** | A810+A829 get `disable_gmem=True` + KGSL chip_ids; **A825 not upstream** (fully injected). |
| `apply_a7xx_gen1_quirks.py` | `a7xx_gen1` GPUProps | **needed** | Forces `has_early_preamble/has_scalar_predicates=False` for A720/725/730. |
| `apply_a7xx_gen2_ubwc_hint.py` | X1-85 / FD740 add_gpus block | **needed** | Adds `enable_tp_ubwc_flag_hint`. That block still lacks it upstream. |
| `disable_64b_image_atomics.py` | `has_64b_image_atomics = True` (×2, gen2+gen3) | **needed (workaround)** | UE5/VKD3D-Proton SM6.6 A8xx GPU-hang workaround. See removal criteria below. |
| `apply_balance_variant.py` (-b) | `tu_autotune.cc` drawcall + bandwidth | **partial** | Only the `*11→*10` bandwidth tweak lands; the `> 5` drawcall anchor was **removed upstream** (now `>= 10`) and is skipped. |
| `apply_perf_variant.py` (-p) | `tu_autotune.cc` + `tu_knl_kgsl.cc` PWR_MAX | **needed** | KGSL PWR_MAX clock-forcing anchors all present. Same autotune drawcall skip as -b. |

### Absorbed / removed by upstream (do NOT re-add)
- **`TU_DEBUG_FLUSHALL` forced for gen8** — upstream removed the forced flush from
`tu_device.cc`. The old `fix_gralloc_flushall.py` half that stripped it is gone.
- **Autotune `drawcall_count > 5` gate** — restructured upstream to `>= 10`. The -b/-p
scripts skip this tweak cleanly; the two variants now differ by **bandwidth + PWR_MAX**,
not the drawcall threshold. (Re-target to the new gate only if a split is desired.)

### Removal criteria to watch on future bumps
- **`disable_64b_image_atomics.py`**: drop once upstream fixes the A8xx 64-bit image
atomic implementation (track follow-ups to `5b87bbfad3b`). Until then, keep — the
feature is still advertised `True` on gen2/gen3.
- **`apply_a8xx_gpus.py` A825 block**: drop the A825 insertion if upstream adds A825
natively (the script already detects `name="Adreno (TM) 825"` / `FD825` and skips).
- **`fix_a8xx_dev_info.py`**: if upstream adds a per-device GMEM-disable mechanism,
migrate A810/A829 to it and retire the custom `disable_gmem` prop.

## Re-verifying on a mesa bump
1. `BUILD_VERSION=<ver> ./build_wn_turnip.sh` (clones latest main, applies patches).
2. Read `build_log_{b,p}.txt`: every script should print an "applied" or an explicit
"already/absent/skipping" line. A bare/missing line or a `WARNING:` means an anchor
drifted — re-diff that script against current upstream before shipping.
3. Update the table above with the new mesa hash.

## Versioning

Releases use the scheme **`v1.NN`** — a two-digit, zero-padded counter after
`1.` (`v1.03`, `v1.04` … `v1.99`). The next version is **the latest published
`v1.NN` release + 1** (draft/prerelease releases are ignored); with no release
yet it floors at `1.03`. So `1.02` released → next `1.03`, `1.09` → `1.10`, etc.

The CI (`.github/workflows/build.yml`):
- **Weekly schedule** (`cron: '0 12 * * 3'`, Wednesdays 12:00 UTC, first run
2026-07-08) builds `-b`/`-p` from latest mesa main and **tags + releases** the
bumped version. Runs every week regardless of whether this repo changed, since
mesa main advances on its own.
- **`workflow_dispatch`** does the same on demand.
- **PR / push** build a preview label only — never tag, never release.

Local builds set the label directly, e.g. `BUILD_VERSION=1.03 ./build_wn_turnip.sh`.

## Repository / contribution flow
This is developed on the fork **`maxjivi05/Drivers`** and contributed upstream to the
main repo **`WinNative-Emu/Drivers`** via pull request. Build/patch changes land on a
branch in the fork, then a PR is opened against `WinNative-Emu/Drivers:main`.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,19 @@ Adrenotools-aware launchers (e.g. WinNative, Winlator) can import each `.zip` di
│ ├── apply_balance_variant.py
│ ├── apply_perf_variant.py
│ └── disable_64b_image_atomics.py
├── MAINTENANCE.md # per-patch upstream status + re-verify checklist
└── LICENSE
```

## Upstream & contributing

Development happens on the fork [`maxjivi05/Drivers`](https://github.com/maxjivi05/Drivers)
and is contributed to the main repo
[`WinNative-Emu/Drivers`](https://github.com/WinNative-Emu/Drivers) via pull request.
Because the build always tracks upstream Mesa main, the `patches/` scripts are written
to adapt as upstream changes — see [`MAINTENANCE.md`](MAINTENANCE.md) for each patch's
current status, what upstream has absorbed, and how to re-verify on a Mesa bump.

## License

MIT — see `LICENSE`.
8 changes: 5 additions & 3 deletions patches/apply_balance_variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
# Raise drawcall threshold from 5 to 7
OLD_DC = "if (cmd_buffer->state.rp.drawcall_count > 5)"
NEW_DC = "if (cmd_buffer->state.rp.drawcall_count > 7)"
if OLD_DC in content:
if NEW_DC in content:
print(f" {AUTOTUNE_FILE}: drawcall threshold already at 7")
elif OLD_DC in content:
content = content.replace(OLD_DC, NEW_DC)
changes = True
print(f" {AUTOTUNE_FILE}: raised drawcall threshold 5 -> 7")
elif "> 7)" in content:
print(f" {AUTOTUNE_FILE}: drawcall threshold already raised")
else:
print(f" {AUTOTUNE_FILE}: '> 5' drawcall anchor absent (upstream restructured) — skipping threshold tweak")

# Reduce GMEM bandwidth multiplier from 11 to 10
OLD_BW = "gmem_bandwidth = (gmem_bandwidth * 11 + total_draw_call_bandwidth) / 10;"
Expand Down
8 changes: 5 additions & 3 deletions patches/apply_perf_variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,14 @@ def ensure_regex(content: str, pattern: str, repl: str, label: str) -> tuple[str

old_dc = "if (cmd_buffer->state.rp.drawcall_count > 5)"
new_dc = "if (cmd_buffer->state.rp.drawcall_count > 10)"
if old_dc in autotune:
if new_dc in autotune:
print(f" {AUTOTUNE_FILE}: drawcall threshold already at 10")
elif old_dc in autotune:
autotune = autotune.replace(old_dc, new_dc)
at_changed = True
print(f" {AUTOTUNE_FILE}: raised drawcall threshold 5 -> 10")
elif "> 10)" in autotune:
print(f" {AUTOTUNE_FILE}: drawcall threshold already raised")
else:
print(f" {AUTOTUNE_FILE}: '> 5' drawcall anchor absent (upstream restructured) — skipping threshold tweak")

old_bw = "gmem_bandwidth = (gmem_bandwidth * 11 + total_draw_call_bandwidth) / 10;"
new_bw = "gmem_bandwidth = (gmem_bandwidth * 10 + total_draw_call_bandwidth) / 10;"
Expand Down
32 changes: 2 additions & 30 deletions patches/fix_gralloc_flushall.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,10 @@
#!/usr/bin/env python3
"""
Idempotent fixes for A840/A8XX critical issues:
1. Remove forced TU_DEBUG_FLUSHALL for gen8 (kills performance)
2. Bypass gmsm magic check in gralloc (fixes horizontal line shredding)

Safe to run multiple times.
Bypass the legacy 'gmsm' gralloc magic check so UBWC buffers are detected on
newer Qualcomm gralloc (A8XX/A840). Idempotent.
"""
import sys

# ── Fix 1: Remove TU_DEBUG_FLUSHALL ──────────────────────────────────────────

TU_DEVICE = "src/freedreno/vulkan/tu_device.cc"

with open(TU_DEVICE, "r") as f:
content = f.read()

if "TU_DEBUG_FLUSHALL" in content:
lines = content.split("\n")
new_lines = []
for line in lines:
if "gen8 TODO" in line and "/*" in line:
continue
if "TU_DEBUG_FLUSHALL" in line:
continue
new_lines.append(line)
content = "\n".join(new_lines)
with open(TU_DEVICE, "w") as f:
f.write(content)
print(f" {TU_DEVICE}: removed TU_DEBUG_FLUSHALL for gen8")
else:
print(f" {TU_DEVICE}: TU_DEBUG_FLUSHALL already removed")

# ── Fix 2: Bypass gmsm gralloc magic check ───────────────────────────────────

GRALLOC = "src/util/u_gralloc/u_gralloc_fallback.c"

with open(GRALLOC, "r") as f:
Expand Down
Loading