mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
perf(fs-player): cut CPU under WebKitGTK software compositing
WebKitGTK on Linux runs with WEBKIT_DISABLE_COMPOSITING_MODE=1, so every
GPU-only effect is software-rasterised per frame. The fullscreen player
piled up large blur shadows, per-line scale transforms, oversized
radial-gradients with color-mix() and a 60 fps rAF spring on top — CPU
saturated and FPS dropped to ~10.
Linux-only via existing html.no-compositing class:
- Apple-style lyrics: drop transform: scale and 48 px / 32 px text-shadow
- Rail-style lyrics: drop 28 px text-shadow + scaleX
- Track title: 40 px glow → 6 px drop-shadow
- Album art wrap + play button: drop accent-coloured outer glows
- Mesh blobs: flatten 130 % radial-gradient + color-mix to solid #06060e
and remove the 400 ms background transition
- Portrait wrap: drop translateZ(0) GPU-layer hint
- FS art: instant cover swap (no opacity crossfade)
- SpringScroller: replace rAF spring with native scrollTo({behavior:'smooth'})
- FsSeekbar: cache last applied values, skip identical DOM writes
Dynamic accent on title / seekbar / play button is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -434,20 +434,40 @@ const FsSeekbar = memo(function FsSeekbar({ duration }: { duration: number }) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const s = usePlayerStore.getState();
|
||||
const pct = s.progress * 100;
|
||||
if (timeRef.current) timeRef.current.textContent = formatTime(s.currentTime);
|
||||
if (playedRef.current) playedRef.current.style.width = `${pct}%`;
|
||||
if (bufRef.current) bufRef.current.style.width = `${Math.max(pct, s.buffered * 100)}%`;
|
||||
if (inputRef.current) inputRef.current.value = String(s.progress);
|
||||
// Cache the last applied values so we can skip DOM writes when nothing
|
||||
// changed. The store subscription fires on every state change (queue,
|
||||
// volume, currentTrack…), not just progress, so without this guard we
|
||||
// were writing identical width/textContent strings dozens of times per
|
||||
// second — each one triggers a style invalidation in WebKitGTK.
|
||||
let lastTime = -1;
|
||||
let lastPct = -1;
|
||||
let lastBufW = -1;
|
||||
let lastProg = -1;
|
||||
|
||||
return usePlayerStore.subscribe(state => {
|
||||
const p = state.progress * 100;
|
||||
if (timeRef.current) timeRef.current.textContent = formatTime(state.currentTime);
|
||||
if (playedRef.current) playedRef.current.style.width = `${p}%`;
|
||||
if (bufRef.current) bufRef.current.style.width = `${Math.max(p, state.buffered * 100)}%`;
|
||||
if (inputRef.current) inputRef.current.value = String(state.progress);
|
||||
});
|
||||
const apply = (s: ReturnType<typeof usePlayerStore.getState>) => {
|
||||
const pct = s.progress * 100;
|
||||
const bufW = Math.max(pct, s.buffered * 100);
|
||||
const sec = s.currentTime | 0;
|
||||
if (sec !== lastTime) {
|
||||
lastTime = sec;
|
||||
if (timeRef.current) timeRef.current.textContent = formatTime(s.currentTime);
|
||||
}
|
||||
if (pct !== lastPct) {
|
||||
lastPct = pct;
|
||||
if (playedRef.current) playedRef.current.style.width = `${pct}%`;
|
||||
}
|
||||
if (bufW !== lastBufW) {
|
||||
lastBufW = bufW;
|
||||
if (bufRef.current) bufRef.current.style.width = `${bufW}%`;
|
||||
}
|
||||
if (s.progress !== lastProg) {
|
||||
lastProg = s.progress;
|
||||
if (inputRef.current) inputRef.current.value = String(s.progress);
|
||||
}
|
||||
};
|
||||
|
||||
apply(usePlayerStore.getState());
|
||||
return usePlayerStore.subscribe(apply);
|
||||
}, []);
|
||||
|
||||
const handleSeek = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user