Commit Graph
6 Commits
Author SHA1 Message Date
vicrodh 2e3c765616 feat(qt): polarity-aware ambient veil and weak-ramp hardening for light themes
The ambient legibility scrim was a fixed BLACK layer at ambientDim — right
for dark themes, but over a LIGHT theme it pushed the field to mid
luminance, the worst ground for dark text, and the grey secondary/muted
ramp drowned. Light themes now veil with their own near-white surfaceMain
a notch stronger, with the in-shader (mode 1) and in-atmosphere (mode 2)
darkening turned off there; dark themes keep the exact previous look.
QbzTheme additionally steps textSecondary/textMuted toward textPrimary
while ambient is on under a light theme, so the weak ramp keeps a
contrast floor everywhere without call-site changes.

cc
2026-08-31 15:47:44 -06:00
vicrodh e6c058c2f2 feat(qt): immersive shader scenes — RHI items, scene menu, GL link pairing
Port the immersive GPU scenes: Plasma, Tunnel, Aurora, Ambient, Spectral
Ribbon and Line Bed as QQuickRhiItems/ShaderEffects under a pulse-driven
ShaderSceneLayer, plus the Qt-only Tunnel Flow (Tauri Canvas2D port) fed
by the Viz16 stream. Scene rows live in the header view menu (no Off row);
g cycles the ring. A fresh immersive open defaults to Split > Lyrics
(owner ruling).

OpenGL link fix (owner smoke: Tunnel black with "uniform _22 declared as
type buf and type buf"): every ShaderEffect fragment with its own uniform
layout now ships a paired vertex stage declaring the identical block —
the shared spectrum.vert only ever linked under Vulkan.

RHI hardening: render pass descriptors on the ping-pong targets (the
Tunnel Flow segfault / Plasma dead window), mirrorVertically tied to the
backend (!== OpenGL), ribbon sub-uploads carry setSourceSize.

km
2026-08-16 08:22:57 -06:00
vicrodh f46c5c95d4 perf(qt): one repaint pulse for the whole shell (GPU 95% -> 25%)
Qt Quick has no partial redraws: any dirty item presents the WHOLE
window, and on the owner's hybrid laptop each full-window present costs
~1.2% GPU flat (KWin composite + dGPU scanout; area-independent, while
the app's own render is 0.08 ms/frame). The bill was presents/s, and
the shell ran unsynchronised ~30 Hz clocks plus two leaks:

- QbzShell.pulseMs: ONE Rust-driven 33 ms edge that every continuous
  animator ticks on (knob QBZ_PULSE_MS). VizSettle now stashes on
  publish and applies on the pulse (its 16 ms interpolation timer is
  gone); AmbientField and ImmersiveAtmosphere ride the same edge.
- VizSettle writes NOTHING while not live: the always-mounted but
  invisible immersive panels were dirtying 28 bar rects per FFT
  publish (named via QT_LOGGING_RULES=qt.quick.dirty=true).
- AmbientField renders inline again: ShaderEffectSource
  scheduleUpdate() double-presents (minimal repro fbo_repro.qml in the
  investigation folder).
- Background mode 2 (ImmersiveAtmosphere) composites its four drifting
  layers + base + dim + scrim in ONE opaque ShaderEffect
  (atmosphere.frag, baked by build.rs); numeric parity vs the Image
  stack verified (atmo-shader-parity-check.py, delta 0.0). The
  four-Image stack stays as the software/failure fallback.

Measured on the owner's machine: 93-97% -> 25-26% GPU, ~34 W -> 12-13 W,
~33 presents/s in both background modes. Full story:
qbz-nix-docs/qt-frontend/2026-08-11-scenegraph-batches/GPU-COST-INVESTIGATION.md

km
2026-08-13 13:01:50 -06:00
vicrodh b580fdafad fix(qbz-qt): rasterize every Canvas on the GUI thread
The owner hit a segfault after clicking around. Cooperative and Threaded
rasterize on the scene-graph RENDER thread, and RoundedImage is instantiated in
25 files — every card, row and queue row — so list recycling creates and
destroys hundreds of these while async artwork callbacks schedule repaints on
them. The render thread can be mid-draw on a pixmap whose Canvas is already
gone. The ambient field makes it worse by repainting 30 times a second for the
whole window lifetime on that same thread.

Immediate keeps paint and destruction on one thread. renderTarget: Image
already meant CPU raster, so nothing looks different.

Not yet confirmed on hardware: the decisive check is running the previous
binary with QSG_RENDER_LOOP=basic, which gives Cooperative no separate thread
to race with. If that survives the repro and the default loop does not, this is
the cause.

cc
2026-07-28 16:22:19 -06:00
vicrodh aabde3be63 fix(qbz-qt): unresolvable QML imports, focus-based freezing, and dock render cost
Three views instantiated components the phase-23 directory move had left
unimportable: PlaylistView and SearchView use TrackRow (now in rows/) and
SectionRail uses AlbumCard (now in cards/). QML resolves lazily, so these
survived cargo check, the test suite and the offscreen smoke — they only
failed when the user opened a playlist. Adds a repo-wide audit of every
instantiated component against the importing file's visible directories; the
three above were all of them.

The ambient field and the new dock band froze on lost focus. That is wrong on
any tiling or mosaic desktop, where windows stay fully visible while
unfocused — the field would stop while the user is looking at it. Both now
gate on the window not being minimized or hidden, which is the real "nobody
can see this". Freezing on not-visible stays.

Dock band render cost, in descending order of impact: the drain publishes
only the stream the active mode consumes (Bars mode was marshalling a
512-float waveform QList 30 times a second for nothing); each mode marshals
its QList into JS once per tick instead of once per delegate; one shared
Gradient replaces 28 per-delegate instances; and the per-bar Behaviors are
gone since qbz-audio already smooths and clamps the amplitudes upstream.

18/18 tests; offscreen smoke clean; component-resolution audit clean.

cc
2026-07-28 12:26:40 -06:00
vicrodh 9a4fec6b70 refactor(qbz-qt): phase 23 part 1 — QML module layout + first five domain bridges
Two fronts of the modularization, both gated green before this commit.

QML leaves the flat qml/ directory for seven modules: cards, controls,
rows, settings, shell, theme, views. Only Main.qml and LoginScreen.qml
stay at the root. build.rs matches the tree exactly (55 files), relative
imports and asset paths were re-anchored for the new depth.

The QbzBridge God-object starts breaking up: QbzSession, QbzShell,
QbzPlayer, QbzQueue and QbzHome are now their own #[cxx_qt::bridge]
singletons, each with its own OnceLock<CxxQtThread> and ui() hop, booted
in order from Main.qml. Bridge files stay directly under src/ because
cxx-qt-build only accepts rust_files from one directory (QTBUG-93443).

bridge.rs keeps 5.5 domains (library, album/artist, settings,
integrations, search, playlist) and dies once they are split out.

cc
2026-07-28 11:08:20 -06:00