Steam Frame beta: README, device findings, release notes and workflow

The README and docs/steam-frame.md now describe the Frame build as a beta
that runs on the headset: what works, the recommended settings (render_scale
1.25 is the panels' native 2160x2160), the known issues (doubled images,
right-eye foveation), building with Docker on another machine (including
registering qemu on hosts whose binfmt_misc is per container), and
installing with Frame Control. A frame-* tag now publishes a source-only
pre-release with the notes in docs/releases/<tag>.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
This commit is contained in:
Claude committed 2026-10-05 05:31:12 +00:00
1 parent 5849356025
commit c13e03e004
4 files changed
+237 -25

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
name: Steam Frame release
# A tag named frame-<something> publishes a GitHub pre-release with the notes in
# docs/releases/<tag>.md. Source only: the game is built from the player's own disc and nothing
# built from it is distributed (README, Requirements). The v* tags stay with package.yml, which
# packages upstream's Windows installer.
on:
push:
tags: ['frame-*']
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Publish the pre-release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
REPOSITORY: ${{ github.repository }}
run: |
notes="docs/releases/${TAG}.md"
if [ ! -f "$notes" ]; then
echo "::error::No release notes at $notes"
exit 1
fi
gh release create "$TAG" --repo "$REPOSITORY" --verify-tag --prerelease \
--title "WiiCompiled VR for the Steam Frame ${TAG#frame-}" --notes-file "$notes"
+77 -23
View File
@@ -4,7 +4,7 @@
<p align="center">
<img alt="Steam Frame, SteamOS ARM64" src="https://img.shields.io/badge/Steam%20Frame-SteamOS%20%C2%B7%20ARM64-1A9FFF?logo=steam&amp;logoColor=white">
<img alt="Status: untested on the headset" src="https://img.shields.io/badge/status-untested%20on%20the%20headset-FF9F0A">
<a href="https://github.com/mitch030504/Wiicompiled_VR_Frame/releases"><img alt="Status: beta" src="https://img.shields.io/badge/status-beta-FF9F0A"></a>
<a href="https://github.com/iChris4/Wiicompiled_VR"><img alt="Fork of WiiCompiled OpenXR VR" src="https://img.shields.io/badge/fork%20of-WiiCompiled%20OpenXR%20VR-8B5CF6"></a>
<a href="LICENSE"><img alt="License: GPLv3" src="https://img.shields.io/badge/license-GPLv3-2EA44F?logo=gnu&amp;logoColor=white"></a>
</p>
@@ -21,9 +21,11 @@ C++ and compiled for the Frame's ARM64 CPU, and it renders through SteamVR's Ope
> against your disc image, and nothing is uploaded.
> [!WARNING]
> **Not yet run on a Steam Frame.** The Frame build compiles and its unit tests pass, but nobody
> has played it on the headset yet. Expect it to fail in ways only the device shows; reports with
> the run log are what moves it forward (see [Reporting problems](#reporting-problems)).
> **Beta.** It runs on a Steam Frame: SteamVR, both eyes at the panels' 2160x2160, the Frame's
> controllers, 120 Hz with every game frame shown, and foveation that follows your eyes. It is not
> finished: images still double in races and on the HUD, and the foveation tracks the right eye
> less well than the left (see [Known issues](#known-issues)). Reports with the run log move it
> forward (see [Reporting problems](#reporting-problems)).
---
@@ -42,11 +44,17 @@ readings it is based on.
button pauses and the left shoulder opens the settings panel.
- **120 Hz.** The headset is asked for 120 Hz, exactly two display refreshes per game frame at the
game's 60 FPS, so motion is even. `[vr] refresh_rate` changes it.
- **Every refresh from the game.** SteamVR halved the app's rate and filled every other refresh
itself. The game now submits its last frame again, at the pose it was rendered for, on each
refresh it has no new frame for (`[vr] repeat_frames`), so SteamVR runs it at the full 120 Hz.
- **Eye-tracked foveation.** Variable-resolution rendering whose sharp centre follows your gaze
through the Frame's eye tracking, instead of staying fixed straight ahead. `[vr] foveation` sets
the strength and `[vr] eye_tracked_foveation` turns the tracking off.
- **Standalone defaults**: 0.8 render scale, the game's own object culling and medium foveation,
as on the Quest, sized for a mobile GPU driving 2160x2160 per eye.
- **Standalone defaults**: the game's own object culling and medium foveation, as on the Quest.
The render scale defaults to 0.8; on the Frame 1.0 is SteamVR's recommended 1728x1728 and **1.25
is the panels' native 2160x2160**, which the Frame renders with time to spare.
- **Crash diagnostics on Linux.** A native crash logs the faulting thread, its pc and a backtrace,
which is how a crash on race restart was traced to the density maps sharing memory Dawn unmaps.
- **Build tooling.** `Launcher/build-dawn-linux.sh` builds the patched Dawn (Aurora's WebGPU
layer) the VR backend needs, and `Launcher/local-build.sh` gained `--openxr`, `--dawn-package`,
`--headset steam_frame` and `--cpu`.
@@ -57,41 +65,85 @@ the memory-sharing extensions the Android backend needs. The native build is the
## Requirements
- A Steam Frame with SteamVR, reachable over SSH (`ssh steamos@<frame-ip>`).
- A Steam Frame with Developer Mode on and SSH set up (Steam Settings → System → Enable Developer
Mode, then set a user password). [Frame Control](https://github.com/saphid/frame-control) makes the
rest easier: it sets up an SSH key and installs the game into your Steam library.
- A clean, unmodified **PAL `RMCP01`** disc image of Mario Kart Wii, dumped by you. ISO, GCM,
GCZ, CISO, WBFS, WIA and RVZ can all be extracted. Other regions and patched executables are
rejected.
- Somewhere to build. Either:
- **an x86_64 Linux PC** with podman and qemu: it builds in an emulated ARM64 container, which is
slow (the first build takes hours) but spares the Frame; or
- Somewhere to build, all of them running an ARM64 Debian container:
- **an x86_64 Linux PC** with podman and qemu (emulated, so slow: the first build takes hours);
- **a stronger Linux machine or server** with Docker, the same way and faster;
- **the Frame itself**, in a podman container there.
- Several GB of free disk space for the toolchain, Dawn and the game.
- About 20 GB of free disk space on the build machine for the toolchain, Dawn and the game.
> [!NOTE]
> Nobody here will tell you where to get the game. Dumping your own disc is on you, and links to
> game files won't be provided or tolerated.
> game files won't be provided or tolerated. For the same reason there is no ready-built game to
> download: releases hold the source, and the game is always built from your own disc.
## Building and installing
The commands are in [`docs/steam-frame.md`](docs/steam-frame.md): [Building it on a Linux
PC](docs/steam-frame.md#building-it-on-a-linux-pc) or [Building it on the
The commands are in [`docs/steam-frame.md`](docs/steam-frame.md): [on a Linux
PC](docs/steam-frame.md#building-it-on-a-linux-pc), [with Docker on another
machine](docs/steam-frame.md#building-it-with-docker-on-another-machine) or [on the
Frame](docs/steam-frame.md#building-it-on-the-frame). In short:
1. Extract your disc with [nodtool](https://github.com/encounter/nod) and copy `sys/main.dol` and
`files/rel/StaticR.rel` into `Assets/`.
`files/rel/StaticR.rel` into `Assets/`. Keep the extracted disc: the game reads it at run time.
2. Start a Debian trixie ARM64 container and install the build packages, the bundled clang 22,
CMake and Ninja (`Launcher/prepare-portable-tools.sh --arch aarch64`), and .NET 8.
3. Build the patched Dawn: `Launcher/build-dawn-linux.sh`.
4. Build the game: `Launcher/local-build.sh ... --openxr --dawn-package <dawn>/package --headset steam_frame`.
5. Copy the output and the extracted disc to the Frame, set `[paths] dvd_root` in
`~/.local/share/WiiCompiled/Config.toml`, start SteamVR, then start `WiiCompiled`.
3. Build the patched Dawn once: `Launcher/build-dawn-linux.sh`. Under emulation give it `--jobs 4`
on a 16 GB machine; more parallel jobs can run it out of memory.
4. Build the game: `Launcher/local-build.sh ... --openxr --dawn-package <dawn>/package --headset
steam_frame`, with `--parallel` sized to the machine's memory (8 for 32 GB).
5. Copy the extracted disc to the Frame, and either send the `out` folder to the Frame with
[Frame Control](docs/steam-frame.md#installing-it-with-frame-control), which adds it to your
Steam library, or copy it over with `scp`.
6. Write `~/.local/share/WiiCompiled/Config.toml` on the Frame with your disc's path, then start
the game from the library in the headset:
```toml
[paths]
dvd_root = "/home/steamos/wiicompiled/disc"
```
### Recommended settings
All of them are in the headset's settings panel (left shoulder button, **VR** tab) as well as in
`Config.toml`:
| Setting | Value | Why |
| --- | --- | --- |
| `[vr] render_scale` | `1.25` | The panels' native 2160x2160 per eye. |
| `[vr] foveation` | `medium` | `off` costs the most GPU time. |
| `[vr] repeat_frames` | `true` (default) | Keeps SteamVR at 120 Hz. |
| `[vr] frame_interpolation_fps` | `0` | Interpolation made things worse on the Frame. |
| `[video] resolution_multiplier` | `2` | The game's own frame; 4x is too heavy for the Frame's GPU. |
Keep SteamVR's own refresh rate at 120 Hz. Motion Smoothing makes no difference here.
## Known issues
- **Doubled images** in races and on the HUD, worst while racing, sometimes in the right eye only.
The suspect is foveation on the Frame's graphics driver. If it bothers you, try foveation **Off**
in the panel (it costs GPU time), and report whether it helped.
- **Foveation follows the right eye less well** than the left, and its sharp area may feel small
on Low and Medium.
- **VR frame interpolation** is not recommended on the Frame.
- **The Quest app's `steamFrame` flavour** (an Android build for the Frame's Lepton layer) cannot
show a picture: Lepton's graphics driver lacks the memory sharing it needs. Use the native build.
## Reporting problems
Open an issue on this repository with the run log from `~/.local/share/WiiCompiled/Logs/` on the
Frame, or the last lines of the failing build step. [Running
it](docs/steam-frame.md#running-it) lists the log lines a working start shows, in order; the
first one missing says where it stopped.
Open an issue on this repository with:
- what you did and what you saw (which eye, where in the picture, racing or menus);
- the run log: each run has a folder under `~/.local/share/WiiCompiled/Logs/` on the Frame, with
`console.log` and, after a crash, `crash_sigsegv.txt`;
- or, for a build problem, the last lines of the failing step.
[Running it](docs/steam-frame.md#running-it) lists the log lines a working start shows, in order;
the first one missing says where it stopped.
Problems that also happen on a PC or a Quest belong upstream, in
[WiiCompiled OpenXR VR](https://github.com/iChris4/Wiicompiled_VR).
@@ -170,6 +222,8 @@ All translated output is verified against real hardware behavior and most import
- **[nod](https://github.com/encounter/nod)** - nodtool, the disc image extractor.
- **[DolphinXR](https://github.com/iChris4/dolphinXR)** - the Steam Frame controller profile's
input paths.
- **[Frame Control](https://github.com/saphid/frame-control)** by saphid - installing the game into
the Frame's Steam library, and its notes on how the Frame's software fits together.
- Everyone in the static recompilation community.
Bundled third-party components and their licenses live in
+43
View File
@@ -0,0 +1,43 @@
The first beta of WiiCompiled VR for the Steam Frame: Mario Kart Wii, statically recompiled to native
ARM64 code, running in VR on SteamOS through SteamVR.
**This release is source only.** The game is always built from your own clean PAL `RMCP01` disc,
and nothing built from it may be distributed, so there is no ready-built game here. Follow
[Building and installing](https://github.com/mitch030504/Wiicompiled_VR_Frame#building-and-installing)
in the README; the full commands are in
[`docs/steam-frame.md`](https://github.com/mitch030504/Wiicompiled_VR_Frame/blob/frame-beta-1/docs/steam-frame.md).
## What works on the Frame
- Starts in VR from the Steam library (installed with Frame Control) with SteamVR's OpenXR runtime.
The runtime creates the GPU device the game renders with, so the eyes go straight into the
headset's swapchain.
- Both eyes at the panels' native 2160x2160 (`render_scale = 1.25`), 120 Hz, every game frame
shown: SteamVR reports 120 Hz with 60 new frames and 60 repeats a second and no late frames.
- The Frame's own controllers: left D-pad as the Wii Remote's D-pad, View to pause, left shoulder
for the settings panel.
- Fragment density map foveation that follows your eyes through the Frame's eye tracking.
## Fixed during device testing
- A crash on race restart inside the Frame's Vulkan driver: density maps shared memory that Dawn
unmaps after buffer uploads. Each map now has its own memory block (Dawn patch, so rebuild Dawn).
- SteamVR halving the game's rate and filling every other refresh itself:
`[vr] repeat_frames` (on by default for the Frame) resubmits the last frame for those refreshes.
- Native crashes on Linux now log the faulting thread, pc and backtrace.
- The eye-tracked foveation region is 8 degrees wider and each eye keeps 128 maps.
## Known issues
- Images double in races and on the HUD, sometimes in the right eye only. Foveation is the main
suspect; try foveation Off if it bothers you, and report the result.
- Foveation follows the right eye less well than the left.
- VR frame interpolation is not recommended on the Frame.
- The Android (`steamFrame`) flavour cannot show a picture in Lepton; use the native build.
## Recommended settings
`render_scale = 1.25`, `foveation = "medium"`, `repeat_frames = true`, `frame_interpolation_fps = 0`
under `[vr]`, and `resolution_multiplier = 2` under `[video]`. SteamVR at 120 Hz.
Reports with the run log from `~/.local/share/WiiCompiled/Logs/` are welcome as issues.
+87 -2
View File
@@ -15,8 +15,14 @@ Most of what this document describes is shared by both: the Frame controller pro
request, eye-tracked foveation and the Frame's defaults. The native build gets them through
`MKW_HEADSET=steam_frame` (`MKW_HEADSET_STEAM_FRAME`), as the Android flavour does.
**Status: not yet run on a Steam Frame.** The native build's VR code compiles and the unit tests
pass; building it on the Frame and the device checks are still to do.
**Status: beta, played on a Steam Frame.** The native build starts in VR under SteamVR, renders
both eyes on the device Dawn shares with the runtime, binds the Frame's controllers, runs at 120 Hz
with every game frame shown (`new=60 repeat=60` a second, no late frames, at `render_scale = 1.25`,
the panels' 2160x2160), and follows the eyes with its foveation. Two device runs found and fixed a
crash on race restart (see [Eye-tracked foveation](#eye-tracked-foveation)) and SteamVR halving the
app's rate (see [Refresh rate](#refresh-rate)). Still open, in [Known issues](#known-issues): doubled
images in races and on the HUD, sometimes in one eye only, and foveation that follows the right eye
less well than the left.
## The native SteamOS build
@@ -122,6 +128,56 @@ scp -r ~/wiicompiled/out steamos@<frame-ip>:wiicompiled/
scp -r ~/wiicompiled/disc-extract steamos@<frame-ip>:wiicompiled/disc
```
### Building it with Docker on another machine
A faster x86_64 machine helps most with the game itself (the translated code is a few thousand large
files). Docker works as podman does, with one difference on hosts whose `binfmt_misc` registrations
are per container (Unraid 7 with kernel 6.18): `tonistiigi/binfmt --install arm64` then succeeds but
only inside its own container, and Debian answers `exec format error`. Register the emulator on the
host instead, with the `P` flag the tonistiigi build of qemu expects (without it every program loses
its first argument: `uname -m` prints `Linux`):
```bash
docker create --name qemu-src tonistiigi/binfmt
docker cp qemu-src:/usr/bin/qemu-aarch64 /usr/local/bin/qemu-aarch64
docker rm qemu-src
echo ':qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-aarch64:POCF' > /proc/sys/fs/binfmt_misc/register
docker run --rm --platform linux/arm64 debian:trixie uname -m # aarch64
```
Unraid keeps `/usr/local/bin` in memory, so this is repeated after a reboot. Copy the work directory
over, without Dawn's build tree (the package is what the game links), and run the build detached so
that a closed SSH session does not stop it:
```bash
rsync -a --exclude dawn/build --exclude 'dawn/dawn-*' --exclude disc-extract \
~/wiicompiled/ root@<server>:/mnt/user/appdata/wiicompiled/ # from the PC
docker run -d --name wiicompiled-frame --platform linux/arm64 \
-v /mnt/user/appdata/wiicompiled:/work debian:trixie \
bash -c 'bash /work/build-game.sh >> /work/game.log 2>&1'
```
`build-game.sh` holds the `apt-get` line of [Building it on the Frame](#building-it-on-the-frame)
and the `local-build.sh` command, with `--parallel 8` for 32 GB of memory (compiles take more memory
under emulation: Dawn's build at 16 jobs froze a 16 GB laptop, and finished at 4). `docker start
wiicompiled-frame` runs it again after a change: the translation is reused and only what changed
is compiled.
### Installing it with Frame Control
[Frame Control](https://github.com/saphid/frame-control) adds a folder to the Frame's Steam library as
a Devkit Game: drop the `out` folder on **Send to Frame** and keep **Launches** on `WiiCompiled`.
It picks `SteamLinuxRuntime_4-arm64` for an ARM64 program, but Steam starts such a title natively,
which this build needs (it uses the system's `libpng16`, `libstdc++` and `libz`). The game then lands
in `~/devkit-game/<name>/` and starts from the library inside the headset, where SteamVR is already
running. An update only replaces the executable; copy it under another name and move it into place,
which also works while an old copy is open:
```bash
scp WiiCompiled frame:devkit-game/WiiCompiled/WiiCompiled.new
ssh frame 'cd ~/devkit-game/WiiCompiled && chmod 755 WiiCompiled.new && mv -f WiiCompiled.new WiiCompiled'
```
### Running it
The game reads its `Config.toml` from `~/.local/share/WiiCompiled/` on SteamOS (it is created on the first start): set
@@ -142,6 +198,35 @@ in order:
`Linux Vulkan OpenXR requires a Dawn built with Aurora's patches` means the build used a stock Dawn:
check that `--dawn-package` pointed at `build-dawn-linux.sh`'s `package` directory.
The game only writes a `Config.toml` when there is none, so a file holding just `[paths]` and
`dvd_root` can be written before the first start. Each run gets its own folder under `Logs/`; a
native crash leaves `crash_sigsegv.txt` there and, in `console.log`, the faulting thread, its pc and
lr and a backtrace as module + offset, which `addr2line -f -C -e native-build/WiiCompiled <offset>`
turns into functions on the build machine (the executable is not stripped).
### Settings that matter on the Frame
| Setting | Recommended | Why |
| --- | --- | --- |
| `[vr] render_scale` | `1.25` | Scales SteamVR's recommended eye size, 1728x1728 on the Frame; 1.25 is the panels' 2160x2160. The eye passes took 9 to 11 ms a game frame there, with medium foveation. |
| `[vr] foveation` | `medium` | `off` shades every pixel and costs the most. See [Known issues](#known-issues) if images double. |
| `[vr] repeat_frames` | `true` (default) | Without it SteamVR halves the app's rate and fills refreshes itself. |
| `[vr] frame_interpolation_fps` | `0` | Rendering in-between frames needs 120 eye pairs a second; at these resolutions it made things worse. |
| `[video] resolution_multiplier` | `2` | The game's own frame, which the eyes are made from, at 2x the Wii's. 4x is far too heavy for the Adreno 750. |
## Known issues
- **Doubled images.** Images double in races and on the HUD, worst while racing, at first in the
right eye only and later in both. Both eyes get the same frames and repeats, so a one-eyed doubling
is not the 60 FPS cadence. The suspect is foveation: Turnip draws a coarse bin at lower resolution
and scales it back up, and each eye's density maps change with its gaze. Comparing foveation off,
on without eye tracking, and on with it is the next test.
- **Foveation and the right eye.** The full-density region follows the left eye better than the
right. Each eye already gets its own gaze direction; convergence on near content (the HUD screen at
2 m, the cockpit) is not yet corrected for.
- **VR frame interpolation** is not recommended on the Frame (see the table above).
- **The Android flavour** cannot show a picture in Lepton (below).
## The Android flavour in Lepton
Everything from here to [Building and installing](#building-and-installing) is the `steamFrame`