[mutter/wayland] meta-window-actor: Throttle obscured frame synced apps
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/wayland] meta-window-actor: Throttle obscured frame synced apps
- Date: Thu, 29 Aug 2013 21:06:12 +0000 (UTC)
commit 3b1b6116344d9ddb2ff091f7628cd84da844ff20
Author: Adel Gadllah <adel gadllah gmail com>
Date: Sat Aug 17 19:11:12 2013 +0200
meta-window-actor: Throttle obscured frame synced apps
We must send frame_drawn and frame_timing messages to even when
we don't actually queue a redraw on screen to comply with the
WM sync spec.
So throttle such apps to down to a ~100ms interval.
https://bugzilla.gnome.org/show_bug.cgi?id=703332
src/compositor/meta-window-actor.c | 235 +++++++++++++++++++++++++++--------
1 files changed, 181 insertions(+), 54 deletions(-)
---
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 537fded..8e1f55a 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -33,6 +33,7 @@
#include "meta-texture-rectangle.h"
#include "region-utils.h"
#include "meta-wayland-private.h"
+#include "monitor-private.h"
enum {
POSITION_CHANGED,
@@ -80,6 +81,9 @@ struct _MetaWindowActorPrivate
/* The region that is visible, used to optimize out redraws */
cairo_region_t *unobscured_region;
+ guint send_frame_messages_timer;
+ gint64 frame_drawn_time;
+
/* Extracted size-invariant shape used for shadows */
MetaWindowShape *shadow_shape;
@@ -189,6 +193,12 @@ static void meta_window_actor_handle_updates (MetaWindowActor *self);
static void check_needs_reshape (MetaWindowActor *self);
+static void do_send_frame_drawn (MetaWindowActor *self, FrameData *frame);
+static void do_send_frame_timings (MetaWindowActor *self,
+ FrameData *frame,
+ gint refresh_interval,
+ gint64 presentation_time);
+
G_DEFINE_TYPE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR);
static void
@@ -448,6 +458,12 @@ meta_window_actor_dispose (GObject *object)
meta_window_actor_detach_x11_pixmap (self);
}
+ if (priv->send_frame_messages_timer != 0)
+ {
+ g_source_remove (priv->send_frame_messages_timer);
+ priv->send_frame_messages_timer = 0;
+ }
+
g_clear_pointer (&priv->unobscured_region, cairo_region_destroy);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->input_region, cairo_region_destroy);
@@ -673,6 +689,16 @@ meta_window_actor_paint (ClutterActor *actor)
gboolean appears_focused = meta_window_appears_focused (priv->window);
MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
+ /* This window got damage when obscured; we set up a timer
+ * to send frame completion events, but since we're drawing
+ * the window now (for some other reason) cancel the timer
+ * and send the completion events normally */
+ if (priv->send_frame_messages_timer != 0)
+ {
+ g_source_remove (priv->send_frame_messages_timer);
+ priv->send_frame_messages_timer = 0;
+ }
+
if (shadow != NULL)
{
MetaShadowParams params;
@@ -935,11 +961,66 @@ meta_window_actor_freeze (MetaWindowActor *self)
self->priv->freeze_count++;
}
+static
+gboolean send_frame_messages_timeout (gpointer data)
+{
+ MetaWindowActor *self = (MetaWindowActor *) data;
+ MetaWindowActorPrivate *priv = self->priv;
+ FrameData *frame = g_slice_new0 (FrameData);
+
+ frame->sync_request_serial = priv->window->sync_request_serial;
+
+ do_send_frame_drawn (self, frame);
+ do_send_frame_timings (self, frame, 0, 0);
+
+ priv->needs_frame_drawn = FALSE;
+ priv->send_frame_messages_timer = 0;
+ frame_data_free (frame);
+
+ return FALSE;
+}
+
+static void
+queue_send_frame_messages_timeout (MetaWindowActor *self)
+{
+ MetaWindowActorPrivate *priv = self->priv;
+ MetaScreen *screen = priv->screen;
+ MetaDisplay *display = meta_screen_get_display (screen);
+ gint64 current_time = meta_compositor_monotonic_time_to_server_time (display, g_get_monotonic_time ());
+ MetaMonitorManager *monitor_manager = meta_monitor_manager_get ();
+ MetaWindow *window = priv->window;
+
+ MetaOutput *outputs;
+ guint n_outputs, i;
+ float refresh_rate = 60.0f;
+ gint interval, offset;
+
+ outputs = meta_monitor_manager_get_outputs (monitor_manager, &n_outputs);
+ for (i = 0; i < n_outputs; i++)
+ {
+ if (outputs[i].output_id == window->monitor->output_id && outputs[i].crtc)
+ {
+ refresh_rate = outputs[i].crtc->current_mode->refresh_rate;
+ break;
+ }
+ }
+
+ interval = (int)(1000000 / refresh_rate) * 6;
+ offset = MAX (0, current_time - priv->frame_drawn_time + interval) / 1000;
+
+ /* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
+ * so the timer will run *after* the clutter frame handling, if a frame is ready
+ * to be drawn when the timer expires.
+ */
+ priv->send_frame_messages_timer = g_timeout_add_full (META_PRIORITY_REDRAW, offset,
send_frame_messages_timeout, self, NULL);
+}
+
static void
meta_window_actor_damage_all (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
CoglTexture *texture;
+ gboolean redraw_queued;
if (!priv->needs_damage_all)
return;
@@ -949,14 +1030,16 @@ meta_window_actor_damage_all (MetaWindowActor *self)
if (!priv->mapped || priv->needs_pixmap)
return;
- meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
- 0, 0,
- cogl_texture_get_width (texture),
- cogl_texture_get_height (texture),
- clutter_actor_has_mapped_clones (priv->actor) ? NULL :
priv->unobscured_region);
+ redraw_queued = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
+ 0, 0,
+ cogl_texture_get_width (texture),
+ cogl_texture_get_height (texture),
+ clutter_actor_has_mapped_clones (priv->actor) ?
+ NULL : priv->unobscured_region);
+
+ priv->repaint_scheduled = priv->repaint_scheduled || redraw_queued;
priv->needs_damage_all = FALSE;
- priv->repaint_scheduled = TRUE;
}
static void
@@ -1012,14 +1095,31 @@ meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
if (!priv->repaint_scheduled)
{
+ gboolean is_obscured = FALSE;
+
+ /* Find out whether the window is completly obscured */
+ if (priv->unobscured_region)
+ {
+ cairo_region_t *unobscured_window_region;
+ unobscured_window_region = cairo_region_copy (priv->shape_region);
+ cairo_region_intersect (unobscured_window_region, priv->unobscured_region);
+ is_obscured = cairo_region_is_empty (unobscured_window_region);
+ cairo_region_destroy (unobscured_window_region);
+ }
+
/* A frame was marked by the client without actually doing any
- * damage, or while we had the window frozen (e.g. during an
- * interactive resize.) We need to make sure that the
+ * damage or any unobscured, or while we had the window frozen
+ * (e.g. during an interactive resize.) We need to make sure that the
* pre_paint/post_paint functions get called, enabling us to
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
- * consistent timing with non-empty frames.
+ * consistent timing with non-empty frames. If the window
+ * is completely obscured we fire off the send_frame_messages timeout.
*/
- if (priv->mapped && (!meta_is_wayland_compositor () || !priv->needs_pixmap))
+ if (is_obscured)
+ {
+ queue_send_frame_messages_timeout (self);
+ }
+ else if (priv->mapped && (!meta_is_wayland_compositor () || !priv->needs_pixmap))
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
@@ -2000,6 +2100,7 @@ meta_window_actor_process_x11_damage (MetaWindowActor *self,
{
MetaWindowActorPrivate *priv = self->priv;
MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
+ gboolean redraw_queued;
priv->received_x11_damage = TRUE;
@@ -2047,13 +2148,16 @@ meta_window_actor_process_x11_damage (MetaWindowActor *self,
if (!priv->mapped || priv->needs_pixmap)
return;
- meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
- event->area.x,
- event->area.y,
- event->area.width,
- event->area.height,
- clutter_actor_has_mapped_clones (priv->actor) ? NULL :
priv->unobscured_region);
- priv->repaint_scheduled = TRUE;
+ redraw_queued = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height,
+ clutter_actor_has_mapped_clones (priv->actor) ?
+ NULL : priv->unobscured_region);
+
+ priv->repaint_scheduled = priv->repaint_scheduled || redraw_queued;
+
}
void
@@ -2507,54 +2611,65 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
}
}
-void
-meta_window_actor_post_paint (MetaWindowActor *self)
+static void
+do_send_frame_drawn (MetaWindowActor *self, FrameData *frame)
{
MetaWindowActorPrivate *priv = self->priv;
+ MetaScreen *screen = priv->screen;
+ MetaDisplay *display = meta_screen_get_display (screen);
+ Display *xdisplay = meta_display_get_xdisplay (display);
- priv->repaint_scheduled = FALSE;
+ XClientMessageEvent ev = { 0, };
- if (priv->needs_frame_drawn)
- {
- MetaScreen *screen = priv->screen;
- MetaDisplay *display = meta_screen_get_display (screen);
- Display *xdisplay = meta_display_get_xdisplay (display);
+ frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
+ g_get_monotonic_time ());
+ priv->frame_drawn_time = frame->frame_drawn_time;
- XClientMessageEvent ev = { 0, };
+ ev.type = ClientMessage;
+ ev.window = meta_window_get_xwindow (priv->window);
+ ev.message_type = display->atom__NET_WM_FRAME_DRAWN;
+ ev.format = 32;
+ ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
+ ev.data.l[1] = frame->sync_request_serial >> 32;
+ ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff);
+ ev.data.l[3] = frame->frame_drawn_time >> 32;
- FrameData *frame = priv->frames->data;
+ meta_error_trap_push (display);
+ XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
+ XFlush (xdisplay);
+ meta_error_trap_pop (display);
+}
+
+void
+meta_window_actor_post_paint (MetaWindowActor *self)
+{
+ MetaWindowActorPrivate *priv = self->priv;
- frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
- g_get_monotonic_time ());
- ev.type = ClientMessage;
- ev.window = meta_window_get_xwindow (priv->window);
- ev.message_type = display->atom__NET_WM_FRAME_DRAWN;
- ev.format = 32;
- ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
- ev.data.l[1] = frame->sync_request_serial >> 32;
- ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff);
- ev.data.l[3] = frame->frame_drawn_time >> 32;
+ priv->repaint_scheduled = FALSE;
- meta_error_trap_push (display);
- XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
- XFlush (xdisplay);
- meta_error_trap_pop (display);
+ /* This window had damage, but wasn't actually redrawn because
+ * it is obscured. So we should wait until timer expiration
+ * before sending _NET_WM_FRAME_* messages.
+ */
+ if (priv->send_frame_messages_timer != 0)
+ return;
+ if (priv->needs_frame_drawn)
+ {
+ do_send_frame_drawn (self, priv->frames->data);
priv->needs_frame_drawn = FALSE;
}
}
static void
-send_frame_timings (MetaWindowActor *self,
- FrameData *frame,
- CoglFrameInfo *frame_info,
- gint64 presentation_time)
+do_send_frame_timings (MetaWindowActor *self,
+ FrameData *frame,
+ gint refresh_interval,
+ gint64 presentation_time)
{
MetaWindowActorPrivate *priv = self->priv;
MetaDisplay *display = meta_screen_get_display (priv->screen);
Display *xdisplay = meta_display_get_xdisplay (display);
- float refresh_rate;
- int refresh_interval;
XClientMessageEvent ev = { 0, };
@@ -2565,13 +2680,6 @@ send_frame_timings (MetaWindowActor *self,
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[1] = frame->sync_request_serial >> 32;
- refresh_rate = cogl_frame_info_get_refresh_rate (frame_info);
- /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
- if (refresh_rate >= 1.0)
- refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
- else
- refresh_interval = 0;
-
if (presentation_time != 0)
{
gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display,
@@ -2593,6 +2701,25 @@ send_frame_timings (MetaWindowActor *self,
meta_error_trap_pop (display);
}
+static void
+send_frame_timings (MetaWindowActor *self,
+ FrameData *frame,
+ CoglFrameInfo *frame_info,
+ gint64 presentation_time)
+{
+ float refresh_rate;
+ int refresh_interval;
+
+ refresh_rate = cogl_frame_info_get_refresh_rate (frame_info);
+ /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
+ if (refresh_rate >= 1.0)
+ refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
+ else
+ refresh_interval = 0;
+
+ do_send_frame_timings (self, frame, refresh_interval, presentation_time);
+}
+
void
meta_window_actor_frame_complete (MetaWindowActor *self,
CoglFrameInfo *frame_info,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]