diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bcbe3ac..1a92616 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: @@ -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: @@ -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 @@ -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: | @@ -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 diff --git a/.gitignore b/.gitignore index 79c2316..9e7e9e9 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ __pycache__/ # os junk .DS_Store Thumbs.db +build_full_*.log diff --git a/MAINTENANCE.md b/MAINTENANCE.md new file mode 100644 index 0000000..29202e6 --- /dev/null +++ b/MAINTENANCE.md @@ -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= ./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`. diff --git a/README.md b/README.md index e1a68dc..025a2be 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/patches/apply_balance_variant.py b/patches/apply_balance_variant.py index af9a81e..8ed1f01 100644 --- a/patches/apply_balance_variant.py +++ b/patches/apply_balance_variant.py @@ -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;" diff --git a/patches/apply_perf_variant.py b/patches/apply_perf_variant.py index bd3674e..2c02ba3 100755 --- a/patches/apply_perf_variant.py +++ b/patches/apply_perf_variant.py @@ -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;" diff --git a/patches/fix_gralloc_flushall.py b/patches/fix_gralloc_flushall.py index 84e007a..fb6b03d 100644 --- a/patches/fix_gralloc_flushall.py +++ b/patches/fix_gralloc_flushall.py @@ -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: