diff --git a/build_scripts/steamframe/package-release.sh b/build_scripts/steamframe/package-release.sh index db28294..df03079 100755 --- a/build_scripts/steamframe/package-release.sh +++ b/build_scripts/steamframe/package-release.sh @@ -67,12 +67,16 @@ EOF cat > "$out/run.sh" </dev/null && sleep 1 -DISPLAY="\${DISPLAY:-:0}" nohup ./$exe --graphics-api "\${1:-opengl}" >/dev/null 2>&1 & -echo "Started with \${1:-opengl}; log: \$(pwd)/AdvancedSettings.log" +DISPLAY="\${DISPLAY:-:0}" nohup ./$exe --graphics-api "\$api" "\$@" >/dev/null 2>&1 & +echo "Started with \$api; log: \$(pwd)/AdvancedSettings.log" EOF chmod +x "$out/install.sh" "$out/uninstall.sh" "$out/run.sh" diff --git a/docs/steamframe/DESIGN.md b/docs/steamframe/DESIGN.md index a0ccde5..a811814 100644 --- a/docs/steamframe/DESIGN.md +++ b/docs/steamframe/DESIGN.md @@ -260,6 +260,27 @@ existing code: private API. - RPATH is only `$ORIGIN` (`BUILD_WITH_INSTALL_RPATH`): the build tree gets packaged. +## 10. Layout diagnosis: font log and `--snapshot` (approved 2026-10-08) + +**Why:** on the Frame the fixed 1200×800 layout overflows: the bottom quarter is cut off +(left: Statistics, Bindings, Settings; right: Microphone, Push-to-Talk), and the navigation +icons overlap their text. `QT_FONT_DPI=96`, `QT_QUICK_CONTROLS_STYLE=Basic` and the Material +"Dense" variant didn't fix it. Font metrics (Noto Sans instead of Segoe UI) are the main +suspect; measuring needs the unwarped rendered image. + +**Changes (platform-neutral):** +- `OverlayController::SetWidget()`: logs once the application font (family, pt, px), what + 20 pt (`MyText`) is in px, the screen's logical/physical DPI and the window's device pixel + ratio. +- Option `--snapshot ` (`setup.*`, `main.cpp`, `overlaycontroller.*`): while the + overlay is visible and rendered, `renderOverlay()` writes `RenderBackend::readback()` as + PNG to ``, at most once per second (OVRAS only renders when the scene changes, so a + frame count could never trigger on a still page). Written to `.tmp` and renamed. + `main.cpp` passes it through a setter, not a new constructor parameter, to keep the diff + small. Without the option nothing changes. +- `run.sh`: arguments after the graphics API go to the program + (`./run.sh opengl --snapshot /tmp/ovras.png`). + ## Tests - **x86_64 locally:** the build succeeds; `objdump` check (R1). diff --git a/docs/steamframe/PROGRESS.md b/docs/steamframe/PROGRESS.md index 7c557ee..05a456a 100644 --- a/docs/steamframe/PROGRESS.md +++ b/docs/steamframe/PROGRESS.md @@ -266,6 +266,28 @@ URL: "steam://launch/1009850/openvroverlay" auto-launch: true`. - **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; `qt6` + sets no style, so Qt 6 uses Fusion on Linux; only `mainwidget.qml` imports 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.vrmanifest` still + lists `steam.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 `, `run.sh` passes further options. +- **Result:** cross build and package succeed, `Qt_6_PRIVATE_API` without `QVulkan*`: 0, + generated `run.sh` checked with `sh -n`. No new warnings in the changed lines. +- **Not tested yet:** on the Frame (log line "Font: …", PNG written). + ### Test checklist on the Frame (step 5) Deploy: `build_scripts/steamframe/deploy-to-frame.sh @` (SteamVR running). diff --git a/docs/steamframe/README.md b/docs/steamframe/README.md index e57792a..9daca88 100644 --- a/docs/steamframe/README.md +++ b/docs/steamframe/README.md @@ -124,11 +124,12 @@ docker run --rm -v "$PWD:$PWD" -w "$PWD" ovras-steamframe-cross-arm64 \ | Command | Effect | |---|---| | `./install.sh` | Registers with SteamVR (auto-start) and starts | -| `./run.sh opengl\|vulkan` | Restarts with that graphics API | +| `./run.sh opengl\|vulkan [options]` | Restarts with that graphics API; further options go to the program | | `./uninstall.sh` | Unregisters | | `AdvancedSettings.log` | Log (in the working directory, i.e. this folder) | -Program options (new): `--graphics-api opengl|vulkan` (default `opengl`). Existing ones +Program options (new): `--graphics-api opengl|vulkan` (default `opengl`); `--snapshot ` +(diagnosis: writes the rendered overlay as PNG at most once per second while visible). Existing ones include `--force-install-manifest` and `--force-remove-manifest`; see `--help`. ## Steam Frame facts the code relies on diff --git a/src/main.cpp b/src/main.cpp index 95fe86f..9fefbbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,6 +97,7 @@ int main( int argc, char* argv[] ) commandLineArgs.forceNoSound, qmlEngine, std::move( renderBackend ) ) ); + controller->setSnapshotPath( commandLineArgs.snapshotPath ); //const auto url // = QUrl( ":/qt/qml/AdvancedSettings/src/res/qml/common/mainwidget.qml" ); QQmlComponent component( &qmlEngine, ":/qt/qml/AdvancedSettings/src/res/qml/common/mainwidget.qml" ); diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 6201430..0611ba1 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -456,6 +459,20 @@ void OverlayController::SetWidget( QQuickItem* quickItem, static_cast( quickItem->width() ), static_cast( quickItem->height() ) ); + // Font and screen metrics that decide how big the fixed-size + // (1200x800) QML layout comes out, e.g. on the Steam Frame (Noto Sans, + // Xft.dpi 144) compared to Windows (Segoe UI, 96 DPI). + const QFont font = QGuiApplication::font(); + const QFontInfo fontInfo( font ); + qInfo() << "Font:" << fontInfo.family() << fontInfo.pointSizeF() + << "pt =" << fontInfo.pixelSize() << "px (requested" + << font.family() << font.pointSizeF() << "pt); 20 pt =" + << QFontInfo( QFont( font.family(), 20 ) ).pixelSize() + << "px; screen" << m_window.screen()->name() << "logical DPI" + << m_window.screen()->logicalDotsPerInch() << "physical DPI" + << m_window.screen()->physicalDotsPerInch() + << "device pixel ratio" << m_window.effectiveDevicePixelRatio(); + vr::HmdVector2_t vecWindowSize = { static_cast( quickItem->width() ), static_cast( quickItem->height() ) }; @@ -572,6 +589,23 @@ void OverlayController::renderOverlay() m_renderControl.endFrame(); m_renderBackend->finishFrame(); + // --snapshot: writes the rendered overlay to a PNG at most once per + // second while it is visible, so the layout can be checked without a + // headset photo. Written to a temporary file and renamed, so readers + // never see a partial file. + if ( !m_snapshotPath.isEmpty() + && ( !m_snapshotTimer.isValid() + || m_snapshotTimer.elapsed() >= 1000 ) ) + { + m_snapshotTimer.start(); + const QString tmp = m_snapshotPath + ".tmp"; + if ( m_renderBackend->readback().save( tmp, "PNG" ) ) + { + QFile::remove( m_snapshotPath ); + QFile::rename( tmp, m_snapshotPath ); + } + } + vr::VRTextureBounds_t bounds = m_renderBackend->vrTextureBounds(); vr::VROverlay()->SetOverlayTextureBounds( m_ulOverlayHandle, &bounds ); vr::Texture_t texture = m_renderBackend->vrTexture(); diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index a53ecd5..9d61a62 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include "render/RenderBackend.h" @@ -117,6 +118,9 @@ private: QQuickWindow m_window{ &m_renderControl }; std::optional m_cached_vr_texture_type; + // --snapshot (see renderOverlay()): target file and time of the last one. + QString m_snapshotPath; + QElapsedTimer m_snapshotTimer; // Set by Shutdown(), which must run only once (see there). bool m_shutdownDone = false; @@ -210,6 +214,12 @@ public: std::unique_ptr renderBackend ); virtual ~OverlayController(); + // Diagnosis (--snapshot): see renderOverlay(). Empty: off. + void setSnapshotPath( const QString& path ) + { + m_snapshotPath = path; + } + void Shutdown(); Q_INVOKABLE void exitApp(); Q_INVOKABLE void setAutoChapProfileName( int index ); diff --git a/src/utils/setup.cpp b/src/utils/setup.cpp index 25504cc..e8647d7 100644 --- a/src/utils/setup.cpp +++ b/src/utils/setup.cpp @@ -65,6 +65,10 @@ CommandLineOptions returnCommandLineParser( const MyQApplication& application ) k_graphicsApiDefault ); parser.addOption( graphicsApi ); + QCommandLineOption snapshot( + k_snapshot, k_snapshotDescription, k_snapshotValueName ); + parser.addOption( snapshot ); + parser.process( application ); const bool desktopModeEnabled = parser.isSet( desktopMode ); @@ -94,13 +98,16 @@ CommandLineOptions returnCommandLineParser( const MyQApplication& application ) const QString graphicsApiValue = parser.value( graphicsApi ); qInfo() << "Graphics API:" << graphicsApiValue; - const CommandLineOptions commandLineArgs{ desktopModeEnabled, - forceNoSoundEnabled, - forceNoManifestEnabled, - forceInstallManifestEnabled, - forceRemoveManifestEnabled, - resetSettingsEnabled, - graphicsApiValue }; + const QString snapshotValue = parser.value( snapshot ); + if ( !snapshotValue.isEmpty() ) + qInfo() << "Overlay snapshots to:" << snapshotValue; + + const CommandLineOptions commandLineArgs{ + desktopModeEnabled, forceNoSoundEnabled, + forceNoManifestEnabled, forceInstallManifestEnabled, + forceRemoveManifestEnabled, resetSettingsEnabled, + graphicsApiValue, snapshotValue + }; qInfo() << "Command line arguments processed."; diff --git a/src/utils/setup.h b/src/utils/setup.h index bfd6e7e..e0062e6 100644 --- a/src/utils/setup.h +++ b/src/utils/setup.h @@ -30,6 +30,8 @@ struct CommandLineOptions const bool resetSettings = false; // Value of --graphics-api; checked in main() with parseGraphicsApi(). const QString graphicsApi = "opengl"; + // Value of --snapshot; empty: no snapshots. + const QString snapshotPath; }; // Manages the programs control flow and main settings. @@ -98,6 +100,13 @@ constexpr auto k_graphicsApiDescription = "Graphics API for rendering the overlay: opengl (default) or vulkan."; constexpr auto k_graphicsApiValueName = "api"; constexpr auto k_graphicsApiDefault = "opengl"; +// Diagnosis: writes the rendered overlay to a PNG while it is visible, to check +// the layout without a headset photo (OverlayController::renderOverlay()). +constexpr auto k_snapshot = "snapshot"; +constexpr auto k_snapshotDescription + = "Writes the rendered overlay to (PNG) at most once per second " + "while it is visible."; +constexpr auto k_snapshotValueName = "file"; constexpr auto k_resetSettings = "reset-steamvr-settings"; constexpr auto k_resetSettingsDescription