onscreen: Add CoglFrameInfo and _add_frame_callback() api

Add a CoglFrameInfo object that tracks timing information for frames
that are drawn. We track a frame counter and frame timing information
for each CoglOnscreen. Internally a CoglFrameInfo is automatically
created for each frame, delimited by cogl_onscreen_swap_buffers() or
cogl_onscreen_swap_region() calls.

CoglFrameInfos are delivered to applications via frame event callbacks
that can be registered with a new cogl_onscreen_add_frame_callback()
api. Two initial event types (dispatched on all platforms) have been
defined; a _SYNC event used for throttling the frame rate of
applications and a _COMPLETE event used so signify the end of a frame.

Note: This new _add_frame_callback() api makes the
cogl_onscreen_add_swap_complete_callback() api redundant and so it
should be considered deprecated. Since the _add_swap_complete_callback()
api is still experimental api, we will be looking to quickly migrate
users to the new api so we can remove the old api.

Reviewed-by: Robert Bragg <robert@linux.intel.com>

(cherry picked from commit 700401667db2522045e4623d78797b17f9184501)
This commit is contained in:
Owen W. Taylor 2012-11-12 11:58:10 -05:00 committed by Robert Bragg
parent 5ce058c0e5
commit 24733abf68
20 changed files with 972 additions and 69 deletions

View File

@ -112,6 +112,7 @@ cogl_experimental_h = \
$(srcdir)/cogl-clip-state.h \ $(srcdir)/cogl-clip-state.h \
$(srcdir)/cogl-framebuffer.h \ $(srcdir)/cogl-framebuffer.h \
$(srcdir)/cogl-onscreen.h \ $(srcdir)/cogl-onscreen.h \
$(srcdir)/cogl-frame-info.h \
$(srcdir)/cogl-vector.h \ $(srcdir)/cogl-vector.h \
$(srcdir)/cogl-euler.h \ $(srcdir)/cogl-euler.h \
$(srcdir)/cogl-output.h \ $(srcdir)/cogl-output.h \
@ -384,6 +385,8 @@ cogl_sources_c = \
$(srcdir)/cogl-spans.c \ $(srcdir)/cogl-spans.c \
$(srcdir)/cogl-journal-private.h \ $(srcdir)/cogl-journal-private.h \
$(srcdir)/cogl-journal.c \ $(srcdir)/cogl-journal.c \
$(srcdir)/cogl-frame-info-private.h \
$(srcdir)/cogl-frame-info.c \
$(srcdir)/cogl-framebuffer-private.h \ $(srcdir)/cogl-framebuffer-private.h \
$(srcdir)/cogl-framebuffer.c \ $(srcdir)/cogl-framebuffer.c \
$(srcdir)/cogl-onscreen-private.h \ $(srcdir)/cogl-onscreen-private.h \

View File

@ -50,6 +50,7 @@
#include "cogl-gpu-info-private.h" #include "cogl-gpu-info-private.h"
#include "cogl-gl-header.h" #include "cogl-gl-header.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-onscreen-private.h"
typedef struct typedef struct
{ {
@ -190,6 +191,11 @@ struct _CoglContext
gboolean have_last_offscreen_allocate_flags; gboolean have_last_offscreen_allocate_flags;
CoglOffscreenAllocateFlags last_offscreen_allocate_flags; CoglOffscreenAllocateFlags last_offscreen_allocate_flags;
GHashTable *swap_callback_closures;
int next_swap_callback_id;
CoglOnscreenEventList onscreen_events_queue;
CoglGLES2Context *current_gles2_context; CoglGLES2Context *current_gles2_context;
GQueue gles2_context_stack; GQueue gles2_context_stack;

View File

@ -312,6 +312,11 @@ cogl_context_new (CoglDisplay *display,
context->current_draw_buffer_state_flushed = 0; context->current_draw_buffer_state_flushed = 0;
context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL; context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL;
context->swap_callback_closures =
g_hash_table_new (g_direct_hash, g_direct_equal);
COGL_TAILQ_INIT (&context->onscreen_events_queue);
g_queue_init (&context->gles2_context_stack); g_queue_init (&context->gles2_context_stack);
context->journal_flush_attributes_array = context->journal_flush_attributes_array =
@ -507,6 +512,9 @@ _cogl_context_free (CoglContext *context)
if (context->blit_texture_pipeline) if (context->blit_texture_pipeline)
cogl_object_unref (context->blit_texture_pipeline); cogl_object_unref (context->blit_texture_pipeline);
if (context->swap_callback_closures)
g_hash_table_destroy (context->swap_callback_closures);
g_warn_if_fail (context->gles2_context_stack.length == 0); g_warn_if_fail (context->gles2_context_stack.length == 0);
if (context->journal_flush_attributes_array) if (context->journal_flush_attributes_array)

View File

@ -208,6 +208,8 @@ cogl_is_context (void *object);
* suported. * suported.
* @COGL_FEATURE_ID_DEPTH_TEXTURE: Whether #CoglFramebuffer support rendering * @COGL_FEATURE_ID_DEPTH_TEXTURE: Whether #CoglFramebuffer support rendering
* the depth buffer to a texture. * the depth buffer to a texture.
* @COGL_FEATURE_ID_PRESENTATION_TIME: Whether frame presentation
* time stamps will be recorded in #CoglFrameInfo objects.
* *
* All the capabilities that can vary between different GPUs supported * All the capabilities that can vary between different GPUs supported
* by Cogl. Applications that depend on any of these features should explicitly * by Cogl. Applications that depend on any of these features should explicitly
@ -237,6 +239,7 @@ typedef enum _CoglFeatureID
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
COGL_FEATURE_ID_GLES2_CONTEXT, COGL_FEATURE_ID_GLES2_CONTEXT,
COGL_FEATURE_ID_DEPTH_TEXTURE, COGL_FEATURE_ID_DEPTH_TEXTURE,
COGL_FEATURE_ID_PRESENTATION_TIME,
/*< private >*/ /*< private >*/
_COGL_N_FEATURE_IDS /*< skip >*/ _COGL_N_FEATURE_IDS /*< skip >*/

View File

@ -0,0 +1,43 @@
/*
* 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_INFO_PRIVATE_H
#define __COGL_FRAME_INFO_PRIVATE_H
#include "cogl-frame-info.h"
#include "cogl-object-private.h"
struct _CoglFrameInfo
{
CoglObject _parent;
int64_t frame_counter;
int64_t presentation_time;
float refresh_rate;
CoglOutput *output;
};
CoglFrameInfo *_cogl_frame_info_new (void);
#endif /* __COGL_FRAME_INFO_PRIVATE_H */

72
cogl/cogl-frame-info.c Normal file
View File

@ -0,0 +1,72 @@
/*
* 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-info-private.h"
static void _cogl_frame_info_free (CoglFrameInfo *info);
COGL_OBJECT_DEFINE (FrameInfo, frame_info);
CoglFrameInfo *
_cogl_frame_info_new (void)
{
CoglFrameInfo *info;
info = g_slice_new0 (CoglFrameInfo);
return _cogl_frame_info_object_new (info);
}
static void
_cogl_frame_info_free (CoglFrameInfo *info)
{
g_slice_free (CoglFrameInfo, info);
}
int64_t
cogl_frame_info_get_frame_counter (CoglFrameInfo *info)
{
return info->frame_counter;
}
int64_t
cogl_frame_info_get_presentation_time (CoglFrameInfo *info)
{
return info->presentation_time;
}
float
cogl_frame_info_get_refresh_rate (CoglFrameInfo *info)
{
return info->refresh_rate;
}
CoglOutput *
cogl_frame_info_get_output (CoglFrameInfo *info)
{
return info->output;
}

129
cogl/cogl-frame-info.h Normal file
View File

@ -0,0 +1,129 @@
/*
* 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_INFO_H
#define __COGL_FRAME_INFO_H
#include <cogl/cogl-types.h>
#include <cogl/cogl-output.h>
#include <glib.h>
G_BEGIN_DECLS
typedef struct _CoglFrameInfo CoglFrameInfo;
#define COGL_FRAME_INFO(X) ((CoglFrameInfo *)(X))
/**
* cogl_is_frame_info:
* @object: A #CoglObject pointer
*
* Gets whether the given object references a #CoglFrameInfo.
*
* Return value: %TRUE if the object references a #CoglFrameInfo
* and %FALSE otherwise.
* Since: 2.0
* Stability: unstable
*/
CoglBool
cogl_is_frame_info (void *object);
/**
* cogl_frame_info_get_frame_counter:
* @info: a #CoglFrameInfo object
*
* Gets the frame counter for the #CoglOnscreen that corresponds
* to this frame.
*
* Return value: The frame counter value
* Since: 1.14
* Stability: unstable
*/
int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info);
/**
* cogl_frame_info_get_presentation_time:
* @info: a #CoglFrameInfo object
*
* Gets the presentation time for the frame. This is the time at which
* the frame became visible to the user.
*
* The presentation time measured in nanoseconds is based on a
* monotonic time source. The time source is not necessarily
* correlated with system/wall clock time and may represent the time
* elapsed since some undefined system event such as when the system
* last booted.
*
* <note>Linux kernel version less that 3.8 can result in
* non-monotonic timestamps being reported when using a drm based
* OpenGL driver. Also some buggy Mesa drivers up to 9.0.1 may also
* incorrectly report non-monotonic timestamps.</note>
*
* Return value: the presentation time for the frame
* Since: 1.14
* Stability: unstable
*/
int64_t cogl_frame_info_get_presentation_time (CoglFrameInfo *info);
/**
* cogl_frame_info_get_refresh_rate:
* @info: a #CoglFrameInfo object
*
* Gets the refresh rate in Hertz for the output that the frame was on
* at the time the frame was presented.
*
* <note>Some platforms can't associate a #CoglOutput with a
* #CoglFrameInfo object but are able to report a refresh rate via
* this api. Therefore if you need this information then this api is
* more reliable than using cogl_frame_info_get_output() followed by
* cogl_output_get_refresh_rate().</note>
*
* Return value: the refresh rate in Hertz
* Since: 1.14
* Stability: unstable
*/
float cogl_frame_info_get_refresh_rate (CoglFrameInfo *info);
/**
* cogl_frame_info_get_output:
* @info: a #CoglFrameInfo object
*
* Gets the #CoglOutput that the swapped frame was presented to.
*
* Return value: The #CoglOutput that the frame was presented to, or
* %NULL if this could not be determined.
* Since: 1.14
* Stability: unstable
*/
CoglOutput *
cogl_frame_info_get_output (CoglFrameInfo *info);
G_END_DECLS
#endif /* __COGL_FRAME_INFO_H */

View File

@ -50,7 +50,8 @@ typedef struct _CoglGLXDisplay
GLXContext glx_context; GLXContext glx_context;
GLXWindow dummy_glxwin; GLXWindow dummy_glxwin;
Window dummy_xwin; Window dummy_xwin;
CoglBool pending_swap_notify; CoglBool pending_sync_notify;
CoglBool pending_complete_notify;
CoglBool pending_resize_notify; CoglBool pending_resize_notify;
} CoglGLXDisplay; } CoglGLXDisplay;

View File

@ -42,6 +42,15 @@ typedef struct _CoglGLXRenderer
/* Vblank stuff */ /* Vblank stuff */
int dri_fd; 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 pointing to libGL which we use to get glX functions out of */
GModule *libgl_module; GModule *libgl_module;

View File

@ -24,6 +24,7 @@
#ifndef __COGL_ONSCREEN_PRIVATE_H #ifndef __COGL_ONSCREEN_PRIVATE_H
#define __COGL_ONSCREEN_PRIVATE_H #define __COGL_ONSCREEN_PRIVATE_H
#include "cogl-onscreen.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-queue.h" #include "cogl-queue.h"
@ -33,17 +34,16 @@
#include <windows.h> #include <windows.h>
#endif #endif
typedef struct _CoglSwapBuffersNotifyEntry CoglSwapBuffersNotifyEntry; COGL_TAILQ_HEAD (CoglFrameCallbackList, CoglFrameClosure);
COGL_TAILQ_HEAD (CoglSwapBuffersNotifyList, CoglSwapBuffersNotifyEntry); struct _CoglFrameClosure
struct _CoglSwapBuffersNotifyEntry
{ {
COGL_TAILQ_ENTRY (CoglSwapBuffersNotifyEntry) list_node; COGL_TAILQ_ENTRY (CoglFrameClosure) list_node;
CoglFrameCallback callback;
CoglSwapBuffersNotify callback;
void *user_data; void *user_data;
unsigned int id; CoglUserDataDestroyCallback destroy;
}; };
typedef struct _CoglResizeNotifyEntry CoglResizeNotifyEntry; typedef struct _CoglResizeNotifyEntry CoglResizeNotifyEntry;
@ -59,6 +59,19 @@ struct _CoglResizeNotifyEntry
unsigned int id; unsigned int id;
}; };
typedef struct _CoglOnscreenEvent CoglOnscreenEvent;
COGL_TAILQ_HEAD (CoglOnscreenEventList, CoglOnscreenEvent);
struct _CoglOnscreenEvent
{
COGL_TAILQ_ENTRY (CoglOnscreenEvent) list_node;
CoglOnscreen *onscreen;
CoglFrameInfo *info;
CoglFrameEvent type;
};
struct _CoglOnscreen struct _CoglOnscreen
{ {
CoglFramebuffer _parent; CoglFramebuffer _parent;
@ -75,11 +88,17 @@ struct _CoglOnscreen
CoglBool swap_throttled; CoglBool swap_throttled;
CoglSwapBuffersNotifyList swap_callbacks; CoglFrameCallbackList frame_closures;
CoglBool resizable; CoglBool resizable;
CoglResizeNotifyList resize_callbacks; CoglResizeNotifyList resize_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() */
GQueue pending_frame_infos;
void *winsys; void *winsys;
}; };
@ -91,9 +110,15 @@ _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer,
int width, int height); int width, int height);
void void
_cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen); _cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info);
void
_cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info);
void void
_cogl_onscreen_notify_resize (CoglOnscreen *onscreen); _cogl_onscreen_notify_resize (CoglOnscreen *onscreen);
void
_cogl_dispatch_onscreen_events (CoglContext *context);
#endif /* __COGL_ONSCREEN_PRIVATE_H */ #endif /* __COGL_ONSCREEN_PRIVATE_H */

View File

@ -27,6 +27,7 @@
#include "cogl-util.h" #include "cogl-util.h"
#include "cogl-onscreen-private.h" #include "cogl-onscreen-private.h"
#include "cogl-frame-info-private.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-onscreen-template-private.h" #include "cogl-onscreen-template-private.h"
#include "cogl-context-private.h" #include "cogl-context-private.h"
@ -45,7 +46,7 @@ _cogl_onscreen_init_from_template (CoglOnscreen *onscreen,
{ {
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
COGL_TAILQ_INIT (&onscreen->swap_callbacks); COGL_TAILQ_INIT (&onscreen->frame_closures);
COGL_TAILQ_INIT (&onscreen->resize_callbacks); COGL_TAILQ_INIT (&onscreen->resize_callbacks);
framebuffer->config = onscreen_template->config; framebuffer->config = onscreen_template->config;
@ -115,7 +116,8 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer);
CoglResizeNotifyEntry *resize_entry; CoglResizeNotifyEntry *resize_entry;
CoglSwapBuffersNotifyEntry *swap_entry; CoglFrameClosure *frame_closure;
CoglFrameInfo *frame_info;
while ((resize_entry = COGL_TAILQ_FIRST (&onscreen->resize_callbacks))) while ((resize_entry = COGL_TAILQ_FIRST (&onscreen->resize_callbacks)))
{ {
@ -123,12 +125,20 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
g_slice_free (CoglResizeNotifyEntry, resize_entry); g_slice_free (CoglResizeNotifyEntry, resize_entry);
} }
while ((swap_entry = COGL_TAILQ_FIRST (&onscreen->swap_callbacks))) while ((frame_closure = COGL_TAILQ_FIRST (&onscreen->frame_closures)))
{ {
COGL_TAILQ_REMOVE (&onscreen->swap_callbacks, swap_entry, list_node); COGL_TAILQ_REMOVE (&onscreen->frame_closures, frame_closure, list_node);
g_slice_free (CoglSwapBuffersNotifyEntry, swap_entry);
if (frame_closure->destroy)
frame_closure->destroy (frame_closure->user_data);
g_slice_free (CoglFrameClosure, frame_closure);
} }
while ((frame_info = g_queue_pop_tail (&onscreen->pending_frame_infos)))
cogl_object_unref (frame_info);
g_queue_clear (&onscreen->pending_frame_infos);
if (framebuffer->context->window_buffer == COGL_FRAMEBUFFER (onscreen)) if (framebuffer->context->window_buffer == COGL_FRAMEBUFFER (onscreen))
framebuffer->context->window_buffer = NULL; framebuffer->context->window_buffer = NULL;
@ -141,22 +151,60 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
g_free (onscreen); g_free (onscreen);
} }
static void
_cogl_onscreen_queue_event (CoglOnscreen *onscreen,
CoglFrameEvent type,
CoglFrameInfo *info)
{
CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context;
CoglOnscreenEvent *event = g_slice_new (CoglOnscreenEvent);
event->onscreen = cogl_object_ref (onscreen);
event->info = cogl_object_ref (info);
event->type = type;
COGL_TAILQ_INSERT_TAIL (&ctx->onscreen_events_queue, event, list_node);
}
void void
cogl_onscreen_swap_buffers (CoglOnscreen *onscreen) cogl_onscreen_swap_buffers (CoglOnscreen *onscreen)
{ {
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
const CoglWinsysVtable *winsys; const CoglWinsysVtable *winsys;
CoglFrameInfo *info;
_COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); _COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN);
info = _cogl_frame_info_new ();
info->frame_counter = onscreen->frame_counter;
g_queue_push_tail (&onscreen->pending_frame_infos, info);
/* FIXME: we shouldn't need to flush *all* journals here! */ /* FIXME: we shouldn't need to flush *all* journals here! */
cogl_flush (); cogl_flush ();
winsys = _cogl_framebuffer_get_winsys (framebuffer); winsys = _cogl_framebuffer_get_winsys (framebuffer);
winsys->onscreen_swap_buffers (COGL_ONSCREEN (framebuffer)); winsys->onscreen_swap_buffers (COGL_ONSCREEN (framebuffer));
cogl_framebuffer_discard_buffers (framebuffer, cogl_framebuffer_discard_buffers (framebuffer,
COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_COLOR |
COGL_BUFFER_BIT_DEPTH | COGL_BUFFER_BIT_DEPTH |
COGL_BUFFER_BIT_STENCIL); COGL_BUFFER_BIT_STENCIL);
if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
{
CoglFrameInfo *info;
g_warn_if_fail (onscreen->pending_frame_infos.length == 1);
info = g_queue_pop_tail (&onscreen->pending_frame_infos);
_cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
_cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
cogl_object_unref (info);
}
onscreen->frame_counter++;
} }
void void
@ -166,9 +214,14 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
{ {
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
const CoglWinsysVtable *winsys; const CoglWinsysVtable *winsys;
CoglFrameInfo *info;
_COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); _COGL_RETURN_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN);
info = _cogl_frame_info_new ();
info->frame_counter = onscreen->frame_counter;
g_queue_push_tail (&onscreen->pending_frame_infos, info);
/* FIXME: we shouldn't need to flush *all* journals here! */ /* FIXME: we shouldn't need to flush *all* journals here! */
cogl_flush (); cogl_flush ();
@ -186,6 +239,22 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_COLOR |
COGL_BUFFER_BIT_DEPTH | COGL_BUFFER_BIT_DEPTH |
COGL_BUFFER_BIT_STENCIL); COGL_BUFFER_BIT_STENCIL);
if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
{
CoglFrameInfo *info;
g_warn_if_fail (onscreen->pending_frame_infos.length == 1);
info = g_queue_pop_tail (&onscreen->pending_frame_infos);
_cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
_cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
cogl_object_unref (info);
}
onscreen->frame_counter++;
} }
int int
@ -286,38 +355,107 @@ cogl_win32_onscreen_get_window (CoglOnscreen *onscreen)
#endif /* COGL_HAS_WIN32_SUPPORT */ #endif /* COGL_HAS_WIN32_SUPPORT */
CoglFrameClosure *
cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen,
CoglFrameCallback callback,
void *user_data,
CoglUserDataDestroyCallback destroy)
{
CoglFrameClosure *closure = g_slice_new0 (CoglFrameClosure);
closure->callback = callback;
closure->user_data = user_data;
closure->destroy = destroy;
COGL_TAILQ_INSERT_TAIL (&onscreen->frame_closures, closure, list_node);
return closure;
}
void
cogl_onscreen_remove_frame_callback (CoglOnscreen *onscreen,
CoglFrameClosure *closure)
{
_COGL_RETURN_IF_FAIL (closure);
if (closure->destroy)
closure->destroy (closure->user_data);
COGL_TAILQ_REMOVE (&onscreen->frame_closures, closure, list_node);
g_slice_free (CoglFrameClosure, closure);
}
typedef struct _SwapBufferCallbackState
{
CoglSwapBuffersNotify callback;
void *user_data;
} SwapBufferCallbackState;
static void
destroy_swap_buffers_callback_state (void *user_data)
{
g_slice_free (SwapBufferCallbackState, user_data);
}
static void
shim_swap_buffers_callback (CoglOnscreen *onscreen,
CoglFrameEvent event,
CoglFrameInfo *info,
void *user_data)
{
SwapBufferCallbackState *state = user_data;
/* XXX: Note that technically it is a change in semantics for this
* interface to forward _SYNC events here and also makes the api
* name somewhat missleading.
*
* In practice though this interface is currently used by
* applications for throttling, not because they are strictly
* interested in knowing when a frame has been presented and so
* forwarding _SYNC events should serve them better.
*/
if (event == COGL_FRAME_EVENT_SYNC)
state->callback (COGL_FRAMEBUFFER (onscreen), state->user_data);
}
unsigned int unsigned int
cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen, cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen,
CoglSwapBuffersNotify callback, CoglSwapBuffersNotify callback,
void *user_data) void *user_data)
{ {
CoglSwapBuffersNotifyEntry *entry = g_slice_new0 (CoglSwapBuffersNotifyEntry); CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context;
static int next_swap_buffers_callback_id = 0; SwapBufferCallbackState *state = g_slice_new (SwapBufferCallbackState);
CoglFrameClosure *closure;
unsigned int id = ctx->next_swap_callback_id++;
entry->callback = callback; state->callback = callback;
entry->user_data = user_data; state->user_data = user_data;
entry->id = next_swap_buffers_callback_id++;
COGL_TAILQ_INSERT_TAIL (&onscreen->swap_callbacks, entry, list_node); closure =
cogl_onscreen_add_frame_callback (onscreen,
shim_swap_buffers_callback,
state,
destroy_swap_buffers_callback_state);
return entry->id; g_hash_table_insert (ctx->swap_callback_closures,
GINT_TO_POINTER (id),
closure);
return id;
} }
void void
cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen, cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen,
unsigned int id) unsigned int id)
{ {
CoglSwapBuffersNotifyEntry *entry; CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context;
CoglFrameClosure *closure = g_hash_table_lookup (ctx->swap_callback_closures,
GINT_TO_POINTER (id));
COGL_TAILQ_FOREACH (entry, &onscreen->swap_callbacks, list_node) _COGL_RETURN_IF_FAIL (closure);
{
if (entry->id == id) cogl_onscreen_remove_frame_callback (onscreen, closure);
{
COGL_TAILQ_REMOVE (&onscreen->swap_callbacks, entry, list_node);
g_slice_free (CoglSwapBuffersNotifyEntry, entry);
break;
}
}
} }
void void
@ -365,16 +503,56 @@ cogl_onscreen_hide (CoglOnscreen *onscreen)
} }
} }
void static void
_cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen) notify_event (CoglOnscreen *onscreen,
CoglFrameEvent event,
CoglFrameInfo *info)
{ {
CoglSwapBuffersNotifyEntry *entry, *tmp; CoglFrameClosure *entry, *tmp;
COGL_TAILQ_FOREACH_SAFE (entry, COGL_TAILQ_FOREACH_SAFE (entry,
&onscreen->swap_callbacks, &onscreen->frame_closures,
list_node, list_node,
tmp) tmp)
entry->callback (COGL_FRAMEBUFFER (onscreen), entry->user_data); {
entry->callback (onscreen, event, info,
entry->user_data);
}
}
void
_cogl_dispatch_onscreen_events (CoglContext *context)
{
CoglOnscreenEvent *event, *tmp;
COGL_TAILQ_FOREACH_SAFE (event,
&context->onscreen_events_queue,
list_node,
tmp)
{
CoglOnscreen *onscreen = event->onscreen;
CoglFrameInfo *info = event->info;
notify_event (onscreen, event->type, info);
cogl_object_unref (onscreen);
cogl_object_unref (info);
COGL_TAILQ_REMOVE (&context->onscreen_events_queue, event, list_node);
g_slice_free (CoglOnscreenEvent, event);
}
}
void
_cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info)
{
notify_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
}
void
_cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info)
{
notify_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
} }
void void
@ -468,3 +646,8 @@ cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen,
} }
} }
int64_t
cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen)
{
return onscreen->frame_counter;
}

View File

@ -34,6 +34,8 @@
#include <cogl/cogl-context.h> #include <cogl/cogl-context.h>
#include <cogl/cogl-framebuffer.h> #include <cogl/cogl-framebuffer.h>
#include <cogl/cogl-frame-info.h>
#include <cogl/cogl-object.h>
COGL_BEGIN_DECLS COGL_BEGIN_DECLS
@ -380,6 +382,143 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
const int *rectangles, const int *rectangles,
int n_rectangles); int n_rectangles);
/**
* CoglFrameEvent:
* @COGL_FRAME_EVENT_SYNC: Notifies that the system compositor has
* acknowledged a frame and is ready for a
* new frame to be created.
* @COGL_FRAME_EVENT_COMPLETE: Notifies that a frame has ended. This
* is a good time for applications to
* collect statistics about the frame
* since the #CoglFrameInfo should hold
* the most data at this point. No other
* events should be expected after a
* @COGL_FRAME_EVENT_COMPLETE event.
*
* Identifiers that are passed to #CoglFrameCallback functions
* (registered using cogl_onscreen_add_frame_callback()) that
* mark the progression of a frame in some way which usually
* means that new information will have been accumulated in the
* frame's corresponding #CoglFrameInfo object.
*
* The last event that will be sent for a frame will be a
* @COGL_FRAME_EVENT_COMPLETE event and so these are a good
* opportunity to collect statistics about a frame since the
* #CoglFrameInfo should hold the most data at this point.
*
* <note>A frame may not be completed before the next frame can start
* so applications should avoid needing to collect all statistics for
* a particular frame before they can start a new frame.</note>
*
* Since: 1.14
* Stability: unstable
*/
typedef enum _CoglFrameEvent
{
COGL_FRAME_EVENT_SYNC = 1,
COGL_FRAME_EVENT_COMPLETE
} CoglFrameEvent;
/**
* CoglFrameCallback:
* @onscreen: The onscreen that the frame is associated with
* @event: A #CoglFrameEvent notifying how the frame has progressed
* @info: The meta information, such as timing information, about
* the frame that has progressed.
* @user_data: The user pointer passed to
* cogl_onscreen_add_frame_callback()
*
* Is a callback that can be registered via
* cogl_onscreen_add_frame_callback() to be called when a frame
* progresses in some notable way.
*
* Please see the documentation for #CoglFrameEvent and
* cogl_onscreen_add_frame_callback() for more details about what
* events can be notified.
*
* Since: 1.14
* Stability: unstable
*/
typedef void (*CoglFrameCallback) (CoglOnscreen *onscreen,
CoglFrameEvent event,
CoglFrameInfo *info,
void *user_data);
/**
* CoglFrameClosure:
*
* An opaque type that tracks a #CoglFrameCallback and associated user
* data. A #CoglFrameClosure pointer will be returned from
* cogl_onscreen_add_frame_callback() and it allows you to remove a
* callback later using cogl_onscreen_remove_frame_callback().
*
* Since: 1.14
* Stability: unstable
*/
typedef struct _CoglFrameClosure CoglFrameClosure;
/**
* cogl_onscreen_add_frame_callback:
* @onscreen: A #CoglOnscreen framebuffer
* @callback: A callback function to call for frame events
* @user_data: A private pointer to be passed to @callback
* @destroy: An optional callback to destroy @user_data when the
* @callback is removed or @onscreen is freed.
*
* Installs a @callback function that will be called for significant
* events relating to the given @onscreen framebuffer.
*
* The @callback will be used to notify when the system compositor is
* ready for this application to render a new frame. In this case
* %COGL_FRAME_EVENT_SYNC will be passed as the event argument to the
* given @callback in addition to the #CoglFrameInfo corresponding to
* the frame beeing acknowledged by the compositor.
*
* The @callback will also be called to notify when the frame has
* ended. In this case %COGL_FRAME_EVENT_COMPLETE will be passed as
* the event argument to the given @callback in addition to the
* #CoglFrameInfo corresponding to the newly presented frame. The
* meaning of "ended" here simply means that no more timing
* information will be collected within the corresponding
* #CoglFrameInfo and so this is a good opportunity to analyse the
* given info. It does not necessarily mean that the GPU has finished
* rendering the corresponding frame.
*
* We highly recommend throttling your application according to
* %COGL_FRAME_EVENT_SYNC events so that your application can avoid
* wasting resources, drawing more frames than your system compositor
* can display.
*
* Return value: a #CoglFrameClosure pointer that can be used to
* remove the callback and associated @user_data later.
* Since: 1.14
* Stability: unstable
*/
CoglFrameClosure *
cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen,
CoglFrameCallback callback,
void *user_data,
CoglUserDataDestroyCallback destroy);
/**
* cogl_onscreen_remove_frame_callback:
* @onscreen: A #CoglOnscreen
* @closure: A #CoglFrameClosure returned from
* cogl_onscreen_add_frame_callback()
*
* Removes a callback and associated user data that were previously
* registered using cogl_onscreen_add_frame_callback().
*
* If a destroy callback was passed to
* cogl_onscreen_add_frame_callback() to destroy the user data then
* this will get called.
*
* Since: 1.14
* Stability: unstable
*/
void
cogl_onscreen_remove_frame_callback (CoglOnscreen *onscreen,
CoglFrameClosure *closure);
typedef void (*CoglSwapBuffersNotify) (CoglFramebuffer *framebuffer, typedef void (*CoglSwapBuffersNotify) (CoglFramebuffer *framebuffer,
void *user_data); void *user_data);
@ -407,6 +546,7 @@ typedef void (*CoglSwapBuffersNotify) (CoglFramebuffer *framebuffer,
* the callback later. * the callback later.
* Since: 1.10 * Since: 1.10
* Stability: unstable * Stability: unstable
* Deprecated: 1.14: Use cogl_onscreen_add_swap_complete_callback
*/ */
unsigned int unsigned int
cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen, cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen,
@ -423,6 +563,7 @@ cogl_onscreen_add_swap_buffers_callback (CoglOnscreen *onscreen,
* *
* Since: 1.10 * Since: 1.10
* Stability: unstable * Stability: unstable
* Deprecated: 1.14: Use cogl_onscreen_remove_swap_complete_callback
*/ */
void void
cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen, cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen,
@ -580,6 +721,21 @@ cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen,
CoglBool CoglBool
cogl_is_onscreen (void *object); 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: 1.14
* Stability: unstable
*/
int64_t
cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen);
COGL_END_DECLS COGL_END_DECLS
#endif /* __COGL_ONSCREEN_H */ #endif /* __COGL_ONSCREEN_H */

View File

@ -44,6 +44,13 @@ cogl_poll_get_info (CoglContext *context,
_COGL_RETURN_IF_FAIL (n_poll_fds != NULL); _COGL_RETURN_IF_FAIL (n_poll_fds != NULL);
_COGL_RETURN_IF_FAIL (timeout != NULL); _COGL_RETURN_IF_FAIL (timeout != NULL);
if (!COGL_TAILQ_EMPTY (&context->onscreen_events_queue))
{
*n_poll_fds = 0;
*timeout = 0;
return;
}
winsys = _cogl_context_get_winsys (context); winsys = _cogl_context_get_winsys (context);
if (winsys->poll_get_info) if (winsys->poll_get_info)
@ -70,6 +77,9 @@ cogl_poll_dispatch (CoglContext *context,
_COGL_RETURN_IF_FAIL (cogl_is_context (context)); _COGL_RETURN_IF_FAIL (cogl_is_context (context));
if (!COGL_TAILQ_EMPTY (&context->onscreen_events_queue))
_cogl_dispatch_onscreen_events (context);
winsys = _cogl_context_get_winsys (context); winsys = _cogl_context_get_winsys (context);
if (winsys->poll_dispatch) if (winsys->poll_dispatch)

View File

@ -83,5 +83,5 @@ cogl_sdl_handle_event (CoglContext *context, SDL_Event *event)
void void
cogl_sdl_idle (CoglContext *context) cogl_sdl_idle (CoglContext *context)
{ {
/* NOP since Cogl doesn't currently need to do anything when idle */ _cogl_dispatch_onscreen_events (context);
} }

View File

@ -832,6 +832,9 @@ typedef enum _CoglWinsysFeature
/* Avaiable if the age of the back buffer can be queried */ /* Avaiable if the age of the back buffer can be queried */
COGL_WINSYS_FEATURE_BUFFER_AGE, COGL_WINSYS_FEATURE_BUFFER_AGE,
/* Avaiable if the winsys directly handles _SYNC and _COMPLETE events */
COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
COGL_WINSYS_FEATURE_N_FEATURES COGL_WINSYS_FEATURE_N_FEATURES
} CoglWinsysFeature; } CoglWinsysFeature;

View File

@ -27,6 +27,7 @@
typedef struct _CoglX11Renderer typedef struct _CoglX11Renderer
{ {
int damage_base; int damage_base;
int randr_base;
} CoglX11Renderer; } CoglX11Renderer;
#endif /* __COGL_RENDERER_X11_PRIVATE_H */ #endif /* __COGL_RENDERER_X11_PRIVATE_H */

View File

@ -105,6 +105,7 @@
#include <cogl/cogl-snippet.h> #include <cogl/cogl-snippet.h>
#include <cogl/cogl-framebuffer.h> #include <cogl/cogl-framebuffer.h>
#include <cogl/cogl-onscreen.h> #include <cogl/cogl-onscreen.h>
#include <cogl/cogl-frame-info.h>
#include <cogl/cogl-poll.h> #include <cogl/cogl-poll.h>
#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT) #if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
#include <cogl/cogl-kms-renderer.h> #include <cogl/cogl-kms-renderer.h>

View File

@ -754,9 +754,13 @@ _cogl_winsys_egl_context_init (CoglContext *context,
{ {
COGL_FLAGS_SET (context->features, COGL_FLAGS_SET (context->features,
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE); COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE);
/* TODO: remove this deprecated feature */
COGL_FLAGS_SET (context->winsys_features, COGL_FLAGS_SET (context->winsys_features,
COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT,
TRUE); TRUE);
COGL_FLAGS_SET (context->winsys_features,
COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
TRUE);
return TRUE; return TRUE;
} }
@ -894,8 +898,13 @@ flush_pending_swap_notify_cb (void *data,
if (kms_onscreen->pending_swap_notify) if (kms_onscreen->pending_swap_notify)
{ {
_cogl_onscreen_notify_swap_buffers (onscreen); CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
_cogl_onscreen_notify_frame_sync (onscreen, info);
_cogl_onscreen_notify_complete (onscreen, info);
kms_onscreen->pending_swap_notify = FALSE; kms_onscreen->pending_swap_notify = FALSE;
cogl_object_unref (info);
} }
} }
} }

View File

@ -177,7 +177,8 @@ COGL_WINSYS_FEATURE_BEGIN (255, 255,
"INTEL\0", "INTEL\0",
"swap_event\0", "swap_event\0",
0, 0,
COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT) COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT |
COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)
COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_END ()
COGL_WINSYS_FEATURE_BEGIN (255, 255, COGL_WINSYS_FEATURE_BEGIN (255, 255,

View File

@ -42,6 +42,7 @@
#include "cogl-texture-2d-private.h" #include "cogl-texture-2d-private.h"
#include "cogl-texture-rectangle-private.h" #include "cogl-texture-rectangle-private.h"
#include "cogl-pipeline-opengl-private.h" #include "cogl-pipeline-opengl-private.h"
#include "cogl-frame-info-private.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-onscreen-private.h" #include "cogl-onscreen-private.h"
#include "cogl-swap-chain-private.h" #include "cogl-swap-chain-private.h"
@ -53,7 +54,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h> #include <fcntl.h>
#include <time.h>
#include <glib/gi18n-lib.h> #include <glib/gi18n-lib.h>
@ -81,7 +84,8 @@ typedef struct _CoglOnscreenGLX
CoglOnscreenXlib _parent; CoglOnscreenXlib _parent;
GLXDrawable glxwin; GLXDrawable glxwin;
uint32_t last_swap_vsync_counter; uint32_t last_swap_vsync_counter;
CoglBool pending_swap_notify; CoglBool pending_sync_notify;
CoglBool pending_complete_notify;
CoglBool pending_resize_notify; CoglBool pending_resize_notify;
} CoglOnscreenGLX; } CoglOnscreenGLX;
@ -171,23 +175,146 @@ find_onscreen_for_xid (CoglContext *context, uint32_t xid)
} }
static void 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;
CoglDisplay *display = context->display; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
CoglGLXDisplay *glx_display = display->winsys; int64_t ust;
int64_t msc;
int64_t sbc;
struct timeval tv;
struct timespec ts;
int64_t current_system_time;
int64_t 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 time source that existing (buggy) linux drm drivers
* use */
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 time source that the newer (fixed) linux drm
* drivers use (Linux >= 3.8) */
clock_gettime (CLOCK_MONOTONIC, &ts);
current_monotonic_time = (ts.tv_sec * G_GINT64_CONSTANT (1000000)) +
(ts.tv_nsec / G_GINT64_CONSTANT (1000));
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 int64_t
ust_to_nanoseconds (CoglRenderer *renderer,
GLXDrawable drawable,
int64_t ust)
{
CoglGLXRenderer *glx_renderer = renderer->winsys;
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:
case COGL_GLX_UST_IS_MONOTONIC_TIME:
return 1000 * ust;
case COGL_GLX_UST_IS_OTHER:
/* In this case the scale of UST is undefined so we can't easily
* scale to nanoseconds.
*
* For example the driver may be reporting the rdtsc CPU counter
* as UST values and so the scale would need to be determined
* empirically.
*
* Potentially we could block for a known duration within
* ensure_ust_type() to measure the timescale of UST but for now
* we just ignore unknown time sources */
return 0;
}
return 0;
}
static void
set_sync_pending (CoglOnscreen *onscreen)
{
CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
CoglGLXDisplay *glx_display = context->display->winsys;
glx_display->pending_sync_notify = TRUE;
glx_onscreen->pending_sync_notify = TRUE;
}
static void
set_complete_pending (CoglOnscreen *onscreen)
{
CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
CoglGLXDisplay *glx_display = context->display->winsys;
glx_display->pending_complete_notify = TRUE;
glx_onscreen->pending_complete_notify = TRUE;
}
static void
notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event)
{
CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable);
CoglOnscreenGLX *glx_onscreen; CoglOnscreenGLX *glx_onscreen;
if (!onscreen) if (!onscreen)
return; return;
glx_onscreen = onscreen->winsys; glx_onscreen = onscreen->winsys;
/* We only want to notify that the swap is complete when the /* We only want to notify that the swap is complete when the
application calls cogl_context_dispatch so instead of immediately application calls cogl_context_dispatch so instead of immediately
notifying we'll set a flag to remember to notify later */ notifying we'll set a flag to remember to notify later */
glx_display->pending_swap_notify = TRUE; set_sync_pending (onscreen);
glx_onscreen->pending_swap_notify = TRUE;
if (swap_event->ust != 0)
{
CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos);
info->presentation_time =
ust_to_nanoseconds (context->display->renderer,
glx_onscreen->glxwin,
swap_event->ust);
}
set_complete_pending (onscreen);
} }
static void static void
@ -295,7 +422,7 @@ glx_event_filter_cb (XEvent *xevent, void *data)
{ {
GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent; GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent;
notify_swap_buffers (context, swap_event->drawable); notify_swap_buffers (context, swap_event);
/* remove SwapComplete events from the queue */ /* remove SwapComplete events from the queue */
return COGL_FILTER_REMOVE; return COGL_FILTER_REMOVE;
@ -434,8 +561,8 @@ update_base_winsys_features (CoglRenderer *renderer)
COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
TRUE); TRUE);
if (glx_renderer->pf_glXWaitVideoSync || if (glx_renderer->glXWaitVideoSync ||
glx_renderer->pf_glXWaitForMsc) glx_renderer->glXWaitForMsc)
COGL_FLAGS_SET (glx_renderer->base_winsys_features, COGL_FLAGS_SET (glx_renderer->base_winsys_features,
COGL_WINSYS_FEATURE_VBLANK_WAIT, COGL_WINSYS_FEATURE_VBLANK_WAIT,
TRUE); TRUE);
@ -569,10 +696,16 @@ update_winsys_features (CoglContext *context, CoglError **error)
COGL_FLAGS_SET (context->winsys_features, COGL_FLAGS_SET (context->winsys_features,
COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE); COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT)) if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
COGL_FLAGS_SET (context->features, {
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, /* TODO: remove this deprecated feature */
TRUE); COGL_FLAGS_SET (context->features,
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
TRUE);
COGL_FLAGS_SET (context->features,
COGL_FEATURE_ID_PRESENTATION_TIME,
TRUE);
}
return TRUE; return TRUE;
} }
@ -1111,7 +1244,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
} }
#ifdef GLX_INTEL_swap_event #ifdef GLX_INTEL_swap_event
if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT)) if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
{ {
GLXDrawable drawable = GLXDrawable drawable =
glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
@ -1280,15 +1413,30 @@ _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
} }
static void 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; CoglGLXRenderer *glx_renderer;
CoglXlibRenderer *xlib_renderer;
glx_renderer = ctx->display->renderer->winsys; glx_renderer = ctx->display->renderer->winsys;
xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer);
if (glx_renderer->glXWaitForMsc || if (glx_renderer->glXWaitForMsc ||
glx_renderer->glXGetVideoSync) glx_renderer->glXGetVideoSync)
{ {
CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos);
if (glx_renderer->glXWaitForMsc) if (glx_renderer->glXWaitForMsc)
{ {
CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
@ -1302,15 +1450,23 @@ _cogl_winsys_wait_for_vblank (CoglContext *ctx)
glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable, glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable,
0, 2, (msc + 1) % 2, 0, 2, (msc + 1) % 2,
&ust, &msc, &sbc); &ust, &msc, &sbc);
info->presentation_time = ust_to_nanoseconds (ctx->display->renderer,
drawable,
ust);
} }
else else
{ {
uint32_t current_count; uint32_t current_count;
struct timespec ts;
glx_renderer->glXGetVideoSync (&current_count); glx_renderer->glXGetVideoSync (&current_count);
glx_renderer->glXWaitVideoSync (2, glx_renderer->glXWaitVideoSync (2,
(current_count + 1) % 2, (current_count + 1) % 2,
&current_count); &current_count);
clock_gettime (CLOCK_MONOTONIC, &ts);
info->presentation_time =
ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec;
} }
} }
} }
@ -1352,6 +1508,22 @@ _cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen)
return age; return age;
} }
static void
set_frame_info_output (CoglOnscreen *onscreen,
CoglOutput *output)
{
CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos);
info->output = output;
if (output)
{
float refresh_rate = cogl_output_get_refresh_rate (output);
if (refresh_rate != 0.0)
info->refresh_rate = refresh_rate;
}
}
static void static void
_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
const int *user_rectangles, const int *user_rectangles,
@ -1369,6 +1541,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
uint32_t end_frame_vsync_counter = 0; uint32_t end_frame_vsync_counter = 0;
CoglBool have_counter; CoglBool have_counter;
CoglBool can_wait; 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 * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple
@ -1379,6 +1552,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
CoglBool blit_sub_buffer_is_synchronized = CoglBool blit_sub_buffer_is_synchronized =
_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_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 framebuffer_height = cogl_framebuffer_get_height (framebuffer);
int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4); int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4);
int i; int i;
@ -1390,7 +1564,24 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
for (i = 0; i < n_rectangles; i++) for (i = 0; i < n_rectangles; i++)
{ {
int *rect = &rectangles[4 * 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]; rect[1] = framebuffer_height - rect[1] - rect[3];
} }
_cogl_framebuffer_flush_state (framebuffer, _cogl_framebuffer_flush_state (framebuffer,
@ -1444,7 +1635,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
* additional extension so we can report the limited region of * additional extension so we can report the limited region of
* the window damage to X/compositors. * the window damage to X/compositors.
*/ */
context->glFinish (); _cogl_winsys_wait_for_gpu (onscreen);
if (blit_sub_buffer_is_synchronized && have_counter && can_wait) if (blit_sub_buffer_is_synchronized && have_counter && can_wait)
{ {
@ -1455,10 +1646,10 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
* any waits if we can see that the video sync count has * any waits if we can see that the video sync count has
* already progressed. */ * already progressed. */
if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter) 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) else if (can_wait)
_cogl_winsys_wait_for_vblank (context); _cogl_winsys_wait_for_vblank (onscreen);
if (glx_renderer->glXCopySubBuffer) if (glx_renderer->glXCopySubBuffer)
{ {
@ -1516,6 +1707,36 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
*/ */
if (have_counter) if (have_counter)
glx_onscreen->last_swap_vsync_counter = end_frame_vsync_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);
set_frame_info_output (onscreen, output);
}
/* XXX: we don't get SwapComplete events based on how we implement
* the _swap_region() API but if cogl-onscreen.c knows we are
* handling _SYNC and _COMPLETE events in the winsys then we need to
* send fake events in this case.
*/
if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
{
set_sync_pending (onscreen);
set_complete_pending (onscreen);
}
} }
static void static void
@ -1575,16 +1796,16 @@ _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
* obviously does not happen when we use _GLX_SWAP and let * obviously does not happen when we use _GLX_SWAP and let
* the driver do the right thing * the driver do the right thing
*/ */
context->glFinish (); _cogl_winsys_wait_for_gpu (onscreen);
if (have_counter && can_wait) if (have_counter && can_wait)
{ {
if (glx_onscreen->last_swap_vsync_counter == if (glx_onscreen->last_swap_vsync_counter ==
end_frame_vsync_counter) end_frame_vsync_counter)
_cogl_winsys_wait_for_vblank (context); _cogl_winsys_wait_for_vblank (onscreen);
} }
else if (can_wait) else if (can_wait)
_cogl_winsys_wait_for_vblank (context); _cogl_winsys_wait_for_vblank (onscreen);
} }
} }
else else
@ -1595,6 +1816,8 @@ _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
if (have_counter) if (have_counter)
glx_onscreen->last_swap_vsync_counter = glx_onscreen->last_swap_vsync_counter =
_cogl_winsys_get_vsync_counter (context); _cogl_winsys_get_vsync_counter (context);
set_frame_info_output (onscreen, xlib_onscreen->output);
} }
static uint32_t static uint32_t
@ -2257,7 +2480,9 @@ _cogl_winsys_poll_get_info (CoglContext *context,
/* If we've already got a pending swap notify then we'll dispatch /* If we've already got a pending swap notify then we'll dispatch
immediately */ immediately */
if (glx_display->pending_swap_notify || glx_display->pending_resize_notify) if (glx_display->pending_sync_notify ||
glx_display->pending_resize_notify ||
glx_display->pending_complete_notify)
*timeout = 0; *timeout = 0;
} }
@ -2272,10 +2497,22 @@ flush_pending_notifications_cb (void *data,
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
if (glx_onscreen->pending_swap_notify) if (glx_onscreen->pending_sync_notify)
{ {
_cogl_onscreen_notify_swap_buffers (onscreen); CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos);
glx_onscreen->pending_swap_notify = FALSE;
_cogl_onscreen_notify_frame_sync (onscreen, info);
glx_onscreen->pending_sync_notify = FALSE;
}
if (glx_onscreen->pending_complete_notify)
{
CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
_cogl_onscreen_notify_complete (onscreen, info);
glx_onscreen->pending_complete_notify = FALSE;
cogl_object_unref (info);
} }
if (glx_onscreen->pending_resize_notify) if (glx_onscreen->pending_resize_notify)
@ -2298,13 +2535,16 @@ _cogl_winsys_poll_dispatch (CoglContext *context,
poll_fds, poll_fds,
n_poll_fds); n_poll_fds);
if (glx_display->pending_swap_notify || glx_display->pending_resize_notify) if (glx_display->pending_sync_notify ||
glx_display->pending_resize_notify ||
glx_display->pending_complete_notify)
{ {
g_list_foreach (context->framebuffers, g_list_foreach (context->framebuffers,
flush_pending_notifications_cb, flush_pending_notifications_cb,
NULL); NULL);
glx_display->pending_swap_notify = FALSE; glx_display->pending_sync_notify = FALSE;
glx_display->pending_resize_notify = FALSE; glx_display->pending_resize_notify = FALSE;
glx_display->pending_complete_notify = FALSE;
} }
} }