Now that dynamic max render time uses a new algorithm and takes dispatch
lateness into account, this seems worth a shot. We'll see how it works
out in the wild.
The net result compared to before these changes is still slightly higher
(by ~0.5 ms) minimum latency for me, as measured by
weston-presentation-shm. It should be less vulnerable to frame drops
though.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2500>
Store only two values per kind of duration: The short term and long term
maximum.
The short term maximum is updated in each frame clock dispatch. The long
term maximum is updated at most once per second: If the short term
maximum is higher, the long term maximum is updated to match it.
Otherwise, a fraction of the delta between the two maxima is subtracted
from the long term maximum.
Compared to the previous algorithm:
* The calculcations are simpler.
* The calculated max render time has a slow exponential drop-off (by at
most a few milliseconds every second) instead of potentially abruptly
dropping after as few as 16 frames.
This should fix https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4830
since the short term maximum should always include a sample from the
clock's second tick.
v2:
* Use divisor 2 instead of 4.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2500>
Dispatch lateness is the difference between when we wanted frame clock
dispatch to run and when it actually started running. This can be up to
1ms even under normal circumstances due to process scheduling
granularity, or even higher under load.
This keeps track of dispatch lateness of the last 16 frame clock
dispatches, and incorporates the maximum into the dynamic render time
estimate.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2500>
When we remove a child, we stop its transitions (animations), but we
didn't stop animations on grand children. What we did, however, was to
clear the stage views of the grand children, and this caused a bunch of
orphaned transitions (ClutterTimeline) and accompanied warnings.
Make it so that if we stop transitions, and clear stage views, also stop
transitions for the grand children. Detached children don't have a way
to continue animating anyway, since they have no stage view (thus frame
clock) to be driven by.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2716>
When a badly behaving ClutterActor implementation manages to invalidate
the allocation after the layout phase and before painting, we have no
idea where the actor should be painted without running the whole layout
machinery again.
For paint volumes in this case we pretend the actor covers the whole
stage and queue full-stage redraws. When updating stage-views, we're
also handling this case, but not in the most graceful way. Just like
with paint volumes, we should assume an actor without a valid allocation
is simply everywhere, so set priv->stage_views to all available stage
views in that case.
Related: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6054
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2694>
We've been sending all events to clients immediately for quite some time
now, so this is only really impacting the Clutter scene graph, not
clients anymore.
That makes this behavior a somewhat unnecessary optimization (it was
useful at the time it was added, but it's not anymore), which will only
make our lives harder when we actually expect an event to be queued
(eg. in tests), so remove it.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2697>
As suggested by Carlos in a review of this MR, refactor the logic of
clutter_do_event() to have both adding and removing of devices from the
devices list in a single place.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2696>
Having the stage device list be responsible for delivering the
same events twice (first immediately to clients, then later to Clutter)
was expected to be tricky, a sneaky problem with it right now is the
following case:
While collecting events for a stage update cycle, we get three touch
events from the backend: TOUCH_BEGIN(seq=1) -> TOUCH_END(seq=1) ->
TOUCH_BEGIN(seq=1)
What we do right now when we see a TOUCH_BEGIN event is adding a device
to the stage right when it comes in from the backend. And when we see
a TOUCH_END, we remove the device from the stage not immediately but
only after it went through the queue.
In the case of the three events mentioned above, with the current
behavior, this will happen when they come in from the backend:
- TOUCH_BEGIN(seq=1): device gets added to the stage with seq 1, event
gets queued
- TOUCH_END(seq=1): Nothing happens, event gets queued
- TOUCH_BEGIN(seq=1): we try to add device to the stage, but seq 1 is
already there, event gets queued
Now when we go through the queue and see the TOUCH_END, the device with
seq 1 gets removed, but on the subsequent TOUCH_BEGIN, we won't add a
new device, so this event (and all events with seq=1 that are still in
the queue) is now ignored by Clutter because it has no device.
What we want to do here is to cut short once the TOUCH_END event comes
in: Process queued events immediately and make sure the device is
removed from the stage list before a new device can be added. Same goes
for any other events that will lead to devices getting removed.
Small note: Since this leads to clutter_stage_get_device_actor()
returning NULL, I was wondering why we never crash because of this:
Turns out _clutter_actor_handle_event() handles self = NULL just fine
without crashing...
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2696>
With commit 6c17aa66c6 we made sure no
stale device entries might land in the stage device list. The same can
happen for pointer devices too in theory, in practice we never really
filter them out, but it's good to handle them here anyway.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2696>
We'll call this function from a few more places for the
CLUTTER_DEVICE_REMOVED case, so move the check for which devices are
valid into the function itself to avoid having to check everywhere.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2696>
Updating of the paint volume used for culling these days happens
during the finish-layout stage, not while painting. Also we have
geometry-based, not paint-based picking anymore.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
Rename the `last_paint_volume` to `visible_paint_volume`: That avoids
confusion with the `had_effects_on_last_paint_volume_update` flag and
also makes it clear that this paint volume is the currently visible one.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
Rename the paint_volume_valid flag to has_paint_volume in order to
better reflect what it's for.
The name "paint_volume_valid" implies that the paint volume can be
invalidated and thus sounds like it's involved with some kind of
caching. The flag that's actually involved with caching is
"needs_paint_volume_update", while "paint_volume_valid" is only meant to
store whether the actor has a paint volume to work with.
So rename paint_volume_valid to has_paint_volume to avoid confusion
about which flag is used for caching.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
For clarity and for further improvements, introduce a separate function
to update the paint volume instead of doing that inside
_clutter_actor_get_paint_volume_mutable().
Also add a FIXME comment for a possible bug I noticed while working on
it.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
Since ClutterActor now properly caches its paint volume and ClutterText
tries hard to invalidate its own cached paint volume on every redraw
anyway (that's more often than ClutterActor invalidates its own paint
volume), we can simply rely on the caching of the paint volume done by
ClutterActor and invalidate that on every redraw.
So remove the private cached paint volume from ClutterText and all its
invalidation machinery.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
The function _clutter_paint_volume_get_stage_paint_box() actually
doesn't modify the paint volume that's passed to it, so make that a bit
more clear by passing a const paint volume as the argument.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
These days it's possible to chain up into the default get_paint_volume()
implementation again, which renders
clutter_actor_get_default_paint_volume() unnecessary. So remove that
function and move clutter_actor_update_default_paint_volume() back into
real_get_paint_volume() where it belongs.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1492>
We traverse the whole screnegraph anyway these days in finish_layout(),
so no need for the whole "set the flag on parents even though we don't
need it" dance anymore.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2667>
There do indeed seem to be places in our own code that trigger grabs on
actors before they are realized. It was not the intention to change the
practical preconditions for GNOME 43, so make it an even lower minimum
that every caller ought to match: That the actor is attached to the stage.
Further constraining of these preconditions will have to wait until
branching for new development.
Fixes: 9c79c7234 (clutter: Only allow grabs to be created on realized actors)
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2670>
The bare minimum that we can ask to an actor before creating a grab
on it is that it is realized (and thus, attached to the stage). Bail
out if that is not the case when creating a grab.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2669>
If an actor is being unrealized or otherwise unparented, it's a good
indication that its grabs are now stale and possibly harmful. Ensure
these are dropped when the actor is unparented.
This is now an unlikely event, since there is code to also dismiss
grabs when a visible grabbed actor goes unmapped. But that may be
prevented from happening, or the ordering of circumstances allow a
grab to be created and an actor destroyed without going unmapped
first. This grab dismission on unmap stays as it matches the UI-level
expectatives that an actor must be visible to be grabbed.
Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/2475
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2669>
The stage view list does not get updated when an actor gets hidden in
order to avoid unnecessary work, such as scale changes. However, we
still want `is_effectively_on_stage_view` to report `FALSE` in this
case.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2662>
The :input-purpose and :input-hints properties were added without
actually handling the get/set operations, whoops.
All code uses the (working) methods, so this only fixes expectations,
not an actual bug :-)
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2659>
If we have a window that match the size (i.e. will pass the "fits
framebuffer" low level check), that doesn't mean it matches the
position. For example, if we have two monitors 2K monitors, with two 2K
sized windows, one on monitor A, and one on monitor both monitor A and
B, overlapping both, if the latter window is above the former, it'll end
up bing scanned out on both if it ends up fitting all the other
requirements.
Fix this by checking that the paint box matches the stage view layout,
as that makes sure the actor we're painting isn't just partially on the
right view.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2387
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2624>
A few calculations and assignments are done unnecessarily when the
last next presentation time is invalid. This increases the cognitive
complexity of the function for no reason.
No change in behavior.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2486>
I don't see how this makes sense at all, ClutterClickAction really
shouldn't mess with BUTTON_RELEASE events that are not part of a
gesture.
So propagate those events instead of stopping them.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2552>
The source field was removed from ClutterEvent with commit
b644ea1bce because the preferred way of
getting the event actor is now to use the device/sequence actor from the
stage directly.
With crossing events it's not that easy though, as crossing events
explicitly have a source and related actor that doesn't have to be the
same actor as the device actor. Since we kept around the "related" field
there anyway, let's also introduce a "source" field in
ClutterCrossingEvent and return that actor when get_source() is called
on a crossing event.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2551>
Just like we did with the ::captured-event signal, add detail to the
::event signal too. At the first glance this might not seem necessary
since there are individual signals like scroll-event or touch-event that
get emitted at the same time, but these don't exist for touchpad gesture
events and others.
As an easy solution for that, just make it possible to use detail on the
event signal as we did with the caputured-event signal.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2431>
In some hardware configurations, presentation timestamps could be
missing from some page flip events, leading to a temporary loss of
vblank synchronization.
This occurs at least with AMD GPUs for atomic commits that only update
the cursor plane. [0]
In those cases, it's better to calculate the next update time
according to the last valid presentation timestamp instead of relying
on the dispatch lateness.
[0] https://gitlab.freedesktop.org/drm/amd/-/issues/2030
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2481>
When taking the scanout path we still want to clear the
redraw-clip from the stage-view in order to ensure we skip
frames in `handle_frame_clock_frame()` if no new redraw-clip
was recorded.
This was not done previously as the accumulated redraw-clip was
needed for the next repaint, likely under the assumption that
scheduling a scanout repeatedly would be computationally cost-free.
This assumption does not hold in a VRR world.
In order to archive both, an accumulated redraw-clip for the next
paint and frame-skipping during scanout, introduce new API to defer
and accumulate redraw-clips until the next repaint.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2480>
Similar to the clutter commits
- Drop all the private structs documentations
- Make use of gi-docgen items linking as much as possible
- Use markdown formatting for code snippets
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2441>
This can happen with the native backend if the previous frame clock
dispatch didn't result in any KMS update, e.g. because it was triggered
by an input event, but the HW cursor didn't need updating on the stage
view. (This is likely to happen on some out of multiple stage views,
but might be possible even with a single stage view if the cursor isn't
visible)
We would previously delay next_presentation_time_us by one refresh
interval in this case, which could result in spuriously leaving one
refresh cycle unused.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2498>
Somewhat long overdue... We've been supporting more than a single
pointer for quite a long time now, let's make sure things don't break if
two pointer devices enter the same ClutterActor: Count the number of
pointers an actor has instead of using a simple boolean value.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2348>
In certain edge cases it's currently possible that an actor never
gets a valid allocation and paint volume.
One such case is adding an unmapped, hidden child to an unmapped
cloned parent and then showing the child. This happens currently
e.g. if a Wayland subsurface is added to a already mapped window
while the user is in the overview.
Ensure relayouts in two more such cases.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2530>
The notification list in the GNOME Shell calendar popup triggers some
interesting interactions when closing a notification:
- Close button is clicked
- The notification animates to be hidden
- The next notification ends up hovered as a result of the animation
- The notification being hovered sets its close button as non-transparent
and reactive
- The pointer is now again over a close button
At this point the reactiveness change should trigger a repick, so that
the new notification's close button is picked, and future button presses
are directed to it, but we do not handle this situation.
To fix this, handle actors becoming reactive so that if the closest
reactive parent has a pointer, it will be repicked again just in case
the pointer is over the newly reactive actor.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2364
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2532>