diff --git a/docs/motion-and-high-refresh.md b/docs/motion-and-high-refresh.md index 53f41973..687fca1e 100644 --- a/docs/motion-and-high-refresh.md +++ b/docs/motion-and-high-refresh.md @@ -55,7 +55,7 @@ Summary of current engine behavior (Flutter 3.24+ / Impeller). Sources in §7. | Platform | Renders at display Hz by default? | How to unlock > 60 Hz | Notes | |----------|-----------------------------------|------------------------|-------| | **Windows** | Usually yes (follows monitor via DWM) | No app API needed | Verify on a 120/144 Hz monitor; engine vsyncs to the compositor. | -| **Linux** | Depends on compositor (GTK embedder) | No app API; compositor-dependent | Wayland compositors with VRR may need monitor config; X11 follows monitor. | +| **Linux** | Depends on compositor (GTK embedder) | **No app unlock** — `RefreshRate.enable()` is a **no-op** | Plugin is **query-only** (`gdk_monitor_get_refresh_rate`). Overlay shows compositor-reported Hz; it does **not** force 120+. Configure the Wayland/X11 compositor / monitor for VRR/high-Hz. | | **macOS** | Capped to 60 on some setups | macOS 14+ can opt into ProMotion/high-Hz | Historically Flutter macOS did not always hit ProMotion; needs verification. | | **iOS** (future) | No — capped to 60 | `CADisableMinimumFrameDurationOnPhone=true` in `Info.plist` | Not applicable today (no `ios/`), document for when mobile lands. | | **Android** (future) | No — often picks 60 | `flutter_displaymode` / `Surface.setFrameRate()` | Not applicable today (no `android/`). | @@ -64,9 +64,10 @@ Root cause (engine): per `flutter/flutter#160952`, the engine "can render at 120 ### Practical implication for Querya (desktop) -- **Windows / Linux:** most likely already render at monitor Hz; the job is to **measure and verify**, then ensure no app-side code caps frames (e.g. heavy `setState`, unbounded rebuilds during animation). -- **macOS:** the real high-Hz work — confirm ProMotion behavior; unlock via `refresh_rate` if capped at 60. -- Use `refresh_rate` (or a thin wrapper) primarily for **diagnostics**: a debug-only FPS/Hz overlay and a benchmark to prove smoothness on each machine, plus the macOS unlock call in `main()`. +- **Windows:** usually follows DWM monitor Hz; **measure and verify**. +- **Linux:** **query-only** — `enable` / prefer-max are no-ops in `third_party/refresh_rate` Linux plugin. Overlay/`RefreshRate.info` report what GDK sees; actual frame pacing is compositor-dependent. Do not treat a 120/144 reading as “unlocked by Querya”. +- **macOS:** unlock via `RefreshRate.enable()` when capped at 60 (ProMotion / high-Hz). +- Use `refresh_rate` for **diagnostics** (debug overlay / benchmark) plus the macOS unlock in `main()`. - **Implementation:** `lib/core/motion/display_refresh_service.dart` calls `RefreshRate.enable()` in `main()`. Debug Hz badge: `flutter run --dart-define=QUERYA_REFRESH_OVERLAY=true`. --- @@ -167,7 +168,7 @@ The table below shows the measured refresh rates and frame times on target monit | Platform | Monitor Target | Before 0.4.4 | After 0.4.4 | Frame Build/Raster Time (Max) | Status | |----------|----------------|--------------|-------------|--------------------------------|--------| | **Windows 11 (DWM)** | 120 Hz | 120 Hz | 120 Hz | 4.2 ms / 2.8 ms (under 8.3ms) | verified | -| **Linux (Ubuntu X11)** | 144 Hz | 144 Hz | 144 Hz | 3.5 ms / 3.0 ms (under 6.9ms) | verified | +| **Linux (Ubuntu X11)** | 144 Hz | 144 Hz | 144 Hz | 3.5 ms / 3.0 ms (under 6.9ms) | verified (compositor Hz; query-only) | | **macOS 14+ (ProMotion)** | 120 Hz | 60 Hz | 120 Hz | 4.8 ms / 3.2 ms (under 8.3ms) | verified (unlocked) | *Note: macOS ProMotion requires `RefreshRate.enable()` called in `main()` to bypass the default 60 Hz cap.* diff --git a/docs/perf-baseline.md b/docs/perf-baseline.md index 2593c25f..aaf8043c 100644 --- a/docs/perf-baseline.md +++ b/docs/perf-baseline.md @@ -19,6 +19,7 @@ To verify that the motion system conforms to the budget and does not cause jank - 120 Hz budget: **8.3 ms** per frame - 144 Hz budget: **6.9 ms** per frame 6. **Hz Verification**: Run the app with `--dart-define=QUERYA_REFRESH_OVERLAY=true` in a debug/profile build. The floating overlay must show the correct target Hz. + - **Linux:** overlay is **query-only** (`RefreshRate.enable` is a no-op). Confirm the compositor/monitor is actually driving that Hz (Wayland/X11 settings); Querya cannot unlock a higher rate. 7. **Animation Smoothness (DevTools)**: - Record the timeline in the **Performance** tab while triggering animations (dialog fade-in, tree expand/collapse, tab cross-fading, dropdown show). - Ensure the frame build and raster times stay below the respective Hz budget (e.g., < 8.3 ms on a 120 Hz monitor). diff --git a/lib/core/motion/display_refresh_service.dart b/lib/core/motion/display_refresh_service.dart index 55b4e76e..870d9d2f 100644 --- a/lib/core/motion/display_refresh_service.dart +++ b/lib/core/motion/display_refresh_service.dart @@ -3,9 +3,13 @@ import 'package:refresh_rate/refresh_rate.dart'; /// Desktop display refresh-rate unlock and diagnostics (UI-A4 / #174). /// -/// Calls [RefreshRate.enable] once at startup (macOS 14+ ProMotion unlock; -/// query on Windows/Linux). Debug overlay: run with -/// `--dart-define=QUERYA_REFRESH_OVERLAY=true`. +/// Calls [RefreshRate.enable] once at startup: +/// - **macOS 14+:** ProMotion / high-Hz unlock +/// - **Windows:** typically follows DWM (enable is effectively a no-op unlock) +/// - **Linux:** **query-only** — enable is a no-op; [RefreshRate.info] / overlay +/// report GDK monitor refresh (compositor decides actual pacing) +/// +/// Debug overlay: `flutter run --dart-define=QUERYA_REFRESH_OVERLAY=true`. abstract final class DisplayRefreshService { static const bool _overlayFromEnvironment = bool.fromEnvironment( 'QUERYA_REFRESH_OVERLAY',