shader-based scrolling wallpaper mode#1802
Conversation
|
rebased for 85b00d3, i think checks should pass now |
|
Sorry, havent had time to take a look at this - will get to it soon. |
|
Thanks for getting it up to date, we will get this one reviewed. Apologies for the delay! |
|
that's alright :) I needed to rebase & I got around to one last fix, it's otherwise been good for my daily driving for the past while |
|
cleaned up the branch; should be a smaller diff now. Finally figured out the weird wl_output surface reseating issue I was having, turns out it was racing against if the unlock surface had cleared before or after output reconnect. |
|
Thanks @PandorasFox, I think the largest hurdle here is having to prefix |
|
yeah, the need for the 'simple' animation driver to get >60hz frame callbacks from quickshell is still kinda funky to me. As far as I could tell, the default animation driver always runs in 16ms ticks, so >60hz updates weren't possible - which leads to bad motion on higher refresh rate panels, compared to niri's workspace switch animations. if there's another way to get unthrottled frame hints/callback timers, or properly vsynced frame callbacks in the default driver, that'd be great, but I don't think such an interface existed last I checked. If there's a way to just dynamically change the QSG animation driver for just the wallpaper renderer, when in scrolling mode, that might also be ideal - though, I think that might have weird implications for blurred foreground surfaces (e.g. popouts) that then update at 60hz? I'm poking at the output/surface re-init after display events and putting this back in draft for a bit, since I think the advent of blur has given me a good bit more to test with this. |
A new `scrolling` wallpaper fill mode that translates the wallpaper crop with the active workspace, similar to android wallpapers & lateral launcher pages, but with vertically-scrolling workspaces, instead. The image is scaled to cover the screen on its non-scroll axis; the workspace index drives a fractional offset into the cropped overflow on the scroll axis. Per-monitor scroll position is published into SessionData so the lock screen renders the same crop as the active workspace, preserving visual continuity across lock/unlock. Spring-driven scroll animations run at native refresh on high-Hz displays (QSG_USE_SIMPLE_ANIMATION_DRIVER=1 exported to the spawned quickshell process). Survives wl_output rebind cycles (e.g. OLED image-cleaning when DPMS soft-off) via output-lifecycle re-anchoring of the scroll target, ShaderEffect rebuild against the current render context, and a wallpaper-layer surface re-attach on unlock for parallax-active monitors. Issues encountered included, but were not limited to: - race conditions upon unlock/display reseat: inconsistent void/'stuck' wallpaper backgrounds. Resurfacing needed to be guarded against unlock state so that the shader gets reliable frame hints. - default QSG animation driver always throttling/frame-hinting at 16ms, which lead to disjointed motion pacing between compositor animations and wallpaper shader
|
here's where things are presently:
2026-05-02.11-52-04.mp4overall it works reasonably well, but the re-surfacing bits to survive output reseats could perhaps be polished a bit more - i recall seeing a release note a while ago about something similar for the bar re-surfacing itself to survive output reseats, so I can look into standardizing this a bit more? the other note is that wallpaper switching blanks to void while loading rather than doing a transition effect; I figure there's room for bridging that w/ the state the lockscreen uses (+ not killing the old canvas immediately? haven't dug into that one still) |
After the spring settles, the wallpaper window's wayland surface stops committing — `updatesEnabled` drops, and even within the 1s `_renderSettling` tail nothing is actually dirty, so QSG skips render+ commit. The compositor in turn drops the surface from its frame schedule. The next workspace switch then cold-starts: the first ~3 frames land late while the surface is reinserted, producing visible hitching on the leading edge of the spring. Add a 30Hz heartbeat timer that, while parallax is active and the spring isn't running, bumps a `_parallaxHeartbeat` property bound as an unused uniform on the parallax `ShaderEffect`. Property change dirties the effect, QSG renders+commits a single frame per tick, the `wl_surface.frame` callback chain stays alive, and the surface keeps its slot in the compositor's vsync schedule. Heartbeat naturally pauses when `frameAnim.running` is true (real spring motion already drives commits at native rate) and resumes the moment it stops.
some notes:
QSG_USE_SIMPLE_ANIMATION_DRIVER=1 qs -p quickshellif your display refresh rate is >60hz (rolled into the .service file, potentially undesirable on devices that might want to throttle based on battery status? needs investigation beyond the desktop case..)overall much happier with this approach than the lazy software render impl; did require some boilerplating of the 8th fill mode across the shaders.