mirror of
https://github.com/Toastconcern/FrameStudio.git
synced 2026-10-29 04:02:56 +01:00
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>
This commit is contained in:
1 parent
283e19f87c
commit
d22cc04cb3
14 files changed
+1156
-117
No files matched your search
@@ -1,128 +1,122 @@
|
||||
# Frame Home on a Windows PC running SteamVR: your homes become SteamVR's environment, and the Environments panel
|
||||
# in the Steam menu switches them. It shows the environments in Frame Studio's My Environments
|
||||
# (Documents\Frame Studio\Environments): save one in Studio and it's there.
|
||||
# Frame Home on a Windows PC running SteamVR: your environments become SteamVR's environment itself — drawn where
|
||||
# Aurora would be, so they show at boot, during game loads and behind the Steam menu, with the controllers and
|
||||
# passthrough. The Environments panel in the Steam menu switches them. It shows the environments in Frame Studio's
|
||||
# My Environments (Documents\Frame Studio\Environments): save one in Studio and it's there.
|
||||
#
|
||||
# powershell -ExecutionPolicy Bypass -File install-frame-home-pc.ps1 install or update
|
||||
# powershell -ExecutionPolicy Bypass -File install-frame-home-pc.ps1 -Uninstall SteamVR's own environment back
|
||||
# powershell -ExecutionPolicy Bypass -File install-frame-home-pc.ps1 -Uninstall SteamVR's own Aurora back
|
||||
#
|
||||
# How: Frame Home becomes SteamVR's home app (SteamVR runs it whenever no game is open), with SteamVR's classic
|
||||
# environment in place of Aurora showing a 360-degree picture of the current home for the moments before Frame Home
|
||||
# draws. The SteamVR settings this changes are saved first and put back exactly on -Uninstall. Works with SteamVR
|
||||
# running (through its API) or not (its config files, read when it next starts).
|
||||
# How: a small Direct3D 11 layer (d3d11.dll beside SteamVR's compositor) draws the home in Aurora's place, fed by
|
||||
# frame-home --system; the real d3d11.dll is kept beside it as fh_d3d11real.dll and every call it makes passes
|
||||
# straight through. The Environments panel runs frame-home --system and switches homes. -Uninstall takes the layer
|
||||
# off (deleting those two files) and SteamVR is exactly as before.
|
||||
param([switch]$Uninstall)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$root = $(if ($env:FRAME_HOME_ROOT) { $env:FRAME_HOME_ROOT } else { Join-Path $env:LOCALAPPDATA 'Frame Home' }) # (FRAME_HOME_ROOT: tests)
|
||||
$bin = Join-Path $root 'bin'
|
||||
$layer = Join-Path $root 'layer'
|
||||
function Say($m) { Write-Host "frame-install: $m" }
|
||||
|
||||
# SteamVR's config folder, from OpenVR's own record of where things are.
|
||||
# SteamVR, from OpenVR's own record of where things are: its config folder and where it's installed (for the compositor).
|
||||
$vrpath = Join-Path $env:LOCALAPPDATA 'openvr\openvrpaths.vrpath'
|
||||
if (-not (Test-Path $vrpath)) { Say "SteamVR isn't installed on this PC (no $vrpath). Install SteamVR from Steam first."; exit 1 }
|
||||
$openvr = Get-Content $vrpath -Raw | ConvertFrom-Json
|
||||
$cfg = @($openvr.config)[0]
|
||||
if (-not $cfg) { Say "SteamVR's config folder isn't listed in $vrpath"; exit 1 }
|
||||
$runtime = @($openvr.runtime)[0]
|
||||
if (-not $cfg -or -not $runtime) { Say "SteamVR's folders aren't listed in $vrpath"; exit 1 }
|
||||
$bin64 = Join-Path $runtime 'bin\win64'
|
||||
$proxy = Join-Path $bin64 'd3d11.dll'
|
||||
$real = Join-Path $bin64 'fh_d3d11real.dll'
|
||||
$running = [bool](Get-Process vrserver -ErrorAction SilentlyContinue)
|
||||
|
||||
# JSON as SteamVR writes it: UTF-8 without a byte-order mark, replaced in one step.
|
||||
function Load($p) {
|
||||
if (-not (Test-Path $p)) { return [pscustomobject]@{} }
|
||||
try { return Get-Content $p -Raw | ConvertFrom-Json } catch { Say "$p does not read: left as it is"; exit 1 }
|
||||
}
|
||||
function StopHome { Get-Process frame-home, frame-switcher -ErrorAction SilentlyContinue | Stop-Process -Force; Start-Sleep -Milliseconds 400 }
|
||||
|
||||
# JSON as SteamVR writes it: UTF-8 without a byte-order mark.
|
||||
function Load($p) { if (-not (Test-Path $p)) { return [pscustomobject]@{} } try { return Get-Content $p -Raw | ConvertFrom-Json } catch { Say "$p does not read: left as it is"; exit 1 } }
|
||||
function Save($p, $o) {
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $p) | Out-Null
|
||||
$json = $o | ConvertTo-Json -Depth 32
|
||||
[IO.File]::WriteAllText("$p.tmp", $json, (New-Object Text.UTF8Encoding $false))
|
||||
[IO.File]::WriteAllText("$p.tmp", ($o | ConvertTo-Json -Depth 32), (New-Object Text.UTF8Encoding $false))
|
||||
Move-Item -Force "$p.tmp" $p
|
||||
}
|
||||
function SetProp($o, $name, $value) {
|
||||
if ($o.PSObject.Properties[$name]) { $o.$name = $value } else { $o | Add-Member -NotePropertyName $name -NotePropertyValue $value }
|
||||
}
|
||||
function SetProp($o, $name, $value) { if ($o.PSObject.Properties[$name]) { $o.$name = $value } else { $o | Add-Member -NotePropertyName $name -NotePropertyValue $value } }
|
||||
|
||||
$manifests = @((Join-Path $root 'frame-home.vrmanifest'), (Join-Path $root 'frame-switcher.vrmanifest'))
|
||||
# enableHomeApp off (no separate home app) and environmentMode 1 (the compositor draws Aurora, which the layer
|
||||
# replaces). Saved once as they were, put back on -Uninstall. Also the panel keeps them so while SteamVR runs.
|
||||
$before = Join-Path $root 'steamvr-before.json'
|
||||
|
||||
# SteamVR not running: the same registration, in its config files.
|
||||
function Offline($install) {
|
||||
$ac = Join-Path $cfg 'appconfig.json'
|
||||
$a = Load $ac
|
||||
if (-not $a.PSObject.Properties['manifest_paths']) { SetProp $a 'manifest_paths' @() }
|
||||
$paths = [System.Collections.ArrayList]@($a.manifest_paths)
|
||||
foreach ($m in $manifests) {
|
||||
if ($install -and -not $paths.Contains($m)) { [void]$paths.Add($m) }
|
||||
if (-not $install -and $paths.Contains($m)) { $paths.Remove($m) }
|
||||
}
|
||||
$a.manifest_paths = @($paths)
|
||||
Save $ac $a
|
||||
function Settings($install) {
|
||||
$vs = Join-Path $cfg 'steamvr.vrsettings'
|
||||
$s = Load $vs
|
||||
if (-not $s.PSObject.Properties['steamvr']) { SetProp $s 'steamvr' ([pscustomobject]@{}) }
|
||||
$sv = $s.steamvr
|
||||
if ($install) {
|
||||
if (-not (Test-Path $before)) { # what was there before Frame Home, saved once
|
||||
$was = [ordered]@{
|
||||
if (-not (Test-Path $before)) {
|
||||
Save $before ([pscustomobject]@{
|
||||
enableHomeApp = [bool]$sv.enableHomeApp
|
||||
environmentMode = $(if ($sv.PSObject.Properties['environmentMode']) { [int]$sv.environmentMode } else { 1 })
|
||||
background = [string]$sv.background
|
||||
backgroundUseDomeProjection = [bool]$sv.backgroundUseDomeProjection
|
||||
backgroundCameraHeight = $(if ($sv.PSObject.Properties['backgroundCameraHeight']) { [double]$sv.backgroundCameraHeight } else { 1.6 })
|
||||
backgroundDomeRadius = [double]$sv.backgroundDomeRadius
|
||||
}
|
||||
Save $before ([pscustomobject]$was)
|
||||
})
|
||||
}
|
||||
SetProp $sv 'enableHomeApp' $true
|
||||
SetProp $sv 'environmentMode' 0
|
||||
SetProp $sv 'enableHomeApp' $false
|
||||
SetProp $sv 'environmentMode' 1
|
||||
} else {
|
||||
$was = $(if (Test-Path $before) { Get-Content $before -Raw | ConvertFrom-Json } else { $null })
|
||||
SetProp $sv 'enableHomeApp' $(if ($was) { [bool]$was.enableHomeApp } else { $false })
|
||||
SetProp $sv 'environmentMode' $(if ($was) { [int]$was.environmentMode } else { 1 })
|
||||
SetProp $sv 'background' $(if ($was) { [string]$was.background } else { '' })
|
||||
SetProp $sv 'backgroundUseDomeProjection' $(if ($was) { [bool]$was.backgroundUseDomeProjection } else { $false })
|
||||
if ($was) { SetProp $sv 'backgroundCameraHeight' ([double]$was.backgroundCameraHeight); SetProp $sv 'backgroundDomeRadius' ([double]$was.backgroundDomeRadius) }
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue $before
|
||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue (Join-Path $root 'background')
|
||||
foreach ($m in $manifests) { Remove-Item -Force -ErrorAction SilentlyContinue $m }
|
||||
}
|
||||
Save $vs $s
|
||||
}
|
||||
|
||||
# The panel, registered with SteamVR (frame-switcher --install) or, when SteamVR isn't running, in its config files.
|
||||
$switcherManifest = Join-Path $root 'frame-switcher.vrmanifest'
|
||||
function PanelOffline($install) {
|
||||
$ac = Join-Path $cfg 'appconfig.json'
|
||||
$a = Load $ac
|
||||
if (-not $a.PSObject.Properties['manifest_paths']) { SetProp $a 'manifest_paths' @() }
|
||||
$paths = [System.Collections.ArrayList]@($a.manifest_paths)
|
||||
if ($install -and -not $paths.Contains($switcherManifest)) { [void]$paths.Add($switcherManifest) }
|
||||
if (-not $install -and $paths.Contains($switcherManifest)) { $paths.Remove($switcherManifest) }
|
||||
$a.manifest_paths = @($paths)
|
||||
Save $ac $a
|
||||
Save (Join-Path $cfg 'vrappconfig\framehome.switcher.vrappconfig') ([pscustomobject]@{ autolaunch = [bool]$install; last_launch_time = '0' })
|
||||
}
|
||||
|
||||
function StopAll { Get-Process frame-home, frame-switcher -ErrorAction SilentlyContinue | Stop-Process -Force; Start-Sleep -Milliseconds 500 }
|
||||
|
||||
if ($Uninstall) {
|
||||
if ($running) {
|
||||
& (Join-Path $bin 'frame-home.exe') --uninstall
|
||||
& (Join-Path $bin 'frame-switcher.exe') --uninstall
|
||||
} else {
|
||||
Offline $false
|
||||
}
|
||||
StopAll
|
||||
Say "SteamVR's own environment is back (your environments are kept in Documents\Frame Studio\Environments)"
|
||||
StopHome
|
||||
if ($running) { & (Join-Path $bin 'frame-switcher.exe') --uninstall } else { PanelOffline $false }
|
||||
Settings $false
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue $proxy # the layer off: SteamVR loads the system d3d11 again
|
||||
Remove-Item -Force -ErrorAction SilentlyContinue $real
|
||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $layer
|
||||
Say "Frame Home is off: SteamVR's own environment is back (restart SteamVR to see it). Your environments are kept in Documents\Frame Studio\Environments"
|
||||
exit 0
|
||||
}
|
||||
|
||||
foreach ($x in 'frame-home.exe', 'frame-switcher.exe') {
|
||||
foreach ($x in 'frame-home.exe', 'frame-switcher.exe', 'd3d11.dll') {
|
||||
if (-not (Test-Path (Join-Path $here $x))) { Say "this installer has no $x"; exit 1 }
|
||||
}
|
||||
# Runs here, before anything installed is touched (a missing Vulkan driver shows up now, not as a black world later).
|
||||
# Frame Home runs on this PC (a missing Vulkan driver shows up now, not as a black world later).
|
||||
& (Join-Path $here 'frame-home.exe') --version | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { Say "Frame Home can't run on this PC (Vulkan driver?): nothing was changed"; exit 1 }
|
||||
|
||||
StopAll
|
||||
New-Item -ItemType Directory -Force -Path $bin, (Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'Frame Studio\Environments') | Out-Null
|
||||
StopHome
|
||||
New-Item -ItemType Directory -Force -Path $bin, $layer, (Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'Frame Studio\Environments') | Out-Null
|
||||
foreach ($x in 'frame-home.exe', 'frame-switcher.exe') { Copy-Item -Force (Join-Path $here $x) (Join-Path $bin $x) }
|
||||
|
||||
# The compositor layer: keep the real d3d11.dll (from System32 — SteamVR's bin\win64 has none of its own) as
|
||||
# fh_d3d11real.dll, then drop our proxy in as d3d11.dll. (We copy the real one only once, never our own proxy.)
|
||||
if (-not (Test-Path $bin64)) { Say "SteamVR's compositor folder isn't where $vrpath says ($bin64)"; exit 1 }
|
||||
if (-not (Test-Path $real)) { Copy-Item -Force (Join-Path $env:windir 'System32\d3d11.dll') $real }
|
||||
Copy-Item -Force (Join-Path $here 'd3d11.dll') $proxy
|
||||
|
||||
Settings $true
|
||||
if ($running) {
|
||||
& (Join-Path $here 'frame-switcher.exe') --install
|
||||
& (Join-Path $here 'frame-home.exe') --install
|
||||
if ($LASTEXITCODE -ne 0) { Say "SteamVR refused Frame Home ($LASTEXITCODE)"; exit 1 }
|
||||
Say "installed: Frame Home is SteamVR's environment now"
|
||||
& (Join-Path $bin 'frame-switcher.exe') --install # registers the panel, auto-launches it, starts it now
|
||||
Say "installed. SteamVR is running, so restart it (or your headset) once to load the environment layer; the Environments panel is in the Steam menu."
|
||||
} else {
|
||||
# (with SteamVR not running these write their manifests and say so)
|
||||
& (Join-Path $here 'frame-switcher.exe') --install | Out-Null
|
||||
& (Join-Path $here 'frame-home.exe') --install | Out-Null
|
||||
Offline $true
|
||||
Say "installed: Frame Home becomes SteamVR's environment when SteamVR next starts"
|
||||
& (Join-Path $bin 'frame-switcher.exe') --install | Out-Null
|
||||
PanelOffline $true
|
||||
Say "installed: your environment becomes SteamVR's when SteamVR next starts."
|
||||
}
|
||||
exit 0
|
||||
Reference in new issue
Block a user