From f37de23dec316e1d4ed616c890339bfd3b4cbfa4 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 3 Jun 2010 20:03:20 +0100 Subject: [PATCH] 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. --- clutter/glx/clutter-backend-glx.c | 20 ++++++--- clutter/glx/clutter-stage-glx.c | 75 +++++++++++++++---------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index c184ab25c..0183177db 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -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)) { diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index 783b09fa4..ea03a569b 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -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);