compositor: Move frame drawn x11 management to MetaSyncCounter
This is part of the same MetaSyncCounter mechanism, so move it together on one place. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2175>
This commit is contained in:
parent
740b8e8cce
commit
b891f8a52c
@ -41,6 +41,7 @@
|
|||||||
#include "meta/meta-x11-errors.h"
|
#include "meta/meta-x11-errors.h"
|
||||||
#include "meta/window.h"
|
#include "meta/window.h"
|
||||||
#include "x11/window-x11.h"
|
#include "x11/window-x11.h"
|
||||||
|
#include "x11/meta-sync-counter.h"
|
||||||
#include "x11/meta-x11-display-private.h"
|
#include "x11/meta-x11-display-private.h"
|
||||||
#include "x11/window-x11.h"
|
#include "x11/window-x11.h"
|
||||||
|
|
||||||
@ -54,19 +55,12 @@ struct _MetaWindowActorX11
|
|||||||
{
|
{
|
||||||
MetaWindowActor parent;
|
MetaWindowActor parent;
|
||||||
|
|
||||||
/* List of FrameData for recent frames */
|
|
||||||
GList *frames;
|
|
||||||
|
|
||||||
guint send_frame_messages_timer;
|
guint send_frame_messages_timer;
|
||||||
int64_t frame_drawn_time;
|
|
||||||
gboolean pending_schedule_update_now;
|
gboolean pending_schedule_update_now;
|
||||||
|
|
||||||
gulong repaint_scheduled_id;
|
gulong repaint_scheduled_id;
|
||||||
gulong size_changed_id;
|
gulong size_changed_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;
|
gboolean repaint_scheduled;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -111,30 +105,6 @@ static void cullable_iface_init (MetaCullableInterface *iface);
|
|||||||
G_DEFINE_TYPE_WITH_CODE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR,
|
G_DEFINE_TYPE_WITH_CODE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR,
|
||||||
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init))
|
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init))
|
||||||
|
|
||||||
/* 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_free (frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
surface_repaint_scheduled (MetaSurfaceActor *actor,
|
surface_repaint_scheduled (MetaSurfaceActor *actor,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
@ -152,165 +122,16 @@ remove_frame_messages_timer (MetaWindowActorX11 *actor_x11)
|
|||||||
g_clear_handle_id (&actor_x11->send_frame_messages_timer, g_source_remove);
|
g_clear_handle_id (&actor_x11->send_frame_messages_timer, g_source_remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
int64_t now_us;
|
|
||||||
|
|
||||||
XClientMessageEvent ev = { 0, };
|
|
||||||
|
|
||||||
COGL_TRACE_BEGIN (MetaWindowActorX11FrameDrawn,
|
|
||||||
"X11: Send _NET_WM_FRAME_DRAWN");
|
|
||||||
|
|
||||||
now_us = g_get_monotonic_time ();
|
|
||||||
frame->frame_drawn_time =
|
|
||||||
meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
|
|
||||||
now_us);
|
|
||||||
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);
|
|
||||||
|
|
||||||
#ifdef COGL_HAS_TRACING
|
|
||||||
if (G_UNLIKELY (cogl_is_tracing_enabled ()))
|
|
||||||
{
|
|
||||||
g_autofree char *description = NULL;
|
|
||||||
|
|
||||||
description = g_strdup_printf ("frame drawn time: %" G_GINT64_FORMAT ", "
|
|
||||||
"sync request serial: %" G_GINT64_FORMAT,
|
|
||||||
frame->frame_drawn_time,
|
|
||||||
frame->sync_request_serial);
|
|
||||||
COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameDrawn,
|
|
||||||
description);
|
|
||||||
COGL_TRACE_END (MetaWindowActorX11FrameDrawn);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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, };
|
|
||||||
|
|
||||||
COGL_TRACE_BEGIN (MetaWindowActorX11FrameTimings,
|
|
||||||
"X11: Send _NET_WM_FRAME_TIMINGS");
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
MetaCompositor *compositor = display->compositor;
|
|
||||||
int64_t presentation_time_server;
|
|
||||||
|
|
||||||
presentation_time_server =
|
|
||||||
meta_compositor_monotonic_to_high_res_xserver_time (compositor,
|
|
||||||
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);
|
|
||||||
|
|
||||||
#ifdef COGL_HAS_TRACING
|
|
||||||
if (G_UNLIKELY (cogl_is_tracing_enabled ()))
|
|
||||||
{
|
|
||||||
g_autofree char *description = NULL;
|
|
||||||
|
|
||||||
description =
|
|
||||||
g_strdup_printf ("refresh interval: %d, "
|
|
||||||
"presentation time: %" G_GINT64_FORMAT ", "
|
|
||||||
"sync request serial: %" G_GINT64_FORMAT,
|
|
||||||
refresh_interval,
|
|
||||||
frame->sync_request_serial,
|
|
||||||
presentation_time);
|
|
||||||
COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameTimings, description);
|
|
||||||
COGL_TRACE_END (MetaWindowActorX11FrameTimings);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
static gboolean
|
||||||
send_frame_messages_timeout (gpointer data)
|
send_frame_messages_timeout (gpointer data)
|
||||||
{
|
{
|
||||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
|
||||||
GList *l;
|
MetaWindow *window =
|
||||||
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
||||||
|
MetaSyncCounter *sync_counter;
|
||||||
|
|
||||||
for (l = actor_x11->frames; l;)
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||||
{
|
meta_sync_counter_finish_incomplete (sync_counter);
|
||||||
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;
|
actor_x11->send_frame_messages_timer = 0;
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
@ -323,6 +144,7 @@ queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
|
|||||||
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
||||||
MetaDisplay *display = meta_window_get_display (window);
|
MetaDisplay *display = meta_window_get_display (window);
|
||||||
MetaLogicalMonitor *logical_monitor;
|
MetaLogicalMonitor *logical_monitor;
|
||||||
|
MetaSyncCounter *sync_counter;
|
||||||
int64_t now_us;
|
int64_t now_us;
|
||||||
int64_t current_time;
|
int64_t current_time;
|
||||||
float refresh_rate;
|
float refresh_rate;
|
||||||
@ -353,7 +175,8 @@ queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
|
|||||||
meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
|
meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
|
||||||
now_us);
|
now_us);
|
||||||
interval = (int) (1000000 / refresh_rate) * 6;
|
interval = (int) (1000000 / refresh_rate) * 6;
|
||||||
offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 1000;
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||||
|
offset = MAX (0, sync_counter->frame_drawn_time + interval - current_time) / 1000;
|
||||||
|
|
||||||
/* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
|
/* 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
|
* so the timer will run *after* the clutter frame handling, if a frame is ready
|
||||||
@ -374,7 +197,7 @@ assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
|
|||||||
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
||||||
MetaCompositor *compositor = window->display->compositor;
|
MetaCompositor *compositor = window->display->compositor;
|
||||||
ClutterStage *stage = meta_compositor_get_stage (compositor);
|
ClutterStage *stage = meta_compositor_get_stage (compositor);
|
||||||
GList *l;
|
MetaSyncCounter *sync_counter;
|
||||||
|
|
||||||
/* If the window is obscured, then we're expecting to deal with sending
|
/* If the window is obscured, then we're expecting to deal with sending
|
||||||
* frame messages in a timeout, rather than in this paint cycle.
|
* frame messages in a timeout, rather than in this paint cycle.
|
||||||
@ -382,13 +205,9 @@ assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
|
|||||||
if (actor_x11->send_frame_messages_timer != 0)
|
if (actor_x11->send_frame_messages_timer != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (l = actor_x11->frames; l; l = l->next)
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||||
{
|
meta_sync_counter_assign_counter_to_frames (sync_counter,
|
||||||
FrameData *frame = l->data;
|
clutter_stage_get_frame_counter (stage));
|
||||||
|
|
||||||
if (frame->frame_counter == -1)
|
|
||||||
frame->frame_counter = clutter_stage_get_frame_counter (stage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -396,37 +215,16 @@ meta_window_actor_x11_frame_complete (MetaWindowActor *actor,
|
|||||||
ClutterFrameInfo *frame_info,
|
ClutterFrameInfo *frame_info,
|
||||||
int64_t presentation_time)
|
int64_t presentation_time)
|
||||||
{
|
{
|
||||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
MetaWindow *window = meta_window_actor_get_meta_window (actor);
|
||||||
GList *l;
|
MetaSyncCounter *sync_counter;
|
||||||
|
|
||||||
if (meta_window_actor_is_destroyed (actor))
|
if (meta_window_actor_is_destroyed (actor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (l = actor_x11->frames; l;)
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||||
{
|
meta_sync_counter_complete_frame (sync_counter,
|
||||||
GList *l_next = l->next;
|
frame_info,
|
||||||
FrameData *frame = l->data;
|
presentation_time);
|
||||||
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_debug ("%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 MetaSurfaceActor *
|
static MetaSurfaceActor *
|
||||||
@ -499,23 +297,10 @@ meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
|
|||||||
gboolean skip_sync_delay)
|
gboolean skip_sync_delay)
|
||||||
{
|
{
|
||||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
||||||
MetaWindow *window =
|
|
||||||
meta_window_actor_get_meta_window (actor);
|
|
||||||
MetaSyncCounter *sync_counter =
|
|
||||||
meta_window_x11_get_sync_counter (window);
|
|
||||||
FrameData *frame;
|
|
||||||
|
|
||||||
if (meta_window_actor_is_destroyed (actor))
|
if (meta_window_actor_is_destroyed (actor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
frame = g_new0 (FrameData, 1);
|
|
||||||
frame->frame_counter = -1;
|
|
||||||
frame->sync_request_serial = sync_counter->sync_request_serial;
|
|
||||||
|
|
||||||
actor_x11->frames = g_list_prepend (actor_x11->frames, frame);
|
|
||||||
|
|
||||||
actor_x11->needs_frame_drawn = TRUE;
|
|
||||||
|
|
||||||
if (skip_sync_delay)
|
if (skip_sync_delay)
|
||||||
{
|
{
|
||||||
ClutterFrameClock *frame_clock;
|
ClutterFrameClock *frame_clock;
|
||||||
@ -1404,6 +1189,7 @@ meta_window_actor_x11_after_paint (MetaWindowActor *actor,
|
|||||||
ClutterStageView *stage_view)
|
ClutterStageView *stage_view)
|
||||||
{
|
{
|
||||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
||||||
|
MetaSyncCounter *sync_counter;
|
||||||
MetaWindow *window;
|
MetaWindow *window;
|
||||||
|
|
||||||
actor_x11->repaint_scheduled = FALSE;
|
actor_x11->repaint_scheduled = FALSE;
|
||||||
@ -1411,28 +1197,19 @@ meta_window_actor_x11_after_paint (MetaWindowActor *actor,
|
|||||||
if (meta_window_actor_is_destroyed (actor))
|
if (meta_window_actor_is_destroyed (actor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
window = meta_window_actor_get_meta_window (actor);
|
||||||
|
|
||||||
/* If the window had damage, but wasn't actually redrawn because
|
/* If the window had damage, but wasn't actually redrawn because
|
||||||
* it is obscured, we should wait until timer expiration before
|
* it is obscured, we should wait until timer expiration before
|
||||||
* sending _NET_WM_FRAME_* messages.
|
* sending _NET_WM_FRAME_* messages.
|
||||||
*/
|
*/
|
||||||
if (actor_x11->send_frame_messages_timer == 0 &&
|
if (actor_x11->send_frame_messages_timer == 0)
|
||||||
actor_x11->needs_frame_drawn)
|
|
||||||
{
|
{
|
||||||
GList *l;
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||||
|
meta_sync_counter_send_frame_drawn (sync_counter);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is for Xwayland, and a no-op on plain Xorg */
|
/* This is for Xwayland, and a no-op on plain Xorg */
|
||||||
window = meta_window_actor_get_meta_window (actor);
|
|
||||||
if (meta_window_x11_should_thaw_after_paint (window))
|
if (meta_window_x11_should_thaw_after_paint (window))
|
||||||
{
|
{
|
||||||
meta_window_x11_thaw_commits (window);
|
meta_window_x11_thaw_commits (window);
|
||||||
@ -1631,7 +1408,10 @@ meta_window_actor_x11_constructed (GObject *object)
|
|||||||
*/
|
*/
|
||||||
if (sync_counter->extended_sync_request_counter &&
|
if (sync_counter->extended_sync_request_counter &&
|
||||||
!meta_window_updates_are_frozen (window))
|
!meta_window_updates_are_frozen (window))
|
||||||
meta_window_actor_queue_frame_drawn (actor, FALSE);
|
{
|
||||||
|
meta_sync_counter_queue_frame_drawn (sync_counter);
|
||||||
|
meta_window_actor_queue_frame_drawn (actor, FALSE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1702,10 +1482,6 @@ meta_window_actor_x11_dispose (GObject *object)
|
|||||||
static void
|
static void
|
||||||
meta_window_actor_x11_finalize (GObject *object)
|
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);
|
G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,10 +21,29 @@
|
|||||||
|
|
||||||
#include "meta-sync-counter.h"
|
#include "meta-sync-counter.h"
|
||||||
|
|
||||||
|
#include "compositor/compositor-private.h"
|
||||||
#include "core/window-private.h"
|
#include "core/window-private.h"
|
||||||
#include "meta/meta-x11-errors.h"
|
#include "meta/meta-x11-errors.h"
|
||||||
#include "x11/meta-x11-display-private.h"
|
#include "x11/meta-x11-display-private.h"
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
void
|
void
|
||||||
meta_sync_counter_init (MetaSyncCounter *sync_counter,
|
meta_sync_counter_init (MetaSyncCounter *sync_counter,
|
||||||
MetaWindow *window,
|
MetaWindow *window,
|
||||||
@ -39,6 +58,7 @@ meta_sync_counter_clear (MetaSyncCounter *sync_counter)
|
|||||||
{
|
{
|
||||||
g_clear_handle_id (&sync_counter->sync_request_timeout_id, g_source_remove);
|
g_clear_handle_id (&sync_counter->sync_request_timeout_id, g_source_remove);
|
||||||
meta_sync_counter_destroy_sync_alarm (sync_counter);
|
meta_sync_counter_destroy_sync_alarm (sync_counter);
|
||||||
|
g_clear_list (&sync_counter->frames, g_free);
|
||||||
sync_counter->window = NULL;
|
sync_counter->window = NULL;
|
||||||
sync_counter->xwindow = None;
|
sync_counter->xwindow = None;
|
||||||
}
|
}
|
||||||
@ -310,8 +330,11 @@ meta_sync_counter_update (MetaSyncCounter *sync_counter,
|
|||||||
sync_counter->disabled = FALSE;
|
sync_counter->disabled = FALSE;
|
||||||
|
|
||||||
if (needs_frame_drawn)
|
if (needs_frame_drawn)
|
||||||
meta_compositor_queue_frame_drawn (window->display->compositor, window,
|
{
|
||||||
no_delay_frame);
|
meta_sync_counter_queue_frame_drawn (sync_counter);
|
||||||
|
meta_compositor_queue_frame_drawn (window->display->compositor, window,
|
||||||
|
no_delay_frame);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef COGL_HAS_TRACING
|
#ifdef COGL_HAS_TRACING
|
||||||
if (G_UNLIKELY (cogl_is_tracing_enabled ()))
|
if (G_UNLIKELY (cogl_is_tracing_enabled ()))
|
||||||
@ -347,3 +370,241 @@ meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter)
|
|||||||
{
|
{
|
||||||
return sync_counter->sync_request_timeout_id != 0;
|
return sync_counter->sync_request_timeout_id != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_send_frame_drawn (MetaSyncCounter *sync_counter,
|
||||||
|
FrameData *frame)
|
||||||
|
{
|
||||||
|
MetaWindow *window = sync_counter->window;
|
||||||
|
MetaDisplay *display = meta_window_get_display (window);
|
||||||
|
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
|
||||||
|
int64_t now_us;
|
||||||
|
XClientMessageEvent ev = { 0, };
|
||||||
|
|
||||||
|
COGL_TRACE_BEGIN (MetaWindowActorX11FrameDrawn,
|
||||||
|
"X11: Send _NET_WM_FRAME_DRAWN");
|
||||||
|
|
||||||
|
now_us = g_get_monotonic_time ();
|
||||||
|
frame->frame_drawn_time =
|
||||||
|
meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
|
||||||
|
now_us);
|
||||||
|
sync_counter->frame_drawn_time = frame->frame_drawn_time;
|
||||||
|
|
||||||
|
ev.type = ClientMessage;
|
||||||
|
ev.window = sync_counter->xwindow;
|
||||||
|
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);
|
||||||
|
|
||||||
|
#ifdef COGL_HAS_TRACING
|
||||||
|
if (G_UNLIKELY (cogl_is_tracing_enabled ()))
|
||||||
|
{
|
||||||
|
g_autofree char *description = NULL;
|
||||||
|
|
||||||
|
description = g_strdup_printf ("frame drawn time: %" G_GINT64_FORMAT ", "
|
||||||
|
"sync request serial: %" G_GINT64_FORMAT,
|
||||||
|
frame->frame_drawn_time,
|
||||||
|
frame->sync_request_serial);
|
||||||
|
COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameDrawn,
|
||||||
|
description);
|
||||||
|
COGL_TRACE_END (MetaWindowActorX11FrameDrawn);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_send_frame_timings (MetaSyncCounter *sync_counter,
|
||||||
|
FrameData *frame,
|
||||||
|
int refresh_interval,
|
||||||
|
int64_t presentation_time)
|
||||||
|
{
|
||||||
|
MetaWindow *window = sync_counter->window;
|
||||||
|
MetaDisplay *display = meta_window_get_display (window);
|
||||||
|
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
|
||||||
|
XClientMessageEvent ev = { 0, };
|
||||||
|
|
||||||
|
COGL_TRACE_BEGIN (MetaWindowActorX11FrameTimings,
|
||||||
|
"X11: Send _NET_WM_FRAME_TIMINGS");
|
||||||
|
|
||||||
|
ev.type = ClientMessage;
|
||||||
|
ev.window = sync_counter->xwindow;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
MetaCompositor *compositor = display->compositor;
|
||||||
|
int64_t presentation_time_server, presentation_time_offset;
|
||||||
|
|
||||||
|
presentation_time_server =
|
||||||
|
meta_compositor_monotonic_to_high_res_xserver_time (compositor,
|
||||||
|
presentation_time);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#ifdef COGL_HAS_TRACING
|
||||||
|
if (G_UNLIKELY (cogl_is_tracing_enabled ()))
|
||||||
|
{
|
||||||
|
g_autofree char *description = NULL;
|
||||||
|
|
||||||
|
description =
|
||||||
|
g_strdup_printf ("refresh interval: %d, "
|
||||||
|
"presentation time: %" G_GINT64_FORMAT ", "
|
||||||
|
"sync request serial: %" G_GINT64_FORMAT,
|
||||||
|
refresh_interval,
|
||||||
|
frame->sync_request_serial,
|
||||||
|
presentation_time);
|
||||||
|
COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameTimings, description);
|
||||||
|
COGL_TRACE_END (MetaWindowActorX11FrameTimings);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_frame_timings (MetaSyncCounter *sync_counter,
|
||||||
|
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 (sync_counter, frame,
|
||||||
|
refresh_interval, presentation_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_counter_queue_frame_drawn (MetaSyncCounter *sync_counter)
|
||||||
|
{
|
||||||
|
FrameData *frame;
|
||||||
|
|
||||||
|
frame = g_new0 (FrameData, 1);
|
||||||
|
frame->frame_counter = -1;
|
||||||
|
frame->sync_request_serial = sync_counter->sync_request_serial;
|
||||||
|
|
||||||
|
sync_counter->frames = g_list_prepend (sync_counter->frames, frame);
|
||||||
|
|
||||||
|
sync_counter->needs_frame_drawn = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_counter_assign_counter_to_frames (MetaSyncCounter *sync_counter,
|
||||||
|
int64_t counter)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = sync_counter->frames; l; l = l->next)
|
||||||
|
{
|
||||||
|
FrameData *frame = l->data;
|
||||||
|
|
||||||
|
if (frame->frame_counter == -1)
|
||||||
|
frame->frame_counter = counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_counter_complete_frame (MetaSyncCounter *sync_counter,
|
||||||
|
ClutterFrameInfo *frame_info,
|
||||||
|
int64_t presentation_time)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = sync_counter->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 = sync_counter->window;
|
||||||
|
|
||||||
|
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_debug ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
|
||||||
|
window->desc, frame->frame_counter);
|
||||||
|
|
||||||
|
sync_counter->frames = g_list_delete_link (sync_counter->frames, l);
|
||||||
|
send_frame_timings (sync_counter, frame, frame_info, presentation_time);
|
||||||
|
g_free (frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
l = l_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_counter_finish_incomplete (MetaSyncCounter *sync_counter)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = sync_counter->frames; l;)
|
||||||
|
{
|
||||||
|
GList *l_next = l->next;
|
||||||
|
FrameData *frame = l->data;
|
||||||
|
|
||||||
|
if (frame->frame_counter == -1)
|
||||||
|
{
|
||||||
|
do_send_frame_drawn (sync_counter, frame);
|
||||||
|
do_send_frame_timings (sync_counter, frame, 0, 0);
|
||||||
|
|
||||||
|
sync_counter->frames = g_list_delete_link (sync_counter->frames, l);
|
||||||
|
g_free (frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
l = l_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_counter->needs_frame_drawn = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_sync_counter_send_frame_drawn (MetaSyncCounter *sync_counter)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
if (!sync_counter->needs_frame_drawn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (l = sync_counter->frames; l; l = l->next)
|
||||||
|
{
|
||||||
|
FrameData *frame = l->data;
|
||||||
|
|
||||||
|
if (frame->frame_drawn_time == 0)
|
||||||
|
do_send_frame_drawn (sync_counter, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_counter->needs_frame_drawn = FALSE;
|
||||||
|
}
|
||||||
|
@ -36,10 +36,17 @@ typedef struct
|
|||||||
guint sync_request_timeout_id;
|
guint sync_request_timeout_id;
|
||||||
/* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
|
/* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
|
||||||
XSyncAlarm sync_request_alarm;
|
XSyncAlarm sync_request_alarm;
|
||||||
|
|
||||||
|
int64_t frame_drawn_time;
|
||||||
|
GList *frames;
|
||||||
|
|
||||||
/* if TRUE, the we have the new form of sync request counter which
|
/* if TRUE, the we have the new form of sync request counter which
|
||||||
* also handles application frames */
|
* also handles application frames */
|
||||||
guint extended_sync_request_counter : 1;
|
guint extended_sync_request_counter : 1;
|
||||||
guint disabled : 1;
|
guint disabled : 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;
|
||||||
} MetaSyncCounter;
|
} MetaSyncCounter;
|
||||||
|
|
||||||
void meta_sync_counter_init (MetaSyncCounter *sync_counter,
|
void meta_sync_counter_init (MetaSyncCounter *sync_counter,
|
||||||
@ -67,4 +74,17 @@ gboolean meta_sync_counter_is_waiting (MetaSyncCounter *sync_counter);
|
|||||||
|
|
||||||
gboolean meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter);
|
gboolean meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter);
|
||||||
|
|
||||||
|
void meta_sync_counter_queue_frame_drawn (MetaSyncCounter *sync_counter);
|
||||||
|
|
||||||
|
void meta_sync_counter_assign_counter_to_frames (MetaSyncCounter *sync_counter,
|
||||||
|
int64_t counter);
|
||||||
|
|
||||||
|
void meta_sync_counter_complete_frame (MetaSyncCounter *sync_counter,
|
||||||
|
ClutterFrameInfo *frame_info,
|
||||||
|
int64_t presentation_time);
|
||||||
|
|
||||||
|
void meta_sync_counter_finish_incomplete (MetaSyncCounter *sync_counter);
|
||||||
|
|
||||||
|
void meta_sync_counter_send_frame_drawn (MetaSyncCounter *sync_counter);
|
||||||
|
|
||||||
#endif /* META_SYNC_COUNTER_H */
|
#endif /* META_SYNC_COUNTER_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user