backend-glx: Make sure to throttle sub region blits

Neither glXCopySubBuffer or glBlitFramebuffer are integrated with the
swap interval of a framebuffer so that means when we do partial stage
updates (as Mutter does in response to window damage) then the blits
aren't throttled which means applications that throw lots of damage
events at the compositor can effectively cause Clutter to run flat out
taking up all the system resources issuing more blits than can even be
seen.

This patch now makes sure we use the GLX_SGI_video_sync or a
DRM_VBLANK_RELATIVE ioctl to throttle blits to the vblank frequency as
we do when using glXSwapBuffers.
This commit is contained in:
Robert Bragg 2010-06-03 20:03:20 +01:00
parent 630a2c5edc
commit f37de23dec
2 changed files with 49 additions and 46 deletions

View File

@ -245,6 +245,20 @@ clutter_backend_glx_get_features (ClutterBackend *backend)
gl_extensions = (const gchar *)glGetString (GL_EXTENSIONS);
/* When using glBlitFramebuffer or glXCopySubBufferMESA for sub stage
* redraws, we cannot rely on glXSwapIntervalSGI to throttle the blits
* so we need to resort to manually synchronizing with the vblank so we
* always check for the video_sync extension...
*/
if (_cogl_check_extension ("GLX_SGI_video_sync", glx_extensions))
{
backend_glx->get_video_sync =
(GetVideoSyncProc) cogl_get_proc_address ("glXGetVideoSyncSGI");
backend_glx->wait_video_sync =
(WaitVideoSyncProc) cogl_get_proc_address ("glXWaitVideoSyncSGI");
}
use_dri = check_vblank_env ("dri");
/* First check for explicit disabling or it set elsewhere (eg NVIDIA) */
@ -315,12 +329,6 @@ clutter_backend_glx_get_features (ClutterBackend *backend)
{
CLUTTER_NOTE (BACKEND, "attempting glXGetVideoSyncSGI vblank setup");
backend_glx->get_video_sync =
(GetVideoSyncProc) cogl_get_proc_address ("glXGetVideoSyncSGI");
backend_glx->wait_video_sync =
(WaitVideoSyncProc) cogl_get_proc_address ("glXWaitVideoSyncSGI");
if ((backend_glx->get_video_sync != NULL) &&
(backend_glx->wait_video_sync != NULL))
{

View File

@ -468,7 +468,7 @@ drm_wait_vblank(int fd, drm_wait_vblank_t *vbl)
#endif /* __linux__ */
static void
glx_wait_for_vblank (ClutterBackendGLX *backend_glx)
wait_for_vblank (ClutterBackendGLX *backend_glx)
{
/* If we are going to wait for VBLANK manually, we not only need
* to flush out pending drawing to the GPU before we sleep, we
@ -486,46 +486,35 @@ glx_wait_for_vblank (ClutterBackendGLX *backend_glx)
* happen when we use GLX_SWAP and let the driver do the right thing
*/
switch (backend_glx->vblank_type)
if (backend_glx->vblank_type == CLUTTER_VBLANK_NONE)
return;
if (backend_glx->wait_video_sync)
{
case CLUTTER_VBLANK_GLX_SWAP:
CLUTTER_NOTE (BACKEND, "Waiting for vblank (swap)");
break;
unsigned int retraceCount;
case CLUTTER_VBLANK_GLX:
{
unsigned int retraceCount;
glFinish ();
glFinish ();
CLUTTER_NOTE (BACKEND, "Waiting for vblank (wait_video_sync)");
backend_glx->get_video_sync (&retraceCount);
backend_glx->wait_video_sync (2,
(retraceCount + 1) % 2,
&retraceCount);
}
break;
case CLUTTER_VBLANK_DRI:
#ifdef __linux__
{
drm_wait_vblank_t blank;
glFinish ();
CLUTTER_NOTE (BACKEND, "Waiting for vblank (drm)");
blank.request.type = DRM_VBLANK_RELATIVE;
blank.request.sequence = 1;
blank.request.signal = 0;
drm_wait_vblank (backend_glx->dri_fd, &blank);
}
#endif
break;
case CLUTTER_VBLANK_NONE:
default:
break;
CLUTTER_NOTE (BACKEND, "Waiting for vblank (wait_video_sync)");
backend_glx->get_video_sync (&retraceCount);
backend_glx->wait_video_sync (2,
(retraceCount + 1) % 2,
&retraceCount);
}
else
#ifdef __linux__
{
drm_wait_vblank_t blank;
glFinish ();
CLUTTER_NOTE (BACKEND, "Waiting for vblank (drm)");
blank.request.type = DRM_VBLANK_RELATIVE;
blank.request.sequence = 1;
blank.request.signal = 0;
drm_wait_vblank (backend_glx->dri_fd, &blank);
}
#endif
}
void
@ -585,9 +574,6 @@ clutter_stage_glx_redraw (ClutterStageGLX *stage_glx,
drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin;
/* wait for the next vblank */
glx_wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
/* push on the screen */
if (backend_glx->can_blit_sub_buffer &&
/* NB: a degenerate redraw clip width == full stage redraw */
@ -658,6 +644,12 @@ clutter_stage_glx_redraw (ClutterStageGLX *stage_glx,
copy_area.width = clip->width;
copy_area.height = clip->height;
/* glXCopySubBufferMESA and glBlitFramebuffer are not integrated with the
* glXSwapIntervalSGI mechanism which we usually use to throttle the
* Clutter framerate to the vertical refresh and so we have to manually
* wait for the vblank period. */
wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer);
_clutter_backend_glx_blit_sub_buffer (backend_glx,
drawable,
@ -679,6 +671,9 @@ clutter_stage_glx_redraw (ClutterStageGLX *stage_glx,
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
stage_glx->pending_swaps++;
if (backend_glx->vblank_type != CLUTTER_VBLANK_GLX_SWAP)
wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
glXSwapBuffers (backend_x11->xdpy, drawable);
CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);