diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c index 30cc6fb12..fb27f5741 100644 --- a/clutter/clutter/clutter-frame-clock.c +++ b/clutter/clutter/clutter-frame-clock.c @@ -37,6 +37,8 @@ static guint signals[N_SIGNALS]; #define SYNC_DELAY_FALLBACK_FRACTION 0.875 +#define MINIMUM_REFRESH_RATE 30 + typedef struct _ClutterFrameListener { const ClutterFrameListenerIface *iface; @@ -54,6 +56,7 @@ typedef enum _ClutterFrameClockState { CLUTTER_FRAME_CLOCK_STATE_INIT, CLUTTER_FRAME_CLOCK_STATE_IDLE, + CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT, CLUTTER_FRAME_CLOCK_STATE_SCHEDULED, CLUTTER_FRAME_CLOCK_STATE_DISPATCHING, CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED, @@ -65,6 +68,8 @@ struct _ClutterFrameClock float refresh_rate; int64_t refresh_interval_us; + int64_t minimum_refresh_interval_us; + ClutterFrameListener listener; GSource *source; @@ -72,6 +77,8 @@ struct _ClutterFrameClock int64_t frame_count; ClutterFrameClockState state; + ClutterFrameClockMode mode; + int64_t last_dispatch_time_us; int64_t last_dispatch_lateness_us; int64_t last_presentation_time_us; @@ -375,6 +382,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, { case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: g_warn_if_reached (); break; @@ -395,6 +403,7 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock) { case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: g_warn_if_reached (); break; @@ -597,6 +606,39 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock, *out_min_render_time_allowed_us = min_render_time_allowed_us; } +static void +calculate_next_idle_timeout_us (ClutterFrameClock *frame_clock, + int64_t *out_next_update_time_us) +{ + int64_t now_us; + int64_t last_presentation_time_us; + int64_t next_presentation_time_us; + int64_t timeout_interval_us; + + now_us = g_get_monotonic_time (); + + last_presentation_time_us = frame_clock->last_presentation_time_us; + + timeout_interval_us = frame_clock->minimum_refresh_interval_us; + + if (last_presentation_time_us == 0) + { + *out_next_update_time_us = + frame_clock->last_dispatch_time_us ? + ((frame_clock->last_dispatch_time_us - + frame_clock->last_dispatch_lateness_us) + timeout_interval_us) : + now_us; + return; + } + + next_presentation_time_us = last_presentation_time_us + timeout_interval_us; + + while (next_presentation_time_us < now_us) + next_presentation_time_us += timeout_interval_us; + + *out_next_update_time_us = next_presentation_time_us; +} + void clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) { @@ -609,6 +651,7 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock) case CLUTTER_FRAME_CLOCK_STATE_INIT: case CLUTTER_FRAME_CLOCK_STATE_IDLE: break; + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: frame_clock->pending_reschedule = TRUE; frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; @@ -650,6 +693,7 @@ 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: + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: next_update_time_us = g_get_monotonic_time (); break; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: @@ -682,15 +726,12 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) { case CLUTTER_FRAME_CLOCK_STATE_INIT: next_update_time_us = g_get_monotonic_time (); - break; + g_source_set_ready_time (frame_clock->source, next_update_time_us); + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + return; case CLUTTER_FRAME_CLOCK_STATE_IDLE: - calculate_next_update_time_us (frame_clock, - &next_update_time_us, - &frame_clock->next_presentation_time_us, - &frame_clock->min_render_time_allowed_us); - frame_clock->is_next_presentation_time_valid = - (frame_clock->next_presentation_time_us != 0); break; + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: return; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: @@ -699,11 +740,56 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock) return; } + switch (frame_clock->mode) + { + case CLUTTER_FRAME_CLOCK_MODE_FIXED: + calculate_next_update_time_us (frame_clock, + &next_update_time_us, + &frame_clock->next_presentation_time_us, + &frame_clock->min_render_time_allowed_us); + frame_clock->is_next_presentation_time_valid = + (frame_clock->next_presentation_time_us != 0); + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED; + break; + case CLUTTER_FRAME_CLOCK_MODE_VARIABLE: + calculate_next_idle_timeout_us (frame_clock, + &next_update_time_us); + frame_clock->is_next_presentation_time_valid = FALSE; + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT; + break; + } + g_warn_if_fail (next_update_time_us != -1); 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 +clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, + ClutterFrameClockMode mode) +{ + if (frame_clock->mode == mode) + return; + + frame_clock->mode = mode; + + switch (frame_clock->state) + { + case CLUTTER_FRAME_CLOCK_STATE_INIT: + case CLUTTER_FRAME_CLOCK_STATE_IDLE: + break; + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: + case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: + frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE; + frame_clock->pending_reschedule = TRUE; + break; + case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: + case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED: + break; + } + + maybe_reschedule_update (frame_clock); } static void @@ -794,6 +880,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock, g_warn_if_reached (); break; case CLUTTER_FRAME_CLOCK_STATE_IDLE: + case CLUTTER_FRAME_CLOCK_STATE_IDLE_TIMEOUT: case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED: break; case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING: @@ -918,6 +1005,10 @@ clutter_frame_clock_new (float refresh_rate, init_frame_clock_source (frame_clock); clutter_frame_clock_set_refresh_rate (frame_clock, refresh_rate); + + frame_clock->minimum_refresh_interval_us = + (int64_t) (0.5 + G_USEC_PER_SEC / MINIMUM_REFRESH_RATE); + frame_clock->vblank_duration_us = vblank_duration_us; return frame_clock; @@ -951,6 +1042,7 @@ static void clutter_frame_clock_init (ClutterFrameClock *frame_clock) { frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_INIT; + frame_clock->mode = CLUTTER_FRAME_CLOCK_MODE_FIXED; } static void diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h index 93ebc9438..e7a191561 100644 --- a/clutter/clutter/clutter-frame-clock.h +++ b/clutter/clutter/clutter-frame-clock.h @@ -54,6 +54,12 @@ typedef struct _ClutterFrameListenerIface gpointer user_data); } ClutterFrameListenerIface; +typedef enum _ClutterFrameClockMode +{ + CLUTTER_FRAME_CLOCK_MODE_FIXED, + CLUTTER_FRAME_CLOCK_MODE_VARIABLE, +} ClutterFrameClockMode; + CLUTTER_EXPORT ClutterFrameClock * clutter_frame_clock_new (float refresh_rate, int64_t vblank_duration_us, @@ -63,6 +69,10 @@ ClutterFrameClock * clutter_frame_clock_new (float re CLUTTER_EXPORT void clutter_frame_clock_destroy (ClutterFrameClock *frame_clock); +CLUTTER_EXPORT +void clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock, + ClutterFrameClockMode mode); + CLUTTER_EXPORT void clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock, ClutterFrameInfo *frame_info); diff --git a/data/61-mutter.rules b/data/61-mutter.rules index 9262b152b..898c64021 100644 --- a/data/61-mutter.rules +++ b/data/61-mutter.rules @@ -1,3 +1,4 @@ +DRIVERS=="i915", SUBSYSTEM=="drm", TAG+="mutter-device-disable-vrr" DRIVERS=="i915", SUBSYSTEM=="drm", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x1602", TAG+="mutter-device-disable-kms-modifiers" DRIVERS=="i915", SUBSYSTEM=="drm", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x1606", TAG+="mutter-device-disable-kms-modifiers" DRIVERS=="i915", SUBSYSTEM=="drm", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x160a", TAG+="mutter-device-disable-kms-modifiers" diff --git a/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml b/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml index af78ec051..896a3ce11 100644 --- a/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml +++ b/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml @@ -343,6 +343,10 @@ - "is-underscanning" (b): whether underscanning is enabled (absence of this means underscanning not being supported) + - "is-vrr-allowed" (b): whether variable refresh rate is allowed + (absence of this means variable refresh + rate not being supported) + - "max-screen-size" (ii): the maximum size a screen may have (absence of this means unlimited screen size) @@ -461,6 +465,9 @@ - "enable_underscanning" (b): enable monitor underscanning; may only be set when underscanning is supported (see GetCurrentState). + - "allow_vrr" (b): whether to allow variable refresh rate; may + only be set when variable refresh rate is + supported (see GetCurrentState). @properties may effect the global monitor configuration state. Possible properties are: diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in index 5d56ae2d3..572dc1ae6 100644 --- a/data/org.gnome.mutter.gschema.xml.in +++ b/data/org.gnome.mutter.gschema.xml.in @@ -5,6 +5,7 @@ + - [] + ["variable-refresh-rate"] Enable experimental features To enable experimental features, add the feature keyword to the list. @@ -133,6 +134,12 @@ 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. + diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c index 60f32ff4c..d17de760e 100644 --- a/src/backends/meta-monitor-config-manager.c +++ b/src/backends/meta-monitor-config-manager.c @@ -285,6 +285,7 @@ assign_monitor_crtc (MetaMonitor *monitor, .is_primary = assign_output_as_primary, .is_presentation = assign_output_as_presentation, .is_underscanning = data->monitor_config->enable_underscanning, + .is_vrr_disallowed = data->monitor_config->disallow_vrr, .has_max_bpc = data->monitor_config->has_max_bpc, .max_bpc = data->monitor_config->max_bpc }; @@ -691,7 +692,8 @@ create_monitor_config (MetaMonitor *monitor, *monitor_config = (MetaMonitorConfig) { .monitor_spec = meta_monitor_spec_clone (monitor_spec), .mode_spec = g_memdup2 (mode_spec, sizeof (MetaMonitorModeSpec)), - .enable_underscanning = meta_monitor_is_underscanning (monitor) + .enable_underscanning = meta_monitor_is_underscanning (monitor), + .disallow_vrr = meta_monitor_is_vrr_disallowed (monitor), }; monitor_config->has_max_bpc = @@ -1045,6 +1047,7 @@ clone_monitor_config_list (GList *monitor_configs_in) .mode_spec = g_memdup2 (monitor_config_in->mode_spec, sizeof (MetaMonitorModeSpec)), .enable_underscanning = monitor_config_in->enable_underscanning, + .disallow_vrr = monitor_config_in->disallow_vrr, .has_max_bpc = monitor_config_in->has_max_bpc, .max_bpc = monitor_config_in->max_bpc }; diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h index 29ac41e92..9b9dcd7ba 100644 --- a/src/backends/meta-monitor-config-manager.h +++ b/src/backends/meta-monitor-config-manager.h @@ -31,6 +31,7 @@ typedef struct _MetaMonitorConfig MetaMonitorSpec *monitor_spec; MetaMonitorModeSpec *mode_spec; gboolean enable_underscanning; + gboolean disallow_vrr; gboolean has_max_bpc; unsigned int max_bpc; } MetaMonitorConfig; diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c index fe9406fd3..8d29e3329 100644 --- a/src/backends/meta-monitor-config-store.c +++ b/src/backends/meta-monitor-config-store.c @@ -165,6 +165,7 @@ typedef enum STATE_MONITOR_MODE_RATE, STATE_MONITOR_MODE_FLAG, STATE_MONITOR_UNDERSCANNING, + STATE_MONITOR_VRR_ALLOWED, STATE_MONITOR_MAXBPC, STATE_DISABLED, STATE_POLICY, @@ -450,6 +451,10 @@ handle_start_element (GMarkupParseContext *context, { parser->state = STATE_MONITOR_UNDERSCANNING; } + else if (g_str_equal (element_name, "vrr-allowed")) + { + parser->state = STATE_MONITOR_VRR_ALLOWED; + } else if (g_str_equal (element_name, "maxbpc")) { parser->state = STATE_MONITOR_MAXBPC; @@ -547,6 +552,13 @@ handle_start_element (GMarkupParseContext *context, return; } + case STATE_MONITOR_VRR_ALLOWED: + { + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, + "Invalid element '%s' under vrr-allowed", element_name); + return; + } + case STATE_MONITOR_MAXBPC: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, @@ -828,6 +840,14 @@ handle_end_element (GMarkupParseContext *context, return; } + case STATE_MONITOR_VRR_ALLOWED: + { + g_assert (g_str_equal (element_name, "vrr-allowed")); + + parser->state = STATE_MONITOR; + return; + } + case STATE_MONITOR_MAXBPC: { g_assert (g_str_equal (element_name, "maxbpc")); @@ -1327,6 +1347,18 @@ handle_text (GMarkupParseContext *context, return; } + case STATE_MONITOR_VRR_ALLOWED: + { + gboolean allow_vrr = TRUE; + + read_bool (text, text_len, + &allow_vrr, + error); + parser->current_monitor_config->disallow_vrr = !allow_vrr; + + return; + } + case STATE_MONITOR_MAXBPC: { int signed_max_bpc; @@ -1526,6 +1558,9 @@ append_monitors (GString *buffer, if (monitor_config->enable_underscanning) g_string_append (buffer, " yes\n"); + if (monitor_config->disallow_vrr) + g_string_append (buffer, " no\n"); + if (monitor_config->has_max_bpc) { g_string_append_printf (buffer, " %u\n", diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h index 242fc2353..c1b47e8dc 100644 --- a/src/backends/meta-monitor-manager-private.h +++ b/src/backends/meta-monitor-manager-private.h @@ -101,6 +101,7 @@ struct _MetaOutputAssignment gboolean is_primary; gboolean is_presentation; gboolean is_underscanning; + gboolean is_vrr_disallowed; gboolean has_max_bpc; unsigned int max_bpc; }; diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c index 3b836f327..000d6bdd6 100644 --- a/src/backends/meta-monitor-manager.c +++ b/src/backends/meta-monitor-manager.c @@ -2136,6 +2136,15 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, g_variant_new_boolean (is_underscanning)); } + if (meta_monitor_is_vrr_capable (monitor)) + { + gboolean vrr_disallowed = meta_monitor_is_vrr_disallowed (monitor); + + g_variant_builder_add (&monitor_properties_builder, "{sv}", + "is-vrr-allowed", + g_variant_new_boolean (!vrr_disallowed)); + } + is_builtin = meta_monitor_is_laptop_panel (monitor); g_variant_builder_add (&monitor_properties_builder, "{sv}", "is-builtin", @@ -2455,6 +2464,8 @@ create_monitor_config_from_variant (MetaMonitorManager *manager, g_autoptr (GVariant) properties_variant = NULL; gboolean enable_underscanning = FALSE; gboolean set_underscanning = FALSE; + gboolean allow_vrr = TRUE; + gboolean set_allow_vrr = FALSE; g_variant_get (monitor_config_variant, "(ss@a{sv})", &connector, @@ -2490,6 +2501,19 @@ create_monitor_config_from_variant (MetaMonitorManager *manager, } } + set_allow_vrr = + g_variant_lookup (properties_variant, "allow_vrr", "b", + &allow_vrr); + if (set_allow_vrr) + { + if (allow_vrr && !meta_monitor_is_vrr_capable (monitor)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Variable refresh rate requested but unsupported"); + return NULL; + } + } + monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor)); monitor_mode_spec = g_new0 (MetaMonitorModeSpec, 1); @@ -2499,7 +2523,8 @@ create_monitor_config_from_variant (MetaMonitorManager *manager, *monitor_config = (MetaMonitorConfig) { .monitor_spec = monitor_spec, .mode_spec = monitor_mode_spec, - .enable_underscanning = enable_underscanning + .enable_underscanning = enable_underscanning, + .disallow_vrr = !allow_vrr, }; return monitor_config; diff --git a/src/backends/meta-monitor.c b/src/backends/meta-monitor.c index 16eddf686..5818343a8 100644 --- a/src/backends/meta-monitor.c +++ b/src/backends/meta-monitor.c @@ -370,6 +370,25 @@ meta_monitor_is_underscanning (MetaMonitor *monitor) return meta_output_is_underscanning (output); } +gboolean +meta_monitor_is_vrr_capable (MetaMonitor *monitor) +{ + const MetaOutputInfo *output_info = + meta_monitor_get_main_output_info (monitor); + + return output_info->vrr_capable; +} + +gboolean +meta_monitor_is_vrr_disallowed (MetaMonitor *monitor) +{ + MetaOutput *output; + + output = meta_monitor_get_main_output (monitor); + + return meta_output_is_vrr_disallowed (output); +} + gboolean meta_monitor_get_max_bpc (MetaMonitor *monitor, unsigned int *max_bpc) diff --git a/src/backends/meta-monitor.h b/src/backends/meta-monitor.h index d8e9fd301..49bd58254 100644 --- a/src/backends/meta-monitor.h +++ b/src/backends/meta-monitor.h @@ -118,6 +118,10 @@ gboolean meta_monitor_supports_color_transform (MetaMonitor *monitor); gboolean meta_monitor_is_underscanning (MetaMonitor *monitor); +gboolean meta_monitor_is_vrr_capable (MetaMonitor *monitor); + +gboolean meta_monitor_is_vrr_disallowed (MetaMonitor *monitor); + gboolean meta_monitor_get_max_bpc (MetaMonitor *monitor, unsigned int *max_bpc); diff --git a/src/backends/meta-output.c b/src/backends/meta-output.c index f40fc6ec1..9b9a17811 100644 --- a/src/backends/meta-output.c +++ b/src/backends/meta-output.c @@ -64,6 +64,8 @@ typedef struct _MetaOutputPrivate gboolean is_underscanning; + gboolean is_vrr_disallowed; + gboolean has_max_bpc; unsigned int max_bpc; @@ -197,6 +199,22 @@ meta_output_is_underscanning (MetaOutput *output) return priv->is_underscanning; } +gboolean +meta_output_is_vrr_capable (MetaOutput *output) +{ + const MetaOutputInfo *output_info = meta_output_get_info (output); + + return output_info->vrr_capable; +} + +gboolean +meta_output_is_vrr_disallowed (MetaOutput *output) +{ + MetaOutputPrivate *priv = meta_output_get_instance_private (output); + + return priv->is_vrr_disallowed; +} + gboolean meta_output_get_max_bpc (MetaOutput *output, unsigned int *max_bpc) @@ -268,6 +286,8 @@ meta_output_assign_crtc (MetaOutput *output, priv->is_presentation = output_assignment->is_presentation; priv->is_underscanning = output_assignment->is_underscanning; + priv->is_vrr_disallowed = output_assignment->is_vrr_disallowed; + priv->has_max_bpc = output_assignment->has_max_bpc; if (priv->has_max_bpc) priv->max_bpc = output_assignment->max_bpc; diff --git a/src/backends/meta-output.h b/src/backends/meta-output.h index 03a4cc385..5161fc74d 100644 --- a/src/backends/meta-output.h +++ b/src/backends/meta-output.h @@ -144,6 +144,8 @@ typedef struct _MetaOutputInfo gboolean supports_underscanning; gboolean supports_color_transform; + gboolean vrr_capable; + unsigned int max_bpc_min; unsigned int max_bpc_max; @@ -228,6 +230,11 @@ gboolean meta_output_is_presentation (MetaOutput *output); META_EXPORT_TEST gboolean meta_output_is_underscanning (MetaOutput *output); +gboolean meta_output_is_vrr_capable (MetaOutput *output); + +META_EXPORT_TEST +gboolean meta_output_is_vrr_disallowed (MetaOutput *output); + META_EXPORT_TEST gboolean meta_output_get_max_bpc (MetaOutput *output, unsigned int *max_bpc); diff --git a/src/backends/meta-renderer-view.c b/src/backends/meta-renderer-view.c index bceb4c765..c05c23763 100644 --- a/src/backends/meta-renderer-view.c +++ b/src/backends/meta-renderer-view.c @@ -33,6 +33,7 @@ #include "backends/meta-renderer-view.h" #include "backends/meta-crtc.h" +#include "backends/meta-output.h" #include "backends/meta-renderer.h" #include "clutter/clutter-mutter.h" #include "compositor/region-utils.h" @@ -43,6 +44,7 @@ enum PROP_TRANSFORM, PROP_CRTC, + PROP_OUTPUT, PROP_LAST }; @@ -54,6 +56,7 @@ typedef struct _MetaRendererViewPrivate MetaMonitorTransform transform; MetaCrtc *crtc; + MetaOutput *output; } MetaRendererViewPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaRendererView, meta_renderer_view, @@ -77,6 +80,15 @@ meta_renderer_view_get_crtc (MetaRendererView *view) return priv->crtc; } +MetaOutput * +meta_renderer_view_get_output (MetaRendererView *view) +{ + MetaRendererViewPrivate *priv = + meta_renderer_view_get_instance_private (view); + + return priv->output; +} + static void meta_renderer_view_get_offscreen_transformation_matrix (ClutterStageView *view, graphene_matrix_t *matrix) @@ -151,6 +163,9 @@ meta_renderer_view_get_property (GObject *object, case PROP_CRTC: g_value_set_object (value, priv->crtc); break; + case PROP_OUTPUT: + g_value_set_object (value, priv->output); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -175,6 +190,9 @@ meta_renderer_view_set_property (GObject *object, case PROP_CRTC: priv->crtc = g_value_get_object (value); break; + case PROP_OUTPUT: + priv->output = g_value_get_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -218,5 +236,14 @@ meta_renderer_view_class_init (MetaRendererViewClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_props[PROP_OUTPUT] = + g_param_spec_object ("output", + "MetaOutput", + "MetaOutput", + META_TYPE_OUTPUT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, PROP_LAST, obj_props); } diff --git a/src/backends/meta-renderer-view.h b/src/backends/meta-renderer-view.h index 005828068..72ecab21b 100644 --- a/src/backends/meta-renderer-view.h +++ b/src/backends/meta-renderer-view.h @@ -36,3 +36,5 @@ MetaMonitorTransform meta_renderer_view_get_transform (MetaRendererView *view); META_EXPORT_TEST MetaCrtc *meta_renderer_view_get_crtc (MetaRendererView *view); + +MetaOutput *meta_renderer_view_get_output (MetaRendererView *view); diff --git a/src/backends/meta-settings-private.h b/src/backends/meta-settings-private.h index c3768c8ac..b317843b9 100644 --- a/src/backends/meta-settings-private.h +++ b/src/backends/meta-settings-private.h @@ -29,9 +29,10 @@ typedef enum _MetaExperimentalFeature { META_EXPERIMENTAL_FEATURE_NONE = 0, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER = (1 << 0), - META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS = (1 << 1), + META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS = (1 << 1), META_EXPERIMENTAL_FEATURE_RT_SCHEDULER = (1 << 2), - META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND = (1 << 3), + META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND = (1 << 3), + META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE = (1 << 4), } MetaExperimentalFeature; typedef enum _MetaXwaylandExtension diff --git a/src/backends/meta-settings.c b/src/backends/meta-settings.c index 1e107b0a7..96541a5bb 100644 --- a/src/backends/meta-settings.c +++ b/src/backends/meta-settings.c @@ -296,6 +296,8 @@ experimental_features_handler (GVariant *features_variant, feature = META_EXPERIMENTAL_FEATURE_RT_SCHEDULER; else if (g_str_equal (feature_str, "autoclose-xwayland")) feature = META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND; + else if (g_str_equal (feature_str, "variable-refresh-rate")) + feature = META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE; if (feature) g_message ("Enabling experimental feature '%s'", feature_str); diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index f01890460..251548984 100644 --- a/src/backends/native/meta-backend-native.c +++ b/src/backends/native/meta-backend-native.c @@ -592,6 +592,9 @@ add_drm_device (MetaBackendNative *backend_native, if (meta_is_udev_device_disable_modifiers (device)) flags |= META_KMS_DEVICE_FLAG_DISABLE_MODIFIERS; + if (meta_is_udev_device_disable_vrr (device)) + flags |= META_KMS_DEVICE_FLAG_DISABLE_VRR; + if (meta_is_udev_device_preferred_primary (device)) flags |= META_KMS_DEVICE_FLAG_PREFERRED_PRIMARY; diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c index 7efd4ef9e..7e88a5e91 100644 --- a/src/backends/native/meta-gpu-kms.c +++ b/src/backends/native/meta-gpu-kms.c @@ -132,6 +132,23 @@ meta_gpu_kms_is_platform_device (MetaGpuKms *gpu_kms) return !!(flags & META_KMS_DEVICE_FLAG_PLATFORM_DEVICE); } +gboolean +meta_gpu_kms_disable_vrr (MetaGpuKms *gpu_kms) +{ + MetaGpu *gpu = META_GPU (gpu_kms); + MetaBackend *backend = meta_gpu_get_backend (gpu); + MetaSettings *settings = meta_backend_get_settings (backend); + MetaKmsDeviceFlag flags; + + if (!meta_settings_is_experimental_feature_enabled ( + settings, + META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE)) + return TRUE; + + flags = meta_kms_device_get_flags (gpu_kms->kms_device); + return !!(flags & META_KMS_DEVICE_FLAG_DISABLE_VRR); +} + static int compare_outputs (gconstpointer one, gconstpointer two) diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h index 43ae0a7c4..b3705819a 100644 --- a/src/backends/native/meta-gpu-kms.h +++ b/src/backends/native/meta-gpu-kms.h @@ -44,6 +44,7 @@ gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, gboolean meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms); gboolean meta_gpu_kms_is_platform_device (MetaGpuKms *gpu_kms); +gboolean meta_gpu_kms_disable_vrr (MetaGpuKms *gpu_kms); MetaKmsDevice * meta_gpu_kms_get_kms_device (MetaGpuKms *gpu_kms); diff --git a/src/backends/native/meta-kms-connector-private.h b/src/backends/native/meta-kms-connector-private.h index 1f8446f5f..d689ac925 100644 --- a/src/backends/native/meta-kms-connector-private.h +++ b/src/backends/native/meta-kms-connector-private.h @@ -39,6 +39,7 @@ typedef enum _MetaKmsConnectorProp META_KMS_CONNECTOR_PROP_MAX_BPC, META_KMS_CONNECTOR_PROP_COLORSPACE, META_KMS_CONNECTOR_PROP_HDR_OUTPUT_METADATA, + META_KMS_CONNECTOR_PROP_VRR_CAPABLE, META_KMS_CONNECTOR_N_PROPS } MetaKmsConnectorProp; diff --git a/src/backends/native/meta-kms-connector.c b/src/backends/native/meta-kms-connector.c index ed56d344d..8f8a328ec 100644 --- a/src/backends/native/meta-kms-connector.c +++ b/src/backends/native/meta-kms-connector.c @@ -408,6 +408,10 @@ state_set_properties (MetaKmsConnectorState *state, state->colorspace.supported = supported_drm_color_spaces_to_output_color_spaces (prop->supported_variants); } + + prop = &props[META_KMS_CONNECTOR_PROP_VRR_CAPABLE]; + if (prop->prop_id) + state->vrr_capable = prop->value; } static CoglSubpixelOrder @@ -840,6 +844,7 @@ meta_kms_connector_state_new (void) state = g_new0 (MetaKmsConnectorState, 1); state->suggested_x = -1; state->suggested_y = -1; + state->vrr_capable = FALSE; return state; } @@ -1013,6 +1018,9 @@ meta_kms_connector_state_changes (MetaKmsConnectorState *state, !hdr_metadata_equal (&state->hdr.value, &new_state->hdr.value)) return META_KMS_RESOURCE_CHANGE_FULL; + if (state->vrr_capable != new_state->vrr_capable) + return META_KMS_RESOURCE_CHANGE_FULL; + if (state->privacy_screen_state != new_state->privacy_screen_state) return META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN; @@ -1357,6 +1365,11 @@ init_properties (MetaKmsConnector *connector, .name = "HDR_OUTPUT_METADATA", .type = DRM_MODE_PROP_BLOB, }, + [META_KMS_CONNECTOR_PROP_VRR_CAPABLE] = + { + .name = "vrr_capable", + .type = DRM_MODE_PROP_RANGE, + }, }, .dpms_enum = { [META_KMS_CONNECTOR_DPMS_ON] = diff --git a/src/backends/native/meta-kms-connector.h b/src/backends/native/meta-kms-connector.h index b988672c3..216efc290 100644 --- a/src/backends/native/meta-kms-connector.h +++ b/src/backends/native/meta-kms-connector.h @@ -69,6 +69,8 @@ typedef struct _MetaKmsConnectorState gboolean supported; gboolean unknown; } hdr; + + gboolean vrr_capable; } MetaKmsConnectorState; META_EXPORT_TEST diff --git a/src/backends/native/meta-kms-crtc-private.h b/src/backends/native/meta-kms-crtc-private.h index 27cb0c08d..e6ed960a8 100644 --- a/src/backends/native/meta-kms-crtc-private.h +++ b/src/backends/native/meta-kms-crtc-private.h @@ -28,9 +28,17 @@ typedef enum _MetaKmsCrtcProp META_KMS_CRTC_PROP_ACTIVE, META_KMS_CRTC_PROP_GAMMA_LUT, META_KMS_CRTC_PROP_GAMMA_LUT_SIZE, + META_KMS_CRTC_PROP_VRR_ENABLED, META_KMS_CRTC_N_PROPS } MetaKmsCrtcProp; +typedef enum _MetaKmsCrtcVRRMode +{ + META_KMS_CRTC_VRR_MODE_DISABLED = 0, + META_KMS_CRTC_VRR_MODE_ENABLED, + META_KMS_CRTC_VRR_MODE_N_PROPS, +} MetaKmsCrtcVRRMode; + MetaKmsCrtc * meta_kms_crtc_new (MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc, int idx, diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c index 707da9dc1..3eb376575 100644 --- a/src/backends/native/meta-kms-crtc.c +++ b/src/backends/native/meta-kms-crtc.c @@ -427,6 +427,11 @@ init_properties (MetaKmsCrtc *crtc, .name = "GAMMA_LUT_SIZE", .type = DRM_MODE_PROP_RANGE, }, + [META_KMS_CRTC_PROP_VRR_ENABLED] = + { + .name = "VRR_ENABLED", + .type = DRM_MODE_PROP_RANGE, + }, } }; } diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c index d3fd77268..49b6fd31c 100644 --- a/src/backends/native/meta-kms-impl-device-atomic.c +++ b/src/backends/native/meta-kms-impl-device-atomic.c @@ -333,6 +333,39 @@ add_crtc_property (MetaKmsImplDevice *impl_device, return TRUE; } +static gboolean +process_crtc_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsCrtcUpdate *crtc_update = update_entry; + MetaKmsCrtc *crtc = crtc_update->crtc; + + if (crtc_update->vrr_mode.has_update) + { + meta_topic (META_DEBUG_KMS, + "[atomic] Setting VRR mode to %d on CRTC %u (%s)", + crtc_update->vrr_mode.is_enabled ? + META_KMS_CRTC_VRR_MODE_ENABLED : + META_KMS_CRTC_VRR_MODE_DISABLED, + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device)); + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_VRR_ENABLED, + crtc_update->vrr_mode.is_enabled, + error)) + return FALSE; + } + + return TRUE; +} + static gboolean process_mode_set (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, @@ -986,6 +1019,16 @@ meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device, &error)) goto err; + if (!process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_crtc_updates (update), + NULL, + process_crtc_update, + &error)) + goto err; + if (!process_entries (impl_device, update, req, diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c index 2d68ba11f..9a583dc62 100644 --- a/src/backends/native/meta-kms-impl-device-simple.c +++ b/src/backends/native/meta-kms-impl-device-simple.c @@ -179,6 +179,47 @@ set_connector_property (MetaKmsImplDevice *impl_device, return TRUE; } +static gboolean +set_crtc_property (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *crtc, + MetaKmsCrtcProp prop, + uint64_t value, + GError **error) +{ + uint32_t prop_id; + int fd; + int ret; + + prop_id = meta_kms_crtc_get_prop_id (crtc, prop); + if (!prop_id) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Property (%s) not found on CRTC %u", + meta_kms_crtc_get_prop_name (crtc, prop), + meta_kms_crtc_get_id (crtc)); + return FALSE; + } + + fd = meta_kms_impl_device_get_fd (impl_device); + + ret = drmModeObjectSetProperty (fd, + meta_kms_crtc_get_id (crtc), + DRM_MODE_OBJECT_CRTC, + prop_id, + value); + if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "Failed to set CRTC %u property %u: %s", + meta_kms_crtc_get_id (crtc), + prop_id, + g_strerror (-ret)); + return FALSE; + } + + return TRUE; +} + static gboolean process_connector_update (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, @@ -268,6 +309,36 @@ process_connector_update (MetaKmsImplDevice *impl_device, return TRUE; } +static gboolean +process_crtc_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + gpointer update_entry, + GError **error) +{ + MetaKmsCrtcUpdate *crtc_update = update_entry; + MetaKmsCrtc *crtc = crtc_update->crtc; + + if (crtc_update->vrr_mode.has_update) + { + meta_topic (META_DEBUG_KMS, + "[simple] Setting VRR mode to %d on CRTC %u (%s)", + crtc_update->vrr_mode.is_enabled ? + META_KMS_CRTC_VRR_MODE_ENABLED : + META_KMS_CRTC_VRR_MODE_DISABLED, + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device)); + + if (!set_crtc_property (impl_device, + crtc, + META_KMS_CRTC_PROP_VRR_ENABLED, + crtc_update->vrr_mode.is_enabled, + error)) + return FALSE; + } + + return TRUE; +} + static CachedModeSet * cached_mode_set_new (GList *connectors, const drmModeModeInfo *drm_mode, @@ -1522,6 +1593,13 @@ meta_kms_impl_device_simple_process_update (MetaKmsImplDevice *impl_device, &error)) goto err; + if (!process_entries (impl_device, + update, + meta_kms_update_get_crtc_updates (update), + process_crtc_update, + &error)) + goto err; + if (!process_plane_assignments (impl_device, update, &failed_planes, &error)) goto err; diff --git a/src/backends/native/meta-kms-types.h b/src/backends/native/meta-kms-types.h index c38d1ab3c..b5a21a6c7 100644 --- a/src/backends/native/meta-kms-types.h +++ b/src/backends/native/meta-kms-types.h @@ -64,6 +64,7 @@ typedef enum _MetaKmsDeviceFlag META_KMS_DEVICE_FLAG_HAS_ADDFB2 = 1 << 5, META_KMS_DEVICE_FLAG_FORCE_LEGACY = 1 << 6, META_KMS_DEVICE_FLAG_DISABLE_CLIENT_MODIFIERS = 1 << 7, + META_KMS_DEVICE_FLAG_DISABLE_VRR = 1 << 8, } MetaKmsDeviceFlag; typedef enum _MetaKmsResourceChanges diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h index edddd3e4a..88883a62c 100644 --- a/src/backends/native/meta-kms-update-private.h +++ b/src/backends/native/meta-kms-update-private.h @@ -111,6 +111,16 @@ typedef struct _MetaKmsConnectorUpdate } hdr; } MetaKmsConnectorUpdate; +typedef struct _MetaKmsCrtcUpdate +{ + MetaKmsCrtc *crtc; + + struct { + gboolean has_update; + gboolean is_enabled; + } vrr_mode; +} MetaKmsCrtcUpdate; + typedef struct _MetaKmsPageFlipListener { gatomicrefcount ref_count; @@ -177,6 +187,9 @@ GList * meta_kms_update_get_page_flip_listeners (MetaKmsUpdate *update); META_EXPORT_TEST GList * meta_kms_update_get_connector_updates (MetaKmsUpdate *update); +META_EXPORT_TEST +GList * meta_kms_update_get_crtc_updates (MetaKmsUpdate *update); + META_EXPORT_TEST GList * meta_kms_update_get_crtc_color_updates (MetaKmsUpdate *update); diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c index 5189c5ab3..7db8aface 100644 --- a/src/backends/native/meta-kms-update.c +++ b/src/backends/native/meta-kms-update.c @@ -40,6 +40,7 @@ struct _MetaKmsUpdate GList *mode_sets; GList *plane_assignments; GList *connector_updates; + GList *crtc_updates; GList *crtc_color_updates; MetaKmsCustomPageFlip *custom_page_flip; @@ -521,6 +522,46 @@ meta_kms_crtc_color_updates_free (MetaKmsCrtcColorUpdate *color_update) g_free (color_update); } +static MetaKmsCrtcUpdate * +ensure_crtc_update (MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + GList *l; + MetaKmsCrtcUpdate *crtc_update; + + for (l = update->crtc_updates; l; l = l->next) + { + crtc_update = l->data; + + if (crtc_update->crtc == crtc) + return crtc_update; + } + + crtc_update = g_new0 (MetaKmsCrtcUpdate, 1); + crtc_update->crtc = crtc; + + update->crtc_updates = g_list_prepend (update->crtc_updates, + crtc_update); + + return crtc_update; +} + +void +meta_kms_update_set_vrr_mode (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + gboolean enabled) +{ + MetaKmsCrtcUpdate *crtc_update; + + g_assert (meta_kms_crtc_get_device (crtc) == update->device); + + crtc_update = ensure_crtc_update (update, crtc); + crtc_update->vrr_mode.has_update = TRUE; + crtc_update->vrr_mode.is_enabled = enabled; + + update_latch_crtc (update, crtc); +} + void meta_kms_update_add_page_flip_listener (MetaKmsUpdate *update, MetaKmsCrtc *crtc, @@ -734,6 +775,12 @@ meta_kms_update_get_connector_updates (MetaKmsUpdate *update) return update->connector_updates; } +GList * +meta_kms_update_get_crtc_updates (MetaKmsUpdate *update) +{ + return update->crtc_updates; +} + GList * meta_kms_update_get_crtc_color_updates (MetaKmsUpdate *update) { @@ -1062,6 +1109,7 @@ meta_kms_update_free (MetaKmsUpdate *update) g_list_free_full (update->page_flip_listeners, (GDestroyNotify) meta_kms_page_flip_listener_unref); g_list_free_full (update->connector_updates, g_free); + g_list_free_full (update->crtc_updates, g_free); g_list_free_full (update->crtc_color_updates, (GDestroyNotify) meta_kms_crtc_color_updates_free); g_clear_pointer (&update->custom_page_flip, meta_kms_custom_page_flip_free); diff --git a/src/backends/native/meta-kms-update.h b/src/backends/native/meta-kms-update.h index c4d120d92..524202857 100644 --- a/src/backends/native/meta-kms-update.h +++ b/src/backends/native/meta-kms-update.h @@ -152,6 +152,10 @@ void meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaGammaLut *gamma); +void meta_kms_update_set_vrr_mode (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + gboolean enabled); + void meta_kms_plane_assignment_set_fb_damage (MetaKmsPlaneAssignment *plane_assignment, const int *rectangles, int n_rectangles); diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c index 7c64e4f06..018e688d0 100644 --- a/src/backends/native/meta-output-kms.c +++ b/src/backends/native/meta-output-kms.c @@ -95,6 +95,26 @@ meta_output_kms_set_underscan (MetaOutputKms *output_kms, } } +void +meta_output_kms_set_vrr_mode (MetaOutputKms *output_kms, + MetaKmsUpdate *kms_update, + gboolean enabled) +{ + MetaOutput *output = META_OUTPUT (output_kms); + const MetaOutputInfo *output_info = meta_output_get_info (output); + MetaCrtc *crtc; + MetaKmsCrtc *kms_crtc; + + g_assert (output_info->vrr_capable); + + crtc = meta_output_get_assigned_crtc (output); + kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); + + meta_kms_update_set_vrr_mode (kms_update, + kms_crtc, + enabled); +} + void meta_output_kms_set_max_bpc (MetaOutputKms *output_kms, MetaKmsUpdate *kms_update) @@ -488,6 +508,9 @@ meta_output_kms_new (MetaGpuKms *gpu_kms, output_info->supports_underscanning = meta_kms_connector_is_underscanning_supported (kms_connector); + output_info->vrr_capable = (connector_state->vrr_capable && + !meta_gpu_kms_disable_vrr (gpu_kms)); + max_bpc_range = meta_kms_connector_get_max_bpc (kms_connector); if (max_bpc_range) { diff --git a/src/backends/native/meta-output-kms.h b/src/backends/native/meta-output-kms.h index 064ee2d72..bf942455d 100644 --- a/src/backends/native/meta-output-kms.h +++ b/src/backends/native/meta-output-kms.h @@ -37,6 +37,10 @@ void meta_output_kms_set_power_save_mode (MetaOutputKms *output_kms, void meta_output_kms_set_underscan (MetaOutputKms *output_kms, MetaKmsUpdate *kms_update); +void meta_output_kms_set_vrr_mode (MetaOutputKms *output_kms, + MetaKmsUpdate *kms_update, + gboolean enabled); + void meta_output_kms_set_max_bpc (MetaOutputKms *output_kms, MetaKmsUpdate *kms_update); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 57cb20b7e..287b25272 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -1441,6 +1441,7 @@ meta_renderer_native_create_view (MetaRenderer *renderer, "stage", meta_backend_get_stage (backend), "layout", &view_layout, "crtc", crtc, + "output", output, "scale", scale, "framebuffer", framebuffer, "offscreen", offscreen, @@ -1567,8 +1568,11 @@ meta_renderer_native_before_redraw (MetaRendererNative *renderer_native, if (COGL_IS_ONSCREEN (framebuffer)) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + MetaRendererViewNative *view_native = META_RENDERER_VIEW_NATIVE (view); meta_onscreen_native_before_redraw (onscreen, frame); + meta_renderer_view_native_maybe_update_frame_sync_mode (view_native, + frame); } } diff --git a/src/backends/native/meta-renderer-view-native.c b/src/backends/native/meta-renderer-view-native.c index 81f40bda8..70b0684fd 100644 --- a/src/backends/native/meta-renderer-view-native.c +++ b/src/backends/native/meta-renderer-view-native.c @@ -22,11 +22,28 @@ #include "backends/native/meta-renderer-view-native.h" +#include "backends/meta-output.h" +#include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-frame-native.h" +#include "backends/native/meta-kms.h" +#include "backends/native/meta-kms-device.h" +#include "backends/native/meta-output-kms.h" + +#include "clutter/clutter.h" + +typedef enum _MetaFrameSyncMode +{ + META_FRAME_SYNC_MODE_INIT, + META_FRAME_SYNC_MODE_ENABLED, + META_FRAME_SYNC_MODE_DISABLED, +} MetaFrameSyncMode; struct _MetaRendererViewNative { MetaRendererView parent; + + MetaFrameSyncMode requested_frame_sync_mode; + MetaFrameSyncMode frame_sync_mode; }; G_DEFINE_TYPE (MetaRendererViewNative, meta_renderer_view_native, @@ -38,6 +55,104 @@ meta_renderer_view_native_new_frame (ClutterStageView *stage_view) return (ClutterFrame *) meta_frame_native_new (); } +static void +update_frame_sync_mode (MetaRendererViewNative *view_native, + ClutterFrame *frame, + MetaOutput *output, + MetaFrameSyncMode sync_mode) +{ + MetaFrameNative *frame_native; + MetaCrtc *crtc; + MetaKmsCrtc *kms_crtc; + MetaKmsDevice *kms_device; + MetaKmsUpdate *kms_update; + ClutterFrameClock *frame_clock; + + frame_native = meta_frame_native_from_frame (frame); + + frame_clock = + clutter_stage_view_get_frame_clock (CLUTTER_STAGE_VIEW (view_native)); + + crtc = meta_output_get_assigned_crtc (output); + kms_crtc = meta_crtc_kms_get_kms_crtc (META_CRTC_KMS (crtc)); + kms_device = meta_kms_crtc_get_device (kms_crtc); + + kms_update = meta_frame_native_ensure_kms_update (frame_native, kms_device); + + switch (sync_mode) + { + case META_FRAME_SYNC_MODE_ENABLED: + clutter_frame_clock_set_mode (frame_clock, + CLUTTER_FRAME_CLOCK_MODE_VARIABLE); + meta_output_kms_set_vrr_mode (META_OUTPUT_KMS (output), + kms_update, + TRUE); + break; + case META_FRAME_SYNC_MODE_DISABLED: + clutter_frame_clock_set_mode (frame_clock, + CLUTTER_FRAME_CLOCK_MODE_FIXED); + meta_output_kms_set_vrr_mode (META_OUTPUT_KMS (output), + kms_update, + FALSE); + break; + case META_FRAME_SYNC_MODE_INIT: + g_assert_not_reached (); + } + + view_native->frame_sync_mode = sync_mode; +} + +static MetaFrameSyncMode +get_applicable_sync_mode (MetaRendererViewNative *view_native, + MetaOutput *output) +{ + if (meta_output_is_vrr_disallowed (output)) + return META_FRAME_SYNC_MODE_DISABLED; + + return view_native->requested_frame_sync_mode; +} + +void +meta_renderer_view_native_maybe_update_frame_sync_mode (MetaRendererViewNative *view_native, + ClutterFrame *frame) +{ + MetaRendererView *view = META_RENDERER_VIEW (view_native); + MetaOutput *output; + MetaFrameSyncMode applicable_sync_mode; + + output = meta_renderer_view_get_output (view); + + if (!meta_output_is_vrr_capable (output)) + return; + + applicable_sync_mode = + get_applicable_sync_mode (view_native, output); + + if (G_LIKELY (applicable_sync_mode == view_native->frame_sync_mode)) + return; + + update_frame_sync_mode (view_native, + frame, + output, + applicable_sync_mode); +} + +void +meta_renderer_view_native_request_frame_sync (MetaRendererViewNative *view_native, + gboolean enabled) +{ + view_native->requested_frame_sync_mode = + enabled + ? META_FRAME_SYNC_MODE_ENABLED + : META_FRAME_SYNC_MODE_DISABLED; +} + +gboolean +meta_renderer_view_native_is_frame_sync_enabled (MetaRendererViewNative *view_native) +{ + return view_native->frame_sync_mode == META_FRAME_SYNC_MODE_ENABLED; +} + static void meta_renderer_view_native_class_init (MetaRendererViewNativeClass *klass) { @@ -49,4 +164,6 @@ meta_renderer_view_native_class_init (MetaRendererViewNativeClass *klass) static void meta_renderer_view_native_init (MetaRendererViewNative *view_native) { + view_native->requested_frame_sync_mode = META_FRAME_SYNC_MODE_DISABLED; + view_native->frame_sync_mode = META_FRAME_SYNC_MODE_INIT; } diff --git a/src/backends/native/meta-renderer-view-native.h b/src/backends/native/meta-renderer-view-native.h index cf0e43722..4924f9cde 100644 --- a/src/backends/native/meta-renderer-view-native.h +++ b/src/backends/native/meta-renderer-view-native.h @@ -27,3 +27,11 @@ #define META_TYPE_RENDERER_VIEW_NATIVE (meta_renderer_view_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaRendererViewNative, meta_renderer_view_native, META, RENDERER_VIEW_NATIVE, MetaRendererView) + +void meta_renderer_view_native_maybe_update_frame_sync_mode (MetaRendererViewNative *view_native, + ClutterFrame *frame); + +void meta_renderer_view_native_request_frame_sync (MetaRendererViewNative *view_native, + gboolean enabled); + +gboolean meta_renderer_view_native_is_frame_sync_enabled (MetaRendererViewNative *view_native); diff --git a/src/backends/native/meta-udev.c b/src/backends/native/meta-udev.c index 73dd92c3e..097e8a283 100644 --- a/src/backends/native/meta-udev.c +++ b/src/backends/native/meta-udev.c @@ -100,6 +100,13 @@ meta_is_udev_device_disable_modifiers (GUdevDevice *device) "mutter-device-disable-kms-modifiers"); } +gboolean +meta_is_udev_device_disable_vrr (GUdevDevice *device) +{ + return meta_has_udev_device_tag (device, + "mutter-device-disable-vrr"); +} + gboolean meta_is_udev_device_ignore (GUdevDevice *device) { diff --git a/src/backends/native/meta-udev.h b/src/backends/native/meta-udev.h index 7e19921b2..8cb5d0788 100644 --- a/src/backends/native/meta-udev.h +++ b/src/backends/native/meta-udev.h @@ -32,6 +32,8 @@ gboolean meta_is_udev_device_boot_vga (GUdevDevice *device); gboolean meta_is_udev_device_disable_modifiers (GUdevDevice *device); +gboolean meta_is_udev_device_disable_vrr (GUdevDevice *device); + gboolean meta_is_udev_device_ignore (GUdevDevice *device); gboolean meta_is_udev_test_device (GUdevDevice *device); diff --git a/src/backends/x11/nested/meta-renderer-x11-nested.c b/src/backends/x11/nested/meta-renderer-x11-nested.c index d89ca1e8c..79579b096 100644 --- a/src/backends/x11/nested/meta-renderer-x11-nested.c +++ b/src/backends/x11/nested/meta-renderer-x11-nested.c @@ -138,6 +138,7 @@ meta_renderer_x11_nested_create_view (MetaRenderer *renderer, "stage", meta_backend_get_stage (backend), "layout", &view_layout, "crtc", crtc, + "output", output, "refresh-rate", mode_info->refresh_rate, "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), "offscreen", COGL_FRAMEBUFFER (offscreen), diff --git a/src/compositor/meta-compositor-native.c b/src/compositor/meta-compositor-native.c index d313793ba..b7525513f 100644 --- a/src/compositor/meta-compositor-native.c +++ b/src/compositor/meta-compositor-native.c @@ -43,6 +43,9 @@ meta_compositor_native_before_paint (MetaCompositor *compositor, compositor); #endif + meta_compositor_view_native_maybe_update_frame_sync_surface (compositor_view_native, + compositor); + parent_class = META_COMPOSITOR_CLASS (meta_compositor_native_parent_class); parent_class->before_paint (compositor, compositor_view); } diff --git a/src/compositor/meta-compositor-view-native.c b/src/compositor/meta-compositor-view-native.c index 33d8c0670..8699e6128 100644 --- a/src/compositor/meta-compositor-view-native.c +++ b/src/compositor/meta-compositor-view-native.c @@ -26,14 +26,20 @@ #include "backends/meta-crtc.h" #include "backends/native/meta-crtc-kms.h" +#include "backends/native/meta-renderer-view-native.h" +#include "clutter/clutter.h" #include "compositor/compositor-private.h" #include "compositor/meta-window-actor-private.h" +#include "core/window-private.h" #ifdef HAVE_WAYLAND #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-surface.h" #endif /* HAVE_WAYLAND */ +static void update_frame_sync_surface (MetaCompositorViewNative *view_native, + MetaSurfaceActor *surface_actor); + struct _MetaCompositorViewNative { MetaCompositorView parent; @@ -41,11 +47,52 @@ struct _MetaCompositorViewNative #ifdef HAVE_WAYLAND MetaWaylandSurface *scanout_candidate; #endif /* HAVE_WAYLAND */ + + MetaSurfaceActor *frame_sync_surface; + + gulong frame_sync_surface_repaint_scheduled_id; + gulong frame_sync_surface_frozen_id; + gulong frame_sync_surface_destroy_id; }; G_DEFINE_TYPE (MetaCompositorViewNative, meta_compositor_view_native, META_TYPE_COMPOSITOR_VIEW) +static void +on_frame_sync_surface_repaint_scheduled (MetaSurfaceActor *surface_actor, + MetaCompositorViewNative *view_native) +{ + MetaCompositorView *compositor_view = META_COMPOSITOR_VIEW (view_native); + ClutterStageView *stage_view; + MetaRendererViewNative *renderer_view_native; + + stage_view = meta_compositor_view_get_stage_view (compositor_view); + renderer_view_native = META_RENDERER_VIEW_NATIVE (stage_view); + + if (meta_renderer_view_native_is_frame_sync_enabled (renderer_view_native)) + { + ClutterFrameClock *frame_clock; + + frame_clock = clutter_stage_view_get_frame_clock (stage_view); + + clutter_frame_clock_schedule_update_now (frame_clock); + } +} + +static void +on_frame_sync_surface_frozen (MetaSurfaceActor *surface_actor, + MetaCompositorViewNative *view_native) +{ + update_frame_sync_surface (view_native, NULL); +} + +static void +on_frame_sync_surface_destroyed (MetaSurfaceActor *surface_actor, + MetaCompositorViewNative *view_native) +{ + update_frame_sync_surface (view_native, NULL); +} + #ifdef HAVE_WAYLAND static void update_scanout_candidate (MetaCompositorViewNative *view_native, @@ -269,6 +316,151 @@ meta_compositor_view_native_maybe_assign_scanout (MetaCompositorViewNative *view } #endif /* HAVE_WAYLAND */ +static MetaSurfaceActor * +find_frame_sync_candidate (MetaCompositorView *compositor_view, + MetaCompositor *compositor) +{ + MetaWindowActor *window_actor; + MetaWindow *window; + ClutterStageView *stage_view; + MtkRectangle view_layout; + MetaSurfaceActor *surface_actor; + + if (meta_compositor_is_unredirect_inhibited (compositor)) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: unredirect inhibited"); + return NULL; + } + + window_actor = + meta_compositor_view_get_top_window_actor (compositor_view); + if (!window_actor) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: no top window actor"); + return NULL; + } + + if (meta_window_actor_is_frozen (window_actor)) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: window-actor is frozen"); + return NULL; + } + + if (meta_window_actor_effect_in_progress (window_actor)) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: window-actor effects in progress"); + return NULL; + } + + if (clutter_actor_has_transitions (CLUTTER_ACTOR (window_actor))) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: window-actor has transition"); + return NULL; + } + + window = meta_window_actor_get_meta_window (window_actor); + if (!window) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: no meta-window"); + return NULL; + } + + stage_view = meta_compositor_view_get_stage_view (compositor_view); + + clutter_stage_view_get_layout (stage_view, &view_layout); + + if (!meta_window_frame_contains_rect (window, &view_layout)) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: stage-view layout not covered " + "by meta-window frame"); + return NULL; + } + + surface_actor = meta_window_actor_get_scanout_candidate (window_actor); + if (!surface_actor) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: window-actor has no scanout candidate"); + return NULL; + } + + if (!meta_surface_actor_contains_rect (surface_actor, + &view_layout)) + { + meta_topic (META_DEBUG_RENDER, + "No frame sync candidate: stage-view layout not covered " + "by surface-actor"); + return NULL; + } + + return surface_actor; +} + +static void +update_frame_sync_surface (MetaCompositorViewNative *view_native, + MetaSurfaceActor *surface_actor) +{ + MetaCompositorView *compositor_view = + META_COMPOSITOR_VIEW (view_native); + ClutterStageView *stage_view; + MetaRendererViewNative *renderer_view_native; + + g_clear_signal_handler (&view_native->frame_sync_surface_repaint_scheduled_id, + view_native->frame_sync_surface); + g_clear_signal_handler (&view_native->frame_sync_surface_frozen_id, + view_native->frame_sync_surface); + g_clear_signal_handler (&view_native->frame_sync_surface_destroy_id, + view_native->frame_sync_surface); + + if (surface_actor) + { + view_native->frame_sync_surface_repaint_scheduled_id = + g_signal_connect (surface_actor, "repaint-scheduled", + G_CALLBACK (on_frame_sync_surface_repaint_scheduled), + view_native); + view_native->frame_sync_surface_frozen_id = + g_signal_connect (surface_actor, "frozen", + G_CALLBACK (on_frame_sync_surface_frozen), + view_native); + view_native->frame_sync_surface_destroy_id = + g_signal_connect (surface_actor, "destroy", + G_CALLBACK (on_frame_sync_surface_destroyed), + view_native); + } + + view_native->frame_sync_surface = surface_actor; + + stage_view = meta_compositor_view_get_stage_view (compositor_view); + renderer_view_native = META_RENDERER_VIEW_NATIVE (stage_view); + + meta_renderer_view_native_request_frame_sync (renderer_view_native, + surface_actor != NULL); +} + +void +meta_compositor_view_native_maybe_update_frame_sync_surface (MetaCompositorViewNative *view_native, + MetaCompositor *compositor) +{ + MetaCompositorView *compositor_view = META_COMPOSITOR_VIEW (view_native); + MetaSurfaceActor *surface_actor; + + surface_actor = find_frame_sync_candidate (compositor_view, + compositor); + + if (G_LIKELY (surface_actor == view_native->frame_sync_surface)) + return; + + update_frame_sync_surface (view_native, + surface_actor); +} + MetaCompositorViewNative * meta_compositor_view_native_new (ClutterStageView *stage_view) { @@ -279,6 +471,25 @@ meta_compositor_view_native_new (ClutterStageView *stage_view) NULL); } +static void +meta_compositor_view_native_dispose (GObject *object) +{ + MetaCompositorViewNative *view_native = META_COMPOSITOR_VIEW_NATIVE (object); + + if (view_native->frame_sync_surface) + { + g_clear_signal_handler (&view_native->frame_sync_surface_repaint_scheduled_id, + view_native->frame_sync_surface); + g_clear_signal_handler (&view_native->frame_sync_surface_destroy_id, + view_native->frame_sync_surface); + g_clear_signal_handler (&view_native->frame_sync_surface_frozen_id, + view_native->frame_sync_surface); + view_native->frame_sync_surface = NULL; + } + + G_OBJECT_CLASS (meta_compositor_view_native_parent_class)->dispose (object); +} + static void meta_compositor_view_native_finalize (GObject *object) { @@ -296,6 +507,7 @@ meta_compositor_view_native_class_init (MetaCompositorViewNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->dispose = meta_compositor_view_native_dispose; object_class->finalize = meta_compositor_view_native_finalize; } diff --git a/src/compositor/meta-compositor-view-native.h b/src/compositor/meta-compositor-view-native.h index d93116267..52a2bef27 100644 --- a/src/compositor/meta-compositor-view-native.h +++ b/src/compositor/meta-compositor-view-native.h @@ -36,3 +36,6 @@ MetaCompositorViewNative *meta_compositor_view_native_new (ClutterStageView *sta void meta_compositor_view_native_maybe_assign_scanout (MetaCompositorViewNative *view_native, MetaCompositor *compositor); #endif /* HAVE_WAYLAND */ + +void meta_compositor_view_native_maybe_update_frame_sync_surface (MetaCompositorViewNative *view_native, + MetaCompositor *compositor); diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c index 3dc3a5481..6414159b1 100644 --- a/src/compositor/meta-surface-actor.c +++ b/src/compositor/meta-surface-actor.c @@ -63,6 +63,7 @@ enum { REPAINT_SCHEDULED, SIZE_CHANGED, + FROZEN, LAST_SIGNAL, }; @@ -296,6 +297,13 @@ meta_surface_actor_class_init (MetaSurfaceActorClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals[FROZEN] = g_signal_new ("frozen", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } gboolean @@ -543,6 +551,22 @@ meta_surface_actor_is_obscured_on_stage_view (MetaSurfaceActor *self, stage_view); } +gboolean +meta_surface_actor_contains_rect (MetaSurfaceActor *surface_actor, + MtkRectangle *rect) +{ + ClutterActor *actor = CLUTTER_ACTOR (surface_actor); + graphene_rect_t bounding_rect; + graphene_rect_t bound_rect; + + clutter_actor_get_transformed_extents (actor, &bounding_rect); + + bound_rect = mtk_rectangle_to_graphene_rect (rect); + + return graphene_rect_contains_rect (&bounding_rect, + &bound_rect); +} + void meta_surface_actor_set_input_region (MetaSurfaceActor *self, cairo_region_t *region) @@ -624,6 +648,9 @@ meta_surface_actor_set_frozen (MetaSurfaceActor *self, priv->frozen = frozen; + if (frozen) + g_signal_emit (self, signals[FROZEN], 0); + if (!frozen && priv->pending_damage) { int i, n_rects = cairo_region_num_rectangles (priv->pending_damage); diff --git a/src/compositor/meta-surface-actor.h b/src/compositor/meta-surface-actor.h index 70eded69d..a6181e188 100644 --- a/src/compositor/meta-surface-actor.h +++ b/src/compositor/meta-surface-actor.h @@ -42,6 +42,9 @@ gboolean meta_surface_actor_is_obscured_on_stage_view (MetaSurfaceActor *self, ClutterStageView *stage_view, float *unobscurred_fraction); +gboolean meta_surface_actor_contains_rect (MetaSurfaceActor *surface_actor, + MtkRectangle *rect); + void meta_surface_actor_set_input_region (MetaSurfaceActor *self, cairo_region_t *region); void meta_surface_actor_set_opaque_region (MetaSurfaceActor *self, diff --git a/src/core/window-private.h b/src/core/window-private.h index 4df552abe..9e2b774d2 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -702,6 +702,9 @@ void meta_window_get_session_geometry (MetaWindow *window, int *width, int *height); +gboolean meta_window_frame_contains_rect (MetaWindow *window, + MtkRectangle *rect); + void meta_window_update_unfocused_button_grabs (MetaWindow *window); void meta_window_update_appears_focused (MetaWindow *window); diff --git a/src/core/window.c b/src/core/window.c index f100a8bf3..63376b8a0 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -4321,6 +4321,14 @@ meta_window_get_session_geometry (MetaWindow *window, window->size_hints.height_inc; } +gboolean +meta_window_frame_contains_rect (MetaWindow *window, + MtkRectangle *rect) +{ + return mtk_rectangle_contains_rect (&window->rect, + rect); +} + /** * meta_window_get_buffer_rect: * @window: a #MetaWindow diff --git a/src/tests/meta-monitor-test-utils.c b/src/tests/meta-monitor-test-utils.c index 1555a5f87..28f36c793 100644 --- a/src/tests/meta-monitor-test-utils.c +++ b/src/tests/meta-monitor-test-utils.c @@ -394,6 +394,10 @@ meta_check_monitor_configuration (MetaContext *context, ==, meta_output_is_underscanning (output)); + g_assert_cmpint (expect->monitors[i].is_vrr_disallowed, + ==, + meta_output_is_vrr_disallowed (output)); + if (!meta_output_get_max_bpc (output, &output_max_bpc)) output_max_bpc = 0; @@ -796,6 +800,7 @@ meta_create_monitor_test_setup (MetaBackend *backend, output_assignment = (MetaOutputAssignment) { .is_underscanning = setup->outputs[i].is_underscanning, + .is_vrr_disallowed = setup->outputs[i].is_vrr_disallowed, .has_max_bpc = !!setup->outputs[i].max_bpc, .max_bpc = setup->outputs[i].max_bpc, }; diff --git a/src/tests/meta-monitor-test-utils.h b/src/tests/meta-monitor-test-utils.h index 6d2c00189..ce7dbd6b8 100644 --- a/src/tests/meta-monitor-test-utils.h +++ b/src/tests/meta-monitor-test-utils.h @@ -105,6 +105,7 @@ typedef struct _MonitorTestCaseOutput float scale; gboolean is_laptop_panel; gboolean is_underscanning; + gboolean is_vrr_disallowed; unsigned int max_bpc; const char *serial; MetaMonitorTransform panel_orientation_transform; @@ -160,6 +161,7 @@ typedef struct _MonitorTestCaseMonitor int width_mm; int height_mm; gboolean is_underscanning; + gboolean is_vrr_disallowed; unsigned int max_bpc; } MonitorTestCaseMonitor; diff --git a/src/tests/monitor-configs/vrr-disallowed.xml b/src/tests/monitor-configs/vrr-disallowed.xml new file mode 100644 index 000000000..e7fe784dc --- /dev/null +++ b/src/tests/monitor-configs/vrr-disallowed.xml @@ -0,0 +1,23 @@ + + + + 0 + 0 + yes + + + DP-1 + MetaProduct's Inc. + MetaMonitor + 0x123456 + + + 1024 + 768 + 60.000495910644531 + + no + + + + diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c index d6ed7665c..0f19b1c7d 100644 --- a/src/tests/monitor-store-unit-tests.c +++ b/src/tests/monitor-store-unit-tests.c @@ -48,6 +48,7 @@ typedef struct _MonitorStoreTestCaseMonitor const char *serial; MonitorStoreTestCaseMonitorMode mode; gboolean is_underscanning; + gboolean disallow_vrr; unsigned int max_bpc; } MonitorStoreTestCaseMonitor; @@ -197,6 +198,9 @@ check_monitor_store_configuration (MetaMonitorConfigStore *config_store, g_assert_cmpint (monitor_config->enable_underscanning, ==, test_monitor->is_underscanning); + g_assert_cmpint (monitor_config->disallow_vrr, + ==, + test_monitor->disallow_vrr); g_assert_cmpint (monitor_config->has_max_bpc, ==, !!test_monitor->max_bpc); @@ -453,6 +457,51 @@ meta_test_monitor_store_underscanning (void) check_monitor_store_configurations (&expect); } +static void +meta_test_monitor_store_vrr_disallowed (void) +{ + MonitorStoreTestExpect expect = { + .configurations = { + { + .logical_monitors = { + { + .layout = { + .x = 0, + .y = 0, + .width = 1024, + .height = 768 + }, + .scale = 1, + .is_primary = TRUE, + .is_presentation = FALSE, + .monitors = { + { + .connector = "DP-1", + .vendor = "MetaProduct's Inc.", + .product = "MetaMonitor", + .serial = "0x123456", + .mode = { + .width = 1024, + .height = 768, + .refresh_rate = 60.000495910644531 + }, + .disallow_vrr = TRUE, + } + }, + .n_monitors = 1, + }, + }, + .n_logical_monitors = 1 + } + }, + .n_configurations = 1 + }; + + meta_set_custom_monitor_config (test_context, "vrr-disallowed.xml"); + + check_monitor_store_configurations (&expect); +} + static void meta_test_monitor_store_max_bpc (void) { @@ -1047,6 +1096,8 @@ init_monitor_store_tests (void) meta_test_monitor_store_primary); g_test_add_func ("/backends/monitor-store/underscanning", meta_test_monitor_store_underscanning); + g_test_add_func ("/backends/monitor-store/vrr-disallowed", + meta_test_monitor_store_vrr_disallowed); g_test_add_func ("/backends/monitor-store/max-bpc", meta_test_monitor_store_max_bpc); g_test_add_func ("/backends/monitor-store/scale", diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c index a95767283..045adf983 100644 --- a/src/tests/monitor-unit-tests.c +++ b/src/tests/monitor-unit-tests.c @@ -3254,6 +3254,100 @@ meta_test_monitor_underscanning_config (void) check_monitor_test_clients_state (); } +static void +meta_test_monitor_vrr_disallowed_config (void) +{ + MonitorTestCase test_case = { + .setup = { + .modes = { + { + .width = 1024, + .height = 768, + .refresh_rate = 60.0 + } + }, + .n_modes = 1, + .outputs = { + { + .crtc = 0, + .modes = { 0 }, + .n_modes = 1, + .preferred_mode = 0, + .possible_crtcs = { 0 }, + .n_possible_crtcs = 1, + .width_mm = 222, + .height_mm = 125, + .is_vrr_disallowed = TRUE, + } + }, + .n_outputs = 1, + .crtcs = { + { + .current_mode = 0 + } + }, + .n_crtcs = 1 + }, + + .expect = { + .monitors = { + { + .outputs = { 0 }, + .n_outputs = 1, + .modes = { + { + .width = 1024, + .height = 768, + .refresh_rate = 60.0, + .crtc_modes = { + { + .output = 0, + .crtc_mode = 0 + } + } + } + }, + .n_modes = 1, + .current_mode = 0, + .width_mm = 222, + .height_mm = 125, + .is_vrr_disallowed = TRUE, + } + }, + .n_monitors = 1, + .logical_monitors = { + { + .monitors = { 0 }, + .n_monitors = 1, + .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, + .scale = 1 + } + }, + .n_logical_monitors = 1, + .primary_logical_monitor = 0, + .n_outputs = 1, + .crtcs = { + { + .current_mode = 0, + } + }, + .n_crtcs = 1, + .screen_width = 1024, + .screen_height = 768 + } + }; + MetaMonitorTestSetup *test_setup; + + test_setup = meta_create_monitor_test_setup (test_backend, + &test_case.setup, + MONITOR_TEST_FLAG_NO_STORED); + emulate_hotplug (test_setup); + META_TEST_LOG_CALL ("Checking monitor configuration", + meta_check_monitor_configuration (test_context, + &test_case.expect)); + check_monitor_test_clients_state (); +} + static void meta_test_monitor_max_bpc_config (void) { @@ -5839,6 +5933,103 @@ meta_test_monitor_custom_underscanning_config (void) check_monitor_test_clients_state (); } +static void +meta_test_monitor_custom_vrr_disallowed_config (void) +{ + MonitorTestCase test_case = { + .setup = { + .modes = { + { + .width = 1024, + .height = 768, + .refresh_rate = 60.000495910644531 + } + }, + .n_modes = 1, + .outputs = { + { + .crtc = 0, + .modes = { 0 }, + .n_modes = 1, + .preferred_mode = 0, + .possible_crtcs = { 0 }, + .n_possible_crtcs = 1, + .width_mm = 222, + .height_mm = 125, + .serial = "0x123456", + }, + }, + .n_outputs = 1, + .crtcs = { + { + .current_mode = 0 + }, + }, + .n_crtcs = 1 + }, + + .expect = { + .monitors = { + { + .outputs = { 0 }, + .n_outputs = 1, + .modes = { + { + .width = 1024, + .height = 768, + .refresh_rate = 60.000495910644531, + .crtc_modes = { + { + .output = 0, + .crtc_mode = 0 + } + } + } + }, + .n_modes = 1, + .current_mode = 0, + .width_mm = 222, + .height_mm = 125, + .is_vrr_disallowed = TRUE, + } + }, + .n_monitors = 1, + .logical_monitors = { + { + .monitors = { 0 }, + .n_monitors = 1, + .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, + .scale = 1 + } + }, + .n_logical_monitors = 1, + .primary_logical_monitor = 0, + .n_outputs = 1, + .crtcs = { + { + .current_mode = 0, + } + }, + .n_crtcs = 1, + .n_tiled_monitors = 0, + .screen_width = 1024, + .screen_height = 768 + } + }; + MetaMonitorTestSetup *test_setup; + + test_setup = meta_create_monitor_test_setup (test_backend, + &test_case.setup, + MONITOR_TEST_FLAG_NONE); + meta_set_custom_monitor_config (test_context, "vrr-disallowed.xml"); + emulate_hotplug (test_setup); + + META_TEST_LOG_CALL ("Checking monitor configuration", + meta_check_monitor_configuration (test_context, + &test_case.expect)); + check_monitor_test_clients_state (); +} + static void meta_test_monitor_custom_scale_config (void) { @@ -9624,6 +9815,8 @@ init_monitor_tests (void) meta_test_monitor_no_outputs); add_monitor_test ("/backends/monitor/underscanning-config", meta_test_monitor_underscanning_config); + add_monitor_test ("/backends/monitor/vrr-disallowed-config", + meta_test_monitor_vrr_disallowed_config); add_monitor_test ("/backends/monitor/max-bpc-config", meta_test_monitor_max_bpc_config); add_monitor_test ("/backends/monitor/preferred-non-first-mode", @@ -9658,6 +9851,8 @@ init_monitor_tests (void) meta_test_monitor_custom_primary_config); add_monitor_test ("/backends/monitor/custom/underscanning-config", meta_test_monitor_custom_underscanning_config); + add_monitor_test ("/backends/monitor/custom/vrr-disallowed-config", + meta_test_monitor_custom_vrr_disallowed_config); add_monitor_test ("/backends/monitor/custom/scale-config", meta_test_monitor_custom_scale_config); add_monitor_test ("/backends/monitor/custom/fractional-scale-config",