diff options
author | Owen W. Taylor <otaylor@fishsoup.net> | 2012-11-12 11:58:10 -0500 |
---|---|---|
committer | Robert Bragg <robert@linux.intel.com> | 2013-01-23 11:36:12 +0000 |
commit | 646c2805342db272a1af53537aacbee1fbb477b2 (patch) | |
tree | 8832e8bf9ef00946f3732d5ce8dbe0b36bcd4548 | |
parent | 394a162738f30f9cbf975f93bb9e15399715c423 (diff) | |
download | cogl-646c2805342db272a1af53537aacbee1fbb477b2.tar.gz |
Add CoglFrameTimings
Add a CoglFrameTimings object that tracks timing information
for frames that are drawn. We track a frame counter and frame
timing information for each CoglOnscreen. Frames timing information
is deliminated by a new cogl_onscreen_begin_frame() and retrieved using
cogl_onscreen_get_frame_timings().
-rw-r--r-- | cogl/Makefile.am | 3 | ||||
-rw-r--r-- | cogl/cogl-frame-timings-private.h | 44 | ||||
-rw-r--r-- | cogl/cogl-frame-timings.c | 78 | ||||
-rw-r--r-- | cogl/cogl-frame-timings.h | 133 | ||||
-rw-r--r-- | cogl/cogl-glx-display-private.h | 1 | ||||
-rw-r--r-- | cogl/cogl-glx-renderer-private.h | 9 | ||||
-rw-r--r-- | cogl/cogl-onscreen-private.h | 28 | ||||
-rw-r--r-- | cogl/cogl-onscreen.c | 114 | ||||
-rw-r--r-- | cogl/cogl-onscreen.h | 118 | ||||
-rw-r--r-- | cogl/cogl-x11-renderer-private.h | 1 | ||||
-rw-r--r-- | cogl/winsys/cogl-winsys-glx.c | 250 |
11 files changed, 764 insertions, 15 deletions
diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 96a11507..b3db4cad 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -110,6 +110,7 @@ cogl_experimental_h = \ $(srcdir)/cogl-attribute.h \ $(srcdir)/cogl-primitive.h \ $(srcdir)/cogl-clip-state.h \ + $(srcdir)/cogl-frame-timings.h \ $(srcdir)/cogl-framebuffer.h \ $(srcdir)/cogl-onscreen.h \ $(srcdir)/cogl-output.h \ @@ -384,6 +385,8 @@ cogl_sources_c = \ $(srcdir)/cogl-spans.c \ $(srcdir)/cogl-journal-private.h \ $(srcdir)/cogl-journal.c \ + $(srcdir)/cogl-frame-timings-private.h \ + $(srcdir)/cogl-frame-timings.c \ $(srcdir)/cogl-framebuffer-private.h \ $(srcdir)/cogl-framebuffer.c \ $(srcdir)/cogl-onscreen-private.h \ diff --git a/cogl/cogl-frame-timings-private.h b/cogl/cogl-frame-timings-private.h new file mode 100644 index 00000000..b17cd34e --- /dev/null +++ b/cogl/cogl-frame-timings-private.h @@ -0,0 +1,44 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * + */ + +#ifndef __COGL_FRAME_TIMINGS_PRIVATE_H +#define __COGL_FRAME_TIMINGS_PRIVATE_H + +#include "cogl-frame-timings.h" +#include "cogl-object-private.h" + +struct _CoglFrameTimings +{ + CoglObject _parent; + + int64_t frame_counter; + int64_t frame_time; + int64_t presentation_time; + int64_t refresh_interval; + + guint complete : 1; +}; + +CoglFrameTimings *_cogl_frame_timings_new (void); + +#endif /* __COGL_FRAME_TIMINGS_PRIVATE_H */ diff --git a/cogl/cogl-frame-timings.c b/cogl/cogl-frame-timings.c new file mode 100644 index 00000000..ba8003e7 --- /dev/null +++ b/cogl/cogl-frame-timings.c @@ -0,0 +1,78 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-frame-timings-private.h" + +static void _cogl_frame_timings_free (CoglFrameTimings *frame_timings); + +COGL_OBJECT_DEFINE (FrameTimings, frame_timings); + +CoglFrameTimings * +_cogl_frame_timings_new (void) +{ + CoglFrameTimings *timings; + + timings = g_slice_new0 (CoglFrameTimings); + + return _cogl_frame_timings_object_new (timings); +} + +static void +_cogl_frame_timings_free (CoglFrameTimings *timings) +{ + g_slice_free (CoglFrameTimings, timings); +} + +CoglBool +cogl_frame_timings_get_complete (CoglFrameTimings *timings) +{ + return timings->complete; +} + +gint64 +cogl_frame_timings_get_frame_counter (CoglFrameTimings *timings) +{ + return timings->frame_counter; +} + +gint64 +cogl_frame_timings_get_frame_time (CoglFrameTimings *timings) +{ + return timings->frame_time; +} + +gint64 +cogl_frame_timings_get_presentation_time (CoglFrameTimings *timings) +{ + return timings->presentation_time; +} + +gint64 +cogl_frame_timings_get_refresh_interval (CoglFrameTimings *timings) +{ + return timings->refresh_interval; +} diff --git a/cogl/cogl-frame-timings.h b/cogl/cogl-frame-timings.h new file mode 100644 index 00000000..c00e920a --- /dev/null +++ b/cogl/cogl-frame-timings.h @@ -0,0 +1,133 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * + * + * Authors: + * Owen Taylor <otaylor@redhat.com> + */ +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only <cogl/cogl.h> can be included directly." +#endif + +#ifndef __COGL_FRAME_TIMINGS_H +#define __COGL_FRAME_TIMINGS_H + +#include <cogl/cogl-types.h> +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _CoglFrameTimings CoglFrameTimings; +#define COGL_FRAME_TIMINGS(X) ((CoglFrameTimings *)(X)) + +/** + * cogl_is_frame_timings: + * @object: A #CoglObject pointer + * + * Gets whether the given object references a #CoglFrameTimings. + * + * Return value: %TRUE if the object references a #CoglFrameTimings + * and %FALSE otherwise. + * Since: 2.0 + * Stability: unstable + */ +CoglBool +cogl_is_frame_timings (void *object); + +/** + * cogl_frame_timings_get_complete: + * @timings: a #CoglFrameTimings object + * + * Gets whether all information that will potentially be provided for + * the frame has been provided. Once a frame timings object is complete, + * no further changes will be made to it. + * + * Return value: whether the frame timings object is complete. + * Since: 2.0 + * Stability: unstable + */ +CoglBool cogl_frame_timings_get_complete (CoglFrameTimings *timings); + +/** + * cogl_frame_timings_get_frame_counter: + * @timings: a #CoglFrameTimings object + * + * Gets the frame counter for the #CoglOnscreen that corresponds + * to this frame. + * + * Return value: The frame counter value + * Since: 2.0 + * Stability: unstable + */ +int64_t cogl_frame_timings_get_frame_counter (CoglFrameTimings *timings); + +/** + * cogl_frame_timings_get_frame_time: + * @timings: a #CoglFrameTimings object + * + * Gets the time used for creating content for the frame. This + * is determined by the time passed to cogl_onscreen_begin_frame(), + * and will typically be the current time when rendering started + * for the frame. + * + * Return value: the time used for coreating content for the frame, + * in the timescale of g_get_monotonic_time(). + * Since: 2.0 + * Stability: unstable + */ +int64_t cogl_frame_timings_get_frame_time (CoglFrameTimings *timings); + +/** + * cogl_frame_timings_get_presentation_time: + * @timings: a #CoglFrameTimings object + * + * Gets the presentation time for the frame. This is the time at which + * the frame became visible to the user. + * + * Return value: the presentation time for the frame, in + * the timescale of g_get_monotonic_time(). + * Since: 2.0 + * Stability: unstable + */ +int64_t cogl_frame_timings_get_presentation_time (CoglFrameTimings *timings); + +/** + * cogl_frame_timings_get_refresh_interval: + * @timings: a #CoglFrameTimings object + * + * Gets the refresh interval for the output that the frame was on at the + * time the frame was presented. This is the number of microseconds between + * refreshes of the screen, and is equal to 1000000 / refresh_rate. + * + * Return value: the refresh interval, in microsecoonds. + * . + * Since: 2.0 + * Stability: unstable + */ +int64_t cogl_frame_timings_get_refresh_interval (CoglFrameTimings *timings); + +G_END_DECLS + +#endif /* __COGL_FRAME_TIMINGS_H */ + + + diff --git a/cogl/cogl-glx-display-private.h b/cogl/cogl-glx-display-private.h index 1ffcd321..cb0fd0d9 100644 --- a/cogl/cogl-glx-display-private.h +++ b/cogl/cogl-glx-display-private.h @@ -52,6 +52,7 @@ typedef struct _CoglGLXDisplay Window dummy_xwin; CoglBool pending_swap_notify; CoglBool pending_resize_notify; + CoglBool pending_frame_timings_notify; } CoglGLXDisplay; #endif /* __COGL_DISPLAY_GLX_PRIVATE_H */ diff --git a/cogl/cogl-glx-renderer-private.h b/cogl/cogl-glx-renderer-private.h index 65fa8d95..7a1fbcd6 100644 --- a/cogl/cogl-glx-renderer-private.h +++ b/cogl/cogl-glx-renderer-private.h @@ -42,6 +42,15 @@ typedef struct _CoglGLXRenderer /* Vblank stuff */ int dri_fd; + /* enumeration with relatioship between OML_sync_control + * UST (unadjusted-system-time) and the system clock */ + enum { + COGL_GLX_UST_IS_UNKNOWN, + COGL_GLX_UST_IS_GETTIMEOFDAY, + COGL_GLX_UST_IS_MONOTONIC_TIME, + COGL_GLX_UST_IS_OTHER + } ust_type; + /* GModule pointing to libGL which we use to get glX functions out of */ GModule *libgl_module; diff --git a/cogl/cogl-onscreen-private.h b/cogl/cogl-onscreen-private.h index 06bdd4dd..8c35f301 100644 --- a/cogl/cogl-onscreen-private.h +++ b/cogl/cogl-onscreen-private.h @@ -33,6 +33,8 @@ #include <windows.h> #endif +#define COGL_ONSCREEN_MAX_FRAME_TIMINGS 16 + typedef struct _CoglSwapBuffersNotifyEntry CoglSwapBuffersNotifyEntry; COGL_TAILQ_HEAD (CoglSwapBuffersNotifyList, CoglSwapBuffersNotifyEntry); @@ -59,6 +61,19 @@ struct _CoglResizeNotifyEntry unsigned int id; }; +typedef struct _CoglFrameTimingsCallbackEntry CoglFrameTimingsCallbackEntry; + +COGL_TAILQ_HEAD (CoglFrameTimingsCallbackList, CoglFrameTimingsCallbackEntry); + +struct _CoglFrameTimingsCallbackEntry +{ + COGL_TAILQ_ENTRY (CoglFrameTimingsCallbackEntry) list_node; + + CoglFrameTimingsCallback callback; + void *user_data; + unsigned int id; +}; + struct _CoglOnscreen { CoglFramebuffer _parent; @@ -80,6 +95,16 @@ struct _CoglOnscreen CoglBool resizable; CoglResizeNotifyList resize_callbacks; + CoglFrameTimingsCallbackList frame_timings_callbacks; + + int64_t frame_counter; + int64_t swap_frame_counter; /* frame counter at last all to + * cogl_onscreen_swap_region() or + * cogl_onscreen_swap_buffers() */ + CoglFrameTimings *frame_timings[COGL_ONSCREEN_MAX_FRAME_TIMINGS]; + int current_frame_timings; + int n_frame_timings; + void *winsys; }; @@ -96,4 +121,7 @@ _cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen); void _cogl_onscreen_notify_resize (CoglOnscreen *onscreen); +void +_cogl_onscreen_notify_frame_timings (CoglOnscreen *onscreen); + #endif /* __COGL_ONSCREEN_PRIVATE_H */ diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c index 3c1e148d..5e25287f 100644 --- a/cogl/cogl-onscreen.c +++ b/cogl/cogl-onscreen.c @@ -27,6 +27,7 @@ #include "cogl-util.h" #include "cogl-onscreen-private.h" +#include "cogl-frame-timings-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-template-private.h" #include "cogl-context-private.h" @@ -35,6 +36,8 @@ static void _cogl_onscreen_free (CoglOnscreen *onscreen); +static void cogl_onscreen_before_swap (CoglOnscreen *onscreen); + COGL_OBJECT_DEFINE_WITH_CODE (Onscreen, onscreen, _cogl_onscreen_class.virt_unref = _cogl_framebuffer_unref); @@ -47,6 +50,7 @@ _cogl_onscreen_init_from_template (CoglOnscreen *onscreen, COGL_TAILQ_INIT (&onscreen->swap_callbacks); COGL_TAILQ_INIT (&onscreen->resize_callbacks); + COGL_TAILQ_INIT (&onscreen->frame_timings_callbacks); framebuffer->config = onscreen_template->config; cogl_object_ref (framebuffer->config.swap_chain); @@ -75,6 +79,9 @@ _cogl_onscreen_new (void) COGL_FRAMEBUFFER (onscreen)->allocated = TRUE; + onscreen->frame_counter = -1; + onscreen->current_frame_timings = COGL_ONSCREEN_MAX_FRAME_TIMINGS - 1; + /* XXX: Note we don't initialize onscreen->winsys in this case. */ return _cogl_onscreen_object_new (onscreen); @@ -149,6 +156,8 @@ cogl_onscreen_swap_buffers (CoglOnscreen *onscreen) _COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); + cogl_onscreen_before_swap (onscreen); + /* FIXME: we shouldn't need to flush *all* journals here! */ cogl_flush (); winsys = _cogl_framebuffer_get_winsys (framebuffer); @@ -169,6 +178,8 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen, _COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); + cogl_onscreen_before_swap (onscreen); + /* FIXME: we shouldn't need to flush *all* journals here! */ cogl_flush (); @@ -452,3 +463,106 @@ cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen, } } +int64_t +cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen) +{ + return onscreen->frame_counter; +} + +void +cogl_onscreen_begin_frame (CoglOnscreen *onscreen, + gint64 frame_time) +{ + onscreen->frame_counter++; + onscreen->current_frame_timings = (onscreen->current_frame_timings + 1) % COGL_ONSCREEN_MAX_FRAME_TIMINGS; + + if (onscreen->n_frame_timings < COGL_ONSCREEN_MAX_FRAME_TIMINGS) + onscreen->n_frame_timings++; + else + cogl_object_unref (onscreen->frame_timings[onscreen->current_frame_timings]); + + onscreen->frame_timings[onscreen->current_frame_timings] = _cogl_frame_timings_new (); + onscreen->frame_timings[onscreen->current_frame_timings]->frame_counter = onscreen->frame_counter; + onscreen->frame_timings[onscreen->current_frame_timings]->frame_time = frame_time; +} + +static void +cogl_onscreen_before_swap (CoglOnscreen *onscreen) +{ + if (onscreen->swap_frame_counter == onscreen->frame_counter) + cogl_onscreen_begin_frame (onscreen, 0); + + onscreen->swap_frame_counter = onscreen->frame_counter; +} + +int64_t +cogl_onscreen_get_frame_history_start (CoglOnscreen *onscreen) +{ + return onscreen->frame_counter - onscreen->n_frame_timings; +} + +CoglFrameTimings * +cogl_onscreen_get_frame_timings (CoglOnscreen *onscreen, + int64_t frame_counter) +{ + int pos; + + if (frame_counter > onscreen->frame_counter) + return NULL; + + if (frame_counter <= onscreen->frame_counter - onscreen->n_frame_timings) + return NULL; + + pos = ((onscreen->current_frame_timings - + (onscreen->frame_counter - frame_counter) + COGL_ONSCREEN_MAX_FRAME_TIMINGS) + % COGL_ONSCREEN_MAX_FRAME_TIMINGS); + + return onscreen->frame_timings[pos]; +} + +unsigned int +cogl_onscreen_add_frame_timings_callback (CoglOnscreen *onscreen, + CoglFrameTimingsCallback callback, + void *user_data) +{ + CoglFrameTimingsCallbackEntry *entry = g_slice_new (CoglFrameTimingsCallbackEntry); + static int next_resize_callback_id = 0; + + entry->callback = callback; + entry->user_data = user_data; + entry->id = next_resize_callback_id++; + + COGL_TAILQ_INSERT_TAIL (&onscreen->frame_timings_callbacks, entry, list_node); + + return entry->id; +} + +void +cogl_onscreen_remove_frame_timings_callback (CoglOnscreen *onscreen, + unsigned int id) +{ + CoglFrameTimingsCallbackEntry *entry; + + COGL_TAILQ_FOREACH (entry, &onscreen->frame_timings_callbacks, list_node) + { + if (entry->id == id) + { + COGL_TAILQ_REMOVE (&onscreen->frame_timings_callbacks, entry, list_node); + g_slice_free (CoglFrameTimingsCallbackEntry, entry); + break; + } + } +} + +void +_cogl_onscreen_notify_frame_timings (CoglOnscreen *onscreen) +{ + CoglFrameTimingsCallbackEntry *entry, *tmp; + + COGL_TAILQ_FOREACH_SAFE (entry, + &onscreen->frame_timings_callbacks, + list_node, + tmp) + entry->callback (onscreen, entry->user_data); +} + diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h index 726052eb..2891a4a4 100644 --- a/cogl/cogl-onscreen.h +++ b/cogl/cogl-onscreen.h @@ -34,6 +34,7 @@ #include <cogl/cogl-context.h> #include <cogl/cogl-framebuffer.h> +#include <cogl/cogl-frame-timings.h> COGL_BEGIN_DECLS @@ -532,6 +533,123 @@ cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen, CoglBool cogl_is_onscreen (void *object); +/** + * cogl_onscreen_get_frame_counter: + * + * Gets the value of the framebuffers frame counter. This is + * a counter that increases by one each time + * cogl_onscreen_swap_buffers() or cogl_onscreen_swap_region() + * is called. + * + * Return value: the current frame counter value + * Since: 2.0 + */ +int64_t +cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen); + +/** + * cogl_onscreen_begin_frame: + * @onscreen: a #CoglOnscreen framebuffer + * @frame_time: the time that should be used for creating + * content for this frame. + * + * Marks the beginning of a frame. This increases the frame + * counter value and creates a new #CoglFrameTimings objeect. + * + * Since: 2.0 + */ +void +cogl_onscreen_begin_frame (CoglOnscreen *onscreen, + gint64 frame_time); + +/** + * cogl_onscreen_get_frame_history_start: + * @onscreen: a #CoglOnscreen framebuffer + * + * Gets the frame counter for the oldest #CoglFrameTiming that is + * being kept in the history. cogl_onscreen_get_frame_timings() will + * always return %NULl for any frame counter before this. + * + * Return value: the frame counter for the oldest #CoglFrameTimings + * in the history. + * Since: 2.0 + */ +int64_t +cogl_onscreen_get_frame_history_start (CoglOnscreen *onscreen); + + +/** + * cogl_onscreen_get_frame_timings: + * @onscreen: A #CoglOnscreen framebuffer + * @frame_counter: the value of cogl_onscreen_get_frame_counter() + * when the frame finished drawing. + * + * Gets frame timing information for a particular frame. + * + * Return value: a #CoglFrameTiming object, or %NULL if frame timing + * information is not available for the given frame. + * Since: 2.0 + */ +CoglFrameTimings * +cogl_onscreen_get_frame_timings (CoglOnscreen *onscreen, + int64_t frame_counter); + +/** + * CoglFrameTimingsCallback: + * @onscreen: A #CoglOnscreen framebuffer that has updated timing information + * @user_data: The private passed to + * cogl_onscreen_add_frame_timings_callback() + * + * Is a callback type used with the + * cogl_onscreen_add_frame_timings_callback() allowing applications to be + * notified whenever new frame timings information is available + * via cogl_onscreen_get_frame_timings(). + * + * <note>A frame timings callback will only ever be called while dispatching + * Cogl events from the system mainloop; so for example during + * cogl_poll_dispatch(). This is so that callbacks shouldn't occur + * while an application might have arbitrary locks held for + * example.</note> + * + * Since: 2.0 + */ +typedef void (*CoglFrameTimingsCallback) (CoglOnscreen *onscreen, + void *user_data); + +/** + * cogl_onscreen_add_frame_timings_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @callback: A callback function to call when new frame timings information is available + * @user_data: A private pointer to be passed to @callback + * + * Installs a @callback function that should be called whenever new data + * is available via cogl_onscreen_get_frame_timings(). + * + * Return value: a unique identifier that can be used to remove to remove + * the callback later. + * Since: 2.0 + * Stability: unstable + */ +unsigned int +cogl_onscreen_add_frame_timings_callback (CoglOnscreen *onscreen, + CoglFrameTimingsCallback callback, + void *user_data); + +/** + * cogl_onscreen_remove_frame_timings_callback: + * @onscreen: A #CoglOnscreen framebuffer + * @id: An identifier returned from cogl_onscreen_add_frame_timings_callback() + * + * Removes a callback that was previously registered + * using cogl_onscreen_add_frame_timings_callback(). + * + * Since: 1.10 + * Stability: unstable + */ +void +cogl_onscreen_remove_frame_timings_callback (CoglOnscreen *onscreen, + unsigned int id); + COGL_END_DECLS #endif /* __COGL_ONSCREEN_H */ diff --git a/cogl/cogl-x11-renderer-private.h b/cogl/cogl-x11-renderer-private.h index 54a5639e..0b70012e 100644 --- a/cogl/cogl-x11-renderer-private.h +++ b/cogl/cogl-x11-renderer-private.h @@ -27,6 +27,7 @@ typedef struct _CoglX11Renderer { int damage_base; + int randr_base; } CoglX11Renderer; #endif /* __COGL_RENDERER_X11_PRIVATE_H */ diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c index 5b092e0e..079cf3b2 100644 --- a/cogl/winsys/cogl-winsys-glx.c +++ b/cogl/winsys/cogl-winsys-glx.c @@ -42,6 +42,7 @@ #include "cogl-texture-2d-private.h" #include "cogl-texture-rectangle-private.h" #include "cogl-pipeline-opengl-private.h" +#include "cogl-frame-timings-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-private.h" #include "cogl-swap-chain-private.h" @@ -53,6 +54,7 @@ #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/time.h> #include <fcntl.h> #include <glib/gi18n-lib.h> @@ -83,6 +85,7 @@ typedef struct _CoglOnscreenGLX uint32_t last_swap_vsync_counter; CoglBool pending_swap_notify; CoglBool pending_resize_notify; + CoglBool pending_frame_timings_notify; } CoglOnscreenGLX; typedef struct _CoglTexturePixmapGLX @@ -171,16 +174,134 @@ find_onscreen_for_xid (CoglContext *context, uint32_t xid) } static void -notify_swap_buffers (CoglContext *context, GLXDrawable drawable) +ensure_ust_type (CoglRenderer *renderer, + GLXDrawable drawable) { - CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)drawable); + CoglGLXRenderer *glx_renderer = renderer->winsys; + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + int64_t ust; + int64_t msc; + int64_t sbc; + struct timeval tv; + gint64 current_system_time; + gint64 current_monotonic_time; + + if (glx_renderer->ust_type != COGL_GLX_UST_IS_UNKNOWN) + return; + + glx_renderer->ust_type = COGL_GLX_UST_IS_OTHER; + + if (glx_renderer->glXGetSyncValues == NULL) + goto out; + + if (!glx_renderer->glXGetSyncValues (xlib_renderer->xdpy, drawable, + &ust, &msc, &sbc)) + goto out; + + /* This is the method that Xorg uses currently */ + gettimeofday(&tv, NULL); + current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec; + + if (current_system_time > ust - 1000000 && current_system_time < ust + 1000000) + { + glx_renderer->ust_type = COGL_GLX_UST_IS_GETTIMEOFDAY; + goto out; + } + + /* This is the method that would make sense for a GL implementation to use - + * clock_gettime (CLOCK_MONOTONIC, &ts) */ + current_monotonic_time = g_get_monotonic_time (); + if (current_monotonic_time > ust - 1000000 && current_monotonic_time < ust + 1000000) + { + glx_renderer->ust_type = COGL_GLX_UST_IS_MONOTONIC_TIME; + goto out; + } + + out: + COGL_NOTE (WINSYS, "Classified OML system time as: %s", + glx_renderer->ust_type == COGL_GLX_UST_IS_GETTIMEOFDAY ? "gettimeofday" : + (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME ? "monotonic" : + "other")); + return; +} + +static gint64 +ust_to_monotonic_time (CoglRenderer *renderer, + GLXDrawable drawable, + int64_t ust) +{ + CoglGLXRenderer *glx_renderer = renderer->winsys; + CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); + + ensure_ust_type (renderer, drawable); + + switch (glx_renderer->ust_type) + { + case COGL_GLX_UST_IS_UNKNOWN: + g_assert_not_reached (); + break; + case COGL_GLX_UST_IS_GETTIMEOFDAY: + { + struct timeval tv; + gint64 current_system_time; + gint64 current_monotonic_time; + + gettimeofday(&tv, NULL); + current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec; + current_monotonic_time = g_get_monotonic_time (); + + return ust + current_monotonic_time - current_system_time; + } + case COGL_GLX_UST_IS_MONOTONIC_TIME: + return ust; + case COGL_GLX_UST_IS_OTHER: + { + if (glx_renderer->glXGetSyncValues) + { + gint64 current_monotonic_time; + int64_t ust; + int64_t msc; + int64_t sbc; + + glx_renderer->glXGetSyncValues (xlib_renderer->xdpy, drawable, + &ust, &msc, &sbc); + + current_monotonic_time = g_get_monotonic_time (); + return ust + current_monotonic_time - ust; + } + break; + } + } + + return 0; +} + +static void +set_timings_complete (CoglOnscreen *onscreen) +{ + CoglOnscreenGLX *glx_onscreen = onscreen->winsys; + CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; + CoglGLXDisplay *glx_display = context->display->winsys; + CoglFrameTimings *timings = cogl_onscreen_get_frame_timings (onscreen, + cogl_onscreen_get_frame_counter (onscreen)); + + timings->complete = TRUE; + + glx_display->pending_frame_timings_notify = TRUE; + glx_onscreen->pending_frame_timings_notify = TRUE; +} + +static void +notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event) +{ + CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable); CoglDisplay *display = context->display; CoglGLXDisplay *glx_display = display->winsys; CoglOnscreenGLX *glx_onscreen; + CoglFrameTimings *timings; if (!onscreen) return; - glx_onscreen = onscreen->winsys; /* We only want to notify that the swap is complete when the @@ -188,6 +309,15 @@ notify_swap_buffers (CoglContext *context, GLXDrawable drawable) notifying we'll set a flag to remember to notify later */ glx_display->pending_swap_notify = TRUE; glx_onscreen->pending_swap_notify = TRUE; + + timings = cogl_onscreen_get_frame_timings (onscreen, + cogl_onscreen_get_frame_counter (onscreen)); + if (swap_event->ust != 0) + timings->presentation_time = ust_to_monotonic_time (context->display->renderer, + glx_onscreen->glxwin, + swap_event->ust); + + set_timings_complete (onscreen); } static void @@ -291,7 +421,7 @@ glx_event_filter_cb (XEvent *xevent, void *data) { GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent; - notify_swap_buffers (context, swap_event->drawable); + notify_swap_buffers (context, swap_event); /* remove SwapComplete events from the queue */ return COGL_FILTER_REMOVE; @@ -428,8 +558,8 @@ update_base_winsys_features (CoglRenderer *renderer) COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, TRUE); - if (glx_renderer->pf_glXWaitVideoSync || - glx_renderer->pf_glXWaitForMsc) + if (glx_renderer->glXWaitVideoSync || + glx_renderer->glXWaitForMsc) COGL_FLAGS_SET (glx_renderer->base_winsys_features, COGL_WINSYS_FEATURE_VBLANK_WAIT, TRUE); @@ -1250,15 +1380,33 @@ _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) } static void -_cogl_winsys_wait_for_vblank (CoglContext *ctx) +_cogl_winsys_wait_for_gpu (CoglOnscreen *onscreen) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *ctx = framebuffer->context; + + ctx->glFinish (); +} + +static void +_cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen) { + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *ctx = framebuffer->context; CoglGLXRenderer *glx_renderer; + CoglXlibRenderer *xlib_renderer; glx_renderer = ctx->display->renderer->winsys; + xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer); if (glx_renderer->glXWaitForMsc || glx_renderer->glXGetVideoSync) { + CoglFrameTimings *timings; + + timings = cogl_onscreen_get_frame_timings (onscreen, + cogl_onscreen_get_frame_counter (onscreen)); + if (glx_renderer->glXWaitForMsc) { CoglOnscreenGLX *glx_onscreen = onscreen->winsys; @@ -1272,6 +1420,9 @@ _cogl_winsys_wait_for_vblank (CoglContext *ctx) glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc); + timings->presentation_time = ust_to_monotonic_time (ctx->display->renderer, + drawable, + ust); } else { @@ -1281,6 +1432,7 @@ _cogl_winsys_wait_for_vblank (CoglContext *ctx) glx_renderer->glXWaitVideoSync (2, (current_count + 1) % 2, ¤t_count); + timings->presentation_time = g_get_monotonic_time (); } } } @@ -1299,6 +1451,19 @@ _cogl_winsys_get_vsync_counter (CoglContext *ctx) } static void +set_refresh_interval_from_output (CoglOnscreen *onscreen, + CoglOutput *output) +{ + float refresh_rate = cogl_output_get_refresh_rate (output); + if (refresh_rate != 0.0) + { + CoglFrameTimings *timings = cogl_onscreen_get_frame_timings (onscreen, + cogl_onscreen_get_frame_counter (onscreen)); + timings->refresh_interval = (int)(0.5 + (1000000. / refresh_rate)); + } +} + +static void _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, const int *user_rectangles, int n_rectangles) @@ -1315,6 +1480,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, uint32_t end_frame_vsync_counter = 0; CoglBool have_counter; CoglBool can_wait; + int x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple @@ -1325,6 +1491,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, CoglBool blit_sub_buffer_is_synchronized = _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED); + int framebuffer_width = cogl_framebuffer_get_width (framebuffer); int framebuffer_height = cogl_framebuffer_get_height (framebuffer); int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4); int i; @@ -1336,7 +1503,24 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, for (i = 0; i < n_rectangles; i++) { int *rect = &rectangles[4 * i]; + + if (i == 0) + { + x_min = rect[0]; + x_max = rect[0] + rect[2]; + y_min = rect[1]; + y_max = rect[1] + rect[3]; + } + else + { + x_min = MIN (x_min, rect[0]); + x_max = MAX (x_max, rect[0] + rect[2]); + y_min = MIN (y_min, rect[1]); + y_max = MAX (y_max, rect[1] + rect[3]); + } + rect[1] = framebuffer_height - rect[1] - rect[3]; + } _cogl_framebuffer_flush_state (framebuffer, @@ -1390,7 +1574,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, * additional extension so we can report the limited region of * the window damage to X/compositors. */ - context->glFinish (); + _cogl_winsys_wait_for_gpu (onscreen); if (blit_sub_buffer_is_synchronized && have_counter && can_wait) { @@ -1401,10 +1585,10 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, * any waits if we can see that the video sync count has * already progressed. */ if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter) - _cogl_winsys_wait_for_vblank (context); + _cogl_winsys_wait_for_vblank (onscreen); } else if (can_wait) - _cogl_winsys_wait_for_vblank (context); + _cogl_winsys_wait_for_vblank (onscreen); if (glx_renderer->glXCopySubBuffer) { @@ -1462,6 +1646,24 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, */ if (have_counter) glx_onscreen->last_swap_vsync_counter = end_frame_vsync_counter; + + if (!xlib_onscreen->is_foreign_xwin) + { + CoglOutput *output; + + x_min = CLAMP (x_min, 0, framebuffer_width); + x_max = CLAMP (x_max, 0, framebuffer_width); + y_min = CLAMP (y_min, 0, framebuffer_width); + y_max = CLAMP (y_max, 0, framebuffer_height); + + output = _cogl_xlib_renderer_output_for_rectangle (context->display->renderer, + xlib_onscreen->x + x_min, xlib_onscreen->y + y_min, + x_max - x_min, y_max - y_min); + if (output) + set_refresh_interval_from_output (onscreen, output); + } + + set_timings_complete (onscreen); } static void @@ -1521,16 +1723,16 @@ _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen) * obviously does not happen when we use _GLX_SWAP and let * the driver do the right thing */ - context->glFinish (); + _cogl_winsys_wait_for_gpu (onscreen); if (have_counter && can_wait) { if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter) - _cogl_winsys_wait_for_vblank (context); + _cogl_winsys_wait_for_vblank (onscreen); } else if (can_wait) - _cogl_winsys_wait_for_vblank (context); + _cogl_winsys_wait_for_vblank (onscreen); } } else @@ -1541,6 +1743,13 @@ _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen) if (have_counter) glx_onscreen->last_swap_vsync_counter = _cogl_winsys_get_vsync_counter (context); + + if (xlib_onscreen->output) + set_refresh_interval_from_output (onscreen, xlib_onscreen->output); + + if (!(glx_renderer->glXSwapInterval && + _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_WAIT))) + set_timings_complete (onscreen); } static uint32_t @@ -2203,7 +2412,9 @@ _cogl_winsys_poll_get_info (CoglContext *context, /* If we've already got a pending swap notify then we'll dispatch immediately */ - if (glx_display->pending_swap_notify || glx_display->pending_resize_notify) + if (glx_display->pending_swap_notify || + glx_display->pending_resize_notify || + glx_display->pending_frame_timings_notify) *timeout = 0; } @@ -2229,6 +2440,12 @@ flush_pending_notifications_cb (void *data, _cogl_onscreen_notify_resize (onscreen); glx_onscreen->pending_resize_notify = FALSE; } + + if (glx_onscreen->pending_frame_timings_notify) + { + _cogl_onscreen_notify_frame_timings (onscreen); + glx_onscreen->pending_frame_timings_notify = FALSE; + } } } @@ -2244,13 +2461,16 @@ _cogl_winsys_poll_dispatch (CoglContext *context, poll_fds, n_poll_fds); - if (glx_display->pending_swap_notify || glx_display->pending_resize_notify) + if (glx_display->pending_swap_notify || + glx_display->pending_resize_notify || + glx_display->pending_frame_timings_notify) { g_list_foreach (context->framebuffers, flush_pending_notifications_cb, NULL); glx_display->pending_swap_notify = FALSE; glx_display->pending_resize_notify = FALSE; + glx_display->pending_frame_timings_notify = FALSE; } } |