* drm: handle CRTC starvation recovery when outputs are disabled
When more displays are connected than CRTCs available, connectors that
arrive after all CRTCs are claimed become starved. The compositor can
free a CRTC by disabling an output, but nothing reclaimed that CRTC for
the starved connector.
Restructure recheckCRTCs() into two passes: first, disabled outputs
release their CRTCs and enabled connectors get priority assignment;
second, any remaining free CRTCs are given to disabled connectors as
backup slots for quick re-enable.
When applyCommit() detects an enabledState transition, schedule
recheckOutputs() via addIdleEvent so starved connectors pick up the
freed CRTC on the next event loop iteration, without reentrancy or
blocking the event loop.
* drm: clear stale page-flip state after suspend/resume
My laptop (i915, 3 external monitors) would go permanently black after
S3 or suspend-then-hibernate. The only recovery was a hard restart.
The root cause: when display hardware powers off during suspend, any
in-flight page-flip completion event is lost. handlePF() never fires,
so isPageFlipPending stays true from the last frame before suspend. On
resume, scheduleFrame() sees the stale flag and bails, commitState()
rejects every frame with "Cannot commit when a page-flip is awaiting",
and nothing ever clears it. Screens stay dark forever.
VT switch doesn't hit this because the kernel preserves DRM state and
queues pending events in the fd buffer. Suspend kills the hardware, so
there's nothing to deliver.
Fix in two places:
1. restoreAfterVT(): clear isPageFlipPending, isFrameRunning, and
frameEventScheduled for all connectors before recheckOutputs(). For
VT switch this is harmless (the events arrive anyway and handlePF
would set them false). For suspend it unblocks frame scheduling.
2. commitState(): record a CLOCK_BOOTTIME timestamp when
isPageFlipPending is set. If a modeset finds a flip pending for
>500ms (well past any vblank), treat it as stale and clear the
flags. CLOCK_BOOTTIME instead of CLOCK_MONOTONIC because MONOTONIC
freezes during suspend on Linux, making elapsed time look like zero
after resume.
Timestamp recorded in both atomic and legacy commit paths.
Relates to Hyprland#8312, Hyprland#6289.
---------
Co-authored-by: j4kuuu <j4kuuu>
* libinput: method to get `libinput_tablet_tool` from ITabletTool
Libinput has some configuration specific to the tablet tool that could
be interesting to set for Tablet users. However there's currently no
way of getting the required `libinput_tablet_tool` ptr from
`ITabletTool`. Meaning it's not possible to build the configuration on
Hyprland (or anything else using aquamarine).
This change adds the virtual function `getLibinputTool()` to
`ITabletTool` as well as an implementation for `CLibInputTabletTool`
* Run clang-format
* drm: remove an unnecessary reset after VT switching (#223)
I've tested DPMS and different monitor modes with this change, and
everything seems to be working exactly as expected.
* backend/drm: skip direct buffer import on multi-gpu during restore
---------
Co-authored-by: Mason Davy <54364725+Nosamdaman@users.noreply.github.com>
initMgpu already does build gl formats so remove the temp creation of
allocator and renderer in onReady() that only built glformats,
also if guard the creation of renderers and allocators to make it less
likely to recreate one that is already existing.
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.
Currently aquamarine initializes all the secondary renderers and keeps them alive all the time, even when no output on that GPU is enabled. Keeping a secondary renderer alive prevents unused GPU from powering down completely, which e.g. makes battery life significantly worse on Optimus laptops with DP/HDMI ports wired to dGPU.
This patch initializes secondary renderers only when at least one connected output is enabled, and deinitializes them when no enabled outputs remain.
Some high-resolution displays (e.g. Apple Studio Display 5K) expose
multiple DRM connectors in a tile group. However, when the driver
handles tiling internally, a single connector will advertise the full
panel resolution, and its other connectors will be inoperative and
redundant.
To avoid ghost outputs from those dead tiles, we can filter out any
tiles where there is a connector in that group that offers the full
resolution by itself.
* atomic: properly check min max bpc values
properly use maxBpcBounds[0] and clamp it in getMaxBpc. lets not go
below or beyond what HW supports.
* atomic: actually set blobid in prepareGammaBlob
set blobid to zero and not the local copied pointer.
* atomic: add hdr blob to apply and rollback aswell
add missing hdr blob to apply and rollback.
* drm: dont leak modeinfo
free currentModeInfo when done with it.
EGL_CONTEXT_RELEASE_BEHAVIOR_KHR determines what happends with implicit
flushes when context changes, on multigpu scenario we change context
frequently when blitting content. while we still rely on explicit sync
fences, the flush is destroying driver optimisations.
setting it to EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR essentially mean
just swap context and continue processing on the next context.
* backend: read idle timer
the manpage for timerfd_create and read states this.
timefd_create creates a new timer object, and returns a file descriptor
that can be used to read the number of expirations that have occurred.
The FD becomes readable when the timer expires.
read removes the “readable” state from the FD.
so most likely it has somewhat worked because of the updateIdleTimer()
function.
* backend: check if fd is readable log otherwise
ensure we dont accidently trigger some blocking read call on a non
readable fd, log an error if this occurs, else read it.
FreeBSD always returning symbolic links '/dev/dri/card*' pointed to '/dev/drm/*',
so output device will never be found if `AQ_DRM_DEVICES` is set as explicit devices
path is converted to canonical file path.
Originally disconnected monitors were not removed from hyprland
correctly and duplications were created when an external monitor was
reattached leading to invalid behavior when switching
(empty desktop is visible).
Now removed monitors are explicitly disconnected during connectors
scanning.
evdi/displaylink devices doesnt give us a rendernode at all, and asahi
was hard to associate if a rendernode was related to the
displaynode/card so we fallback to just first found if that occurs.
might be more appropiate to figure out a proper way to deal with these
cases then workaround ever single driver that doesnt give rendernodes
but so far seems only to be evdi.
problem that occured is a 3 gpu situation, the evdi card ended up using
a rendernode for a complete different gpu because it fallbacked to that.