From d664f869451ab255424108a20df053a0456c3eed Mon Sep 17 00:00:00 2001 From: Matzman Date: Thu, 8 Oct 2026 00:06:09 +0200 Subject: [PATCH] Add layout diagnosis: font metrics in the log, --snapshot option On the Steam Frame, the fixed 1200x800 QML layout overflows: about the bottom quarter is cut off and the navigation icons overlap their text. Forcing 96 DPI and switching the Quick Controls style didn't help, so the cause has to be measured rather than guessed. - SetWidget() logs the application font (family, pt, px), what 20 pt (MyText) is in px, the screen's DPI and the device pixel ratio. - --snapshot writes the rendered overlay (RenderBackend::readback) as PNG at most once per second while it is visible. A time interval, because OVRAS only renders when the scene changes. Written to a temporary file and renamed, so readers never see a partial file. - run.sh passes arguments after the graphics API to the program. Co-Authored-By: Claude Opus 5.5 --- build_scripts/steamframe/package-release.sh | 12 +++++--- docs/steamframe/DESIGN.md | 21 +++++++++++++ docs/steamframe/PROGRESS.md | 22 +++++++++++++ docs/steamframe/README.md | 5 +-- src/main.cpp | 1 + src/overlaycontroller.cpp | 34 +++++++++++++++++++++ src/overlaycontroller.h | 10 ++++++ src/utils/setup.cpp | 21 ++++++++----- src/utils/setup.h | 9 ++++++ 9 files changed, 122 insertions(+), 13 deletions(-) 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