Add GdkFrameHistory and GdkFrameTimings, handle _NET_WM_FRAME_TIMINGS

In order to be able to track statistics about how well we are drawing,
and in order to be able to do sophisticated things with frame timing
like predicting per-frame latencies and synchronizing audio with video,
we need to be able to track exactly when previous frames were drawn
to the screen.

Information about each frame is stored in a new GdkFrameTimings object.
A new GdkFrameHistory object is added which keeps a queue of recent
GdkFrameTimings (this is added to avoid further complicating the
implementation of GdkFrameClock.)

https://bugzilla.gnome.org/show_bug.cgi?id=685460
This commit is contained in:
Owen W. Taylor
2012-11-14 12:49:06 -05:00
parent d761df7e0c
commit 15ee04c66f
10 changed files with 575 additions and 16 deletions

View File

@ -1056,6 +1056,26 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
return return_val;
}
static GdkFrameTimings *
find_frame_timings (GdkFrameClock *clock,
guint64 serial)
{
GdkFrameHistory *history = gdk_frame_clock_get_history (clock);
gint64 start_frame, end_frame, i;
start_frame = gdk_frame_history_get_start (history);
end_frame = gdk_frame_history_get_frame_counter (history);
for (i = end_frame; i >= start_frame; i--)
{
GdkFrameTimings *timings = gdk_frame_history_get_timings (history, i);
if (gdk_frame_timings_get_cookie (timings) == serial)
return timings;
}
return NULL;
}
GdkFilterReturn
_gdk_wm_protocols_filter (GdkXEvent *xev,
GdkEvent *event,
@ -1074,6 +1094,71 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
display = GDK_WINDOW_DISPLAY (win);
/* This isn't actually WM_PROTOCOLS because that wouldn't leave enough space
* in the message for everything that gets stuffed in */
if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_DRAWN"))
{
GdkWindowImplX11 *window_impl;
window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
if (window_impl->toplevel)
{
guint32 d0 = xevent->xclient.data.l[0];
guint32 d1 = xevent->xclient.data.l[1];
guint32 d2 = xevent->xclient.data.l[2];
guint32 d3 = xevent->xclient.data.l[3];
guint64 serial = ((guint64)d0 << 32) | d1;
GdkFrameClock *clock = gdk_window_get_frame_clock (event->any.window);
GdkFrameTimings *timings = find_frame_timings (clock, serial);
if (timings)
gdk_frame_timings_set_drawn_time (timings, ((guint64)d2 << 32) | d3);
if (window_impl->toplevel->frame_pending)
{
window_impl->toplevel->frame_pending = FALSE;
gdk_frame_clock_thaw (clock);
}
}
return GDK_FILTER_REMOVE;
}
if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_TIMINGS"))
{
GdkWindowImplX11 *window_impl;
window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
if (window_impl->toplevel)
{
guint32 d0 = xevent->xclient.data.l[0];
guint32 d1 = xevent->xclient.data.l[1];
guint32 d2 = xevent->xclient.data.l[2];
guint32 d3 = xevent->xclient.data.l[3];
guint64 serial = ((guint64)d0 << 32) | d1;
GdkFrameClock *clock = gdk_window_get_frame_clock (event->any.window);
GdkFrameTimings *timings = find_frame_timings (clock, serial);
if (timings)
{
gint64 drawn_time = gdk_frame_timings_get_drawn_time (timings);
gint32 presentation_time_offset = (gint32)d2;
gint32 refresh_interval = d3;
if (drawn_time && presentation_time_offset)
gdk_frame_timings_set_presentation_time (timings,
drawn_time + presentation_time_offset);
if (refresh_interval)
gdk_frame_timings_set_refresh_interval (timings, refresh_interval);
gdk_frame_timings_set_complete (timings, TRUE);
}
}
}
if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS"))
return GDK_FILTER_CONTINUE;
@ -1145,21 +1230,6 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
return GDK_FILTER_REMOVE;
}
else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_DRAWN"))
{
GdkWindowImplX11 *window_impl;
window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
if (window_impl->toplevel &&
window_impl->toplevel->frame_pending)
{
window_impl->toplevel->frame_pending = FALSE;
gdk_frame_clock_thaw (gdk_window_get_frame_clock (event->any.window));
}
return GDK_FILTER_REMOVE;
}
return GDK_FILTER_CONTINUE;
}

View File

@ -853,7 +853,17 @@ static void
on_frame_clock_after_paint (GdkFrameClock *clock,
GdkWindow *window)
{
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
GdkFrameHistory *history = gdk_frame_clock_get_history (clock);
gint64 frame_counter = gdk_frame_history_get_frame_counter (history);
GdkFrameTimings *timings = gdk_frame_history_get_timings (history, frame_counter);
gdk_x11_window_end_frame (window);
if (toplevel->frame_pending)
gdk_frame_timings_set_cookie (timings, toplevel->current_counter_value);
else
gdk_frame_timings_set_complete (timings, TRUE);
}
void