Skip to content
Merged
5 changes: 5 additions & 0 deletions .github/workflows/windows-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
# Some permanent regression guards inspect historical source via
# `git show <old-sha>:<path>`. The default depth=1 checkout cannot
# satisfy those tests.
fetch-depth: 0

- name: Set up Python 3.11.9
uses: actions/setup-python@v5
Expand Down
1,477 changes: 461 additions & 1,016 deletions Current_Plan.md

Large diffs are not rendered by default.

106 changes: 62 additions & 44 deletions Docs/Compositor_Architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Runtime Presentation Architecture

Last updated: 2026-08-20
Last updated: 2026-08-21

## 1. Decision

Expand Down Expand Up @@ -33,16 +33,17 @@ DisplayWidget
└── OpenGL QRhiWidget path
```

During migration that code is a **reference/rollback implementation**, not the destination.
Until Phase H cutover that code may remain the current production/reference implementation. It is
not the destination and is not a permanent fallback architecture.

Do not:

- expand the old presenter to avoid migration work;
- create new QRhiWidget-specific architecture;
- treat current class names as permanent product contracts.
- treat current old class names as permanent product contracts;
- add a production runtime switch between old and Quick presenters.

Do not delete the reference path until the active migration plan has established the replacement and
passed the required cutover gates.
Do not delete the reference path until the active migration plan reaches the cutover/deletion phases.

## 3. One-surface invariant

Expand All @@ -54,26 +55,27 @@ Allowed inside that surface:
- transition rendering;
- visualizer/card;
- runtime overlays;
- compositor-equivalent custom render items.
- retained Quick widgets;
- inline custom render nodes.

Forbidden:

- separate native visualizer window;
- transparent accelerated overlay window;
- per-widget accelerated top-level surface;
- `QQuickWidget` as the runtime presenter.
- `QQuickWidget` as the runtime presenter;
- per-effect fallback to an independently presented old surface.

## 4. Threading model

The destination presenter requires the Qt Quick **threaded** scene-graph render loop on the supported
Windows path.

The GUI thread remains responsible for GUI/event-loop work and may prepare/publish state.
The GUI thread remains responsible for GUI/event-loop work and may prepare/publish synchronized state.

The Quick render thread owns the rendering phase according to the selected Qt Quick primitive.
The Quick render thread owns custom rendering according to the selected inline scene-graph primitive.

Do not move visualizer logical simulation onto the render thread merely because a render thread now
exists.
Do not move visualizer logical simulation onto the render thread merely because a render thread exists.

`VisualizerLogicalRuntime` remains independent and authoritative for authored visualizer time.

Expand Down Expand Up @@ -103,30 +105,41 @@ Properties:
- generation fencing;
- stale-state rejection.

The exact bridge may use Qt properties/models, explicit synchronization objects, custom item
`synchronize()` state, or another bounded mechanism chosen by the migration plan.
The exact bounded GUI→Quick synchronization object may vary by owner, but those semantics may not.

## 6. Renderer primitives
## 6. Selected renderer primitives

Do not lock the product to one primitive before the migrated scene requires it.
The ordinary retained-presentation primitive is normal Qt Quick items/components.

Possible shapes include:
The selected SRPSS custom-OpenGL primitive is:

- ordinary retained Quick items;
- shader/effect items;
- `QQuickRhiItem`;
- `QSGRenderNode`;
- custom render-stage integration.
```text
QQuickItem(ItemHasContents)
-> updatePaintNode()
-> QSGRenderNode
-> direct OpenGL inside the owning QQuickWindow scene
```

This choice was proved during the Qt Quick foundation and is the current custom-render contract for
transitions and the visualizer migration.

Why this is the selected path:

Prefer the simplest primitive that:
- custom rendering stays inline in the one scene;
- correct stacking with retained Quick content;
- no extra offscreen texture/composite pass solely to reinsert the effect;
- supports existing shaders, meshes, depth, VAOs/VBOs, and context-local resources;
- preserves one physical presentation surface.

- preserves exact visual fidelity;
- keeps one top-level presentation surface;
- respects thread/resource ownership;
- meets physical cadence requirements.
`QQuickRhiItem` is not the normal/final SRPSS custom-render path. `QQuickWidget` is prohibited.

A local native/C++ renderer may be considered only after profiling proves Python callback/render
cost is material. It must remain inside the accepted Quick window architecture.
If pinned PySide/compiled-product evidence proves the selected `QSGRenderNode` seam fundamentally
unusable, stop and deliberately revise the **single** custom-render primitive. Do not keep multiple
product primitives as compatibility fallbacks.

A localized native/C++ renderer may be considered only if profiling of the migrated implementation
proves a specific Python render callback materially limits the result. It must stay inside the same
QQuickWindow/scene ownership.

## 7. Visualizer

Expand All @@ -137,17 +150,19 @@ source/audio
VisualizerLogicalRuntime
latest logical/render state
latest immutable logical/render state
Quick visualizer item synchronization
Quick scene presentation
QSGRenderNode custom GL
```

The logical runtime never mutates Quick scene objects or GPU resources.

The presenter never advances authored visualizer simulation.

Card and visualizer pixels share one scene/fade authority where they must appear as one authored
visual object.
Card and visualizer pixels share one scene/fade/geometry authority where they must appear as one
authored visual object.

## 8. Runtime overlays

Expand All @@ -160,19 +175,18 @@ existing Python data/model owner
small presentation state
Quick runtime item/layer
retained Quick runtime item/layer
```

Avoid reimplementing network/provider/business logic in QML.

The one Quick scene should own runtime pixels that visually coexist over the screensaver.
The one Quick scene owns runtime pixels that visually coexist over the screensaver.

## 9. Readiness / first frame

A runtime window must not be visibly exposed until it can show intentional current-generation
content.
A runtime window must not be visibly exposed until it can show intentional current-generation content.

Eventually preserve:
Preserve:

- no white/default flash;
- no black placeholder;
Expand All @@ -193,18 +207,22 @@ Do not make real audio/source freshness a universal prerequisite for an intentio

Topology, Settings/recreate, Edit, and shutdown remain generation-owned.

Old generation must retire before replacement gains authority.
Old generation retires before replacement gains authority.

Quick scene/render resources are destroyed on the legal render/context owner for the selected
`QSGRenderNode` contract.

Quick scene/render resources must be destroyed on the legal owner/thread for the selected primitive.
Do not copy QRhiWidget-specific context assumptions into Quick.

Do not copy QRhiWidget-specific context assumptions into Quick without verifying the new ownership
contract.
Generation `0` remains valid.

## 11. Transition model

Transition logical/progress semantics remain display-local and monotonic with exactly-once completion.
Transition request/run semantics are display-local, immutable, monotonic, and exactly-once for
completion/cancellation.

The migration should preserve existing transition shaders/behaviour where practical.
Canonical transition implementations resolve lazily and render through the display's inline Quick
custom-render owner. Existing authored shader/math is preserved where valid.

Do not individually retune transitions to hide physical frame holes.

Expand All @@ -220,5 +238,5 @@ Physical presentation is judged primarily by:

Internal render callbacks are not physical-display proof.

The P0 result justifies migration. Future evidence is for implementation/cutover quality, not for
The P0 result justifies migration. Later evidence is for implementation/cutover quality, not for
re-litigating Quick versus the old presenter on every step.
48 changes: 34 additions & 14 deletions Docs/Contracts.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contracts

Last updated: 2026-08-20
Last updated: 2026-08-21

Fast task-to-owner routing during the Qt Quick presentation migration.

Expand All @@ -23,30 +23,45 @@ Do not turn temporary old ownership into a new permanent contract.

| Family | Durable owner/direction | Focused document |
|---|---|---|
| Runtime start/stop/recreate | `ScreensaverEngine` / display lifecycle owners | `Docs/Compositor_Architecture.md` |
| Monitor topology | `DisplayManager` / topology owner | `Docs/Compositor_Architecture.md` |
| Runtime physical surface | destination: one standalone `QQuickWindow` per display | `Docs/Compositor_Architecture.md` |
| Runtime scene pixels | destination: Quick scene/render owner | `Docs/Compositor_Architecture.md` |
| Runtime start/stop/recreate | `ScreensaverEngine` / display lifecycle owners | `Docs/QtQuick_Migration/01_Runtime_Host_Lifecycle.md` |
| Monitor topology | `DisplayManager` / topology owner | `Docs/QtQuick_Migration/01_Runtime_Host_Lifecycle.md` |
| Runtime physical surface | one standalone `QQuickWindow` per display | `Docs/Compositor_Architecture.md` |
| Ordinary runtime scene pixels | retained Quick items/components | `Docs/Compositor_Architecture.md` |
| Custom GL scene pixels | inline `QQuickItem -> QSGRenderNode -> OpenGL` | `Docs/Compositor_Architecture.md` |
| Settings/config UI | existing QWidget/settings owners | `Spec.md` |
| Widget data/provider lifecycle | existing Python owners | `Docs/10_WIDGET_GUIDELINES.md` |
| Runtime widget pixels | destination: display Quick scene | `Docs/10_WIDGET_GUIDELINES.md` |
| Widget data/provider lifecycle | existing/refactored Python owners | `Docs/QtQuick_Migration/04_Widget_Runtime_Presentation.md` |
| Runtime widget pixels | destination: display retained Quick scene | `Docs/QtQuick_Migration/04_Widget_Runtime_Presentation.md` |
| General async work | `ThreadManager` | `Docs/Guardrails/Runtime_Efficiency.md` |
| Resource accounting | `ResourceManager`; never deletion fallback | `Docs/Guardrails.md` |

`QQuickRhiItem` is not the normal SRPSS custom-render path. `QQuickWidget` is not an acceptable runtime presenter.

## Transition ownership

| Family | Owner | Contract |
|---|---|---|
| Canonical id/settings identity | `rendering/transition_registry.py` | stable descriptor/catalog authority |
| GUI/runtime parameter resolution | Quick transition request resolver | canonical defaults/random choices resolved before render ownership |
| Transition lifecycle/time | `TransitionRequest` / `TransitionRun` | immutable, monotonic, exactly-once completion/cancel |
| Transition implementation | lazy static Quick implementation registry | disabled implementations/resources remain dormant |
| Transition pixels/resources | display transition `QSGRenderNode` host + implementation | no old-compositor fallback or state leak |

See `Docs/Transition_Change_Checklist.md` and `Docs/QtQuick_Migration/02_Scene_Renderer_Transitions.md`.

## Visualizer ownership

| Family | Owner | Contract |
|---|---|---|
| Audio capture / analysis | BeatEngine + audio worker/backend | bounded current source |
| Logical cadence | `VisualizerLogicalRuntime` | one authored mode-general clock |
| Logical cadence | `VisualizerLogicalRuntime` | sole authored mode-general clock |
| Logical integration | worker-callable tick pipeline | no GUI/Quick/GL mutation |
| Logical publication | latest-state mailbox/state bridge | latest wins; generation fenced |
| Presentation bridge | migration-owned bounded GUI/Quick synchronization | no paint acknowledgement |
| Visualizer pixels | destination: Quick scene/render item(s) | inside sole display window |
| Logical publication | latest-state mailbox/snapshot bridge | latest wins; generation fenced |
| Presentation bridge | migration-owned bounded GUI/Quick synchronization | immutable; no paint acknowledgement |
| Visualizer pixels | display Quick visualizer item + `QSGRenderNode` | inside sole display window |
| Bubble temporal fidelity | shared chain + Bubble authored state | BTF binding |

The historical `SpotifyBarsGLOverlay` may remain as temporary state/resource code during migration.
Its class name is not a contract and it must not become a separately presented surface.
The historical `SpotifyBarsGLOverlay` may remain as temporary state/resource/reference code during
migration. Its class name is not a contract and it must not become a separately presented surface.

## Physical presentation

Expand All @@ -57,13 +72,16 @@ QQuickWindow per display
threaded scene-graph render loop
retained Quick items + inline QSGRenderNode custom GL
one composed runtime scene
```

Forbidden:

- `QQuickWidget` presenter;
- second accelerated visualizer window;
- per-effect old-compositor fallback;
- paint/present acknowledgement;
- producer/display divisor gating;
- FIFO/catch-up;
Expand All @@ -75,7 +93,7 @@ Forbidden:
There is no scheduled native/C++ presenter migration.

Native code may be used only for a measured local renderer problem and must preserve the one
`QQuickWindow` presentation topology.
`QQuickWindow` presentation topology and the same logical/state contracts.

## Readiness

Expand All @@ -102,6 +120,8 @@ A presentation-owned idle scene may reveal while real reactive source is unavail
| Active work | `Current_Plan.md` |
| Stable architecture | `Spec.md` |
| Presentation architecture | `Docs/Compositor_Architecture.md` |
| Cross-cutting safety | `Docs/Guardrails.md` |
| Transitions | `Docs/Transition_Change_Checklist.md` |
| Visualizer presentation | `Docs/Guardrails/Visualizer_Presentation.md` |
| Bubble | `Docs/Guardrails/Bubble_Temporal_Fidelity.md` |
| Qt Quick architecture evidence | `Docs/Performance_Evidence/QtQuick-P0-Comparison-2026-08-20.md` |
Expand Down
Loading
Loading