Files
qbz/scripts/qt-diag.sh
T
vicrodh ce7f29ff9c chore(qt): ship the binary as qbz, and log the first MPRIS metadata push
The artifact is now `target/<profile>/qbz` (owner, 2026-08-04). The package
stays `qbz-qt`, so `cargo build -p qbz-qt` is unchanged.

⚠ `crates/qbz` — the Slint binary — declares the SAME bin name and shares
this workspace's target dir, so both write `target/<profile>/qbz` and the
last build wins. Survivable only because the Slint binary is never built in
this worktree (the qbz-ui 20-30 GB wall); if it ever is, a smoke against
"the wrong qbz" is silent. Written into Cargo.toml beside the name.

The MPRIS line exists because "Plasma sees nothing" has two very different
causes — the player never registered, or it registered and no metadata was
ever pushed — and the log could not tell them apart. It settled that
question the same day: the owner's log shows `[mpris] metadata pushed:
Holy Diver — Dio (350s)`, so the port's half works and what remains is
desktop-side.

cc
2026-08-04 11:23:07 -06:00

133 lines
6.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# QBZ Qt — RENDERING DIAGNOSTIC. Launches the app with Qt's scene-graph and RHI
# logging on, samples the process's CPU while you drive it, and leaves ONE log
# file you can copy off the machine.
#
# Why this exists: the whole "this port runs on the software renderer" doctrine
# — repeated across ~8 QML file headers and used to justify pre-baked icon
# tints, Canvas-based rounded covers and a blanket no-effects rule — came from
# an observation made OFFSCREEN, and QT_QPA_PLATFORM=offscreen forces software
# by definition. On Linux, QSG_INFO in a real window says otherwise: OpenGL RHI,
# threaded render loop, Intel Arc. macOS is still unmeasured. So: measure, in a
# REAL graphical session, never over ssh and never offscreen.
#
# WHAT TO DO WHILE IT RUNS (the log is only as good as the session):
# 1. Let it sit idle on Home for ~15s.
# 2. Play a track. Let it settle ~15s.
# 3. Switch the now-playing bar through its modes (New / Classic / Small /
# Large), pausing ~15s on each — Large is the one that mounts the FFT
# spectrum, which is the suspect.
# 4. Hover a few album cards.
# 5. Close the app normally. The summary is written on exit.
#
# Usage: ./scripts/qt-diag.sh # release binary, must already be built
# DEBUG=1 ./scripts/qt-diag.sh # debug binary instead
# INTERVAL=2 ./scripts/qt-diag.sh # CPU sample period in seconds (default 2)
set -uo pipefail
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
UNAME_S="$(uname -s)"
PROFILE=$([[ "${DEBUG:-0}" == 1 ]] && echo debug || echo release)
BIN="crates/target/${PROFILE}/qbz"
INTERVAL="${INTERVAL:-2}"
[[ -x "${BIN}" ]] || { echo "[qt-diag] no binary at ${BIN} — build first (./scripts/qt-run.sh NORUN=1)" >&2; exit 1; }
# Refuse to produce a log that answers the wrong question. Offscreen forces
# software, and a headless ssh session has no compositor to talk to — either
# would make the backend line meaningless, which is the one line we are here for.
if [[ "${QT_QPA_PLATFORM:-}" == offscreen ]]; then
echo "[qt-diag] QT_QPA_PLATFORM=offscreen forces the software backend — that is the mistake this script exists to correct. Unset it." >&2
exit 1
fi
if [[ "${UNAME_S}" != "Darwin" && -z "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ]]; then
echo "[qt-diag] no DISPLAY / WAYLAND_DISPLAY — run this from the machine's own graphical session, not over ssh." >&2
exit 1
fi
stamp="$(date +%Y%m%d-%H%M%S)"
host="$(hostname -s 2>/dev/null || hostname)"
LOG="${PWD}/qt-diag-${host}-${stamp}.log"
{
echo "=============================================================="
echo " QBZ Qt rendering diagnostic"
echo " host : ${host}"
echo " when : $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo " os : $(uname -srm)"
[[ "${UNAME_S}" == "Darwin" ]] && echo " macos : $(sw_vers -productVersion)"
echo " binary : ${BIN} ($(date -r "${BIN}" '+%Y-%m-%d %H:%M' 2>/dev/null || stat -c %y "${BIN}" 2>/dev/null))"
echo " profile : ${PROFILE}"
echo "=============================================================="
echo
echo "---- display ----"
if [[ "${UNAME_S}" == "Darwin" ]]; then
system_profiler SPDisplaysDataType 2>/dev/null | grep -iE 'resolution|ui looks like|chipset|vram|metal' || true
else
xrandr 2>/dev/null | grep -E ' connected|\*' || echo "(xrandr unavailable — wayland?)"
echo "session type: ${XDG_SESSION_TYPE:-unknown}"
fi
echo
echo "---- qt ----"
(qmake6 -query QT_VERSION 2>/dev/null || /opt/homebrew/opt/qt/bin/qmake -query QT_VERSION 2>/dev/null || echo unknown) | sed 's/^/qt version: /'
echo "effects modules present:"
for d in /usr/lib64/qt6/qml /usr/lib/qt6/qml /opt/homebrew/share/qt/qml; do
[[ -d "$d/QtQuick/Effects" ]] && echo " QtQuick.Effects -> $d"
[[ -d "$d/Qt5Compat/GraphicalEffects" ]] && echo " Qt5Compat.GraphicalEffects -> $d"
done
echo "backend forced by env: QT_QUICK_BACKEND='${QT_QUICK_BACKEND:-}' QSG_RHI_BACKEND='${QSG_RHI_BACKEND:-}' QSG_RENDER_LOOP='${QSG_RENDER_LOOP:-}'"
echo
echo "---- app log (QSG_INFO + scenegraph/rhi categories) ----"
} > "${LOG}"
echo "[qt-diag] log -> ${LOG}"
echo "[qt-diag] drive the app as described in this script's header, then close it normally."
# The two lines that answer the question are 'Creating QRhi with backend X' and
# 'Prefer software device: N'. The rhi/scenegraph categories carry them.
QSG_INFO=1 \
QT_LOGGING_RULES="qt.scenegraph.general=true;qt.rhi.general=true" \
"./${BIN}" >> "${LOG}" 2>&1 &
app_pid=$!
# CPU sampler. ps reports a per-process percentage on both platforms; on macOS
# it is a share of ONE core (so >100 is possible), same as Linux.
samples="$(mktemp)"
(
while kill -0 "${app_pid}" 2>/dev/null; do
cpu=$(ps -o %cpu= -p "${app_pid}" 2>/dev/null | tr -d ' ')
rss=$(ps -o rss= -p "${app_pid}" 2>/dev/null | tr -d ' ')
[[ -n "${cpu}" ]] && printf '%s %s %s\n' "$(date '+%H:%M:%S')" "${cpu}" "${rss:-0}" >> "${samples}"
sleep "${INTERVAL}"
done
) &
sampler=$!
trap 'kill "${app_pid}" 2>/dev/null || true' INT TERM
wait "${app_pid}" 2>/dev/null
kill "${sampler}" 2>/dev/null || true
wait "${sampler}" 2>/dev/null || true
{
echo
echo "---- cpu samples (time, %cpu of one core, rss KB) ----"
cat "${samples}" 2>/dev/null
echo
echo "---- cpu summary ----"
awk '{n++; s+=$2; if($2>mx)mx=$2; if(n==1||$2<mn)mn=$2}
END{ if(n) printf "samples=%d min=%.1f%% avg=%.1f%% max=%.1f%%\n", n, mn, s/n, mx;
else print "(no samples — the process exited immediately)" }' "${samples}" 2>/dev/null
echo
echo "---- what to look for ----"
echo " * 'Creating QRhi with backend <X>' -> OpenGL/Metal/Vulkan = GPU; 'software' = not."
echo " * 'Prefer software device: 1' -> something asked for software explicitly."
echo " * 'Animation Driver: using vsync' -> ms per frame; 5.56 = 180Hz, 10.0 = 100Hz."
echo " * a max% that tracks the NPB Large window is the FFT spectrum band."
} >> "${LOG}"
rm -f "${samples}"
echo
echo "[qt-diag] done. Backend line:"
grep -aE 'Creating QRhi|Prefer software|using vsync' "${LOG}" | sed 's/^/ /' || echo " (none found — did the window open?)"
echo "[qt-diag] full log: ${LOG}"