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",