https://bugs.kde.org/show_bug.cgi?id=527563
Bug ID: 527563
Summary: Sliding notifications effect: fade spring diverges at
AnimationDurationFactor < ~0.104, notifications render
solid black
Classification: Plasma
Product: kwin
Version First 6.8.1
Reported In:
Platform: Arch Linux
OS: Linux
Status: REPORTED
Severity: normal
Priority: NOR
Component: effects-various
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
Created attachment 197485
--> https://bugs.kde.org/attachment.cgi?id=197485&action=edit
spring.py
DESCRIPTION
Since 6.8.0 (sliding notifications ported from TimeLine to SpringMotion),
notification popups render solid black for their entire lifetime when
AnimationDurationFactor is below ~0.104. The new fade spring (stiffness 2000 /
animationTimeFactor) goes numerically unstable, so window opacity diverges to
huge negative values and the animation never finishes.
The root cause is in SpringMotion::evaluate(): it returns `.dp =
state.velocity` instead of `.dp = next.velocity`. Because every RK4 stage
reuses the starting velocity, the integrator is far less stable than real RK4.
STEPS TO REPRODUCE
1. Set an animation factor in the affected range. The current slider can't
produce one (its slowest non-instant value is 0.5), but values like this are
left over from Plasma <= 6.6, whose 17-step slider produced 1/2^3.5 = 0.0884
one notch before "Instant":
kwriteconfig6 --file kdeglobals --group KDE --key AnimationDurationFactor
0.0884
2. Make sure the "slidingnotifications" effect is enabled.
3. Run `notify-send "Test" "Can you read this?"`
Observed: the notification popup is solid black and unreadable for as long as
it is shown.
Expected: the notification fades and slides in normally.
Disabling the slidingnotifications effect makes it go away, so does setting the
factor to a value the current slider offers. Clock/launcher popups are
unaffected. Restarting plasmashell, toggling blur, QSG_RHI_BACKEND=vulkan and
downgrading the NVIDIA driver (615.78.08 -> 610.57.04) did not help.
Analysis
slidingnotifications.cpp (6.8.1):
static SpringMotion makeFadeSpring()
{
SpringMotion spring(2000.0 / effects->animationTimeFactor(), 1.0);
spring.setEpsilon(0.01);
return spring;
}
At factor 0.0884 the spring constant is ~22627, so omega*dt = sqrt(k) * 0.01 ~=
1.50 with SpringMotion's fixed 10 ms timestep. Real RK4 is stable there
(amplification ~0.27 per step), but SpringMotion::evaluate() computes
return Slope{
.dp = state.velocity, // should be next.velocity
.dv = acceleration,
};
so every stage's position slope is the step's starting velocity. I ported
SpringMotion and AnimationClock::tick to Python line-for-line and ran them at
360 Hz (script attached):
- As shipped: the fade spring diverges below a factor of ~0.1037 (omega*dt ~=
1.39). At 0.0884, opacity goes 0 -> -0.06 -> -0.3 -> ... -> ~-3e10 after 400
frames and isMoving() never returns false.
- With `.dp = next.velocity`: at 0.0884 the fade settles at 1.0 in ~117 ms, at
0.0625 in ~136 ms.
- The motion springs (900 / factor) only diverge below ~0.047, which matches
the popup still appearing in the right place.
Why the popup is black rather than invisible is not clear to me.
WindowPaintData::multiplyOpacity() does not clamp, and base.frag applies it
with `result *= modulation`, so every channel including alpha is multiplied by
a huge negative value. If the colour-management path runs (doTonemapping /
nitsToDestinationEncoding), linearToSrgb() also evaluates pow() on negative
input, which is undefined in GLSL. However, the popup is still black with HDR
and Wide Color Gamut both turned off (plain sRGB outputs), so HDR/WCG is not
required. How out-of-range values turn into black is probably driver-dependent;
I've only tested on NVIDIA. Clamping the opacity (fix 2 below) should avoid
this whatever the exact mechanism is.
Upstream master still has `.dp = state.velocity` in springmotion.cpp and the
same makeFadeSpring(), so this looks unfixed.
SUGGESTED FIXES
1. SpringMotion::evaluate(): use `next.velocity` for `.dp`. This affects every
SpringMotion user (e.g. the slide effect), so its curves will change slightly.
Tested: I rebuilt Arch's kwin 6.8.1-1 package with only this one-line change
(patch attached, no opacity clamp) and installed it on the affected machine.
After logging in again, with AnimationDurationFactor=0.0884 and
slidingnotifications enabled, notifications fade and slide in normally instead
of rendering black. Before the change, the same setup reproduced the bug every
time.
2. Clamp the fade spring's output to [0, 1] in
SlideNotificationAnimation::opacity(), as defence in depth.
3. Optionally, substep or cap stiffness so very small animation factors can't
push omega*dt out of the stable region.
RELATED
The animation speed slider was cut to 7 fixed values in plasma-desktop
592c5b8c6 (first in 6.7) without converting existing values. A leftover factor
like 0.0884 is still honoured by KWin, but the slider displays it as "Instant"
(`valueMapping.findIndex(item => item <= factor)` matches 0). Users upgrading
from <= 6.6 with fast animations hit this KWin bug with no way to see why from
the UI. This may deserve its own report against plasma-desktop.
SOFTWARE/OS VERSIONS
Operating System (available in the Info Center app, or by running `kinfo` in a
terminal window): Arch Linux
KDE Plasma Version: 6.8.1
KDE Frameworks Version: 6.31.0
Qt Version: 6.12.0
This analysis was assisted by Claude Opus 5.5. I, Jacob Kiesel, was present
throughout the entire process, guided the investigation, and reviewed the
results.
--
You are receiving this mail because:
You are watching all bug changes.