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/window.h"
|
||||
#include "x11/window-x11.h"
|
||||
#include "x11/meta-sync-counter.h"
|
||||
#include "x11/meta-x11-display-private.h"
|
||||
#include "x11/window-x11.h"
|
||||
|
||||
@ -54,19 +55,12 @@ struct _MetaWindowActorX11
|
||||
{
|
||||
MetaWindowActor parent;
|
||||
|
||||
/* List of FrameData for recent frames */
|
||||
GList *frames;
|
||||
|
||||
guint send_frame_messages_timer;
|
||||
int64_t frame_drawn_time;
|
||||
gboolean pending_schedule_update_now;
|
||||
|
||||
gulong repaint_scheduled_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;
|
||||
|
||||
/*
|
||||
@ -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_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
|
||||
surface_repaint_scheduled (MetaSurfaceActor *actor,
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
send_frame_messages_timeout (gpointer 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;)
|
||||
{
|
||||
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;
|
||||
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||
meta_sync_counter_finish_incomplete (sync_counter);
|
||||
actor_x11->send_frame_messages_timer = 0;
|
||||
|
||||
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));
|
||||
MetaDisplay *display = meta_window_get_display (window);
|
||||
MetaLogicalMonitor *logical_monitor;
|
||||
MetaSyncCounter *sync_counter;
|
||||
int64_t now_us;
|
||||
int64_t current_time;
|
||||
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,
|
||||
now_us);
|
||||
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,
|
||||
* 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));
|
||||
MetaCompositor *compositor = window->display->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
|
||||
* 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)
|
||||
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);
|
||||
}
|
||||
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||
meta_sync_counter_assign_counter_to_frames (sync_counter,
|
||||
clutter_stage_get_frame_counter (stage));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -396,37 +215,16 @@ 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;
|
||||
MetaWindow *window = meta_window_actor_get_meta_window (actor);
|
||||
MetaSyncCounter *sync_counter;
|
||||
|
||||
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_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;
|
||||
}
|
||||
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||
meta_sync_counter_complete_frame (sync_counter,
|
||||
frame_info,
|
||||
presentation_time);
|
||||
}
|
||||
|
||||
static MetaSurfaceActor *
|
||||
@ -499,23 +297,10 @@ 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);
|
||||
MetaSyncCounter *sync_counter =
|
||||
meta_window_x11_get_sync_counter (window);
|
||||
FrameData *frame;
|
||||
|
||||
if (meta_window_actor_is_destroyed (actor))
|
||||
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)
|
||||
{
|
||||
ClutterFrameClock *frame_clock;
|
||||
@ -1404,6 +1189,7 @@ meta_window_actor_x11_after_paint (MetaWindowActor *actor,
|
||||
ClutterStageView *stage_view)
|
||||
{
|
||||
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
||||
MetaSyncCounter *sync_counter;
|
||||
MetaWindow *window;
|
||||
|
||||
actor_x11->repaint_scheduled = FALSE;
|
||||
@ -1411,28 +1197,19 @@ meta_window_actor_x11_after_paint (MetaWindowActor *actor,
|
||||
if (meta_window_actor_is_destroyed (actor))
|
||||
return;
|
||||
|
||||
window = meta_window_actor_get_meta_window (actor);
|
||||
|
||||
/* 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)
|
||||
if (actor_x11->send_frame_messages_timer == 0)
|
||||
{
|
||||
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;
|
||||
sync_counter = meta_window_x11_get_sync_counter (window);
|
||||
meta_sync_counter_send_frame_drawn (sync_counter);
|
||||
}
|
||||
|
||||
/* 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))
|
||||
{
|
||||
meta_window_x11_thaw_commits (window);
|
||||
@ -1631,8 +1408,11 @@ meta_window_actor_x11_constructed (GObject *object)
|
||||
*/
|
||||
if (sync_counter->extended_sync_request_counter &&
|
||||
!meta_window_updates_are_frozen (window))
|
||||
{
|
||||
meta_sync_counter_queue_frame_drawn (sync_counter);
|
||||
meta_window_actor_queue_frame_drawn (actor, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_window_actor_x11_cull_out (MetaCullable *cullable,
|
||||
@ -1702,10 +1482,6 @@ meta_window_actor_x11_dispose (GObject *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);
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,29 @@
|
||||
|
||||
#include "meta-sync-counter.h"
|
||||
|
||||
#include "compositor/compositor-private.h"
|
||||
#include "core/window-private.h"
|
||||
#include "meta/meta-x11-errors.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
|
||||
meta_sync_counter_init (MetaSyncCounter *sync_counter,
|
||||
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);
|
||||
meta_sync_counter_destroy_sync_alarm (sync_counter);
|
||||
g_clear_list (&sync_counter->frames, g_free);
|
||||
sync_counter->window = NULL;
|
||||
sync_counter->xwindow = None;
|
||||
}
|
||||
@ -310,8 +330,11 @@ meta_sync_counter_update (MetaSyncCounter *sync_counter,
|
||||
sync_counter->disabled = FALSE;
|
||||
|
||||
if (needs_frame_drawn)
|
||||
{
|
||||
meta_sync_counter_queue_frame_drawn (sync_counter);
|
||||
meta_compositor_queue_frame_drawn (window->display->compositor, window,
|
||||
no_delay_frame);
|
||||
}
|
||||
|
||||
#ifdef COGL_HAS_TRACING
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
/* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
|
||||
XSyncAlarm sync_request_alarm;
|
||||
|
||||
int64_t frame_drawn_time;
|
||||
GList *frames;
|
||||
|
||||
/* if TRUE, the we have the new form of sync request counter which
|
||||
* also handles application frames */
|
||||
guint extended_sync_request_counter : 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;
|
||||
|
||||
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);
|
||||
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user