Skip to content

drm: re-send HDR metadata blob on modeset#250

Merged
vaxerski merged 1 commit into
hyprwm:mainfrom
mattchue:fix-hdr-modeset
Feb 17, 2026
Merged

drm: re-send HDR metadata blob on modeset#250
vaxerski merged 1 commit into
hyprwm:mainfrom
mattchue:fix-hdr-modeset

Conversation

@mattchue

@mattchue mattchue commented Feb 17, 2026

Copy link
Copy Markdown
Contributor

After sleep/resume, the kernel discards all DRM property blobs including HDR_OUTPUT_METADATA. restoreAfterVT() correctly copies the in-memory HDR metadata into the commit data, but prepareConnector() skips blob creation because STATE.committed was cleared by onCommit() after the last pre-sleep commit.

The monitor ends up with 10-bit format active but zero HDR metadata and colors wash out permanently until the color mode is changed to standard and back.

This adds a data.modeset check to bypass the committed flag, since a modeset means the kernel state was fully reset and all properties need to be re-sent. Other connector properties (max_bpc, colorspace, content_type, vrr) have no STATE.committed guard at all — they're sent every commit. HDR metadata was the only one gated behind the committed flag, making it uniquely vulnerable to state loss after suspend/resume. The existing has_value() guard prevents false positives so non-HDR modesets don't populate data.hdrMetadata.

Tested on NVIDIA RTX 4090 with a HDR display.

Ref: hyprwm/Hyprland#13036

After a VT switch or sleep/resume, the kernel discards all DRM property
blobs. restoreAfterVT() rebuilds the atomic commit with the in-memory
HDR metadata, but prepareConnector() skips blob creation because
STATE.committed (the "what changed" bitmask) was cleared after the last
pre-sleep commit.

During a modeset the kernel state is fully reset, so all properties must
be re-sent regardless of the committed flags. Add a data.modeset guard
to bypass the committed check, matching how max_bpc, colorspace, and VRR
are already handled unconditionally.

The existing has_value() check prevents false positives: non-HDR
modesets (resolution changes etc.) don't populate data.hdrMetadata, so
the block is still skipped.
@vaxerski vaxerski merged commit 6d02092 into hyprwm:main Feb 17, 2026
1 check passed
vaxerski pushed a commit that referenced this pull request May 10, 2026
A previous compositor (kwin etc.) can leave HDR, gamma, degamma and CTM
properties set on a connector when it exits. Aquamarine only re-emitted
these on user commits, so anything we don't actively manage stayed at
the previous compositor's value, manifesting as washed-out colors or
shifted saturation when starting Hyprland after kwin.

HDR (#250) and CTM-with-non-identity (#256) were already covered. Extend
the modeset path to also:

- re-send gamma_lut/degamma_lut on modeset; an empty STATE.gammaLut
  produces a zero blob, which clears the kernel state. only ride this
  path when the prop actually exists, otherwise we'd log a spurious
  "no gamma_lut prop" error on every modeset for connectors without
  programmable gamma.
- re-send CTM unconditionally on modeset, identity included, so the
  kernel always replaces a stale matrix with our current one. the blob
  builder produces a real identity matrix when STATE.ctm == Mat3x3(),
  so we never send blob_id=0 (which #256 documented as problematic).

Fixes #127.

Co-authored-by: j4kuuu <j4kuuu>
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.

2 participants