window-actor: Move X11-specific code to MetaWindowActorX11

MetaWindowActor handles sending _NET_WM_FRAME_* X atoms to
clients - even pure Wayland clients.

Now that we have Wayland- and X11-specific implementations of
MetaWindowActor, we can delegate this to MetaWindowActorX11,
and allow pure Wayland apps to not even connect to
MetaSurfaceActor:repaint-scheduled.

Do that by moving all the X11-specific code to the X11-specific
MetaWindowActorX11 class. Add vfuncs to MetaWindowActorClass
that are necessary for the move, namely:

 * pre_paint() and post_paint()
 * post_init()
 * frame_complete()
 * set_surface_actor()
 * queue_frame_drawn()

https://gitlab.gnome.org/GNOME/mutter/merge_requests/368
This commit is contained in:
Georges Basile Stavracas Neto 2018-12-21 18:51:02 -02:00
parent ac2f8cad0c
commit 80e3c1de57
No known key found for this signature in database
GPG Key ID: 886C17EE170D1385
4 changed files with 584 additions and 396 deletions

View File

@ -12,6 +12,22 @@
struct _MetaWindowActorClass struct _MetaWindowActorClass
{ {
ClutterActorClass parent; ClutterActorClass parent;
void (*frame_complete) (MetaWindowActor *actor,
ClutterFrameInfo *frame_info,
int64_t presentation_time);
void (*set_surface_actor) (MetaWindowActor *actor,
MetaSurfaceActor *surface);
void (*queue_frame_drawn) (MetaWindowActor *actor,
gboolean skip_sync_delay);
void (*post_init) (MetaWindowActor *actor);
void (*pre_paint) (MetaWindowActor *actor);
void (*post_paint) (MetaWindowActor *actor);
void (*queue_destroy) (MetaWindowActor *actor);
}; };
MetaWindowActor *meta_window_actor_new (MetaWindow *window); MetaWindowActor *meta_window_actor_new (MetaWindow *window);

View File

@ -20,6 +20,7 @@
* Georges Basile Stavracas Neto <gbsneto@gnome.org> * Georges Basile Stavracas Neto <gbsneto@gnome.org>
*/ */
#include "compositor/meta-surface-actor.h"
#include "compositor/meta-window-actor-wayland.h" #include "compositor/meta-window-actor-wayland.h"
#include "meta/meta-window-actor.h" #include "meta/meta-window-actor.h"
@ -30,9 +31,61 @@ struct _MetaWindowActorWayland
G_DEFINE_TYPE (MetaWindowActorWayland, meta_window_actor_wayland, META_TYPE_WINDOW_ACTOR) G_DEFINE_TYPE (MetaWindowActorWayland, meta_window_actor_wayland, META_TYPE_WINDOW_ACTOR)
static void
meta_window_actor_wayland_frame_complete (MetaWindowActor *actor,
ClutterFrameInfo *frame_info,
int64_t presentation_time)
{
}
static void
meta_window_actor_wayland_set_surface_actor (MetaWindowActor *actor,
MetaSurfaceActor *surface)
{
MetaWindowActorClass *parent_class =
META_WINDOW_ACTOR_CLASS (meta_window_actor_wayland_parent_class);
parent_class->set_surface_actor (actor, surface);
}
static void
meta_window_actor_wayland_queue_frame_drawn (MetaWindowActor *actor,
gboolean skip_sync_delay)
{
}
static void
meta_window_actor_wayland_post_init (MetaWindowActor *actor)
{
}
static void
meta_window_actor_wayland_pre_paint (MetaWindowActor *actor)
{
}
static void
meta_window_actor_wayland_post_paint (MetaWindowActor *actor)
{
}
static void
meta_window_actor_wayland_queue_destroy (MetaWindowActor *actor)
{
}
static void static void
meta_window_actor_wayland_class_init (MetaWindowActorWaylandClass *klass) meta_window_actor_wayland_class_init (MetaWindowActorWaylandClass *klass)
{ {
MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass);
window_actor_class->frame_complete = meta_window_actor_wayland_frame_complete;
window_actor_class->set_surface_actor = meta_window_actor_wayland_set_surface_actor;
window_actor_class->queue_frame_drawn = meta_window_actor_wayland_queue_frame_drawn;
window_actor_class->post_init = meta_window_actor_wayland_post_init;
window_actor_class->pre_paint = meta_window_actor_wayland_pre_paint;
window_actor_class->post_paint = meta_window_actor_wayland_post_paint;
window_actor_class->queue_destroy = meta_window_actor_wayland_queue_destroy;
} }
static void static void

View File

@ -20,19 +20,518 @@
* Georges Basile Stavracas Neto <gbsneto@gnome.org> * Georges Basile Stavracas Neto <gbsneto@gnome.org>
*/ */
#include "backends/meta-logical-monitor.h"
#include "compositor/compositor-private.h"
#include "compositor/meta-surface-actor.h"
#include "compositor/meta-window-actor-x11.h" #include "compositor/meta-window-actor-x11.h"
#include "core/window-private.h"
#include "meta/compositor.h"
#include "meta/meta-window-actor.h" #include "meta/meta-window-actor.h"
#include "meta/meta-x11-errors.h"
#include "meta/window.h"
#include "x11/meta-x11-display-private.h"
struct _MetaWindowActorX11 struct _MetaWindowActorX11
{ {
MetaWindowActor parent; MetaWindowActor parent;
/* List of FrameData for recent frames */
GList *frames;
guint send_frame_messages_timer;
int64_t frame_drawn_time;
guint repaint_scheduled_id;
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
* client message for one or more messages in ->frames */
gboolean needs_frame_drawn;
gboolean repaint_scheduled;
}; };
G_DEFINE_TYPE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR) G_DEFINE_TYPE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR)
/* Each time the application updates the sync request counter to a new even value
* value, we queue a frame into the windows list of frames. Once we're painting
* an update "in response" to the window, we fill in frame_counter with the
* Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the
* frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback.
*
* As an exception, if a window is completely obscured, we try to throttle drawning
* to a slower frame rate. In this case, frame_counter stays -1 until
* send_frame_message_timeout() runs, at which point we send both the
* _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages.
*/
typedef struct
{
uint64_t sync_request_serial;
int64_t frame_counter;
int64_t frame_drawn_time;
} FrameData;
static void
frame_data_free (FrameData *frame)
{
g_slice_free (FrameData, frame);
}
static void
surface_repaint_scheduled (MetaSurfaceActor *actor,
gpointer user_data)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (user_data);
actor_x11->repaint_scheduled = TRUE;
}
static void
remove_frame_messages_timer (MetaWindowActorX11 *actor_x11)
{
g_assert (actor_x11->send_frame_messages_timer != 0);
g_source_remove (actor_x11->send_frame_messages_timer);
actor_x11->send_frame_messages_timer = 0;
}
static void
do_send_frame_drawn (MetaWindowActorX11 *actor_x11,
FrameData *frame)
{
MetaWindow *window =
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
MetaDisplay *display = meta_window_get_display (window);
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
XClientMessageEvent ev = { 0, };
frame->frame_drawn_time =
meta_compositor_monotonic_time_to_server_time (display,
g_get_monotonic_time ());
actor_x11->frame_drawn_time = frame->frame_drawn_time;
ev.type = ClientMessage;
ev.window = meta_window_get_xwindow (window);
ev.message_type = display->x11_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;
meta_x11_error_trap_push (display->x11_display);
XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
XFlush (xdisplay);
meta_x11_error_trap_pop (display->x11_display);
}
static void
do_send_frame_timings (MetaWindowActorX11 *actor_x11,
FrameData *frame,
int refresh_interval,
int64_t presentation_time)
{
MetaWindow *window =
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
MetaDisplay *display = meta_window_get_display (window);
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
XClientMessageEvent ev = { 0, };
ev.type = ClientMessage;
ev.window = meta_window_get_xwindow (window);
ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
ev.format = 32;
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
ev.data.l[1] = frame->sync_request_serial >> 32;
if (presentation_time != 0)
{
int64_t presentation_time_server =
meta_compositor_monotonic_time_to_server_time (display,
presentation_time);
int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
if (presentation_time_offset == 0)
presentation_time_offset = 1;
if ((int32_t)presentation_time_offset == presentation_time_offset)
ev.data.l[2] = presentation_time_offset;
}
ev.data.l[3] = refresh_interval;
ev.data.l[4] = 1000 * META_SYNC_DELAY;
meta_x11_error_trap_push (display->x11_display);
XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
XFlush (xdisplay);
meta_x11_error_trap_pop (display->x11_display);
}
static void
send_frame_timings (MetaWindowActorX11 *actor_x11,
FrameData *frame,
ClutterFrameInfo *frame_info,
int64_t presentation_time)
{
float refresh_rate;
int refresh_interval;
refresh_rate = frame_info->refresh_rate;
/* 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 (actor_x11, frame, refresh_interval, presentation_time);
}
static gboolean
send_frame_messages_timeout (gpointer data)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
GList *l;
for (l = actor_x11->frames; l;)
{
GList *l_next = l->next;
FrameData *frame = l->data;
if (frame->frame_counter == -1)
{
do_send_frame_drawn (actor_x11, frame);
do_send_frame_timings (actor_x11, frame, 0, 0);
actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
frame_data_free (frame);
}
l = l_next;
}
actor_x11->needs_frame_drawn = FALSE;
actor_x11->send_frame_messages_timer = 0;
return G_SOURCE_REMOVE;
}
static void
queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
{
MetaWindow *window =
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
MetaDisplay *display = meta_window_get_display (window);
MetaLogicalMonitor *logical_monitor;
int64_t current_time;
float refresh_rate;
int interval, offset;
if (actor_x11->send_frame_messages_timer != 0)
return;
logical_monitor = meta_window_get_main_logical_monitor (window);
if (logical_monitor)
{
GList *monitors = meta_logical_monitor_get_monitors (logical_monitor);
MetaMonitor *monitor;
MetaMonitorMode *mode;
monitor = g_list_first (monitors)->data;
mode = meta_monitor_get_current_mode (monitor);
refresh_rate = meta_monitor_mode_get_refresh_rate (mode);
}
else
{
refresh_rate = 60.0f;
}
current_time =
meta_compositor_monotonic_time_to_server_time (display,
g_get_monotonic_time ());
interval = (int) (1000000 / refresh_rate) * 6;
offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 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.
*/
actor_x11->send_frame_messages_timer =
g_timeout_add_full (META_PRIORITY_REDRAW, offset,
send_frame_messages_timeout,
actor_x11, NULL);
g_source_set_name_by_id (actor_x11->send_frame_messages_timer,
"[mutter] send_frame_messages_timeout");
}
static void
assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
{
MetaWindow *window =
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
MetaCompositor *compositor = window->display->compositor;
ClutterStage *stage = CLUTTER_STAGE (compositor->stage);
GList *l;
/* If the window is obscured, then we're expecting to deal with sending
* frame messages in a timeout, rather than in this paint cycle.
*/
if (actor_x11->send_frame_messages_timer != 0)
return;
for (l = actor_x11->frames; l; l = l->next)
{
FrameData *frame = l->data;
if (frame->frame_counter == -1)
frame->frame_counter = clutter_stage_get_frame_counter (stage);
}
}
static void
meta_window_actor_x11_frame_complete (MetaWindowActor *actor,
ClutterFrameInfo *frame_info,
int64_t presentation_time)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
GList *l;
if (meta_window_actor_is_destroyed (actor))
return;
for (l = actor_x11->frames; l;)
{
GList *l_next = l->next;
FrameData *frame = l->data;
int64_t frame_counter = frame_info->frame_counter;
if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
{
MetaWindow *window =
meta_window_actor_get_meta_window (actor);
if (G_UNLIKELY (frame->frame_drawn_time == 0))
g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
window->desc);
if (G_UNLIKELY (frame->frame_counter < frame_counter))
g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
window->desc, frame->frame_counter);
actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
send_frame_timings (actor_x11, frame, frame_info, presentation_time);
frame_data_free (frame);
}
l = l_next;
}
}
static void
meta_window_actor_x11_set_surface_actor (MetaWindowActor *actor,
MetaSurfaceActor *surface)
{
MetaWindowActorClass *parent_class =
META_WINDOW_ACTOR_CLASS (meta_window_actor_x11_parent_class);
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
MetaSurfaceActor *old_surface;
old_surface = meta_window_actor_get_surface (actor);
if (old_surface)
{
g_signal_handler_disconnect (old_surface,
actor_x11->repaint_scheduled_id);
actor_x11->repaint_scheduled_id = 0;
}
parent_class->set_surface_actor (actor, surface);
if (surface)
{
actor_x11->repaint_scheduled_id =
g_signal_connect (surface, "repaint-scheduled",
G_CALLBACK (surface_repaint_scheduled),
actor_x11);
}
}
static void
meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
gboolean skip_sync_delay)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
MetaWindow *window =
meta_window_actor_get_meta_window (actor);
FrameData *frame;
if (meta_window_actor_is_destroyed (actor))
return;
frame = g_slice_new0 (FrameData);
frame->frame_counter = -1;
frame->sync_request_serial = window->sync_request_serial;
actor_x11->frames = g_list_prepend (actor_x11->frames, frame);
actor_x11->needs_frame_drawn = TRUE;
if (skip_sync_delay)
{
ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (actor_x11));
clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
}
if (!actor_x11->repaint_scheduled)
{
MetaSurfaceActor *surface;
gboolean is_obscured;
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
if (surface)
is_obscured = meta_surface_actor_is_obscured (surface);
else
is_obscured = FALSE;
/* A frame was marked by the client without actually doing any
* 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. If the window
* is completely obscured we fire off the send_frame_messages timeout.
*/
if (is_obscured)
{
queue_send_frame_messages_timeout (actor_x11);
}
else if (surface)
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (surface), &clip);
actor_x11->repaint_scheduled = TRUE;
}
}
}
static void
meta_window_actor_x11_post_init (MetaWindowActor *actor)
{
MetaWindow *window = meta_window_actor_get_meta_window (actor);
/* If a window doesn't start off with updates frozen, we should
* we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
*/
if (window->extended_sync_request_counter &&
!meta_window_updates_are_frozen (window))
meta_window_actor_queue_frame_drawn (actor, FALSE);
}
static void
meta_window_actor_x11_pre_paint (MetaWindowActor *actor)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
assign_frame_counter_to_frames (actor_x11);
}
static void
meta_window_actor_x11_post_paint (MetaWindowActor *actor)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
actor_x11->repaint_scheduled = FALSE;
if (meta_window_actor_is_destroyed (actor))
return;
/* If the window had damage, but wasn't actually redrawn because
* it is obscured, we should wait until timer expiration before
* sending _NET_WM_FRAME_* messages.
*/
if (actor_x11->send_frame_messages_timer == 0 &&
actor_x11->needs_frame_drawn)
{
GList *l;
for (l = actor_x11->frames; l; l = l->next)
{
FrameData *frame = l->data;
if (frame->frame_drawn_time == 0)
do_send_frame_drawn (actor_x11, frame);
}
actor_x11->needs_frame_drawn = FALSE;
}
}
static void
meta_window_actor_x11_queue_destroy (MetaWindowActor *actor)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
if (actor_x11->send_frame_messages_timer != 0)
remove_frame_messages_timer (actor_x11);
}
static void
meta_window_actor_x11_paint (ClutterActor *actor)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
/* 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 (actor_x11->send_frame_messages_timer != 0)
{
remove_frame_messages_timer (actor_x11);
assign_frame_counter_to_frames (actor_x11);
}
CLUTTER_ACTOR_CLASS (meta_window_actor_x11_parent_class)->paint (actor);
}
static void
meta_window_actor_x11_dispose (GObject *object)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
if (actor_x11->send_frame_messages_timer != 0)
remove_frame_messages_timer (actor_x11);
G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->dispose (object);
}
static void
meta_window_actor_x11_finalize (GObject *object)
{
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
g_list_free_full (actor_x11->frames, (GDestroyNotify) frame_data_free);
G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->finalize (object);
}
static void static void
meta_window_actor_x11_class_init (MetaWindowActorX11Class *klass) meta_window_actor_x11_class_init (MetaWindowActorX11Class *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass);
window_actor_class->frame_complete = meta_window_actor_x11_frame_complete;
window_actor_class->set_surface_actor = meta_window_actor_x11_set_surface_actor;
window_actor_class->queue_frame_drawn = meta_window_actor_x11_queue_frame_drawn;
window_actor_class->post_init = meta_window_actor_x11_post_init;
window_actor_class->pre_paint = meta_window_actor_x11_pre_paint;
window_actor_class->post_paint = meta_window_actor_x11_post_paint;
window_actor_class->queue_destroy = meta_window_actor_x11_queue_destroy;
actor_class->paint = meta_window_actor_x11_paint;
object_class->dispose = meta_window_actor_x11_dispose;
object_class->finalize = meta_window_actor_x11_finalize;
} }
static void static void

View File

@ -12,9 +12,6 @@
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include "backends/meta-backend-private.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor-manager-private.h"
#include "backends/meta-screen-cast-window.h" #include "backends/meta-screen-cast-window.h"
#include "clutter/clutter-mutter.h" #include "clutter/clutter-mutter.h"
#include "clutter/x11/clutter-x11.h" #include "clutter/x11/clutter-x11.h"
@ -34,9 +31,7 @@
#include "meta/meta-enum-types.h" #include "meta/meta-enum-types.h"
#include "meta/meta-shadow-factory.h" #include "meta/meta-shadow-factory.h"
#include "meta/meta-shaped-texture.h" #include "meta/meta-shaped-texture.h"
#include "meta/meta-x11-errors.h"
#include "meta/window.h" #include "meta/window.h"
#include "x11/meta-x11-display-private.h"
#ifdef HAVE_WAYLAND #ifdef HAVE_WAYLAND
#include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-surface-actor-wayland.h"
@ -83,10 +78,6 @@ typedef struct _MetaWindowActorPrivate
MetaShadowMode shadow_mode; MetaShadowMode shadow_mode;
guint send_frame_messages_timer;
gint64 frame_drawn_time;
guint repaint_scheduled_id;
guint size_changed_id; guint size_changed_id;
/* /*
@ -100,18 +91,11 @@ typedef struct _MetaWindowActorPrivate
gint map_in_progress; gint map_in_progress;
gint destroy_in_progress; gint destroy_in_progress;
/* List of FrameData for recent frames */
GList *frames;
guint freeze_count; guint freeze_count;
guint visible : 1; guint visible : 1;
guint disposed : 1; guint disposed : 1;
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
* client message for one or more messages in ->frames */
guint needs_frame_drawn : 1;
guint repaint_scheduled : 1;
guint needs_reshape : 1; guint needs_reshape : 1;
guint recompute_focused_shadow : 1; guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1; guint recompute_unfocused_shadow : 1;
@ -122,26 +106,6 @@ typedef struct _MetaWindowActorPrivate
guint first_frame_state : 2; /* FirstFrameState */ guint first_frame_state : 2; /* FirstFrameState */
} MetaWindowActorPrivate; } MetaWindowActorPrivate;
typedef struct _FrameData FrameData;
/* Each time the application updates the sync request counter to a new even value
* value, we queue a frame into the windows list of frames. Once we're painting
* an update "in response" to the window, we fill in frame_counter with the
* Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the
* frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback.
*
* As an exception, if a window is completely obscured, we try to throttle drawning
* to a slower frame rate. In this case, frame_counter stays -1 until
* send_frame_message_timeout() runs, at which point we send both the
* _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages.
*/
struct _FrameData
{
guint64 sync_request_serial;
int64_t frame_counter;
gint64 frame_drawn_time;
};
enum enum
{ {
FIRST_FRAME, FIRST_FRAME,
@ -160,7 +124,6 @@ enum
}; };
static void meta_window_actor_dispose (GObject *object); static void meta_window_actor_dispose (GObject *object);
static void meta_window_actor_finalize (GObject *object);
static void meta_window_actor_constructed (GObject *object); static void meta_window_actor_constructed (GObject *object);
static void meta_window_actor_set_property (GObject *object, static void meta_window_actor_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -175,7 +138,8 @@ static void meta_window_actor_paint (ClutterActor *actor);
static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor, static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume); ClutterPaintVolume *volume);
static void set_surface (MetaWindowActor *actor,
MetaSurfaceActor *surface);
static gboolean meta_window_actor_has_shadow (MetaWindowActor *self); static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
@ -183,12 +147,6 @@ static void meta_window_actor_handle_updates (MetaWindowActor *self);
static void check_needs_reshape (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);
static void cullable_iface_init (MetaCullableInterface *iface); static void cullable_iface_init (MetaCullableInterface *iface);
static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface); static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface);
@ -199,9 +157,10 @@ G_DEFINE_TYPE_WITH_CODE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR,
G_IMPLEMENT_INTERFACE (META_TYPE_SCREEN_CAST_WINDOW, screen_cast_window_iface_init)); G_IMPLEMENT_INTERFACE (META_TYPE_SCREEN_CAST_WINDOW, screen_cast_window_iface_init));
static void static void
frame_data_free (FrameData *frame) meta_window_actor_real_set_surface_actor (MetaWindowActor *actor,
MetaSurfaceActor *surface)
{ {
g_slice_free (FrameData, frame); set_surface (actor, surface);
} }
static void static void
@ -212,7 +171,6 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
GParamSpec *pspec; GParamSpec *pspec;
object_class->dispose = meta_window_actor_dispose; object_class->dispose = meta_window_actor_dispose;
object_class->finalize = meta_window_actor_finalize;
object_class->set_property = meta_window_actor_set_property; object_class->set_property = meta_window_actor_set_property;
object_class->get_property = meta_window_actor_get_property; object_class->get_property = meta_window_actor_get_property;
object_class->constructed = meta_window_actor_constructed; object_class->constructed = meta_window_actor_constructed;
@ -220,6 +178,8 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
actor_class->paint = meta_window_actor_paint; actor_class->paint = meta_window_actor_paint;
actor_class->get_paint_volume = meta_window_actor_get_paint_volume; actor_class->get_paint_volume = meta_window_actor_get_paint_volume;
klass->set_surface_actor = meta_window_actor_real_set_surface_actor;
/** /**
* MetaWindowActor::first-frame: * MetaWindowActor::first-frame:
* @actor: the #MetaWindowActor instance * @actor: the #MetaWindowActor instance
@ -314,17 +274,6 @@ surface_size_changed (MetaSurfaceActor *actor,
meta_window_actor_update_shape (self); meta_window_actor_update_shape (self);
} }
static void
surface_repaint_scheduled (MetaSurfaceActor *actor,
gpointer user_data)
{
MetaWindowActor *self = META_WINDOW_ACTOR (user_data);
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
priv->repaint_scheduled = TRUE;
}
static gboolean static gboolean
is_argb32 (MetaWindowActor *self) is_argb32 (MetaWindowActor *self)
{ {
@ -421,9 +370,7 @@ set_surface (MetaWindowActor *self,
if (priv->surface) if (priv->surface)
{ {
g_signal_handler_disconnect (priv->surface, priv->repaint_scheduled_id);
g_signal_handler_disconnect (priv->surface, priv->size_changed_id); g_signal_handler_disconnect (priv->surface, priv->size_changed_id);
priv->repaint_scheduled_id = 0;
clutter_actor_remove_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface)); clutter_actor_remove_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface));
g_object_unref (priv->surface); g_object_unref (priv->surface);
} }
@ -433,8 +380,6 @@ set_surface (MetaWindowActor *self,
if (priv->surface) if (priv->surface)
{ {
g_object_ref_sink (priv->surface); g_object_ref_sink (priv->surface);
priv->repaint_scheduled_id = g_signal_connect (priv->surface, "repaint-scheduled",
G_CALLBACK (surface_repaint_scheduled), self);
priv->size_changed_id = g_signal_connect (priv->surface, "size-changed", priv->size_changed_id = g_signal_connect (priv->surface, "size-changed",
G_CALLBACK (surface_size_changed), self); G_CALLBACK (surface_size_changed), self);
clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface)); clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface));
@ -501,12 +446,6 @@ meta_window_actor_dispose (GObject *object)
priv->disposed = TRUE; priv->disposed = TRUE;
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->shape_region, cairo_region_destroy); g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
@ -524,18 +463,6 @@ meta_window_actor_dispose (GObject *object)
G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object); G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object);
} }
static void
meta_window_actor_finalize (GObject *object)
{
MetaWindowActor *self = META_WINDOW_ACTOR (object);
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
g_list_free_full (priv->frames, (GDestroyNotify) frame_data_free);
G_OBJECT_CLASS (meta_window_actor_parent_class)->finalize (object);
}
static void static void
meta_window_actor_set_property (GObject *object, meta_window_actor_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -706,30 +633,6 @@ clip_shadow_under_window (MetaWindowActor *self)
return is_non_opaque (self) && priv->window->frame; return is_non_opaque (self) && priv->window->frame;
} }
static void
assign_frame_counter_to_frames (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
MetaCompositor *compositor = priv->compositor;
ClutterStage *stage = CLUTTER_STAGE (compositor->stage);
GList *l;
/* If the window is obscured, then we're expecting to deal with sending
* frame messages in a timeout, rather than in this paint cycle.
*/
if (priv->send_frame_messages_timer != 0)
return;
for (l = priv->frames; l; l = l->next)
{
FrameData *frame = l->data;
if (frame->frame_counter == -1)
frame->frame_counter = clutter_stage_get_frame_counter (stage);
}
}
static void static void
meta_window_actor_paint (ClutterActor *actor) meta_window_actor_paint (ClutterActor *actor)
{ {
@ -742,18 +645,6 @@ meta_window_actor_paint (ClutterActor *actor)
shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow; 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;
assign_frame_counter_to_frames (self);
}
if (shadow != NULL) if (shadow != NULL)
{ {
MetaShadowParams params; MetaShadowParams params;
@ -968,140 +859,12 @@ meta_window_actor_is_destroyed (MetaWindowActor *self)
return priv->disposed || priv->needs_destroy; return priv->disposed || priv->needs_destroy;
} }
static gboolean
send_frame_messages_timeout (gpointer data)
{
MetaWindowActor *self = (MetaWindowActor *) data;
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
GList *l;
for (l = priv->frames; l;)
{
GList *l_next = l->next;
FrameData *frame = l->data;
if (frame->frame_counter == -1)
{
do_send_frame_drawn (self, frame);
do_send_frame_timings (self, frame, 0, 0);
priv->frames = g_list_delete_link (priv->frames, l);
frame_data_free (frame);
}
l = l_next;
}
priv->needs_frame_drawn = FALSE;
priv->send_frame_messages_timer = 0;
return FALSE;
}
static void
queue_send_frame_messages_timeout (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
MetaWindow *window = priv->window;
MetaDisplay *display = meta_window_get_display (priv->window);
MetaLogicalMonitor *logical_monitor;
int64_t current_time;
float refresh_rate;
int interval, offset;
if (priv->send_frame_messages_timer != 0)
return;
logical_monitor = meta_window_get_main_logical_monitor (window);
if (logical_monitor)
{
GList *monitors = meta_logical_monitor_get_monitors (logical_monitor);
MetaMonitor *monitor;
MetaMonitorMode *mode;
monitor = g_list_first (monitors)->data;
mode = meta_monitor_get_current_mode (monitor);
refresh_rate = meta_monitor_mode_get_refresh_rate (mode);
}
else
{
refresh_rate = 60.0f;
}
current_time =
meta_compositor_monotonic_time_to_server_time (display,
g_get_monotonic_time ());
interval = (int)(1000000 / refresh_rate) * 6;
offset = MAX (0, priv->frame_drawn_time + interval - current_time) / 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);
g_source_set_name_by_id (priv->send_frame_messages_timer, "[mutter] send_frame_messages_timeout");
}
void void
meta_window_actor_queue_frame_drawn (MetaWindowActor *self, meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
gboolean no_delay_frame) gboolean no_delay_frame)
{ {
MetaWindowActorPrivate *priv = META_WINDOW_ACTOR_GET_CLASS (self)->queue_frame_drawn (self,
meta_window_actor_get_instance_private (self); no_delay_frame);
FrameData *frame;
if (meta_window_actor_is_destroyed (self))
return;
frame = g_slice_new0 (FrameData);
frame->frame_counter = -1;
priv->needs_frame_drawn = TRUE;
frame->sync_request_serial = priv->window->sync_request_serial;
priv->frames = g_list_prepend (priv->frames, frame);
if (no_delay_frame)
{
ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self));
clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
}
if (!priv->repaint_scheduled)
{
gboolean is_obscured;
if (priv->surface)
is_obscured = meta_surface_actor_is_obscured (priv->surface);
else
is_obscured = FALSE;
/* A frame was marked by the client without actually doing any
* 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. If the window
* is completely obscured we fire off the send_frame_messages timeout.
*/
if (is_obscured)
{
queue_send_frame_messages_timeout (self);
}
else
{
if (priv->surface)
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (priv->surface), &clip);
priv->repaint_scheduled = TRUE;
}
}
}
} }
gboolean gboolean
@ -1317,11 +1080,7 @@ meta_window_actor_queue_destroy (MetaWindowActor *self)
meta_window_set_compositor_private (window, NULL); meta_window_set_compositor_private (window, NULL);
if (priv->send_frame_messages_timer != 0) META_WINDOW_ACTOR_GET_CLASS (self)->queue_destroy (self);
{
g_source_remove (priv->send_frame_messages_timer);
priv->send_frame_messages_timer = 0;
}
if (window_type == META_WINDOW_DROPDOWN_MENU || if (window_type == META_WINDOW_DROPDOWN_MENU ||
window_type == META_WINDOW_POPUP_MENU || window_type == META_WINDOW_POPUP_MENU ||
@ -1507,11 +1266,7 @@ meta_window_actor_new (MetaWindow *window)
else else
priv->first_frame_state = DRAWING_FIRST_FRAME; priv->first_frame_state = DRAWING_FIRST_FRAME;
/* If a window doesn't start off with updates frozen, we should META_WINDOW_ACTOR_GET_CLASS (self)->post_init (self);
* we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
*/
if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
meta_window_actor_queue_frame_drawn (self, FALSE);
meta_window_actor_sync_actor_geometry (self, priv->window->placed); meta_window_actor_sync_actor_geometry (self, priv->window->placed);
@ -2043,36 +1798,7 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
meta_window_actor_handle_updates (self); meta_window_actor_handle_updates (self);
assign_frame_counter_to_frames (self); META_WINDOW_ACTOR_GET_CLASS (self)->pre_paint (self);
}
static void
do_send_frame_drawn (MetaWindowActor *self, FrameData *frame)
{
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
MetaDisplay *display = meta_window_get_display (priv->window);
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
XClientMessageEvent ev = { 0, };
frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
g_get_monotonic_time ());
priv->frame_drawn_time = frame->frame_drawn_time;
ev.type = ClientMessage;
ev.window = meta_window_get_xwindow (priv->window);
ev.message_type = display->x11_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;
meta_x11_error_trap_push (display->x11_display);
XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
XFlush (xdisplay);
meta_x11_error_trap_pop (display->x11_display);
} }
void void
@ -2081,31 +1807,11 @@ meta_window_actor_post_paint (MetaWindowActor *self)
MetaWindowActorPrivate *priv = MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self); meta_window_actor_get_instance_private (self);
priv->repaint_scheduled = FALSE; META_WINDOW_ACTOR_GET_CLASS (self)->post_paint (self);
if (meta_window_actor_is_destroyed (self)) if (meta_window_actor_is_destroyed (self))
return; return;
/* If the window had damage, but wasn't actually redrawn because
* it is obscured, we should wait until timer expiration before
* sending _NET_WM_FRAME_* messages.
*/
if (priv->send_frame_messages_timer == 0 &&
priv->needs_frame_drawn)
{
GList *l;
for (l = priv->frames; l; l = l->next)
{
FrameData *frame = l->data;
if (frame->frame_drawn_time == 0)
do_send_frame_drawn (self, frame);
}
priv->needs_frame_drawn = FALSE;
}
if (priv->first_frame_state == DRAWING_FIRST_FRAME) if (priv->first_frame_state == DRAWING_FIRST_FRAME)
{ {
priv->first_frame_state = EMITTED_FIRST_FRAME; priv->first_frame_state = EMITTED_FIRST_FRAME;
@ -2113,100 +1819,14 @@ meta_window_actor_post_paint (MetaWindowActor *self)
} }
} }
static void
do_send_frame_timings (MetaWindowActor *self,
FrameData *frame,
gint refresh_interval,
gint64 presentation_time)
{
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
MetaDisplay *display = meta_window_get_display (priv->window);
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
XClientMessageEvent ev = { 0, };
ev.type = ClientMessage;
ev.window = meta_window_get_xwindow (priv->window);
ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
ev.format = 32;
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[1] = frame->sync_request_serial >> 32;
if (presentation_time != 0)
{
gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display,
presentation_time);
gint64 presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
if (presentation_time_offset == 0)
presentation_time_offset = 1;
if ((gint32)presentation_time_offset == presentation_time_offset)
ev.data.l[2] = presentation_time_offset;
}
ev.data.l[3] = refresh_interval;
ev.data.l[4] = 1000 * META_SYNC_DELAY;
meta_x11_error_trap_push (display->x11_display);
XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
XFlush (xdisplay);
meta_x11_error_trap_pop (display->x11_display);
}
static void
send_frame_timings (MetaWindowActor *self,
FrameData *frame,
ClutterFrameInfo *frame_info,
gint64 presentation_time)
{
float refresh_rate;
int refresh_interval;
refresh_rate = frame_info->refresh_rate;
/* 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 void
meta_window_actor_frame_complete (MetaWindowActor *self, meta_window_actor_frame_complete (MetaWindowActor *self,
ClutterFrameInfo *frame_info, ClutterFrameInfo *frame_info,
gint64 presentation_time) gint64 presentation_time)
{ {
MetaWindowActorPrivate *priv = META_WINDOW_ACTOR_GET_CLASS (self)->frame_complete (self,
meta_window_actor_get_instance_private (self); frame_info,
GList *l; presentation_time);
if (meta_window_actor_is_destroyed (self))
return;
for (l = priv->frames; l;)
{
GList *l_next = l->next;
FrameData *frame = l->data;
gint64 frame_counter = frame_info->frame_counter;
if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
{
if (G_UNLIKELY (frame->frame_drawn_time == 0))
g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
priv->window->desc);
if (G_UNLIKELY (frame->frame_counter < frame_counter))
g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
priv->window->desc, frame->frame_counter);
priv->frames = g_list_delete_link (priv->frames, l);
send_frame_timings (self, frame, frame_info, presentation_time);
frame_data_free (frame);
}
l = l_next;
}
} }
void void