19 KiB
19 KiB
Steam Frame port: progress
Status (2026-10-07): iteration 1 implemented. Next: test checklist T1–T11 on the Frame (at the end of this file), then assessment. Getting started: README.md.
Iteration 1
Step 1: build fixes, bytecode-only QML (2026-10-07)
Changes:
CMakeLists.txt:Core5Compatremoved (unused, not on the Frame).GuiPrivaterequested explicitly. This is temporary, until step 3 replaces QRhi.QT_QMLCACHEGEN_ARGUMENTS --only-bytecode.- OpenVR library directory per architecture (
linux64/linuxarm64/win64), for linking and for the copy next to the binary. RPATH $ORIGIN.libXtstlinked.
overlaycontroller.cpp:d3d11.handcase QRhi::D3D11only under_WIN32.- Logging: 8 active easylogging
LOG(INFO)calls becameqInfo()(overlaycontroller.cpp6,ChaperoneTabController.cpp1,MoveCenterTabController.cpp1). Commented-outLOGlines are left unchanged. - Only changed lines are formatted (
git clang-format).
Results (x86_64, Arch, Qt 6.11.1, GCC):
- The build succeeds, with 1463 compiler warnings, as in
qt6. objdump -T: 7Qt_6_PRIVATE_APIsymbols, allQRhi…(step 3). TheQQmlPrivate::AOTCompiledContextsymbols are gone (bytecode-only QML).libopenvr_api.sois resolved from the binary's directory (ldd).- Not started: without a running SteamVR the start hangs in OpenVR init (expected; tested on the Frame in step 5).
Step 2: platform option, R7, tick (2026-10-07)
Changes:
- Platform option:
- CMake option
OVRAS_STEAM_FRAME(default OFF) sets the compile definitionOVRAS_STEAM_FRAME=1. - New
src/utils/platform_capabilities.hevaluates it as the only place. It holdsk_chaperoneCommitSupported,k_defaultVsyncDisabledandk_defaultCustomTickRateMs.
- CMake option
- Tick (R6):
- In "vsync disabled" mode,
m_pumpEventsTimerruns directly atcustomTickRateMs(applyTickInterval(),Qt::PreciseTimer); the 1 ms counting is gone. - Changing the setting restarts the timer.
- Setting defaults in
settings_controller.hcome from the constants: Frametrue/11 ms, otherwise unchangedfalse/20 ms.
- In "vsync disabled" mode,
- R7: with
!k_chaperoneCommitSupported:updateChaperoneResetData()andapplyChaperoneResetData()skipCommitWorkingCopy/RevertWorkingCopy. Each logs once per function.- The init autosave profile is skipped.
Results:
- Both variants (default and
-DOVRAS_STEAM_FRAME=ON) build on x86_64. The definition is only set in the Frame variant (compile_commands.json). - Not tested at runtime yet (Frame, step 5).
Notes:
- Another commit at start-up remains, but only on opt-in.
SetWidget()applies a chaperone profile and commits when "auto-apply chaperone" (autoApplyChaperoneToggle, defaultfalse) is on. That's a chaperone-profile feature, so it belongs to R5 (iteration 2). - clang-format reflows comment blocks next to new comments. Separate new comments from existing commented-out code with a blank line, otherwise the diff grows.
Step 3: rendering through public API, --graphics-api, mouse y, shutdown order (2026-10-07)
Changes:
src/render/(new):RenderBackend,OpenGLRenderBackend,VulkanRenderBackendfrom the template (4a5b090).- Changes to the template code:
VRDASH_*→OVRAS_*names, origin note in the file header, references toOverlayController. - Formatted completely in OVRAS style.
- One comment in
VulkanRenderBackend.hwas wrapped by hand, because the automatic wrapping had broken its column layout.
- Changes to the template code:
OverlayController:- QRhi block replaced by
configureWindow→renderControl.initialize→attachWindow→createRenderTarget. renderOverlay()wraps the frame inbeginFrame/finishFrame. It submitsvrTextureBounds+vrTexturewhile the context is current.SetOverlayFromQRhiTexture, the QRhi members andQVulkanInstanceare removed.- Render delay 5 → 16 ms (about 60 fps).
- QRhi block replaced by
- Shutdown:
Shutdown()runs only once. It callsVR_Shutdown(), then frees the backend's render target (releaseRenderTarget,detachWindow). - Mouse y: always flipped; the Linux un-flip and its TODO are removed.
openvr_init::renderInterop(): Vulkan extensions and HMD GPU from SteamVR.main.cpp: parses--graphics-api, thencreateRenderBackend→initialize→QQuickWindow::setGraphicsApi→OverlayController.setup.*:--graphics-api opengl|vulkan(defaultopengl). Unknown or not built in → error, exit codeGENERAL_FAILURE.- CMake:
GuiPrivateis gone.- The Vulkan backend is built when
QT_FEATURE_vulkanis set (OVRAS_HAS_VULKAN).
Results (x86_64, Qt 6.11.1):
- The build succeeds. The Vulkan backend is built in.
objdump -T: onlyQVulkanDeviceFunctions::*/QVulkanFunctions::*remain asQt_6_PRIVATE_API. Acceptance criterion R1 (symbols) is met.- Warnings 1463 → 1630. The new code itself has none; the additional ones come from
openvr.hin the three new translation units. - Not tested yet:
- running both backends, Vulkan validation, the snapshot test (step 5, with the containers)
- mouse direction (Frame)
Risk:
MyTextFieldopens the SteamVR keyboard onactiveFocus. The template had to send the offscreen window an explicitFocusIn, otherwiseactiveFocusItemstayed null. Theqt6branch doesn't send it. If text fields don't respond on the Frame, that is the cause (R4).
Step 4: manifest and installation (2026-10-07)
Changes:
manifest.vrmanifest:binary_path_linuxandbinary_path_linux_arm→AdvancedSettings_app.manifest::handleManifests:VR_Init(Utility); onVRInitError_Init_NotAvailableToUtilityApps,VR_Init(Background).- Other errors: message and exit code
OPENVR_INIT_ERROR, instead ofinitializeOpenVR's retry loop.
main.cpp:--graphics-apiis checked before connecting to OpenVR. A wrong value used to fail only afterinitializeOpenVR's retry loop.
Results (x86_64, without SteamVR):
--force-install-manifestexits right away withOPENVR_INIT_ERROR(−2).--graphics-api fooexits right away withGENERAL_FAILURE(−1).- The build tree contains the binary,
libopenvr_api.so, both manifests,default_action_manifests/andres/. - Not tested yet:
Backgroundon the Frame (step 5).
Step 5: Frame build, package, tests (2026-10-07)
Changes:
build_scripts/steamframe/(new, see DESIGN.md section 9): containers, toolchain, build, test, package and deploy scripts.test/render/(new): render test for both backends (OVRAS_BUILD_TESTS=ON).CMakeLists.txt:- option
OVRAS_BUILD_TESTS - RPATH only
$ORIGIN(BUILD_WITH_INSTALL_RPATH). Before, the cross binary also had the host's absolute link directory in its RUNPATH.
- option
.gitignore:dist/.
Results:
- Render tests:
- locally (Arch, Qt 6.11, Intel Iris Xe): OpenGL and Vulkan, 4/4 passed each
- in the container (Debian trixie, Qt 6.8, same GPU via
/dev/dri): 2/2 passed, no Vulkan validation messages - Proof that the layer was active:
VK_LOADER_DEBUG=layershows "Inserted device layer VK_LAYER_KHRONOS_validation".
- Cross build (aarch64, Qt 6.8 Debian): succeeds once the container has
libxt-dev(input_sender_X11.cppincludesX11/Intrinsic.h).Qt_6_PRIVATE_APIwithoutQVulkan*: 0 → R1 symbols met.- Qt version tag:
Qt_6.8(fits the Frame's 6.8.0). - GLIBC ≤ 2.38 (the Frame has 2.39); GLIBCXX ≤ 3.4.32 (not checked against the Frame).
- NEEDED: Qt6 Core/Gui/Qml/Quick/Widgets/Network/DBus/Multimedia/WebSockets, libX11, libXtst, libpulse, libopenvr_api, libstdc++/libc.
- Package
dist/AdvancedSettings-5.7.4-linux-arm64.tar.gz: 3.0 MB (9.1 MB unpacked); since the version fix below…-5.8.17-…. The version 5.7.4 comes fromqt6'sproject()(master is at 5.8.17).
Open (needs the Frame):
- Are the QML modules OVRAS imports present in the Frame's system Qt? QtQuick.Controls (Material), Layouts, Dialogs, Multimedia.
- Do libXt/libXtst exist on SteamOS? Is libstdc++ new enough?
Version from compile_version_string.txt (2026-10-07)
- Problem:
qt6'sCMakeLists.txthad the version hard-coded as 5.7.4. The qmake build read it frombuild_scripts/compile_version_string.txt(5.8.17-release).masteris fully merged intoqt6, so only the number was stale. Consequence: the version check (comparison withver/versioncheck.json, 5.8.17) would have reported an update. - Fix: CMake reads the file.
APPLICATION_VERSION= full string, as in qmake.project(VERSION)= numeric part.
- Result:
APPLICATION_VERSION="5.8.17-release",CMAKE_PROJECT_VERSION=5.8.17, packagedist/AdvancedSettings-5.8.17-linux-arm64.tar.gz(3.0 MB).
Fix: manifest commands without a display (2026-10-07)
- Problem (first deploy to the Frame):
install.shaborted with a core dump in./AdvancedSettings_app --force-install-manifest. Log: "could not connect to display", then "no Qt platform plugin could be initialized" (fatal). - Cause:
main.cppcreates theQApplicationbefore it handles the manifest options. Qt's default platform plugin (xcb) aborts without a display, and over SSH there is none.install.shsetDISPLAY=:0only for the start, not for registering.uninstall.sh(--force-remove-manifest) had the same problem. - Fix (
package-release.sh, generated scripts only): both manifest calls run withQT_QPA_PLATFORM=offscreen. Registering draws nothing, so it needs no display at all. The normal start keepsDISPLAY=:0(xcb), because rendering needs a real OpenGL/Vulkan context. No change to the program. - Result: package rebuilt (
--no-build), generated scripts checked withsh -n. - Not tested yet: the fix itself on the Frame (T1, T11). No local x86 build was at hand for a test without a display.
Fix: OpenVR library for aarch64 (2026-10-07)
- Problem (second deploy): with the display fix,
--force-install-manifestgot as far asVR_Initand failed there: "Installation path could not be located (110)", and on stderr "Unable to read VR Path Registry from /data/work/openvrpaths.vrpath".~/.config/openvr/openvrpaths.vrpathon the Frame is correct (runtime/opt/steamvr). - Cause: the package shipped
third-party/openvr/lib/linuxarm64/libopenvr_api.so. It is byte-identical to the OpenVR SDK 2.15.6'slib/linuxarm64and looks for the path registry in the fixed path/data/work/openvrpaths.vrpath. The SDK'sbin/linuxarm64library (used by the template, verified on the Frame) and SteamVR's own one in/opt/steamvr/bin/linuxarm64don't contain that path. OVRAS'sthird-party/openvrhad nobin/linuxarm64. - Fix:
- New file
third-party/openvr/bin/linuxarm64/libopenvr_api.sofrom the SDK v2.15.6 (raw.githubusercontent.com/ValveSoftware/openvr/v2.15.6/bin/linuxarm64/…, SHA-256cc3671d2…47e9). CMakeLists.txt:OPENVR_LIB_DIR=bin/linuxarm64for aarch64 only. x86_64 and Windows are unchanged.- Rejected: setting
VR_PATHREG_OVERRIDEin the scripts. It wouldn't help when SteamVR autostarts OVRAS.
- New file
- Result: cross build and package succeed. The package contains the new library (no
/data/work),Qt_6_PRIVATE_APIwithoutQVulkan*: 0, RUNPATH$ORIGIN. Package 1.6 MB (was 3.0 MB, the old library was 5.4 MB). - Not tested yet: on the Frame (T1).
Finding: app key already registered by Steam (2026-10-07)
- Third try of T1 (by hand on the Frame, with the two fixes above):
VR_Initworks, asUtility(vrserver.txt: "New Connect message … (VRApplication_Utility)"; SteamVR did not refuse Utility this time, unlike with the template). Then: "Manifest Previously Installed", "Could not find working directory of already installed application: VRApplicationError_PropertyNotSet". - Cause: Steam's generated
steamapps.vrmanifestlists every owned VR app, including OVRAS (steam.overlay.1009850, launch viasteam://URL), although the Steam version is uninstalled. Not a leftover of the uninstallation; it can't be removed permanently. - Next: design DESIGN.md section 2a (approved 2026-10-07).
- Implementation (
setup.cpp,setup.h): as designed: key without working directory → nothing removed,AddApplicationManifestdirectly (error name on refusal);logApplicationEntry()after "Manifest reinstalled." logs source, launch type, working directory, binary, URL and auto-launch for the key. - Result: cross build and package succeed,
Qt_6_PRIVATE_APIwithoutQVulkan*: 0. - Not tested yet: on the Frame. The log decides:
AppKeyAlreadyExistsor Steam's entry (steam://URL) → own app key needed (option 2); our working directory and binary → enough.
Result on the Frame (2026-10-07, deploy with ec82b5f): SteamVR accepts our manifest
(no error) but keeps using Steam's entry for the key:
source: "steam" launch type: "url" working dir: PropertyNotSet binary: PropertyNotSet URL: "steam://launch/1009850/openvroverlay" auto-launch: true.
- Consequence: the native build needs its own app key (option 2, DESIGN.md 2a, follow-up). Design pending.
- Side effect:
--force-install-manifestswitched auto-launch on for Steam's key. At the next SteamVR start, SteamVR may launchsteam://launch/1009850(the uninstalled Steam version). To be switched off again. - OVRAS itself was started by
install.shand keeps running (not yet checked in the dashboard, T2).
Fix: debug output only with debugSymbolsAndLogs (2026-10-07)
- Problem: the log filled with
[D] updateAllPulseData done.about every 11 ms (8399 lines in 6 s).AudioTabController::eventLoopTick()reads the microphone's mute state on every tick, and every read callsupdateAllPulseData()(AudioManagerPulse_internal.h:312). - Cause (regression in
qt6): onmasterthe message was easylogging'sLOG(DEBUG), and the Debug level was disabled unless built withdebugSymbolsAndLogs(ENABLE_DEBUG_LOGGING).qt6switched toqDebug()and lost that; the constantdebugLoggingEnabledinsetup.cppwas left unused, so all 46qDebug()calls were always active. - Fix (
setup.cpp,setUpLogging()): withoutENABLE_DEBUG_LOGGING,QLoggingCategory::setFilterRules("*.debug=false").QT_LOGGING_RULES="*.debug=true"takes precedence and turns debug output on again without a rebuild. - Result: cross build and package succeed,
Qt_6_PRIVATE_APIwithoutQVulkan*: 0. - Not tested yet: on the Frame (log without
[D]lines; withQT_LOGGING_RULESwith them). - Open for R6/T10: each tick makes three synchronous PulseAudio queries (server info, all sinks, all sources) for the mute state. Not changed now.
Layout cut off on the Frame; diagnosis tools (2026-10-08)
- Observed (headset photo): the dashboard tab appears, navigation and laser work. The fixed 1200×800 layout overflows: about the bottom quarter is cut off (left: Statistics, Bindings, Settings; right: Microphone half, Push-to-Talk). Navigation icons overlap their text. The right edge is complete.
- Ruled out by experiment on the Frame:
QT_FONT_DPI=96(verified in the process environment): no change.QT_QUICK_CONTROLS_STYLE=Basic(Qt 5's "Default" style;qt6sets no style, so Qt 6 uses Fusion on Linux; onlymainwidget.qmlimports Material): no visible change.QT_QUICK_CONTROLS_MATERIAL_VARIANT=Dense: slightly better, still cut off. - Facts: Frame X screen 1920×1080,
Xft.dpi: 144; default font Noto Sans (fc-match sans-serif). Template renders 1600×1000. - Also seen: after removing the Steam license, Steam's
steamapps.vrmanifeststill listssteam.overlay.1009850(not yet regenerated); the debug-output fix works (0[D]lines). - Diagnosis tools (DESIGN.md section 10): font/screen metrics in the log, option
--snapshot <file>,run.shpasses further options. - Result: cross build and package succeed,
Qt_6_PRIVATE_APIwithoutQVulkan*: 0, generatedrun.shchecked withsh -n. No new warnings in the changed lines. - Not tested yet: on the Frame (log line "Font: …", PNG written).
Fix: Quick Controls style Fusion (2026-10-08)
- Measurements on the Frame (snapshots of the root page, OpenGL):
- Log:
Font: "Noto Sans" 9 pt = 12 px (requested "Sans Serif" 9 pt); 20 pt = 27 px; screen "gamescope" logical DPI 96 physical DPI 63.972 device pixel ratio 1. Fonts, DPI and scaling are as intended. - The PNG itself is cut off: OVRAS renders the content too large; SteamVR doesn't crop.
- Navigation buttons: as
qt6is, 65 px (pitch 75) = Material. WithQT_QUICK_CONTROLS_STYLE=Basic(verified in the process environment): identical, the import overrides it. Test build without the Material import (not committed): 45 px (pitch 55) = Fusion, everything fits. Same build with Basic: 49 px (pitch 59), the last button is cut off.
- Log:
- Fix (DESIGN.md section 11):
mainwidget.qmlimportsQtQuick.Controlsagain;main.cppsetsQQuickStyle::setStyle("Fusion"). - Result: cross build and package succeed,
Qt_6_PRIVATE_APIwithoutQVulkan*: 0. - Frame test of the committed build (
ff4ea8f, 2026-10-08, OpenGL): root page fits completely (12 navigation buttons 45 px, pitch 55, Microphone, Push-to-Talk, version visible), same as the test build; "Chaperone Settings" fits completely too. NoQT_variables set, so Fusion comes from the code. Log: no new errors. - Not tested yet: the other pages, Vulkan.
- Seen, not changed: on "Chaperone" and "Space Fix" the icon still overlaps the text
slightly (
MyPushButton: icon as wide as the button is high, text only indented by spaces).
Test checklist on the Frame (step 5)
Deploy: build_scripts/steamframe/deploy-to-frame.sh <user>@<frame> (SteamVR running).
| # | Check | Criterion | Result |
|---|---|---|---|
| T1 | install.sh: registration works (log: "Manifest reinstalled", via Background) |
R2 | first try failed: abort without a display (fixed, see above); retest open |
| T2 | Tab appears in the dashboard, UI correct (OpenGL) | R2, R3 | open |
| T3 | ./run.sh vulkan: UI correct |
R3 | open |
| T4 | Laser right way up, click, scrolling on several pages (both APIs) | R4 | open |
| T5 | Text field (e.g. new profile name): SteamVR keyboard opens, input arrives | R4 | open |
| T6 | An OVRAS action arrives (e.g. push-to-talk, log/notification) | R4 | open |
| T7 | Play area before/after start unchanged; log: "CommitWorkingCopy skipped" | R7 | open |
| T8 | Quit app (OVRAS "Exit" and Quit in the SteamVR dashboard): no crash (both APIs) | R2 | open |
| T9 | Auto-start after a SteamVR restart / reboot | R2 | open |
| T10 | pidstat -p $(pgrep -f AdvancedSettings_app) 10 6: dashboard closed / OVRAS visible / other tab visible |
R6 | open |
| T11 | uninstall.sh unregisters it |
R2 | open |
Wrap-up (2026-10-07)
- Remotes:
origin=git@git.matzman.net:SteamFrame/OpenVR-AdvancedSettings-frame.git(branchessteamframe,qt6,master);upstream= GitHub OVRAS. - Handover documentation:
docs/steamframe/README.md(entry point),CLAUDE.md(working rules, conventions),docs/steamframe/OVRAS-FEATURES.md(feature analysis). - Still open in iteration 1: phase 4 (tests on the Frame, T1–T11) and phase 5 (assessment).