Repository navigation
FG-Legacy: frame generation levels (2×–4×), capability test, in-stream toggle and pacer fixes - #1300
Conversation
…splay refresh Frame generation grows from a single 2x switch into a measured feature set: - Levels off/2x/3x/4x (`frameGeneration`), a target output rate (`frameGenerationTargetFps`, 0 = follow the level; the level is derived as round(target / stream fps)) and a display refresh override (`frameGenerationDisplayHz`, 0 = the screen OpenNOW is on). The core validates all of them plus a bounded `frameGenerationProfiles` map. - StreamFramePacer schedules N-1 generated frames per source interval (`generated()` / `takeNext()`), the display gate scales with the factor and the fixed 120 FPS ceiling is replaced by a 4 ms source floor. The interpolator contract reports how many frames it can synthesise (`generatedCount()` / `generatedTexture()`); the block-matching engine stays midpoint-only, so 3x/4x need the FG-Legacy engine. - FrameGenerationBenchmark runs the engine offscreen on a private QRhi at the selected stream resolution with procedural frames and reports p50/p95 per generated frame (GPU timestamps when the backend delivers them, wall time around the blocking offscreen frame otherwise); `evaluate()` allows a level when p95 * (N-1) fits 60 % of a source interval, the display shows fps * N and the engine supports N. Results are saved per preset (engine|gpu|WxH|fps). Levels and targets stay locked in Settings until a passing profile exists; a selection the test rules out resets to Off. SettingsState owns the logic; desktop and console screens share it. - Settings rows: level segments, Run test, a GitHub link to the Framegen repository, Target FPS and Display refresh; stats badge FRAME GEN Nx; "Source rate too high" replaces the 120 FPS limit label. - FG-Legacy attribution: third_party/framegen notices and a THIRD_PARTY_NOTICES section; docs/fg-legacy.md describes the design. Tests: framepacer (N-x schedule, display scaling, floor), a new framegenerationbenchmark test (decision rules + a real measurement), nativeframegeneration/streamvideo/frameinterpolator/localization, the frame-generation QML acceptance (locked until tested, unlocked by a profile) and the Rust settings tests. QML smoke tests that open the stream route only run with a visible window on Windows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eature - One mode control instead of three parallel settings: Off / Multiplier / Target FPS / Display (`frameGenerationMode`). Only the control the mode needs is shown: multiplier chips, or the output rates the display can show at 2x..4x the stream rate. Display mode takes the largest allowed multiplier for the detected refresh rate. Profiles saved before the mode existed with a level set read as multiplier mode. - The capability test is its own row (status + Run test + Framegen on GitHub). Until it has passed for the current preset the feature is unavailable everywhere: mode controls are disabled and the stream surfaces ignore a stored level (`frameGenerationFactor()` is 0 unless the profile allows the requested factor). The stats acceptance seeds a passing profile; the QML acceptance checks the locked and unlocked states. - Display refresh override moves under Advanced (Auto shows the detected rate); the first-run Boost hint mentions the test. - Measurement falls back to wall time around the blocking offscreen frame when the backend reports no GPU timestamps (D3D11 does for offscreen frames); `gpuTimed` reflects it. New opennow-framegenerationbenchmark-tests covers the decision rules and a real measurement. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The capability test measured the GPU correctly but the verdict was frozen at test time and every empty result read "Too slow", even when the display was the limit. Profiles now store the measurement only, keyed by engine, GPU and resolution (the cost does not depend on the frame rate); which levels fit is recomputed from the selected frame rate and the current display whenever either changes. The status names the actual reason: the display cannot show fps x 2, the GPU exceeds the budget, or the engine is midpoint-only; chip hints follow the same reasons. Measurements saved with the old fps-suffixed key remain valid. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…el table Run test now opens a dialog that shows every option at once instead of one verdict for the current preset: the detected display rate with its override, the stream rate chips, the mode chips, and a table with one row per stream rate and one cell per level (2x, 3x, 4x). Each cell shows the output rate and a check mark or the reason it does not fit (display, GPU, engine); greyed cells explain the numbers on hover. Clicking a row picks the stream rate, clicking a cell the level or target, and Apply writes fps, mode and level together. The GPU is measured once when no profile exists. SettingsState gains frameGenerationVerdictFor(fps) and frameGenerationMatrix(); --smoke-frame-generation-dialog seeds a 2 ms / 240 Hz measurement and opens the dialog for screenshots. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rant pacer; persisted choices Ctrl+G gains a Frame generation tile while the feature is in use (on, or switched off from the menu this session); switching it off remembers mode, level and target in memory and switching it on restores them. The same toggle is bound to shortcutToggleFrameGeneration (default Ctrl+Shift+G) and reports through the stream control message. Frame generation choices now go through setSetting, so they persist across restarts (they were only applied to the local settings map before). The presenter acquires the latest decoded frame, so a burst skips one source frame: StreamFramePacer now treats a single skipped frame as continuous once the cadence is known (per-frame deltas feed the cadence estimate) and only starts the two-second overload cooldown after four collisions within a second; a single early arrival just drops the stale schedule. The stats overlay names the reason behind a Discontinuity. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…times every level The synthesize pass takes t: the previous frame travels t of its forward flow and the current frame (1 - t) of its backward flow, blended 1 - t : t, so one pair yields N - 1 frames at t = k / N. Each generated frame has its own render target and its own uniform buffer per slot, so the passes of one pair never overwrite each other's t inside a command buffer. MaximumFactor is 4; midpointTexture() keeps returning the frame nearest t = 0.5. The capability test now times 2x, 3x and 4x in turn (one second each) and stores p95Ms, p95Ms3 and p95Ms4; evaluate() uses the measured per-level cost and falls back to p95 x (N - 1) for profiles from before. The engine maximum comes from the installed engine, not the stored profile, so an earlier measurement no longer caps the levels. On the RTX 4060 Ti the synthesis pass is nearly free next to the flow (0.23 / 0.27 / 0.23 ms per source frame at 640x360). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
StreamVideoTextureRenderer held three external texture slots (two history frames and the midpoint), so the second generated frame of a 3x or 4x pair failed selectTexture() and generation switched itself off with 'unavailable'. Five slots now cover the two history frames plus up to three generated frames. New tst_nativeframegeneration case drains 4x pairs through the production callback and asserts the status never drops to unavailable. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ctors The display gate used a stricter tolerance than the capability test and judged it on a single frame's timestamp delta, so 60 FPS x 4 on a 240 Hz panel flipped between active and display-refresh with every jittery frame. The pacer now smooths the source interval with the median of the last eight per-frame timestamp deltas (arrival deltas as the fallback) and applies the test's 0.975 tolerance. A source frame that lands on an unfinished pair is a burst, not overload: the stale schedule is dropped and the newest pair wins. Overload is reserved for a stalled presenter, which the render callback reports when nothing new has been swapped for max(100 ms, 4 x interval). Every frame-generation status change is written to qt-native.log with the pacer's numbers, throttled to five lines a second, so flicker can be diagnosed from a real session. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The pacer's overload state means the display stopped taking frames, not GPU load; the stats overlay now says so instead of 'Overloaded'. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ghtly short; log GUI stalls A live '60 FPS' cloud stream ran at 61.7 FPS, so 4x needed ~247 Hz and the 240 Hz panel kept flipping generation into display-refresh. The display gate now tolerates 5 % overshoot and, when the display is still too slow, lowers the factor for the pair (the render callback ingests and schedules the pacer's effective factor); only when 2x does not fit is generation off. The live log also showed a three-frame sequence gap every second. Frames are requested from the GUI thread, so StreamVideoItem now runs a 10 ms GUI-thread probe and logs stalls of 35 ms or more to qt-native.log. A wall-clock sensitive native test step was tightened so its arrival cadence stays inside the pacer's window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…live capture diagnostic Settings rows can carry a status pill; frame generation shows an Experimental badge and, on desktop, console and in the capability dialog, a notice that the current engine was not designed for games and shows strong artifacts on thin edges and fast motion until the FlyHigh model replaces it. Diagnostics: OPENNOW_FG_CAPTURE_DIR makes the render callback save the pair and every generated frame as PNG a few times per session, and OPENNOW_FG_DUMP runs the engine on two image files in tst_frameinterpolator (skipped otherwise). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
|
@capyai give me screenshots how the settings looks |
|
Screenshots of the frame generation settings at this PR's head, from the desktop settings smoke harness (Linux, Xvfb, 1440×900, 1440p/120 FPS profile). The "tested" states use the PR's Before the capability test (frame generation locked) Capability test dialog After the test: Multiplier mode Target FPS mode Display mode Advanced → Display refresh override Crash on Linux: on this branch the Stream settings page segfaults as soon as it opens, on every Vulkan (non-Windows, non-Metal) build. Declaring struct PrivateRhi {
- std::unique_ptr<QRhi> rhi;
#if !defined(Q_OS_WIN) && !QT_CONFIG(metal) && QT_CONFIG(vulkan) && __has_include(<vulkan/vulkan.h>)
std::unique_ptr<QVulkanInstance> instance;
#endif
+ std::unique_ptr<QRhi> rhi;
QString error;
};Windows (D3D11) and macOS (Metal) don't take this path, which is why it didn't show up in the Windows testing. |
|
Thanks for this. The design holds up: generation stays inside the existing presenter, it's off by default, storage is bounded, and sender timestamps are kept. Review on Crash (P1)
Capability gate (P2)
Pacer (P2)
Test
Full suite ( |
|
@MONZikWasTaken GitHub currently reports merge conflicts with |
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
Pushed two separately reviewable repairs without merging this PR:
After the final edits, the application builds and all 551 noninteractive Qt CTests pass. The parent independently reran the benchmark, native frame-generation and orchestration targets successfully. Windows-only adapter tests still need Windows CI; Linux uses llvmpipe and does not establish physical hybrid-GPU acceptance. This screenshot shows the real Qt dialog with seeded smoke timings, not measured GPU performance: The seven existing dev-integration conflicts remain, so these passing checks do not make the feature merge-ready. |
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
Published current-dev integration as merge commit All seven real conflicts are resolved. The integration retains dev's sender-authored timing, sender-paced admission/deadlines, exact borrowed-handle release, and scene-graph/device teardown. Native streamer and New combined-contract tests cover sender-paced bypass at factors 0/2/4, borrowed NO_FRAME/STALE_FRAME handles while draining a 4× pair, and optional-control navigation in windowed/fullscreen modes. The benchmark is now included in dev's CI unit targets and Windows runtime/deployment lists; its registration regression failed before the fix. Validation on the final integrated tree:
Fresh exact-head cross-platform CI/review is still required. Physical Windows D3D11/hybrid-GPU adapter selection and presentation feedback, macOS pointer confinement, and real GFN gameplay are not validated by this Linux/llvmpipe run. The real Qt menu below uses synthetic acceptance telemetry and optional-control visibility, not gameplay or physical pointer-lock evidence. |
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
Published two separately scoped follow-ups:
The behavioral pacing patch passes all 591 Qt CTests and the 36-case pacer suite. After the final parentheses-only normalization, the app rebuild and all five affected native/pacer/sender/benchmark/stream-video targets pass; parent independently reran those same five targets. Fresh exact-head Windows CI is required to clear the reported failure. The prior Windows job did compile both the selected-adapter benchmark source and its tests, but stopped before executing them at the callback compile error. |
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
All seven checks passed at Published |
|
Exact-head verification at The recorded adapter is Microsoft Basic Render Driver / D3D11, with |








Description
Reworks the native Qt frame generation so it can be tested, controlled and trusted on real hardware, and restores the attribution to the Framegen project the earlier Electron integration came from (#718 → #763). The feature is called FG-Legacy in the app and docs; it is a stepping stone until the FlyHigh model replaces the engine.
What changes
Levels and modes
N − 1frames att = k / N(each with its own render target and uniform buffer); the presenter keeps five external texture slots so every frame of a 4× pair is selectable.Capability test (gates the feature)
FrameGenerationBenchmarkmeasures the engine offscreen on a private QRhi at the stream resolution, timing 2×, 3× and 4× separately (p95Ms,p95Ms3,p95Ms4). Results are stored perengine|gpu|resolutionin settings (Rust-normalised, bounded).cost(N) ≤ 0.6 × interval,refresh ≥ N × fps × 0.975.In-stream control
Pacer, fixed against a live GFN session (CS2, 1080p "60" FPS, 240 Hz)
display-refreshconstantly. The interval is now the median of the last eight per-frame deltas; the gate tolerates 5 % and lowers the factor for a pair instead of switching generation off.Discontinuity; every status change is logged toqt-native.logwith the pacer's numbers, and a GUI-thread stall probe logsgui-stall ms=…(frames are requested from the GUI thread).Attribution
third_party/framegen/README.md,WEIGHTS_LICENSE.mdand aTHIRD_PARTY_NOTICESentry restored; GitHub icon in the capability-test row opens the Framegen repository. Design notes indocs/fg-legacy.md.Diagnostics (opt-in, no effect unless set)
OPENNOW_FG_CAPTURE_DIR=<dir>saves the source pair and every generated frame as PNG a few times per session.OPENNOW_FG_DUMP=<prev.png>;<cur.png>;<outdir>;<factor>runs the engine on two images intst_frameinterpolator.Testing
Windows 11, RTX 4060 Ti, D3D11, Qt 6.8.3:
tst_framepacer24/24,tst_frameinterpolator32/32 plus the opt-in dump case skipped (new 3×/4× step test: generated frames land exactly att = k/Non translated texture),tst_framegenerationbenchmark5/5,tst_nativeframegeneration13/13 (new 4× presenter test),cargo testforopennow-core,cargo fmt --check, translation and keyboard-layout checks.Known limitations
QT_QPA_PLATFORM=offscreenon Windows ("Could not select the graphics adapter") with or without this change; they pass with a visible window.🤖 Generated with Claude Code