mirror of
https://github.com/brl/mutter.git
synced 2025-02-23 16:34:10 +00:00
For NVIDIA proprietary drivers, implement sync events with a thread
It's a good guess that the buffer swap will occur at the next vblank, so use glXWaitVideoSync in a separate thread to deliver a sync event rather than just letting the client block when frame drawing, which can signficantly change app logic as compared to the INTEL_swap_event case. https://bugzilla.gnome.org/show_bug.cgi?id=779039
This commit is contained in:
parent
e078838774
commit
690b232258
@ -77,6 +77,9 @@ typedef enum
|
|||||||
COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE,
|
COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE,
|
||||||
COGL_PRIVATE_FEATURE_GL_EMBEDDED,
|
COGL_PRIVATE_FEATURE_GL_EMBEDDED,
|
||||||
COGL_PRIVATE_FEATURE_GL_WEB,
|
COGL_PRIVATE_FEATURE_GL_WEB,
|
||||||
|
/* This is currently only implemented for GLX, but isn't actually
|
||||||
|
* that winsys dependent */
|
||||||
|
COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT,
|
||||||
|
|
||||||
COGL_N_PRIVATE_FEATURES
|
COGL_N_PRIVATE_FEATURES
|
||||||
} CoglPrivateFeature;
|
} CoglPrivateFeature;
|
||||||
|
@ -65,12 +65,16 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
/* This is a relatively new extension */
|
/* This is a relatively new extension */
|
||||||
#ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
|
#ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
|
||||||
#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
|
#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
|
||||||
@ -100,6 +104,14 @@ typedef struct _CoglOnscreenGLX
|
|||||||
CoglBool pending_sync_notify;
|
CoglBool pending_sync_notify;
|
||||||
CoglBool pending_complete_notify;
|
CoglBool pending_complete_notify;
|
||||||
CoglBool pending_resize_notify;
|
CoglBool pending_resize_notify;
|
||||||
|
|
||||||
|
GThread *swap_wait_thread;
|
||||||
|
GQueue *swap_wait_queue;
|
||||||
|
GCond swap_wait_cond;
|
||||||
|
GMutex swap_wait_mutex;
|
||||||
|
int swap_wait_pipe[2];
|
||||||
|
GLXContext swap_wait_context;
|
||||||
|
CoglBool closing_down;
|
||||||
} CoglOnscreenGLX;
|
} CoglOnscreenGLX;
|
||||||
|
|
||||||
typedef struct _CoglPixmapTextureEyeGLX
|
typedef struct _CoglPixmapTextureEyeGLX
|
||||||
@ -885,6 +897,28 @@ update_winsys_features (CoglContext *context, CoglError **error)
|
|||||||
COGL_FEATURE_ID_PRESENTATION_TIME,
|
COGL_FEATURE_ID_PRESENTATION_TIME,
|
||||||
TRUE);
|
TRUE);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CoglGpuInfo *info = &context->gpu;
|
||||||
|
if (glx_display->have_vblank_counter &&
|
||||||
|
info->vendor == COGL_GPU_INFO_VENDOR_NVIDIA)
|
||||||
|
{
|
||||||
|
COGL_FLAGS_SET (context->winsys_features,
|
||||||
|
COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, TRUE);
|
||||||
|
COGL_FLAGS_SET (context->winsys_features,
|
||||||
|
COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE);
|
||||||
|
/* TODO: remove this deprecated feature */
|
||||||
|
COGL_FLAGS_SET (context->features,
|
||||||
|
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
|
||||||
|
TRUE);
|
||||||
|
COGL_FLAGS_SET (context->features,
|
||||||
|
COGL_FEATURE_ID_PRESENTATION_TIME,
|
||||||
|
TRUE);
|
||||||
|
COGL_FLAGS_SET (context->private_features,
|
||||||
|
COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT,
|
||||||
|
TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* We'll manually handle queueing dirty events in response to
|
/* We'll manually handle queueing dirty events in response to
|
||||||
* Expose events from X */
|
* Expose events from X */
|
||||||
@ -1481,7 +1515,8 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GLX_INTEL_swap_event
|
#ifdef GLX_INTEL_swap_event
|
||||||
if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
|
if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT) &&
|
||||||
|
!_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT))
|
||||||
{
|
{
|
||||||
GLXDrawable drawable =
|
GLXDrawable drawable =
|
||||||
glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
|
glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
|
||||||
@ -1524,6 +1559,31 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
|
|||||||
xlib_onscreen->output = NULL;
|
xlib_onscreen->output = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (glx_onscreen->swap_wait_thread)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
glx_onscreen->closing_down = TRUE;
|
||||||
|
g_cond_signal (&glx_onscreen->swap_wait_cond);
|
||||||
|
g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
g_thread_join (glx_onscreen->swap_wait_thread);
|
||||||
|
glx_onscreen->swap_wait_thread = NULL;
|
||||||
|
|
||||||
|
g_cond_clear (&glx_onscreen->swap_wait_cond);
|
||||||
|
g_mutex_clear (&glx_onscreen->swap_wait_mutex);
|
||||||
|
|
||||||
|
g_queue_free (glx_onscreen->swap_wait_queue);
|
||||||
|
glx_onscreen->swap_wait_queue = NULL;
|
||||||
|
|
||||||
|
_cogl_poll_renderer_remove_fd (context->display->renderer,
|
||||||
|
glx_onscreen->swap_wait_pipe[0]);
|
||||||
|
|
||||||
|
close (glx_onscreen->swap_wait_pipe[0]);
|
||||||
|
close (glx_onscreen->swap_wait_pipe[1]);
|
||||||
|
|
||||||
|
glx_renderer->glXDestroyContext (xlib_renderer->xdpy,
|
||||||
|
glx_onscreen->swap_wait_context);
|
||||||
|
}
|
||||||
|
|
||||||
_cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
|
_cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
|
||||||
|
|
||||||
drawable =
|
drawable =
|
||||||
@ -1757,6 +1817,199 @@ set_frame_info_output (CoglOnscreen *onscreen,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
threaded_swap_wait (gpointer data)
|
||||||
|
{
|
||||||
|
CoglOnscreen *onscreen = data;
|
||||||
|
|
||||||
|
CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
|
||||||
|
|
||||||
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
|
||||||
|
CoglContext *context = framebuffer->context;
|
||||||
|
CoglDisplay *display = context->display;
|
||||||
|
CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer);
|
||||||
|
CoglGLXDisplay *glx_display = display->winsys;
|
||||||
|
CoglGLXRenderer *glx_renderer = display->renderer->winsys;
|
||||||
|
GLXDrawable dummy_drawable;
|
||||||
|
|
||||||
|
if (glx_display->dummy_glxwin)
|
||||||
|
dummy_drawable = glx_display->dummy_glxwin;
|
||||||
|
else
|
||||||
|
dummy_drawable = glx_display->dummy_xwin;
|
||||||
|
|
||||||
|
glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
|
||||||
|
dummy_drawable,
|
||||||
|
dummy_drawable,
|
||||||
|
glx_onscreen->swap_wait_context);
|
||||||
|
|
||||||
|
g_mutex_lock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
gpointer queue_element;
|
||||||
|
uint32_t vblank_counter;
|
||||||
|
|
||||||
|
while (!glx_onscreen->closing_down && glx_onscreen->swap_wait_queue->length == 0)
|
||||||
|
g_cond_wait (&glx_onscreen->swap_wait_cond, &glx_onscreen->swap_wait_mutex);
|
||||||
|
|
||||||
|
if (glx_onscreen->closing_down)
|
||||||
|
break;
|
||||||
|
|
||||||
|
queue_element = g_queue_pop_tail (glx_onscreen->swap_wait_queue);
|
||||||
|
vblank_counter = GPOINTER_TO_UINT(queue_element);
|
||||||
|
|
||||||
|
g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
glx_renderer->glXWaitVideoSync (2,
|
||||||
|
(vblank_counter + 1) % 2,
|
||||||
|
&vblank_counter);
|
||||||
|
g_mutex_lock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
|
||||||
|
if (!glx_onscreen->closing_down)
|
||||||
|
{
|
||||||
|
int bytes_written = 0;
|
||||||
|
|
||||||
|
union {
|
||||||
|
char bytes[8];
|
||||||
|
int64_t presentation_time;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
u.presentation_time = get_monotonic_time_ns ();
|
||||||
|
|
||||||
|
while (bytes_written < 8)
|
||||||
|
{
|
||||||
|
int res = write (glx_onscreen->swap_wait_pipe[1], u.bytes + bytes_written, 8 - bytes_written);
|
||||||
|
if (res == -1)
|
||||||
|
{
|
||||||
|
if (errno != EINTR)
|
||||||
|
g_error ("Error writing to swap notification pipe: %s\n",
|
||||||
|
g_strerror (errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytes_written += res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
|
||||||
|
glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t
|
||||||
|
threaded_swap_wait_pipe_prepare (void *user_data)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
threaded_swap_wait_pipe_dispatch (void *user_data, int revents)
|
||||||
|
{
|
||||||
|
CoglOnscreen *onscreen = user_data;
|
||||||
|
CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
|
||||||
|
|
||||||
|
CoglFrameInfo *info;
|
||||||
|
|
||||||
|
if ((revents & COGL_POLL_FD_EVENT_IN))
|
||||||
|
{
|
||||||
|
int bytes_read = 0;
|
||||||
|
|
||||||
|
union {
|
||||||
|
char bytes[8];
|
||||||
|
int64_t presentation_time;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
while (bytes_read < 8)
|
||||||
|
{
|
||||||
|
int res = read (glx_onscreen->swap_wait_pipe[0], u.bytes + bytes_read, 8 - bytes_read);
|
||||||
|
if (res == -1)
|
||||||
|
{
|
||||||
|
if (errno != EINTR)
|
||||||
|
g_error ("Error reading from swap notification pipe: %s\n",
|
||||||
|
g_strerror (errno));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytes_read += res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_sync_pending (onscreen);
|
||||||
|
set_complete_pending (onscreen);
|
||||||
|
|
||||||
|
info = g_queue_peek_head (&onscreen->pending_frame_infos);
|
||||||
|
info->presentation_time = u.presentation_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_threaded_swap_wait (CoglOnscreen *onscreen,
|
||||||
|
uint32_t vblank_counter)
|
||||||
|
{
|
||||||
|
CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
|
||||||
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
|
||||||
|
CoglContext *context = framebuffer->context;
|
||||||
|
|
||||||
|
if (glx_onscreen->swap_wait_thread == NULL)
|
||||||
|
{
|
||||||
|
CoglDisplay *display = context->display;
|
||||||
|
CoglGLXRenderer *glx_renderer = display->renderer->winsys;
|
||||||
|
CoglGLXDisplay *glx_display = display->winsys;
|
||||||
|
CoglOnscreenXlib *xlib_onscreen = onscreen->winsys;
|
||||||
|
CoglXlibRenderer *xlib_renderer =
|
||||||
|
_cogl_xlib_renderer_get_data (display->renderer);
|
||||||
|
|
||||||
|
GLXDrawable drawable =
|
||||||
|
glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ensure_ust_type (display->renderer, drawable);
|
||||||
|
|
||||||
|
if ((pipe (glx_onscreen->swap_wait_pipe) == -1))
|
||||||
|
g_error ("Couldn't create pipe for swap notification: %s\n",
|
||||||
|
g_strerror (errno));
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (fcntl(glx_onscreen->swap_wait_pipe[i], F_SETFD,
|
||||||
|
fcntl(glx_onscreen->swap_wait_pipe[i], F_GETFD, 0) | FD_CLOEXEC) == -1)
|
||||||
|
g_error ("Couldn't set swap notification pipe CLOEXEC: %s\n",
|
||||||
|
g_strerror (errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
_cogl_poll_renderer_add_fd (display->renderer,
|
||||||
|
glx_onscreen->swap_wait_pipe[0],
|
||||||
|
COGL_POLL_FD_EVENT_IN,
|
||||||
|
threaded_swap_wait_pipe_prepare,
|
||||||
|
threaded_swap_wait_pipe_dispatch,
|
||||||
|
onscreen);
|
||||||
|
|
||||||
|
glx_onscreen->swap_wait_queue = g_queue_new ();
|
||||||
|
g_mutex_init (&glx_onscreen->swap_wait_mutex);
|
||||||
|
g_cond_init (&glx_onscreen->swap_wait_cond);
|
||||||
|
glx_onscreen->swap_wait_context =
|
||||||
|
glx_renderer->glXCreateNewContext (xlib_renderer->xdpy,
|
||||||
|
glx_display->fbconfig,
|
||||||
|
GLX_RGBA_TYPE,
|
||||||
|
glx_display->glx_context,
|
||||||
|
True);
|
||||||
|
glx_onscreen->swap_wait_thread = g_thread_new ("cogl_glx_swap_wait",
|
||||||
|
threaded_swap_wait,
|
||||||
|
onscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_lock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
g_queue_push_head (glx_onscreen->swap_wait_queue, GUINT_TO_POINTER(vblank_counter));
|
||||||
|
g_cond_signal (&glx_onscreen->swap_wait_cond);
|
||||||
|
g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
@ -2000,19 +2253,38 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
|
|||||||
|
|
||||||
if (framebuffer->config.swap_throttled)
|
if (framebuffer->config.swap_throttled)
|
||||||
{
|
{
|
||||||
uint32_t end_frame_vsync_counter = 0;
|
|
||||||
|
|
||||||
have_counter = glx_display->have_vblank_counter;
|
have_counter = glx_display->have_vblank_counter;
|
||||||
|
|
||||||
/* If the swap_region API is also being used then we need to track
|
if (glx_renderer->glXSwapInterval)
|
||||||
* the vsync counter for each swap request so we can manually
|
|
||||||
* throttle swap_region requests. */
|
|
||||||
if (have_counter)
|
|
||||||
end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
|
|
||||||
|
|
||||||
if (!glx_renderer->glXSwapInterval)
|
|
||||||
{
|
{
|
||||||
CoglBool can_wait = glx_display->can_vblank_wait;
|
if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT))
|
||||||
|
{
|
||||||
|
/* If we didn't wait for the GPU here, then it's easy to get the case
|
||||||
|
* where there is a VBlank between the point where we get the vsync counter
|
||||||
|
* and the point where the GPU is ready to actually perform the glXSwapBuffers(),
|
||||||
|
* and the swap wait terminates at the first VBlank rather than the one
|
||||||
|
* where the swap buffers happens. Calling glFinish() here makes this a
|
||||||
|
* rare race since the GPU is already ready to swap when we call glXSwapBuffers().
|
||||||
|
* The glFinish() also prevents any serious damage if the rare race happens,
|
||||||
|
* since it will wait for the preceding glXSwapBuffers() and prevent us from
|
||||||
|
* getting premanently ahead. (For NVIDIA drivers, glFinish() after glXSwapBuffers()
|
||||||
|
* waits for the buffer swap to happen.)
|
||||||
|
*/
|
||||||
|
_cogl_winsys_wait_for_gpu (onscreen);
|
||||||
|
start_threaded_swap_wait (onscreen, _cogl_winsys_get_vsync_counter (context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CoglBool can_wait = have_counter || glx_display->can_vblank_wait;
|
||||||
|
|
||||||
|
uint32_t end_frame_vsync_counter = 0;
|
||||||
|
|
||||||
|
/* If the swap_region API is also being used then we need to track
|
||||||
|
* the vsync counter for each swap request so we can manually
|
||||||
|
* throttle swap_region requests. */
|
||||||
|
if (have_counter)
|
||||||
|
end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
|
||||||
|
|
||||||
/* If we are going to wait for VBLANK manually, we not only
|
/* If we are going to wait for VBLANK manually, we not only
|
||||||
* need to flush out pending drawing to the GPU before we
|
* need to flush out pending drawing to the GPU before we
|
||||||
|
Loading…
x
Reference in New Issue
Block a user