The rule was derived from two data points and generalised into a negation:
owner's Vulkan + mirror=false -> upside-down (Vulkan needs the mirror)
Xvfb Mesa GL + mirror=true -> upside-down (GL does not)
"mirror on everything EXCEPT OpenGL" fits both, and it was wrong about the one
API nobody had run. On the Mac mini M2 the Line Bed comes out upside-down, so
Metal wants mirror=false like OpenGL rather than =true like Vulkan — the
generalisation reached a third platform on the strength of a shape it had never
been tested against.
Kept as a NEGATION with Metal excluded rather than rewritten as "Vulkan ||
D3D". Naming the APIs that DO mirror would silently stop mirroring on any enum
this file failed to list: an unknown member is `undefined` in QML and compares
false, so a typo or a future backend would degrade to "no mirror" with no error.
This way only Metal's behaviour changes and every other backend keeps exactly
what it was verified with.
All four RHI scenes carry the line, because the convention belongs to the
graphics API and not to the scene — a per-scene split would be the surprising
result, not the expected one. Only LineBed made it observable: it is the one
directional scene, so a flip reads instantly, where Plasma and TunnelFlow are
near-radial and hide it. RIBBON IS THE ONE TO CHECK NEXT — it is a spectrogram
with a time axis, so if the convention really is per-API it should have been
flipped too, and if it now looks flipped the rule is per-scene after all.
The three sibling headers said "mirror on Vulkan/Metal/D3D" and were corrected
in place; they had become comments that contradict their own code.
Built and smoked on Linux (gate green). The observation this fixes is the
owner's, on the Mac.
cc
70 lines
3.5 KiB
QML
70 lines
3.5 KiB
QML
// Line Bed (immersive shader scene, mode 5) — the QML wrapper for the C++
|
|
// `LineBedItem` (cxx/linebed_item.*, the project's first QQuickRhiItem).
|
|
// Block A4 of the 2026-08-15 immersive-completion contract (spec 01 §2.5).
|
|
//
|
|
// Its own file (not an inline Component in ShaderSceneLayer.qml) so the RHI
|
|
// item's contract is one screen of QML — and so the C++ type name only has
|
|
// to resolve when this file compiles, which the tier gate already ensures
|
|
// never happens where the RHI can't run it (the Loader in
|
|
// ShaderSceneLayer.qml activates only while `root.active && scene === 5`).
|
|
//
|
|
// THE PULSE LAW: the item repaints ONLY from `pulseTick()`, which
|
|
// ShaderSceneLayer calls on the shared shell pulse (QbzShell.pulseMs) with
|
|
// the ring publish it stashed on the viz tick. `heights` has no NOTIFY and
|
|
// nothing binds it; the palette colors ride QbzShell's ambient triad (the
|
|
// same ambient_qt source every other scene uses — they change on track
|
|
// change, not per tick).
|
|
|
|
import QtQuick
|
|
import QtQuick.Window
|
|
import com.blitzfc.qbz
|
|
|
|
LineBedItem {
|
|
id: root
|
|
|
|
// The empirical orientation matrix (2026-08-15, BOTH verified today):
|
|
// owner's Vulkan + mirror=false -> bed UPSIDE-DOWN
|
|
// Xvfb Mesa GL + mirror=true -> bed UPSIDE-DOWN (peaks hang down)
|
|
// So the correct rule is mirror on everything EXCEPT OpenGL — my first
|
|
// guess (GL-only) had it exactly backwards, and "unconditional" broke GL.
|
|
// METAL JOINED THE MATRIX 2026-08-19, and it sides with OpenGL.
|
|
//
|
|
// The rule was derived from two data points and generalised to a negation:
|
|
// owner's Vulkan + mirror=false -> upside-down (Vulkan needs the mirror)
|
|
// Xvfb Mesa GL + mirror=true -> upside-down (GL does not)
|
|
// "mirror on everything EXCEPT OpenGL" fit both, and it was wrong about the
|
|
// API nobody had run: on the Mac mini M2 (Metal) the bed came out
|
|
// upside-down, i.e. Metal wants mirror=false like GL, not =true like
|
|
// Vulkan.
|
|
//
|
|
// Kept as a NEGATION with Metal excluded rather than rewritten as
|
|
// "Vulkan || D3D": naming the APIs that DO mirror would silently stop
|
|
// mirroring on any enum this file does not list — an unknown member is
|
|
// `undefined` in QML and compares false, so a typo or a future backend
|
|
// degrades to "no mirror" with no error. This way only Metal changes and
|
|
// every other backend keeps exactly the behaviour it was verified with.
|
|
//
|
|
// LineBed is what made it visible — it is the one directional scene, so a
|
|
// flip reads instantly. The convention belongs to the graphics API and not
|
|
// to the scene, so all four RHI items carry the same line.
|
|
mirrorVertically: GraphicsInfo.api !== GraphicsInfo.OpenGL
|
|
&& GraphicsInfo.api !== GraphicsInfo.Metal
|
|
|
|
// The ambient palette triad (shell_bridge.rs; pushed per track by
|
|
// playback_qt.rs). The Slint reference defaults #00dcc8 / #3fd9c8 live
|
|
// in the C++ member initializers, so a pre-art track still looks right.
|
|
primary: QbzShell.ambientPrimary
|
|
accent: QbzShell.ambientAccent
|
|
|
|
// Called by ShaderSceneLayer on the pulse edge ONLY. `h` is the stashed
|
|
// 256x200 f32 ring (QbzShaderScene.linebedHeights, raw bytes) or null
|
|
// when the viz drain published nothing new (player paused); the repaint
|
|
// still runs so the item tracks its parent's resizes/palette changes at
|
|
// the same cadence the sibling ShaderEffect scenes tick their clocks.
|
|
function pulseTick(h) {
|
|
if (h !== null)
|
|
heights = h
|
|
update()
|
|
}
|
|
}
|