Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:tjyrinki_suse:branches:openSUSE:Factory
mutter
Support-Dynamic-triple-buffering.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File Support-Dynamic-triple-buffering.patch of Package mutter
From: Daniel van Vugt <daniel.van.vugt@canonical.com> Date: Wed, 10 Nov 2021 18:55:53 +0800 Subject: Support Dynamic triple/double buffering Use triple buffering if and when the previous frame is running late. This means the next frame will be dispatched on time instead of also starting late. It also triggers a GPU clock boost if deemed necessary by the driver. Although frequency scaling is not required to get a performance gain here because even a fixed frequency GPU will benefit from not over-sleeping anymore. If the previous frame is not running late then we stick to double buffering so there's no latency penalty when the system is able to maintain full frame rate. Formatted for Debian (as of 46~beta) with: git remote add vanvugt git@ssh.gitlab.gnome.org:vanvugt/mutter.git git fetch vanvugt git merge --squash -e vanvugt/triple-buffering-v4 And then git commit but using all this as the header instead of what git suggests. Also add Gbp-Pq: Topic debian Bug: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3760 Forwarded: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441 Applied-upstream: no, see also https://bugs.debian.org/1050020 Last-Update: 2024-04-29 --- clutter/clutter/clutter-frame-clock.c | 368 +++++++++++++++++++++++------ clutter/clutter/clutter-frame-clock.h | 11 +- clutter/clutter/clutter-frame-private.h | 1 + clutter/clutter/clutter-frame.c | 13 + clutter/clutter/clutter-frame.h | 7 + clutter/clutter/clutter-stage-view.c | 11 +- cogl/cogl/cogl-onscreen-private.h | 5 +- cogl/cogl/cogl-onscreen.c | 8 + meson.build | 1 - po/pt.po | 142 +++++------ src/backends/meta-stage-impl.c | 2 + src/backends/native/meta-kms-impl-device.c | 4 +- src/backends/native/meta-kms.c | 9 + src/backends/native/meta-kms.h | 2 + src/backends/native/meta-onscreen-native.c | 368 +++++++++++++++++++++++------ src/backends/native/meta-onscreen-native.h | 2 + src/backends/native/meta-renderer-native.c | 34 ++- src/meson.build | 1 - src/tests/native-kms-render.c | 106 +++++++-- src/wayland/meta-wayland-keyboard.c | 11 +- src/wayland/meta-wayland-surface.c | 2 +- src/wayland/meta-wayland-tablet-tool.c | 50 +++- 22 files changed, 907 insertions(+), 251 deletions(-) diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 93e4c93..b637fac 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -42,6 +42,15 @@ enum static guint signals[N_SIGNALS]; +typedef enum +{ + TRIPLE_BUFFERING_MODE_NEVER, + TRIPLE_BUFFERING_MODE_AUTO, + TRIPLE_BUFFERING_MODE_ALWAYS, +} TripleBufferingMode; + +static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; + #define SYNC_DELAY_FALLBACK_FRACTION 0.875 #define MINIMUM_REFRESH_RATE 30.f @@ -70,8 +79,10 @@ typedef enum _ClutterFrameClockState CLUTTER_FRAME_CLOCK_STATE_IDLE, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW, - CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, - CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW, + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO, } ClutterFrameClockState; struct _ClutterFrameClock @@ -92,6 +103,7 @@ struct _ClutterFrameClock ClutterFrameClockMode mode; int64_t last_dispatch_time_us; + int64_t prev_last_dispatch_time_us; int64_t last_dispatch_lateness_us; int64_t last_presentation_time_us; int64_t next_update_time_us; @@ -111,6 +123,9 @@ struct _ClutterFrameClock int64_t vblank_duration_us; /* Last KMS buffer submission time. */ int64_t last_flip_time_us; + int64_t prev_last_flip_time_us; + + ClutterFrameHint last_flip_hints; /* Last time we promoted short-term maximum to long-term one */ int64_t longterm_promotion_us; @@ -245,10 +260,6 @@ static void maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info) { - /* Do not update long-term max if there has been no measurement */ - if (!frame_clock->shortterm_max_update_duration_us) - return; - if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) < G_USEC_PER_SEC) return; @@ -275,6 +286,12 @@ void clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info) { +#ifdef CLUTTER_ENABLE_DEBUG + const char *debug_state = + frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO ? + "Triple buffering" : "Double buffering"; +#endif + COGL_TRACE_BEGIN_SCOPED (ClutterFrameClockNotifyPresented, "Clutter::FrameClock::presented()"); COGL_TRACE_DESCRIBE (ClutterFrameClockNotifyPresented, @@ -361,22 +378,52 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, frame_clock->got_measurements_last_frame = FALSE; - if (frame_info->cpu_time_before_buffer_swap_us != 0 && - frame_info->has_valid_gpu_rendering_duration) + if ((frame_info->cpu_time_before_buffer_swap_us != 0 && + frame_info->has_valid_gpu_rendering_duration) || + frame_clock->ever_got_measurements) { int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us; + int64_t dispatch_time_us = 0, flip_time_us = 0; - dispatch_to_swap_us = - frame_info->cpu_time_before_buffer_swap_us - - frame_clock->last_dispatch_time_us; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + dispatch_time_us = frame_clock->last_dispatch_time_us; + flip_time_us = frame_clock->last_flip_time_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + dispatch_time_us = frame_clock->prev_last_dispatch_time_us; + flip_time_us = frame_clock->prev_last_flip_time_us; + break; + } + + if (frame_info->cpu_time_before_buffer_swap_us == 0) + { + /* Cursor-only updates with no "swap" or "flip" */ + dispatch_to_swap_us = 0; + swap_to_flip_us = 0; + } + else + { + dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us - + dispatch_time_us; + swap_to_flip_us = flip_time_us - + frame_info->cpu_time_before_buffer_swap_us; + } swap_to_rendering_done_us = frame_info->gpu_rendering_duration_ns / 1000; - swap_to_flip_us = - frame_clock->last_flip_time_us - - frame_info->cpu_time_before_buffer_swap_us; CLUTTER_NOTE (FRAME_TIMINGS, - "update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + "%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs", + debug_state, frame_clock->last_dispatch_lateness_us, dispatch_to_swap_us, swap_to_rendering_done_us, @@ -386,7 +433,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, CLAMP (frame_clock->last_dispatch_lateness_us + dispatch_to_swap_us + MAX (swap_to_rendering_done_us, swap_to_flip_us), frame_clock->shortterm_max_update_duration_us, - frame_clock->refresh_interval_us); + 2 * frame_clock->refresh_interval_us); maybe_update_longterm_max_duration_us (frame_clock, frame_info); @@ -395,7 +442,8 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, } else { - CLUTTER_NOTE (FRAME_TIMINGS, "update2dispatch %ld µs", + CLUTTER_NOTE (FRAME_TIMINGS, "%s: update2dispatch %ld µs", + debug_state, frame_clock->last_dispatch_lateness_us); } @@ -413,11 +461,22 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } } @@ -435,26 +494,37 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: g_warn_if_reached (); break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } } -static int64_t -clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) +static gboolean +clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock, + int64_t *max_render_time_us) { int64_t refresh_interval_us; - int64_t max_render_time_us; refresh_interval_us = frame_clock->refresh_interval_us; if (!frame_clock->ever_got_measurements || G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME)) - return refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; + return FALSE; /* Max render time shows how early the frame clock needs to be dispatched * to make it to the predicted next presentation time. It is an estimate of @@ -468,15 +538,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock) * - The duration of vertical blank. * - A constant to account for variations in the above estimates. */ - max_render_time_us = + *max_render_time_us = MAX (frame_clock->longterm_max_update_duration_us, frame_clock->shortterm_max_update_duration_us) + frame_clock->vblank_duration_us + clutter_max_render_time_constant_us; - max_render_time_us = CLAMP (max_render_time_us, 0, refresh_interval_us); + *max_render_time_us = CLAMP (*max_render_time_us, 0, 2 * refresh_interval_us); - return max_render_time_us; + return TRUE; } static void @@ -491,7 +561,9 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, int64_t min_render_time_allowed_us; int64_t max_render_time_allowed_us; int64_t next_presentation_time_us; + int64_t next_smooth_presentation_time_us = 0; int64_t next_update_time_us; + gboolean max_render_time_is_known; now_us = g_get_monotonic_time (); @@ -511,10 +583,13 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } min_render_time_allowed_us = refresh_interval_us / 2; - max_render_time_allowed_us = - clutter_frame_clock_compute_max_render_time_us (frame_clock); - if (min_render_time_allowed_us > max_render_time_allowed_us) + max_render_time_is_known = + clutter_frame_clock_compute_max_render_time_us (frame_clock, + &max_render_time_allowed_us); + + if (max_render_time_is_known && + min_render_time_allowed_us > max_render_time_allowed_us) min_render_time_allowed_us = max_render_time_allowed_us; /* @@ -535,7 +610,28 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, * */ last_presentation_time_us = frame_clock->last_presentation_time_us; - next_presentation_time_us = last_presentation_time_us + refresh_interval_us; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + next_smooth_presentation_time_us = last_presentation_time_us + + refresh_interval_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + next_smooth_presentation_time_us = last_presentation_time_us + + 2 * refresh_interval_us; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + next_smooth_presentation_time_us = last_presentation_time_us + + 3 * refresh_interval_us; + break; + } + + next_presentation_time_us = next_smooth_presentation_time_us; /* * However, the last presentation could have happened more than a frame ago. @@ -601,7 +697,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } } - if (next_presentation_time_us != last_presentation_time_us + refresh_interval_us) + if (next_presentation_time_us != next_smooth_presentation_time_us) { /* There was an idle period since the last presentation, so there seems * be no constantly updating actor. In this case it's best to start @@ -613,6 +709,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, } else { + /* If the max render time isn't known then using the current value of + * next_presentation_time_us is suboptimal. Targeting always one frame + * prior to that we'd lose the ability to scale up to triple buffering + * on late presentation. But targeting two frames prior we would be + * always triple buffering even when not required. + * So the algorithm for deciding when to scale up to triple buffering + * in the absence of render time measurements is to simply target full + * frame rate. If we're keeping up then we'll stay double buffering. If + * we're not keeping up then this will switch us to triple buffering. + */ + if (!max_render_time_is_known) + { + max_render_time_allowed_us = + refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION; + next_presentation_time_us = + last_presentation_time_us + refresh_interval_us; + } + while (next_presentation_time_us - min_render_time_allowed_us < now_us) next_presentation_time_us += refresh_interval_us; @@ -644,7 +758,9 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, refresh_interval_us = frame_clock->refresh_interval_us; - if (frame_clock->last_presentation_time_us == 0) + if (frame_clock->last_presentation_time_us == 0 || + !clutter_frame_clock_compute_max_render_time_us (frame_clock, + &max_render_time_allowed_us)) { *out_next_update_time_us = frame_clock->last_dispatch_time_us ? @@ -657,9 +773,6 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock, return; } - max_render_time_allowed_us = - clutter_frame_clock_compute_max_render_time_us (frame_clock); - last_presentation_time_us = frame_clock->last_presentation_time_us; next_presentation_time_us = last_presentation_time_us + refresh_interval_us; @@ -733,8 +846,17 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: break; } @@ -753,6 +875,25 @@ clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock) maybe_reschedule_update (frame_clock); } +static gboolean +want_triple_buffering (ClutterFrameClock *frame_clock) +{ + switch (triple_buffering_mode) + { + case TRIPLE_BUFFERING_MODE_NEVER: + return FALSE; + case TRIPLE_BUFFERING_MODE_AUTO: + return frame_clock->mode == CLUTTER_FRAME_CLOCK_MODE_FIXED && + !(frame_clock->last_flip_hints & + CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED); + case TRIPLE_BUFFERING_MODE_ALWAYS: + return TRUE; + } + + g_assert_not_reached (); + return FALSE; +} + void clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) { @@ -770,11 +911,24 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + if (want_triple_buffering (frame_clock)) + { + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW; + break; + } + G_GNUC_FALLTHROUGH; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; frame_clock->pending_reschedule_now = TRUE; return; @@ -803,13 +957,17 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock) frame_clock->next_update_time_us = next_update_time_us; g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; } void clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) { int64_t next_update_time_us = -1; + TripleBufferingMode current_mode = triple_buffering_mode; + + if (current_mode == TRIPLE_BUFFERING_MODE_AUTO && + !want_triple_buffering (frame_clock)) + current_mode = TRIPLE_BUFFERING_MODE_NEVER; if (frame_clock->inhibit_count > 0) { @@ -825,12 +983,33 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; return; case CLUTTER_FRAME_CLOCK_STATE_IDLE: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: return; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + switch (current_mode) + { + case TRIPLE_BUFFERING_MODE_NEVER: + frame_clock->pending_reschedule = TRUE; + return; + case TRIPLE_BUFFERING_MODE_AUTO: + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + break; + case TRIPLE_BUFFERING_MODE_ALWAYS: + next_update_time_us = g_get_monotonic_time (); + frame_clock->next_presentation_time_us = 0; + frame_clock->is_next_presentation_time_valid = FALSE; + frame_clock->state = + CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED; + return; + } + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: frame_clock->pending_reschedule = TRUE; return; } @@ -859,7 +1038,6 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) frame_clock->next_update_time_us = next_update_time_us; g_source_set_ready_time (frame_clock->source, next_update_time_us); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; } void @@ -875,6 +1053,8 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, { case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: break; case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: frame_clock->pending_reschedule = TRUE; @@ -885,8 +1065,14 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, frame_clock->pending_reschedule_now = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->pending_reschedule = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->pending_reschedule = TRUE; + frame_clock->pending_reschedule_now = TRUE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; break; } @@ -922,7 +1108,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, frame_clock->refresh_interval_us; lateness_us = time_us - ideal_dispatch_time_us; - if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us) + if (lateness_us < 0 || lateness_us >= frame_clock->refresh_interval_us / 4) frame_clock->last_dispatch_lateness_us = 0; else frame_clock->last_dispatch_lateness_us = lateness_us; @@ -943,10 +1129,27 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, } #endif + frame_clock->prev_last_dispatch_time_us = frame_clock->last_dispatch_time_us; frame_clock->last_dispatch_time_us = time_us; g_source_set_ready_time (frame_clock->source, -1); - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHING; + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + g_warn_if_reached (); + return; + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO; + break; + } frame_count = frame_clock->frame_count++; @@ -977,26 +1180,36 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, result = iface->frame (frame_clock, frame, frame_clock->listener.user_data); COGL_TRACE_END (ClutterFrameClockFrame); - switch (frame_clock->state) + switch (result) { - case CLUTTER_FRAME_CLOCK_STATE_INIT: - case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: - g_warn_if_reached (); + case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: break; - case CLUTTER_FRAME_CLOCK_STATE_IDLE: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: - case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: - break; - case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: - switch (result) + case CLUTTER_FRAME_RESULT_IDLE: + /* The frame was aborted; nothing to paint/present */ + switch (frame_clock->state) { - case CLUTTER_FRAME_RESULT_PENDING_PRESENTED: - frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED; + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW: + g_warn_if_reached (); break; - case CLUTTER_FRAME_RESULT_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE: frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; maybe_reschedule_update (frame_clock); break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW; + maybe_reschedule_update (frame_clock); + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE; + maybe_reschedule_update (frame_clock); + break; } break; } @@ -1029,21 +1242,31 @@ frame_clock_source_dispatch (GSource *source, } void -clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, - int64_t flip_time_us) +clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, + int64_t flip_time_us, + ClutterFrameHint hints) { + frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us; frame_clock->last_flip_time_us = flip_time_us; + frame_clock->last_flip_hints = hints; } GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock) { + int64_t max_render_time_us; int64_t max_update_duration_us; GString *string; - string = g_string_new (NULL); - g_string_append_printf (string, "Max render time: %ld µs", - clutter_frame_clock_compute_max_render_time_us (frame_clock)); + string = g_string_new ("Max render time: "); + if (!clutter_frame_clock_compute_max_render_time_us (frame_clock, + &max_render_time_us)) + { + g_string_append (string, "unknown"); + return string; + } + + g_string_append_printf (string, "%ld µs", max_render_time_us); if (frame_clock->got_measurements_last_frame) g_string_append_printf (string, " ="); @@ -1210,8 +1433,6 @@ clutter_frame_clock_dispose (GObject *object) { ClutterFrameClock *frame_clock = CLUTTER_FRAME_CLOCK (object); - g_warn_if_fail (frame_clock->state != CLUTTER_FRAME_CLOCK_STATE_DISPATCHING); - if (frame_clock->source) { g_signal_emit (frame_clock, signals[DESTROY], 0); @@ -1235,6 +1456,15 @@ static void clutter_frame_clock_class_init (ClutterFrameClockClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + const char *mode_str; + + mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING"); + if (!g_strcmp0 (mode_str, "never")) + triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER; + else if (!g_strcmp0 (mode_str, "auto")) + triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO; + else if (!g_strcmp0 (mode_str, "always")) + triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS; object_class->dispose = clutter_frame_clock_dispose; diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h index a7be5ef..bfc89bd 100644 --- a/clutter/clutter/clutter-frame-clock.h +++ b/clutter/clutter/clutter-frame-clock.h @@ -33,6 +33,12 @@ typedef enum _ClutterFrameResult CLUTTER_FRAME_RESULT_IDLE, } ClutterFrameResult; +typedef enum _ClutterFrameHint +{ + CLUTTER_FRAME_HINT_NONE = 0, + CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED = 1 << 0, +} ClutterFrameHint; + #define CLUTTER_TYPE_FRAME_CLOCK (clutter_frame_clock_get_type ()) CLUTTER_EXPORT G_DECLARE_FINAL_TYPE (ClutterFrameClock, clutter_frame_clock, @@ -102,7 +108,8 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock, CLUTTER_EXPORT float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock); -void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock, - int64_t flip_time_us); +void clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock, + int64_t flip_time_us, + ClutterFrameHint hints); GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock); diff --git a/clutter/clutter/clutter-frame-private.h b/clutter/clutter/clutter-frame-private.h index ef66b87..ce14056 100644 --- a/clutter/clutter/clutter-frame-private.h +++ b/clutter/clutter/clutter-frame-private.h @@ -36,6 +36,7 @@ struct _ClutterFrame gboolean has_result; ClutterFrameResult result; + ClutterFrameHint hints; }; CLUTTER_EXPORT diff --git a/clutter/clutter/clutter-frame.c b/clutter/clutter/clutter-frame.c index 7436f9f..53c289b 100644 --- a/clutter/clutter/clutter-frame.c +++ b/clutter/clutter/clutter-frame.c @@ -115,3 +115,16 @@ clutter_frame_set_result (ClutterFrame *frame, frame->result = result; frame->has_result = TRUE; } + +void +clutter_frame_set_hint (ClutterFrame *frame, + ClutterFrameHint hint) +{ + frame->hints |= hint; +} + +ClutterFrameHint +clutter_frame_get_hints (ClutterFrame *frame) +{ + return frame->hints; +} diff --git a/clutter/clutter/clutter-frame.h b/clutter/clutter/clutter-frame.h index 34f0770..c7b3d02 100644 --- a/clutter/clutter/clutter-frame.h +++ b/clutter/clutter/clutter-frame.h @@ -54,4 +54,11 @@ void clutter_frame_set_result (ClutterFrame *frame, CLUTTER_EXPORT gboolean clutter_frame_has_result (ClutterFrame *frame); +CLUTTER_EXPORT +void clutter_frame_set_hint (ClutterFrame *frame, + ClutterFrameHint hint); + +CLUTTER_EXPORT +ClutterFrameHint clutter_frame_get_hints (ClutterFrame *frame); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFrame, clutter_frame_unref) diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index f5188e2..d53e377 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -898,14 +898,21 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock, _clutter_stage_window_redraw_view (stage_window, view, frame); - clutter_frame_clock_record_flip_time (frame_clock, - g_get_monotonic_time ()); + clutter_frame_clock_record_flip (frame_clock, + g_get_monotonic_time (), + clutter_frame_get_hints (frame)); clutter_stage_emit_after_paint (stage, view, frame); if (_clutter_context_get_show_fps ()) end_frame_timing_measurement (view); } + else + { + clutter_frame_clock_record_flip (frame_clock, + g_get_monotonic_time (), + clutter_frame_get_hints (frame)); + } _clutter_stage_window_finish_frame (stage_window, view, frame); diff --git a/cogl/cogl/cogl-onscreen-private.h b/cogl/cogl/cogl-onscreen-private.h index 959a605..86d8ea2 100644 --- a/cogl/cogl/cogl-onscreen-private.h +++ b/cogl/cogl/cogl-onscreen-private.h @@ -78,4 +78,7 @@ COGL_EXPORT CoglFrameInfo * cogl_onscreen_peek_tail_frame_info (CoglOnscreen *onscreen); COGL_EXPORT CoglFrameInfo * -cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); +cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen); + +COGL_EXPORT unsigned int +cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen); diff --git a/cogl/cogl/cogl-onscreen.c b/cogl/cogl/cogl-onscreen.c index afb648b..086be7e 100644 --- a/cogl/cogl/cogl-onscreen.c +++ b/cogl/cogl/cogl-onscreen.c @@ -515,6 +515,14 @@ cogl_onscreen_pop_head_frame_info (CoglOnscreen *onscreen) return g_queue_pop_head (&priv->pending_frame_infos); } +unsigned int +cogl_onscreen_count_pending_frames (CoglOnscreen *onscreen) +{ + CoglOnscreenPrivate *priv = cogl_onscreen_get_instance_private (onscreen); + + return g_queue_get_length (&priv->pending_frame_infos); +} + CoglFrameClosure * cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, CoglFrameCallback callback, diff --git a/meson.build b/meson.build index 131c19f..544b39c 100644 --- a/meson.build +++ b/meson.build @@ -160,7 +160,6 @@ if have_x11_client xkbfile_dep = dependency('xkbfile') xkeyboard_config_dep = dependency('xkeyboard-config') xkbcommon_x11_dep = dependency('xkbcommon-x11') - xrender_dep = dependency('xrender') x11_xcb_dep = dependency('x11-xcb') xrandr_dep = dependency('xrandr', version: xrandr_req) xcb_randr_dep = dependency('xcb-randr') diff --git a/po/pt.po b/po/pt.po index 88da880..c9968e9 100644 --- a/po/pt.po +++ b/po/pt.po @@ -5,14 +5,14 @@ # Pedro Albuquerque <palbuquerque73@openmailbox.com>, 2015. # Tiago Santos <tiagofsantos81@sapo.pt>, 2016. # Juliano de Souza Camargo <julianosc@protonmail.com>, 2020. -# Hugo Carvalho <hugokarvalho@hotmail.com>, 2020, 2021, 2022, 2023. +# Hugo Carvalho <hugokarvalho@hotmail.com>, 2020, 2021, 2022, 2023, 2024. # msgid "" msgstr "" "Project-Id-Version: 3.10\n" -"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/mutter/issues\n" -"POT-Creation-Date: 2023-07-16 01:41+0000\n" -"PO-Revision-Date: 2023-08-04 17:11+0100\n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/mutter/issues/\n" +"POT-Creation-Date: 2024-04-21 15:14+0000\n" +"PO-Revision-Date: 2024-04-28 21:14+0100\n" "Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n" "Language-Team: https://l10n.gnome.org/teams/pt/\n" "Language: pt\n" @@ -20,7 +20,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.3.2\n" +"X-Generator: Poedit 3.4.2\n" "X-DamnedLies-Scope: partial\n" #: data/50-mutter-navigation.xml:6 @@ -251,11 +251,11 @@ msgstr "Maximizar janela verticalmente" msgid "Maximize window horizontally" msgstr "Maximizar janela horizontalmente" -#: data/50-mutter-windows.xml:41 data/org.gnome.mutter.gschema.xml.in:164 +#: data/50-mutter-windows.xml:41 data/org.gnome.mutter.gschema.xml.in:167 msgid "View split on left" msgstr "Ver a divisão à esquerda" -#: data/50-mutter-windows.xml:45 data/org.gnome.mutter.gschema.xml.in:169 +#: data/50-mutter-windows.xml:45 data/org.gnome.mutter.gschema.xml.in:172 msgid "View split on right" msgstr "Ver a divisão à direita" @@ -396,39 +396,43 @@ msgid "" "space, while scaling monitor framebuffers instead of window content, to " "manage HiDPI monitors. Does not require a restart. • “kms-modifiers” — makes " "mutter always allocate scanout buffers with explicit modifiers, if supported " -"by the driver. Requires a restart. • “rt-scheduler” — makes mutter request a " -"low priority real-time scheduling. Requires a restart. • “autoclose-" -"xwayland” — automatically terminates Xwayland if all relevant X11 clients " -"are gone. Requires a restart." +"by the driver. Requires a restart. • “autoclose-xwayland” — automatically " +"terminates Xwayland if all relevant X11 clients are gone. Requires a " +"restart. • “variable-refresh-rate” — makes mutter dynamically adjust the " +"refresh rate of the monitor when applicable if supported by the monitor, GPU " +"and DRM driver. Configurable in Settings. Requires a restart." msgstr "" -"Para ativar as funcionalidades experimentais, adicione a respetiva palavra-" -"chave à lista. Se deve reiniciar o compositor após ativá-la, depende de cada " -"funcionalidade. Qualquer funcionalidade experimental não necessita de estar " -"disponível ou configurável. Não espere que adicionar algo nas definições " -"seja mantido por tempo indeterminado. Atualmente as palavras-chave são: • " -"“scale-monitor-framebuffer” — torna o mutter padrão para a disposição de " -"monitores lógicos num espaço de coordenadas de pixel lógico, enquanto " -"dimensiona os framebuffers do monitor em vez do conteúdo da janela, para " -"gerir monitores HiDPI. Não requer um reinício. • “kms-modifiers” — faz o " -"mutter publicitar sempre modificadores buffer. se suportado pelo " -"controlador. Requer um reinício. - \"rt-scheduler\" - torna o pedido do " -"mutter uma programação em tempo real de baixa prioridade. Requer um " -"reinício. • “autoclose-xwayland” — Termina automaticamente o Xwayland se " -"todos os clientes X11 relevantes desapareceram. Requer um reinício." - -#: data/org.gnome.mutter.gschema.xml.in:141 +"Para ativar funcionalidades experimentais, adicione a palavra-chave " +"caraterística à lista. O facto de a funcionalidade exigir o reinício do " +"compositor depende da funcionalidade em causa. Não é necessário que qualquer " +"recurso experimental ainda esteja disponível ou configurável. Não espere que " +"adicionar algo nesta configuração seja à prova de futuro. Palavras-chave " +"atualmente possíveis: - “scale-monitor-framebuffer” - torna o mutter padrão " +"para a disposição de monitores lógicos em um espaço lógico de coordenadas de " +"pixel, enquanto escala os framebuffers do monitor em vez do conteúdo da " +"janela, para gerenciar monitores HiDPI. Não requer uma reinicialização. - " +"“kms-modifiers” - faz com que o mutter sempre aloque buffers de \"scanout\" " +"com modificadores explícitos, se suportados pelo controlador. Requer uma " +"reinicialização. - “autoclose-xwayland” - termina automaticamente o Xwayland " +"se todos os clientes X11 relevantes tiverem desaparecido. Requer um " +"reinício. - “variable-refresh-rate” - faz com que o mutter ajuste " +"dinamicamente a taxa de atualização do monitor quando aplicável, se " +"suportado pelo monitor, GPU e driver DRM. Configurável em Definições. Requer " +"uma reinicialização." + +#: data/org.gnome.mutter.gschema.xml.in:144 msgid "Modifier to use to locate the pointer" msgstr "Modificador para localizar o cursor" -#: data/org.gnome.mutter.gschema.xml.in:142 +#: data/org.gnome.mutter.gschema.xml.in:145 msgid "This key will initiate the “locate pointer” action." msgstr "Esta chave iniciará a ação “localizar cursor”." -#: data/org.gnome.mutter.gschema.xml.in:149 +#: data/org.gnome.mutter.gschema.xml.in:152 msgid "Timeout for check-alive ping" msgstr "Expirou o teste de atividade" -#: data/org.gnome.mutter.gschema.xml.in:150 +#: data/org.gnome.mutter.gschema.xml.in:153 msgid "" "Number of milliseconds a client has to respond to a ping request in order to " "not be detected as frozen. Using 0 will disable the alive check completely." @@ -437,15 +441,15 @@ msgstr "" "atividade de maneira que não seja detetado como inativo. Usar 0 desativará o " "teste de atividade completamente." -#: data/org.gnome.mutter.gschema.xml.in:174 +#: data/org.gnome.mutter.gschema.xml.in:177 msgid "Switch monitor configurations" msgstr "Alternar configurações de ecrã" -#: data/org.gnome.mutter.gschema.xml.in:179 +#: data/org.gnome.mutter.gschema.xml.in:182 msgid "Rotates the built-in monitor configuration" msgstr "Alternar as configurações nativas do ecrã" -#: data/org.gnome.mutter.gschema.xml.in:184 +#: data/org.gnome.mutter.gschema.xml.in:187 msgid "Cancel any active input capture session" msgstr "Cancelar qualquer sessão de captura de entrada ativa" @@ -603,26 +607,26 @@ msgstr "" "configuração. O Xwayland precisa de ser reiniciado para que esta " "configuração tenha efeito." -#: src/backends/meta-monitor.c:253 +#: src/backends/meta-monitor.c:251 msgid "Built-in display" msgstr "Ecrã embutido" -#: src/backends/meta-monitor.c:280 +#: src/backends/meta-monitor.c:278 msgid "Unknown" msgstr "Desconhecido" -#: src/backends/meta-monitor.c:282 +#: src/backends/meta-monitor.c:280 msgid "Unknown Display" msgstr "Ecrã desconhecido" -#: src/backends/meta-monitor.c:290 +#: src/backends/meta-monitor.c:288 #, c-format msgctxt "" "This is a monitor vendor name, followed by a size in inches, like 'Dell 15\"'" msgid "%s %s" msgstr "%s %s" -#: src/backends/meta-monitor.c:298 +#: src/backends/meta-monitor.c:296 #, c-format msgctxt "" "This is a monitor vendor name followed by product/model name where size in " @@ -634,78 +638,82 @@ msgstr "%s %s" msgid "Bell event" msgstr "Evento de campainha" -#: src/core/display.c:718 +#: src/core/display.c:734 msgid "Privacy Screen Enabled" msgstr "Ecrã de privacidade ativado" -#: src/core/display.c:719 +#: src/core/display.c:735 msgid "Privacy Screen Disabled" msgstr "Ecrã de privacidade desativado" -#: src/core/meta-context-main.c:581 +#: src/core/meta-context-main.c:601 msgid "Replace the running window manager" msgstr "Substituir o gestor de janelas em execução" -#: src/core/meta-context-main.c:587 +#: src/core/meta-context-main.c:607 msgid "X Display to use" msgstr "Ecrã X a utilizar" -#: src/core/meta-context-main.c:593 +#: src/core/meta-context-main.c:613 msgid "Disable connection to session manager" msgstr "Desativar a ligação ao gestor de sessão" -#: src/core/meta-context-main.c:599 +#: src/core/meta-context-main.c:619 msgid "Specify session management ID" msgstr "Especificar a ID de gestão de sessão" -#: src/core/meta-context-main.c:605 +#: src/core/meta-context-main.c:625 msgid "Initialize session from savefile" msgstr "Inicializar a sessão a partir de um ficheiro de gravação de sessão" -#: src/core/meta-context-main.c:611 +#: src/core/meta-context-main.c:631 msgid "Make X calls synchronous" msgstr "Fazer as chamadas X sincronamente" -#: src/core/meta-context-main.c:619 +#: src/core/meta-context-main.c:639 msgid "Run as a wayland compositor" msgstr "Executar como compositor wayland" -#: src/core/meta-context-main.c:625 +#: src/core/meta-context-main.c:645 msgid "Run as a nested compositor" msgstr "Executar como compositor aninhado" -#: src/core/meta-context-main.c:631 +#: src/core/meta-context-main.c:651 msgid "Run wayland compositor without starting Xwayland" msgstr "Executar o compositor wayland sem a utilização do Xwayland" -#: src/core/meta-context-main.c:637 +#: src/core/meta-context-main.c:657 msgid "Specify Wayland display name to use" msgstr "Especificar o nome do visor Wayland a utilizar" -#: src/core/meta-context-main.c:645 +#: src/core/meta-context-main.c:665 msgid "Run as a full display server, rather than nested" msgstr "Executar como servidor de ecrã inteiro, em vez de aninhado" -#: src/core/meta-context-main.c:650 +#: src/core/meta-context-main.c:670 msgid "Run as a headless display server" msgstr "Executar como um servidor sem monitor" -#: src/core/meta-context-main.c:655 +#: src/core/meta-context-main.c:675 msgid "Add persistent virtual monitor (WxH or WxH@R)" msgstr "Adicionar monitor virtual persistente (WxH ou WxH@R)" -#: src/core/meta-context-main.c:667 +#: src/core/meta-context-main.c:687 msgid "Run with X11 backend" msgstr "Executar com a infira-estrutura X11" -#: src/core/meta-context-main.c:673 +#: src/core/meta-context-main.c:693 msgid "Profile performance using trace instrumentation" msgstr "Desempenho do perfil utilizando instrumentação de rastreio" +#: src/core/meta-context-main.c:699 +msgid "Enable debug control D-Bus interface" +msgstr "Ativar o controlo de depuração da interface D-Bus" + #. TRANSLATORS: This string refers to a button that switches between #. * different modes. #. -#: src/core/meta-pad-action-mapper.c:861 +#: src/core/meta-pad-action-mapper.c:826 #, c-format msgid "Mode Switch (Group %d)" msgstr "Alteração de modo (Grupo %d)" @@ -713,16 +721,16 @@ msgstr "Alteração de modo (Grupo %d)" #. TRANSLATORS: This string refers to an action, cycles drawing tablets' #. * mapping through the available outputs. #. -#: src/core/meta-pad-action-mapper.c:884 +#: src/core/meta-pad-action-mapper.c:848 msgid "Switch monitor" msgstr "Alternar monitor" -#: src/core/meta-pad-action-mapper.c:886 +#: src/core/meta-pad-action-mapper.c:850 msgid "Show on-screen help" msgstr "Mostrar ajuda no ecrã" #. Translators: this string will appear in Sysprof -#: src/core/meta-profiler.c:111 src/core/meta-profiler.c:301 +#: src/core/meta-profiler.c:109 src/core/meta-profiler.c:299 msgid "Compositor" msgstr "Compositor" @@ -734,7 +742,7 @@ msgstr "Imprimir a versão" msgid "Mutter plugin to use" msgstr "Extensão Mutter a utilizar" -#: src/core/prefs.c:1843 +#: src/core/prefs.c:1842 #, c-format msgid "Workspace %d" msgstr "Área de trabalho %d" @@ -743,16 +751,16 @@ msgstr "Área de trabalho %d" msgid "Mutter was compiled without support for verbose mode" msgstr "O Mutter foi compilado sem suporte para modo verbose" -#: src/core/workspace.c:541 +#: src/core/workspace.c:510 msgid "Workspace switched" msgstr "Área de trabalho alterada" -#: src/wayland/meta-wayland-tablet-pad.c:530 +#: src/wayland/meta-wayland-tablet-pad.c:532 #, c-format msgid "Mode Switch: Mode %d" msgstr "Alteração de Modo: Modo %d" -#: src/x11/meta-x11-display.c:708 +#: src/x11/meta-x11-display.c:723 #, c-format msgid "" "Display “%s” already has a window manager; try using the --replace option to " @@ -761,19 +769,19 @@ msgstr "" "O ecrã “%s” já tem um gestor de janelas; tente utilizar a opção --replace " "para substituir o gestor de janelas atual." -#: src/x11/meta-x11-display.c:1073 +#: src/x11/meta-x11-display.c:1088 #, c-format msgid "Failed to open X Window System display “%s”" msgstr "Falha ao abrir ecrã “%s” do sistema Janelas X" -#: src/x11/meta-x11-display.c:1219 +#: src/x11/meta-x11-display.c:1268 #, c-format msgid "Screen %d on display “%s” is invalid" msgstr "Ecrã %d no monitor “%s” é inválido" #. This probably means that a non-WM compositor like xcompmgr is running; #. * we have no way to get it to exit -#: src/x11/meta-x11-display.c:2550 +#: src/x11/meta-x11-display.c:2547 #, c-format msgid "" "Another compositing manager is already running on screen %i on display “%s”." @@ -785,7 +793,7 @@ msgstr "" msgid "Format %s not supported" msgstr "O formato “%s” não é suportado" -#: src/x11/window-props.c:548 +#: src/x11/window-props.c:528 #, c-format msgid "%s (on %s)" msgstr "%s (em %s)" diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c index 7aa2443..727e1a5 100644 --- a/src/backends/meta-stage-impl.c +++ b/src/backends/meta-stage-impl.c @@ -774,6 +774,8 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window, { g_autoptr (GError) error = NULL; + clutter_frame_set_hint (frame, CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED); + if (meta_stage_impl_scanout_view (stage_impl, stage_view, scanout, diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index b15eee1..05bc89e 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -1559,9 +1559,11 @@ meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, meta_kms_update_merge_from (crtc_frame->pending_update, update); meta_kms_update_free (update); update = g_steal_pointer (&crtc_frame->pending_update); - disarm_crtc_frame_deadline_timer (crtc_frame); } + if (crtc_frame->deadline.armed) + disarm_crtc_frame_deadline_timer (crtc_frame); + meta_kms_device_handle_flush (priv->device, latch_crtc); feedback = do_process (impl_device, latch_crtc, update, flags); diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c index 795008b..70d1e79 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c @@ -63,6 +63,8 @@ struct _MetaKms int kernel_thread_inhibit_count; MetaKmsCursorManager *cursor_manager; + + gboolean shutting_down; }; G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD) @@ -354,6 +356,7 @@ static void on_prepare_shutdown (MetaBackend *backend, MetaKms *kms) { + kms->shutting_down = TRUE; meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL); meta_thread_flush_callbacks (META_THREAD (kms)); @@ -408,6 +411,12 @@ meta_kms_new (MetaBackend *backend, return kms; } +gboolean +meta_kms_is_shutting_down (MetaKms *kms) +{ + return kms->shutting_down; +} + static void meta_kms_finalize (GObject *object) { diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h index 7434014..f6b1952 100644 --- a/src/backends/native/meta-kms.h +++ b/src/backends/native/meta-kms.h @@ -60,6 +60,8 @@ MetaKmsDevice * meta_kms_create_device (MetaKms *kms, MetaKmsDeviceFlag flags, GError **error); +gboolean meta_kms_is_shutting_down (MetaKms *kms); + MetaKms * meta_kms_new (MetaBackend *backend, MetaKmsFlags flags, GError **error); diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 1a31f04..9836663 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -76,7 +76,7 @@ typedef struct _MetaOnscreenNativeSecondaryGpuState struct { MetaDrmBufferDumb *current_dumb_fb; - MetaDrmBufferDumb *dumb_fbs[2]; + MetaDrmBufferDumb *dumb_fbs[3]; } cpu; gboolean noted_primary_gpu_copy_ok; @@ -98,9 +98,13 @@ struct _MetaOnscreenNative struct { struct gbm_surface *surface; MetaDrmBuffer *current_fb; + MetaDrmBuffer *posted_fb; MetaDrmBuffer *next_fb; + MetaDrmBuffer *stalled_fb; CoglScanout *current_scanout; + CoglScanout *posted_scanout; CoglScanout *next_scanout; + CoglScanout *stalled_scanout; } gbm; #ifdef HAVE_EGL_DEVICE @@ -125,6 +129,16 @@ struct _MetaOnscreenNative gulong privacy_screen_changed_handler_id; gulong color_space_changed_handler_id; gulong hdr_metadata_changed_handler_id; + + gboolean needs_flush; + + unsigned int swaps_pending; + + struct { + int *rectangles; /* 4 x n_rectangles */ + int n_rectangles; + ClutterFrame *frame; + } next_post; }; G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, @@ -132,44 +146,42 @@ G_DEFINE_TYPE (MetaOnscreenNative, meta_onscreen_native, static GQuark blit_source_quark = 0; +static void +try_post_latest_swap (CoglOnscreen *onscreen); + +static void +post_finish_frame (MetaOnscreenNative *onscreen_native, + MetaKmsUpdate *kms_update); + static gboolean init_secondary_gpu_state (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, GError **error); -static void -free_current_bo (CoglOnscreen *onscreen) -{ - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - - g_clear_object (&onscreen_native->gbm.current_fb); - g_clear_object (&onscreen_native->gbm.current_scanout); -} - static void meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - if (!onscreen_native->gbm.next_fb) + if (!onscreen_native->gbm.posted_fb) return; - free_current_bo (onscreen); + g_set_object (&onscreen_native->gbm.current_fb, + onscreen_native->gbm.posted_fb); + g_clear_object (&onscreen_native->gbm.posted_fb); - g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); - g_clear_object (&onscreen_native->gbm.next_fb); g_set_object (&onscreen_native->gbm.current_scanout, - onscreen_native->gbm.next_scanout); - g_clear_object (&onscreen_native->gbm.next_scanout); + onscreen_native->gbm.posted_scanout); + g_clear_object (&onscreen_native->gbm.posted_scanout); } static void -meta_onscreen_native_clear_next_fb (CoglOnscreen *onscreen) +meta_onscreen_native_clear_posted_fb (CoglOnscreen *onscreen) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - g_clear_object (&onscreen_native->gbm.next_fb); - g_clear_object (&onscreen_native->gbm.next_scanout); + g_clear_object (&onscreen_native->gbm.posted_fb); + g_clear_object (&onscreen_native->gbm.posted_scanout); } static void @@ -207,7 +219,7 @@ meta_onscreen_native_notify_frame_complete (CoglOnscreen *onscreen) info = cogl_onscreen_pop_head_frame_info (onscreen); - g_assert (!cogl_onscreen_peek_head_frame_info (onscreen)); + g_return_if_fail (info); _cogl_onscreen_notify_frame_sync (onscreen, info); _cogl_onscreen_notify_complete (onscreen, info); @@ -243,6 +255,7 @@ notify_view_crtc_presented (MetaRendererView *view, meta_onscreen_native_notify_frame_complete (onscreen); meta_onscreen_native_swap_drm_fb (onscreen); + try_post_latest_swap (onscreen); } static void @@ -292,15 +305,13 @@ page_flip_feedback_ready (MetaKmsCrtc *kms_crtc, CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); CoglFrameInfo *frame_info; frame_info = cogl_onscreen_peek_head_frame_info (onscreen); frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - g_warn_if_fail (!onscreen_native->gbm.next_fb); - meta_onscreen_native_notify_frame_complete (onscreen); + try_post_latest_swap (onscreen); } static void @@ -350,7 +361,8 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_clear_next_fb (onscreen); + meta_onscreen_native_clear_posted_fb (onscreen); + try_post_latest_swap (onscreen); } static const MetaKmsPageFlipListenerVtable page_flip_listener_vtable = { @@ -411,18 +423,41 @@ custom_egl_stream_page_flip (gpointer custom_page_flip_data, } #endif /* HAVE_EGL_DEVICE */ -void -meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) +static void +drop_stalled_swap (CoglOnscreen *onscreen) { CoglFrameInfo *frame_info; + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - meta_onscreen_native_swap_drm_fb (onscreen); + /* Remember we can't compare stalled_fb because it's not used by + * META_RENDERER_NATIVE_MODE_EGL_DEVICE. So we judge stalled to be whenever + * swaps_pending > 1. + */ + if (onscreen_native->swaps_pending <= 1) + return; + + onscreen_native->swaps_pending--; + + g_clear_object (&onscreen_native->gbm.stalled_fb); + g_clear_object (&onscreen_native->gbm.stalled_scanout); frame_info = cogl_onscreen_peek_tail_frame_info (onscreen); frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); } +void +meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen) +{ + drop_stalled_swap (onscreen); + + /* If the monitor just woke up and the shell is fully idle (has nothing + * more to swap) then we just woke to an indefinitely black screen. Let's + * fix that using the last swap (which is never classified as "stalled"). + */ + try_post_latest_swap (onscreen); +} + static void apply_transform (MetaCrtcKms *crtc_kms, MetaKmsPlaneAssignment *kms_plane_assignment, @@ -521,13 +556,21 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: - buffer = onscreen_native->gbm.next_fb; + g_set_object (&onscreen_native->gbm.posted_fb, + onscreen_native->gbm.next_fb); + g_clear_object (&onscreen_native->gbm.next_fb); + + buffer = onscreen_native->gbm.posted_fb; - if (onscreen_native->gbm.next_scanout) + g_set_object (&onscreen_native->gbm.posted_scanout, + onscreen_native->gbm.next_scanout); + g_clear_object (&onscreen_native->gbm.next_scanout); + + if (onscreen_native->gbm.posted_scanout) { - cogl_scanout_get_src_rect (onscreen_native->gbm.next_scanout, + cogl_scanout_get_src_rect (onscreen_native->gbm.posted_scanout, &src_rect); - cogl_scanout_get_dst_rect (onscreen_native->gbm.next_scanout, + cogl_scanout_get_dst_rect (onscreen_native->gbm.posted_scanout, &dst_rect); } else @@ -918,12 +961,17 @@ static MetaDrmBufferDumb * secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { MetaDrmBufferDumb *current_dumb_fb; + const int n_dumb_fbs = G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); + int i; current_dumb_fb = secondary_gpu_state->cpu.current_dumb_fb; - if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[0]) - return secondary_gpu_state->cpu.dumb_fbs[1]; - else - return secondary_gpu_state->cpu.dumb_fbs[0]; + for (i = 0; i < n_dumb_fbs; i++) + { + if (current_dumb_fb == secondary_gpu_state->cpu.dumb_fbs[i]) + return secondary_gpu_state->cpu.dumb_fbs[(i + 1) % n_dumb_fbs]; + } + + return secondary_gpu_state->cpu.dumb_fbs[0]; } static MetaDrmBuffer * @@ -1255,10 +1303,17 @@ swap_buffer_result_feedback (const MetaKmsFeedback *kms_feedback, g_warning ("Page flip failed: %s", error->message); frame_info = cogl_onscreen_peek_head_frame_info (onscreen); - frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; - meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_clear_next_fb (onscreen); + /* After resuming from suspend, drop_stalled_swap might have done this + * already and emptied the frame_info queue. + */ + if (frame_info) + { + frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; + meta_onscreen_native_notify_frame_complete (onscreen); + } + + meta_onscreen_native_clear_posted_fb (onscreen); } static const MetaKmsResultListenerVtable swap_buffer_result_listener_vtable = { @@ -1279,32 +1334,37 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; - MetaRenderer *renderer = META_RENDERER (renderer_native); - MetaBackend *backend = meta_renderer_get_backend (renderer); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; MetaGpuKms *render_gpu = onscreen_native->render_gpu; MetaDeviceFile *render_device_file; ClutterFrame *frame = user_data; - MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); - MetaKmsUpdate *kms_update; CoglOnscreenClass *parent_class; gboolean create_timestamp_query = TRUE; gboolean egl_context_changed = FALSE; - MetaPowerSave power_save_mode; g_autoptr (GError) error = NULL; MetaDrmBufferFlags buffer_flags; MetaDrmBufferGbm *buffer_gbm; g_autoptr (MetaDrmBuffer) primary_gpu_fb = NULL; g_autoptr (MetaDrmBuffer) secondary_gpu_fb = NULL; - MetaKmsCrtc *kms_crtc; - MetaKmsDevice *kms_device; + size_t rectangles_size; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, "Meta::OnscreenNative::swap_buffers_with_damage()"); + if (meta_is_topic_enabled (META_DEBUG_KMS)) + { + unsigned int frames_pending = + cogl_onscreen_count_pending_frames (onscreen); + + meta_topic (META_DEBUG_KMS, + "Swap buffers: %u frames pending (%s-buffering)", + frames_pending, + frames_pending == 1 ? "double" : + frames_pending == 2 ? "triple" : + "?"); + } + secondary_gpu_fb = update_secondary_gpu_state_pre_swap_buffers (onscreen, rectangles, @@ -1379,7 +1439,17 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: - g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); + if (onscreen_native->gbm.next_fb != NULL) + { + g_warn_if_fail (onscreen_native->gbm.stalled_fb == NULL); + drop_stalled_swap (onscreen); + g_assert (onscreen_native->gbm.stalled_fb == NULL); + onscreen_native->gbm.stalled_fb = + g_steal_pointer (&onscreen_native->gbm.next_fb); + onscreen_native->gbm.stalled_scanout = + g_steal_pointer (&onscreen_native->gbm.next_scanout); + } + if (onscreen_native->secondary_gpu_state) g_set_object (&onscreen_native->gbm.next_fb, secondary_gpu_fb); else @@ -1404,6 +1474,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, #endif } + clutter_frame_set_result (frame, + CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + /* * If we changed EGL context, cogl will have the wrong idea about what is * current, making it fail to set it when it needs to. Avoid that by making @@ -1413,12 +1486,78 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, if (egl_context_changed) _cogl_winsys_egl_ensure_current (cogl_display); - kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (onscreen_native->crtc)); - kms_device = meta_kms_crtc_get_device (kms_crtc); + rectangles_size = n_rectangles * 4 * sizeof (int); + onscreen_native->next_post.rectangles = + g_realloc (onscreen_native->next_post.rectangles, rectangles_size); + memcpy (onscreen_native->next_post.rectangles, rectangles, rectangles_size); + onscreen_native->next_post.n_rectangles = n_rectangles; + + g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); + onscreen_native->next_post.frame = clutter_frame_ref (frame); + + onscreen_native->swaps_pending++; + try_post_latest_swap (onscreen); +} + +static void +try_post_latest_swap (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer); + CoglRenderer *cogl_renderer = cogl_context->display->renderer; + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; + MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; + MetaRenderer *renderer = META_RENDERER (renderer_native); + MetaBackend *backend = meta_renderer_get_backend (renderer); + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + MetaPowerSave power_save_mode; + MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + MetaKmsUpdate *kms_update; + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + g_autoptr (ClutterFrame) frame = NULL; + MetaFrameNative *frame_native; + + if (onscreen_native->next_post.frame == NULL || + onscreen_native->view == NULL || + meta_kms_is_shutting_down (kms)) + return; power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { + unsigned int frames_pending = + cogl_onscreen_count_pending_frames (onscreen); + unsigned int posts_pending; + + g_assert (frames_pending >= onscreen_native->swaps_pending); + posts_pending = frames_pending - onscreen_native->swaps_pending; + if (posts_pending > 0) + return; /* wait for the next frame notification and then try again */ + + frame = g_steal_pointer (&onscreen_native->next_post.frame); + frame_native = meta_frame_native_from_frame (frame); + + if (onscreen_native->swaps_pending == 0) + { + if (frame_native) + { + kms_update = meta_frame_native_steal_kms_update (frame_native); + if (kms_update) + post_finish_frame (onscreen_native, kms_update); + } + return; + } + + drop_stalled_swap (onscreen); + onscreen_native->swaps_pending--; + kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); meta_kms_update_add_result_listener (kms_update, @@ -1433,15 +1572,13 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, onscreen_native->crtc, kms_update, META_KMS_ASSIGN_PLANE_FLAG_NONE, - rectangles, - n_rectangles); + onscreen_native->next_post.rectangles, + onscreen_native->next_post.n_rectangles); } else { meta_renderer_native_queue_power_save_page_flip (renderer_native, onscreen); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } @@ -1461,8 +1598,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, kms_update = meta_frame_native_steal_kms_update (frame_native); meta_renderer_native_queue_mode_set_update (renderer_native, kms_update); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } else if (meta_renderer_native_has_pending_mode_set (renderer_native)) @@ -1476,8 +1611,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, meta_frame_native_steal_kms_update (frame_native); meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } break; @@ -1493,8 +1626,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, kms_update); meta_renderer_native_post_mode_set_updates (renderer_native); - clutter_frame_set_result (frame, - CLUTTER_FRAME_RESULT_PENDING_PRESENTED); return; } break; @@ -1509,7 +1640,6 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, kms_update = meta_frame_native_steal_kms_update (frame_native); meta_kms_device_post_update (kms_device, kms_update, META_KMS_UPDATE_FLAG_NONE); - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); } gboolean @@ -1580,7 +1710,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, g_warning ("Direct scanout page flip failed: %s", error->message); - cogl_scanout_notify_failed (onscreen_native->gbm.next_scanout, + cogl_scanout_notify_failed (onscreen_native->gbm.posted_scanout, onscreen); clutter_stage_view_add_redraw_clip (view, NULL); clutter_stage_view_schedule_update_now (view); @@ -1590,7 +1720,7 @@ scanout_result_feedback (const MetaKmsFeedback *kms_feedback, frame_info->flags |= COGL_FRAME_INFO_FLAG_SYMBOLIC; meta_onscreen_native_notify_frame_complete (onscreen); - meta_onscreen_native_clear_next_fb (onscreen); + meta_onscreen_native_clear_posted_fb (onscreen); } static const MetaKmsResultListenerVtable scanout_result_listener_vtable = { @@ -1642,6 +1772,18 @@ meta_onscreen_native_direct_scanout (CoglOnscreen *onscreen, return FALSE; } + /* Our direct scanout frame counts as 1, so more than that means we would + * be jumping the queue (and post would fail). + */ + if (cogl_onscreen_count_pending_frames (onscreen) > 1) + { + g_set_error_literal (error, + COGL_SCANOUT_ERROR, + COGL_SCANOUT_ERROR_INHIBITED, + "Direct scanout is inhibited during triple buffering"); + return FALSE; + } + renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, render_gpu); @@ -1757,11 +1899,7 @@ meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, ClutterFrame *frame) { MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); - MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc); - MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); - meta_kms_device_await_flush (meta_kms_crtc_get_device (kms_crtc), - kms_crtc); maybe_update_frame_sync (onscreen_native, frame); } @@ -1877,22 +2015,79 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); MetaFrameNative *frame_native = meta_frame_native_from_frame (frame); MetaKmsUpdate *kms_update; + unsigned int frames_pending = cogl_onscreen_count_pending_frames (onscreen); + unsigned int swaps_pending = onscreen_native->swaps_pending; + unsigned int posts_pending = frames_pending - swaps_pending; - kms_update = meta_frame_native_steal_kms_update (frame_native); - if (!kms_update) + onscreen_native->needs_flush |= meta_kms_device_handle_flush (kms_device, + kms_crtc); + + if (!meta_frame_native_has_kms_update (frame_native)) { - if (meta_kms_device_handle_flush (kms_device, kms_crtc)) - { - kms_update = meta_kms_update_new (kms_device); - meta_kms_update_set_flushing (kms_update, kms_crtc); - } - else + if (!onscreen_native->needs_flush || posts_pending) { clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); return; } } + if (posts_pending && !swaps_pending) + { + g_return_if_fail (meta_frame_native_has_kms_update (frame_native)); + g_warn_if_fail (onscreen_native->next_post.frame == NULL); + + g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); + onscreen_native->next_post.frame = clutter_frame_ref (frame); + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); + return; + } + + kms_update = meta_frame_native_steal_kms_update (frame_native); + + if (posts_pending && swaps_pending) + { + MetaFrameNative *older_frame_native; + MetaKmsUpdate *older_kms_update; + + g_return_if_fail (kms_update); + g_return_if_fail (onscreen_native->next_post.frame != NULL); + + older_frame_native = + meta_frame_native_from_frame (onscreen_native->next_post.frame); + older_kms_update = + meta_frame_native_ensure_kms_update (older_frame_native, kms_device); + meta_kms_update_merge_from (older_kms_update, kms_update); + meta_kms_update_free (kms_update); + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); + return; + } + + if (!kms_update) + { + kms_update = meta_kms_update_new (kms_device); + g_warn_if_fail (onscreen_native->needs_flush); + } + + if (onscreen_native->needs_flush) + { + meta_kms_update_set_flushing (kms_update, kms_crtc); + onscreen_native->needs_flush = FALSE; + } + + post_finish_frame (onscreen_native, kms_update); + + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); +} + +static void +post_finish_frame (MetaOnscreenNative *onscreen_native, + MetaKmsUpdate *kms_update) +{ + MetaCrtc *crtc = onscreen_native->crtc; + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); + g_autoptr (MetaKmsFeedback) kms_feedback = NULL; + meta_kms_update_add_result_listener (kms_update, &finish_frame_result_listener_vtable, NULL, @@ -1915,7 +2110,19 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, meta_kms_update_set_flushing (kms_update, kms_crtc); meta_kms_device_post_update (kms_device, kms_update, META_KMS_UPDATE_FLAG_NONE); - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); +} + +void +meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen) +{ + MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen); + + onscreen_native->swaps_pending = 0; + + g_clear_object (&onscreen_native->gbm.stalled_fb); + g_clear_object (&onscreen_native->gbm.stalled_scanout); + g_clear_object (&onscreen_native->gbm.next_fb); + g_clear_object (&onscreen_native->gbm.next_scanout); } static gboolean @@ -2830,8 +3037,11 @@ meta_onscreen_native_dispose (GObject *object) { case META_RENDERER_NATIVE_MODE_GBM: g_clear_object (&onscreen_native->gbm.next_fb); + g_clear_object (&onscreen_native->gbm.posted_fb); + g_clear_object (&onscreen_native->gbm.current_fb); g_clear_object (&onscreen_native->gbm.next_scanout); - free_current_bo (onscreen); + g_clear_object (&onscreen_native->gbm.posted_scanout); + g_clear_object (&onscreen_native->gbm.current_scanout); break; case META_RENDERER_NATIVE_MODE_SURFACELESS: g_assert_not_reached (); @@ -2865,6 +3075,10 @@ meta_onscreen_native_dispose (GObject *object) g_clear_object (&onscreen_native->output); g_clear_object (&onscreen_native->crtc); + + g_clear_pointer (&onscreen_native->next_post.rectangles, g_free); + g_clear_pointer (&onscreen_native->next_post.frame, clutter_frame_unref); + onscreen_native->next_post.n_rectangles = 0; } static void diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h index 0e11933..e30357d 100644 --- a/src/backends/native/meta-onscreen-native.h +++ b/src/backends/native/meta-onscreen-native.h @@ -48,6 +48,8 @@ void meta_onscreen_native_dummy_power_save_page_flip (CoglOnscreen *onscreen); gboolean meta_onscreen_native_is_buffer_scanout_compatible (CoglOnscreen *onscreen, CoglScanout *scanout); +void meta_onscreen_native_discard_pending_swaps (CoglOnscreen *onscreen); + void meta_onscreen_native_set_view (CoglOnscreen *onscreen, MetaRendererView *view); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index aa76d01..3c22b4e 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -731,12 +731,18 @@ static gboolean dummy_power_save_page_flip_cb (gpointer user_data) { MetaRendererNative *renderer_native = user_data; + GList *old_list = + g_steal_pointer (&renderer_native->power_save_page_flip_onscreens); - g_list_foreach (renderer_native->power_save_page_flip_onscreens, + g_list_foreach (old_list, (GFunc) meta_onscreen_native_dummy_power_save_page_flip, NULL); - g_clear_list (&renderer_native->power_save_page_flip_onscreens, + g_clear_list (&old_list, g_object_unref); + + if (renderer_native->power_save_page_flip_onscreens != NULL) + return G_SOURCE_CONTINUE; + renderer_native->power_save_page_flip_source_id = 0; return G_SOURCE_REMOVE; @@ -748,6 +754,9 @@ meta_renderer_native_queue_power_save_page_flip (MetaRendererNative *renderer_na { const unsigned int timeout_ms = 100; + if (g_list_find (renderer_native->power_save_page_flip_onscreens, onscreen)) + return; + if (!renderer_native->power_save_page_flip_source_id) { renderer_native->power_save_page_flip_source_id = @@ -1529,6 +1538,26 @@ detach_onscreens (MetaRenderer *renderer) } } +static void +discard_pending_swaps (MetaRenderer *renderer) +{ + GList *views = meta_renderer_get_views (renderer);; + GList *l; + + for (l = views; l; l = l->next) + { + ClutterStageView *stage_view = l->data; + CoglFramebuffer *fb = clutter_stage_view_get_onscreen (stage_view); + CoglOnscreen *onscreen; + + if (!COGL_IS_ONSCREEN (fb)) + continue; + + onscreen = COGL_ONSCREEN (fb); + meta_onscreen_native_discard_pending_swaps (onscreen); + } +} + static void meta_renderer_native_rebuild_views (MetaRenderer *renderer) { @@ -1539,6 +1568,7 @@ meta_renderer_native_rebuild_views (MetaRenderer *renderer) MetaRendererClass *parent_renderer_class = META_RENDERER_CLASS (meta_renderer_native_parent_class); + discard_pending_swaps (renderer); meta_kms_discard_pending_page_flips (kms); g_hash_table_remove_all (renderer_native->mode_set_updates); diff --git a/src/meson.build b/src/meson.build index 05df3bf..3060b28 100644 --- a/src/meson.build +++ b/src/meson.build @@ -115,7 +115,6 @@ if have_x11 xkbfile_dep, xkeyboard_config_dep, xkbcommon_x11_dep, - xrender_dep, x11_xcb_dep, xcb_randr_dep, xcb_res_dep, diff --git a/src/tests/native-kms-render.c b/src/tests/native-kms-render.c index f5ebc23..2f870fd 100644 --- a/src/tests/native-kms-render.c +++ b/src/tests/native-kms-render.c @@ -39,6 +39,8 @@ #include "tests/meta-wayland-test-driver.h" #include "tests/meta-wayland-test-utils.h" +#define N_FRAMES_PER_TEST 30 + typedef struct { int number_of_frames_left; @@ -46,12 +48,15 @@ typedef struct struct { int n_paints; - uint32_t fb_id; + int n_presentations; + int n_direct_scanouts; + GList *fb_ids; } scanout; gboolean wait_for_scanout; struct { + int scanouts_attempted; gboolean scanout_sabotaged; gboolean fallback_painted; guint repaint_guard_id; @@ -101,7 +106,7 @@ meta_test_kms_render_basic (void) gulong handler_id; test = (KmsRenderingTest) { - .number_of_frames_left = 10, + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), }; handler_id = g_signal_connect (stage, "after-update", @@ -123,7 +128,6 @@ on_scanout_before_update (ClutterStage *stage, KmsRenderingTest *test) { test->scanout.n_paints = 0; - test->scanout.fb_id = 0; } static void @@ -135,6 +139,7 @@ on_scanout_before_paint (ClutterStage *stage, CoglScanout *scanout; CoglScanoutBuffer *scanout_buffer; MetaDrmBuffer *buffer; + uint32_t fb_id; scanout = clutter_stage_view_peek_scanout (stage_view); if (!scanout) @@ -143,8 +148,13 @@ on_scanout_before_paint (ClutterStage *stage, scanout_buffer = cogl_scanout_get_buffer (scanout); g_assert_true (META_IS_DRM_BUFFER (scanout_buffer)); buffer = META_DRM_BUFFER (scanout_buffer); - test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer); - g_assert_cmpuint (test->scanout.fb_id, >, 0); + fb_id = meta_drm_buffer_get_fb_id (buffer); + g_assert_cmpuint (fb_id, >, 0); + test->scanout.fb_ids = g_list_append (test->scanout.fb_ids, + GUINT_TO_POINTER (fb_id)); + + /* Triple buffering, but no higher */ + g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2); } static void @@ -173,12 +183,12 @@ on_scanout_presented (ClutterStage *stage, MetaDeviceFile *device_file; GError *error = NULL; drmModeCrtc *drm_crtc; + uint32_t first_fb_id_expected; - if (test->wait_for_scanout && test->scanout.n_paints > 0) + if (test->wait_for_scanout && test->scanout.fb_ids == NULL) return; - if (test->wait_for_scanout && test->scanout.fb_id == 0) - return; + test->scanout.n_presentations++; device_pool = meta_backend_native_get_device_pool (backend_native); @@ -197,15 +207,41 @@ on_scanout_presented (ClutterStage *stage, drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file), meta_kms_crtc_get_id (kms_crtc)); g_assert_nonnull (drm_crtc); - if (test->scanout.fb_id == 0) - g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id); + + if (test->scanout.fb_ids) + { + test->scanout.n_direct_scanouts++; + first_fb_id_expected = GPOINTER_TO_UINT (test->scanout.fb_ids->data); + test->scanout.fb_ids = g_list_delete_link (test->scanout.fb_ids, + test->scanout.fb_ids); + } else - g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id); + { + first_fb_id_expected = 0; + } + + /* The buffer ID won't match on the first frame because switching from + * triple buffered compositing to double buffered direct scanout takes + * an extra frame to drain the queue. Thereafter we are in direct scanout + * mode and expect the buffer IDs to match. + */ + if (test->scanout.n_presentations > 1) + { + if (first_fb_id_expected == 0) + g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected); + else + g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected); + } + drmModeFreeCrtc (drm_crtc); meta_device_file_release (device_file); - g_main_loop_quit (test->loop); + test->number_of_frames_left--; + if (test->number_of_frames_left <= 0) + g_main_loop_quit (test->loop); + else + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } typedef enum @@ -244,7 +280,9 @@ meta_test_kms_render_client_scanout (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), + .scanout = {0}, .wait_for_scanout = TRUE, }; @@ -270,7 +308,8 @@ meta_test_kms_render_client_scanout (void) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); g_debug ("Unmake fullscreen"); window = meta_find_window_from_title (test_context, "dma-buf-scanout-test"); @@ -292,10 +331,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 10); test.wait_for_scanout = FALSE; + test.number_of_frames_left = N_FRAMES_PER_TEST; + test.scanout.n_presentations = 0; + test.scanout.n_direct_scanouts = 0; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, ==, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 0); g_debug ("Moving back to 0, 0"); meta_window_move_frame (window, TRUE, 0, 0); @@ -307,10 +351,15 @@ meta_test_kms_render_client_scanout (void) g_assert_cmpint (buffer_rect.y, ==, 0); test.wait_for_scanout = TRUE; + test.number_of_frames_left = N_FRAMES_PER_TEST; + test.scanout.n_presentations = 0; + test.scanout.n_direct_scanouts = 0; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_main_loop_run (test.loop); - g_assert_cmpuint (test.scanout.fb_id, >, 0); + g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST); + g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST); g_signal_handler_disconnect (stage, before_update_handler_id); g_signal_handler_disconnect (stage, before_paint_handler_id); @@ -364,6 +413,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage, if (!scanout) return; + test->scanout_fallback.scanouts_attempted++; + + /* The first scanout candidate frame will get composited due to triple + * buffering draining the queue to drop to double buffering. So don't + * sabotage that first frame. + */ + if (test->scanout_fallback.scanouts_attempted < 2) + return; + g_assert_false (test->scanout_fallback.scanout_sabotaged); if (is_atomic_mode_setting (kms_device)) @@ -401,6 +459,15 @@ on_scanout_fallback_paint_view (ClutterStage *stage, g_clear_handle_id (&test->scanout_fallback.repaint_guard_id, g_source_remove); test->scanout_fallback.fallback_painted = TRUE; + test->scanout_fallback.scanout_sabotaged = FALSE; + } + else if (test->scanout_fallback.scanouts_attempted == 1) + { + /* Now that we've seen the first scanout attempt that was inhibited by + * triple buffering, try a second frame. The second one should scanout + * and will be sabotaged. + */ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } } @@ -410,11 +477,11 @@ on_scanout_fallback_presented (ClutterStage *stage, ClutterFrameInfo *frame_info, KmsRenderingTest *test) { - if (!test->scanout_fallback.scanout_sabotaged) - return; + if (test->scanout_fallback.fallback_painted) + g_main_loop_quit (test->loop); - g_assert_true (test->scanout_fallback.fallback_painted); - g_main_loop_quit (test->loop); + test->number_of_frames_left--; + g_assert_cmpint (test->number_of_frames_left, >, 0); } static void @@ -443,6 +510,7 @@ meta_test_kms_render_client_scanout_fallback (void) g_assert_nonnull (wayland_test_client); test = (KmsRenderingTest) { + .number_of_frames_left = N_FRAMES_PER_TEST, .loop = g_main_loop_new (NULL, FALSE), }; diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c index 80c772a..3c741a0 100644 --- a/src/wayland/meta-wayland-keyboard.c +++ b/src/wayland/meta-wayland-keyboard.c @@ -77,6 +77,7 @@ struct _MetaWaylandKeyboard struct wl_array pressed_keys; GHashTable *key_down_serials; uint32_t last_key_up_serial; + uint32_t last_key_up; MetaWaylandXkbInfo xkb_info; enum xkb_state_component mods_changed; @@ -279,6 +280,13 @@ meta_wayland_keyboard_broadcast_key (MetaWaylandKeyboard *keyboard, serial = meta_wayland_input_device_next_serial (input_device); + if (keyboard->last_key_up) + { + g_hash_table_remove (keyboard->key_down_serials, + GUINT_TO_POINTER (keyboard->last_key_up)); + keyboard->last_key_up = 0; + } + if (state) { g_hash_table_insert (keyboard->key_down_serials, @@ -288,9 +296,8 @@ meta_wayland_keyboard_broadcast_key (MetaWaylandKeyboard *keyboard, } else { - g_hash_table_remove (keyboard->key_down_serials, - GUINT_TO_POINTER (key)); keyboard->last_key_up_serial = serial; + keyboard->last_key_up = key; } wl_resource_for_each (resource, &keyboard->focus_resource_list) diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 6dc5006..64d9408 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -2457,7 +2457,7 @@ committed_state_handle_highest_scale_monitor (MetaWaylandSurface *surface) transform = meta_wayland_surface_get_output_transform (surface); if (transform != surface->preferred_transform) { - wl_surface_send_preferred_buffer_transform (surface->resource, ceiled_scale); + wl_surface_send_preferred_buffer_transform (surface->resource, transform); surface->preferred_transform = transform; } } diff --git a/src/wayland/meta-wayland-tablet-tool.c b/src/wayland/meta-wayland-tablet-tool.c index 5d83383..c262391 100644 --- a/src/wayland/meta-wayland-tablet-tool.c +++ b/src/wayland/meta-wayland-tablet-tool.c @@ -64,9 +64,14 @@ struct _MetaWaylandTabletTool float grab_x, grab_y; + gulong current_surface_destroyed_handler_id; + MetaWaylandTablet *current_tablet; }; +static void meta_wayland_tablet_tool_set_current_surface (MetaWaylandTabletTool *tool, + MetaWaylandSurface *surface); + static MetaBackend * backend_from_tool (MetaWaylandTabletTool *tool) { @@ -453,6 +458,7 @@ meta_wayland_tablet_tool_free (MetaWaylandTabletTool *tool) { struct wl_resource *resource, *next; + meta_wayland_tablet_tool_set_current_surface (tool, NULL); meta_wayland_tablet_tool_set_focus (tool, NULL, NULL); meta_wayland_tablet_tool_set_cursor_surface (tool, NULL); g_clear_object (&tool->cursor_renderer); @@ -595,10 +601,41 @@ meta_wayland_tablet_tool_account_button (MetaWaylandTabletTool *tool, } static void -sync_focus_surface (MetaWaylandTabletTool *tool, - const ClutterEvent *event) +current_surface_destroyed (MetaWaylandSurface *surface, + MetaWaylandTabletTool *tool) { - meta_wayland_tablet_tool_set_focus (tool, tool->current, event); + meta_wayland_tablet_tool_set_current_surface (tool, NULL); +} + +static void +meta_wayland_tablet_tool_set_current_surface (MetaWaylandTabletTool *tool, + MetaWaylandSurface *surface) +{ + MetaWaylandTabletSeat *tablet_seat; + MetaWaylandInput *input; + + if (tool->current == surface) + return; + + if (tool->current) + { + g_clear_signal_handler (&tool->current_surface_destroyed_handler_id, + tool->current); + tool->current = NULL; + } + + if (surface) + { + tool->current = surface; + tool->current_surface_destroyed_handler_id = + g_signal_connect (surface, "destroy", + G_CALLBACK (current_surface_destroyed), + tool); + } + + tablet_seat = tool->seat; + input = meta_wayland_seat_get_input (tablet_seat->seat); + meta_wayland_input_invalidate_focus (input, tool->device, NULL); } static void @@ -607,6 +644,7 @@ repick_for_event (MetaWaylandTabletTool *tool, { MetaBackend *backend = backend_from_tool (tool); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); + MetaWaylandSurface *surface; ClutterActor *actor; actor = clutter_stage_get_device_actor (stage, @@ -614,11 +652,11 @@ repick_for_event (MetaWaylandTabletTool *tool, clutter_event_get_event_sequence (for_event)); if (META_IS_SURFACE_ACTOR_WAYLAND (actor)) - tool->current = meta_surface_actor_wayland_get_surface (META_SURFACE_ACTOR_WAYLAND (actor)); + surface = meta_surface_actor_wayland_get_surface (META_SURFACE_ACTOR_WAYLAND (actor)); else - tool->current = NULL; + surface = NULL; - sync_focus_surface (tool, for_event); + meta_wayland_tablet_tool_set_current_surface (tool, surface); meta_wayland_tablet_tool_update_cursor_surface (tool); }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor