Files
Toastconcern--FrameStudio/.github/workflows/build.yml
T
ToastconcernandClaude Opus 4.8 d22cc04cb3 Frame Home as the real SteamVR environment on a PC (Windows too), not a home app
On the Steam Frame a layer inside SteamVR's compositor draws the home where it would draw Aurora, so the home is the
environment itself: at boot, during game loads, behind the Steam menu, with the controllers and passthrough, and it
steps aside for a game the instant it opens. On a PC this was faked with SteamVR's home-app slot and a flat 360 photo
in the gaps, which launched and handed off like an app and showed a picture, not the live home. This does it properly
on any PC that runs SteamVR, the Frame's way:

- Windows: the compositor is Direct3D 11, so the layer is a proxy d3d11.dll beside the compositor (layer/
  frame_home_layer_d3d11.cpp, built from the .def that forwards every other export to the real d3d11.dll, kept as
  fh_d3d11real.dll). It wraps the compositor's device, finds Aurora's draws by their shader bytecode and draws the
  home over the sky, leaving the floor and stars off; everything else passes straight through. Pure pass-through in
  any process that isn't vrcompositor.
- The home's frames cross from Frame Home (Vulkan) to the compositor (D3D11): the layer owns kSlots shared textures
  and hands Frame Home their NT handles over a named pipe; Frame Home imports them (VK_KHR_external_memory_win32) and
  renders into them. No cross-API lock — a slot is published only once the GPU has finished it and the four rotate,
  as on Linux. The shared State stays Frame Home's.
- The Environments panel runs frame-home --system and keeps it alive (one at a time, a pid file across panel
  restarts), the same systemEnv path as the Frame. The installer places/removes the proxy and marks the layer with a
  root/layer folder, in place of the old home-app registration and background pictures.
- Safe like the Vulkan layer: no home, or one stale over half a second, and the compositor draws Aurora as usual;
  the layer turns itself off after two unhealthy compositor starts, and root/layer/off turns it off by hand.

Linux keeps its Vulkan layer (unchanged). Verified without a headset on the null driver: the proxy loads and wraps
the compositor without crashing, recognises Aurora's shaders, and the frame hand-over completes (Frame Home connects,
imports the textures, renders). The compositor only draws the environment while presenting to a headset, so the final
on-headset image is the one step the null driver can't exercise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-21 19:39:36 -05:00

165 lines
7.1 KiB
YAML

# Builds Frame Studio for every computer it runs on, and the Frame Home installer, on every push (any branch or tag),
# every pull request, and by hand (Actions > build > Run workflow). Each build is a downloadable artifact of the run.
# A push to main also replaces the "latest" pre-release with them (one place to download the newest of everything);
# a tag "v*" publishes them as that version's release.
name: build
on:
push:
pull_request:
workflow_dispatch:
# A newer push to the same branch replaces a run still going (its builds would be out of date anyway).
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true
jobs:
linux:
name: Linux ${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- { arch: x64, runner: ubuntu-24.04, extra: "" }
- { arch: x86, runner: ubuntu-24.04, extra: "gcc-multilib g++-multilib" }
- { arch: arm64, runner: ubuntu-24.04-arm, extra: "" }
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Tools and X11 headers
run: |
sudo apt-get update
sudo apt-get install -y ninja-build cmake libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxkbcommon-dev ${{ matrix.extra }}
- name: Build
run: sh scripts/build-studio.sh linux-${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: FrameStudio-linux-${{ matrix.arch }}
path: dist/FrameStudio-linux-${{ matrix.arch }}.tar.gz
# Frame Home for SteamVR on a Windows PC (frame-home.exe, frame-switcher.exe and the PC installer), cross-built with
# llvm-mingw as it's developed: the pc/ folder of the Windows x64 Studio, and a download of its own.
pc-home-windows:
name: Frame Home for Windows PCs
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: llvm-mingw
run: |
sudo apt-get update && sudo apt-get install -y ninja-build cmake
url=$(curl -s https://api.github.com/repos/mstorsjo/llvm-mingw/releases/latest | grep -o 'https://[^"]*ucrt-ubuntu-[0-9.]*-x86_64\.tar\.xz' | head -1)
curl -sL "$url" | tar -xJ -C "$HOME" && mv "$HOME"/llvm-mingw-* "$HOME/llvm-mingw"
- name: Build
run: |
export PATH="$HOME/llvm-mingw/bin:$PATH"
cmake -S . -B build/pc-win64 -G Ninja -DCMAKE_BUILD_TYPE=Release -DFRAME_HOME_PC=ON -DCMAKE_TOOLCHAIN_FILE=cmake/llvm-mingw.cmake -DWINDOWS_ARCH=x86_64 -DLLVM_MINGW="$HOME/llvm-mingw"
cmake --build build/pc-win64 --target frame-home frame-switcher d3d11.dll --parallel
mkdir -p "build/stage/FrameHome-PC-windows-x64" dist
cp build/pc-win64/frame-home.exe build/pc-win64/frame-switcher.exe build/pc-win64/d3d11.dll frame/pc/install-frame-home-pc.ps1 \
"frame/pc/Install Frame Home on this PC.bat" "frame/pc/Remove Frame Home from this PC.bat" "build/stage/FrameHome-PC-windows-x64/"
cp LICENSE build/stage/FrameHome-PC-windows-x64/LICENSE.txt
(cd build/stage && zip -qr ../../dist/FrameHome-PC-windows-x64.zip FrameHome-PC-windows-x64)
- uses: actions/upload-artifact@v4
with:
name: FrameHome-PC-windows-x64
path: dist/FrameHome-PC-windows-x64.zip
windows:
name: Windows ${{ matrix.arch }}
needs: [pc-home-windows]
strategy:
fail-fast: false
matrix:
arch: [x64, x86, arm64]
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
if: matrix.arch == 'x64'
with:
name: FrameHome-PC-windows-x64
path: pc
- name: Build (MSVC)
shell: cmd
run: studio\build_windows.bat ${{ matrix.arch }} "%GITHUB_WORKSPACE%\build\windows-${{ matrix.arch }}"
- name: Package
shell: pwsh
run: |
$name = "FrameStudio-windows-${{ matrix.arch }}"
$stage = "build/stage/$name"
New-Item -ItemType Directory -Force $stage | Out-Null
Copy-Item "build/windows-${{ matrix.arch }}/Frame Studio.exe" $stage
Copy-Item -Recurse "build/windows-${{ matrix.arch }}/frame" "$stage/frame"
Copy-Item LICENSE "$stage/LICENSE.txt"
if ("${{ matrix.arch }}" -eq "x64") { # Frame Home for this PC's SteamVR, beside Studio
Expand-Archive "pc/FrameHome-PC-windows-x64.zip" -DestinationPath build/pc
Copy-Item -Recurse "build/pc/FrameHome-PC-windows-x64" "$stage/pc"
Remove-Item "$stage/pc/LICENSE.txt"
}
New-Item -ItemType Directory -Force dist | Out-Null
Compress-Archive -Path $stage -DestinationPath "dist/$name.zip" -Force
- uses: actions/upload-artifact@v4
with:
name: FrameStudio-windows-${{ matrix.arch }}
path: dist/FrameStudio-windows-${{ matrix.arch }}.zip
macos:
name: macOS ${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- { arch: arm64, runner: macos-14 }
- { arch: x64, runner: macos-13 }
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Tools and MoltenVK (Vulkan on Metal, bundled into the app)
run: brew install ninja molten-vk
- name: Build
run: sh scripts/build-studio.sh macos-${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: FrameStudio-macos-${{ matrix.arch }}
path: dist/FrameStudio-macos-${{ matrix.arch }}.zip
frame-home:
name: Frame Home installer
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Package
run: sh scripts/build-studio.sh frame-home
- uses: actions/upload-artifact@v4
with:
name: FrameHome-installer
path: dist/FrameHome-installer.zip
release:
name: Publish
# (also when a platform failed: "latest" gets every build that worked; a version release needs them all)
if: ${{ !cancelled() && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') }}
needs: [linux, windows, macos, frame-home, pc-home-windows]
runs-on: ubuntu-24.04
permissions:
contents: write
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Version release (tag v*)
if: ${{ startsWith(github.ref, 'refs/tags/v') && !contains(needs.*.result, 'failure') }}
run: gh release create "${{ github.ref_name }}" dist/* --repo "$REPO" --title "Frame Studio ${{ github.ref_name }}" --generate-notes
- name: Latest build (push to main)
if: github.ref == 'refs/heads/main'
run: |
gh release delete latest --repo "$REPO" --yes --cleanup-tag 2>/dev/null || true
gh release create latest dist/* --repo "$REPO" --prerelease --target "${{ github.sha }}" \
--title "Latest build (${GITHUB_SHA::7})" \
--notes "Built automatically from ${{ github.sha }} on $(date -u '+%Y-%m-%d %H:%M UTC'). Replaced by every push to main."