Skip to content

Scale brightness transition steps to the backlight's range - #172

Open
TheDiscordian wants to merge 3 commits into
max-baz:mainfrom
TheDiscordian:scale-brightness-steps-to-device-range
Open

Scale brightness transition steps to the backlight's range#172
TheDiscordian wants to merge 3 commits into
max-baz:mainfrom
TheDiscordian:scale-brightness-steps-to-device-range

Conversation

@TheDiscordian

@TheDiscordian TheDiscordian commented Jul 12, 2026

Copy link
Copy Markdown

Problem

On sysfs backlights with a large max_brightness, wluma writes to the brightness file essentially continuously. My laptop (Framework 16, amdgpu, max_brightness = 62194) sees ~540 sysfs writes per second in steady state: prediction noise of a few units (±0.003% of the range) starts a new transition, and since any delta ≤ 200 units gets step = 1 with a 1 ms step interval, wluma crawls unit-by-unit toward a target that never stops moving.

This is more than log noise. Every write emits a kernel uevent, and systemd rebroadcasts each one on the system D-Bus as PropertiesChanged for the device unit and its systemd-backlight@ service (~2,200 signals/s measured with dbus-monitor --system). Busy peers subscribed to broadcasts can't drain their queues and get force-disconnected by dbus-broker:

dbus-broker[996]: Peer :1.972 is being disconnected as it does not have the resources to receive a signal it subscribed to.

In my case that killed a running Steam game and Steam itself mid-session. Devices with a small brightness range (100–255) never see this because prediction noise rounds to zero units — which is probably why it has gone unnoticed.

Fix

  • Add min_step()max_brightness / 256, floor 1 — to Backlight, exposed via Brightness.
  • Use it as a deadband in update_target: predictions within min_step of the current value are ignored (generalizes the existing desired == current check).
  • Use it as the minimum transition step, so a full-range transition is at most ~256 writes.
  • When min_step binds, stretch the per-step interval so the ramp still spans TRANSITION_MAX_MS — fewer writes, same transition duration, so transitions stay visually smooth instead of completing in a few milliseconds.
  • Clamp transition writes to the target so the enlarged step lands exactly on desired instead of overshooting.

Devices with max_brightness ≤ 256 get min_step = 1, which preserves the previous behaviour exactly — all existing tests pass unchanged. DDC/CI outputs are unaffected (min_step = 1 on their 0–100 range).

Results (Framework 16, amdgpu, max 62194)

backlight uevents
before ~540/s sustained
after ~2.5/s (real adjustments only)

Auto-adjustment still works. Measured a forced dim/re-brighten cycle: ~70 evenly spaced steps spanning the full transition window per direction.

On sysfs backlights with a large max_brightness (e.g. amdgpu with 62194
levels), absolute transition steps of a few units combined with the 1 ms
step interval turn every prediction change into a continuous stream of
sysfs writes (hundreds per second). Each write emits a kernel uevent
that systemd rebroadcasts on the system bus as PropertiesChanged
signals for the device unit; busy D-Bus peers subscribed to broadcasts
can overflow their queues and get disconnected by dbus-broker.

Introduce a per-device minimum step (max_brightness / 256, floor 1)
used both as a deadband for new predictions and as the minimum
transition step, and clamp transition writes to the target so the last
step lands exactly on it. Devices with max_brightness <= 256 keep the
previous behaviour exactly (min_step = 1).

Measured on a Framework 16 (amdgpu, max_brightness 62194): ~540
backlight uevents/s before, ~2.5/s after, with auto-adjustment still
working.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TheDiscordian

TheDiscordian commented Jul 13, 2026

Copy link
Copy Markdown
Author

So sorry, my Claude opened this entirely without me knowing. I haven't reviewed this, feel free to close. if this PR is unwelcome

For context my games were crashing and I couldn't figure out why. After months fable suddenly figured out the culprit: wluma. This patch does seem to fix the crash (and it seems to be a reasonable change, but again, haven't really reviewed it myself).

Edit: While this does fix the crash. Playing a game like Satisfactory it makes the brightness adjustments a bit jarring and jumpy, but normal use it seems fine. I'm making a service to simply not run wluma while mangohud is running (hacky way to check if I'm running a game).

TheDiscordian and others added 2 commits July 15, 2026 14:22
Enlarging the step while keeping the 1 ms interval made small and
medium transitions complete in a few milliseconds, which looks like an
instant jump. When min_step binds, stretch the per-step interval so the
ramp still spans the transition duration; when it does not bind, step
and interval are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant