/* A redraw clip represents (in stage coordinates) the bounding box of * something that needs to be redraw. Typically they are added to the * StageWindow as a result of clutter_actor_queue_clipped_redraw() by * actors such as ClutterGLXTexturePixmap. All redraw clips are * discarded after the next paint. * * A NULL stage_clip means the whole stage needs to be redrawn. * * What we do with this information: * - we keep track of the bounding box for all redraw clips * - when we come to redraw; if the bounding box is smaller than the * stage we scissor the redraw to that box and use * GLX_MESA_copy_sub_buffer to present the redraw to the front * buffer. Some heuristics are used to decide when a clipped redraw * should be promoted into a full stage redraw. * * Currently we simply check that the bounding box height is < 300 * pixels. * * XXX: we don't have any empirical data telling us what a sensible * thresholds is! * * TODO - we should use different heuristics depending on whether the * framebuffer is on screen and not redirected by a compositor VS * offscreen (either due to compositor redirection or because we are * rendering to a CoglOffscreen framebuffer) * * When not redirected glXCopySubBuffer (on intel hardware at least) * will block the GPU until the vertical trace is at the optimal point * so the copy can be done without tearing. In this case we don't want * to copy tall regions because they increase the average time spent * blocking the GPU. * * When rendering offscreen (CoglOffscreen or redirected by * compositor) then no extra synchronization is needed before the copy * can start. * * In all cases we need to consider that glXCopySubBuffer implies a * blit which may be avoided by promoting to a full stage redraw if: * - the framebuffer is redirected offscreen or a CoglOffscreen. * - the framebuffer is onscreen and fullscreen. * By promoting to a full stage redraw we trade off the cost involved * in rasterizing the extra pixels vs avoiding to use a blit to * present the back buffer. * */ static void clutter_stage_glx_add_redraw_clip (ClutterStageWindow *stage_window, ClutterGeometry *stage_clip) { ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); /* If we are already forced to do a full stage redraw then bail early */ if (clutter_stage_glx_ignoring_redraw_clips (stage_window)) return; /* A NULL stage clip means a full stage redraw has been queued and * we keep track of this by setting a degenerate * stage_glx->bounding_redraw_clip */ if (stage_clip == NULL) { stage_glx->bounding_redraw_clip.width = 0; return; } /* Do nothing on an empty clip to avoid confusing with out magic-flag * degenerate clip */ if (stage_clip->width == 0 || stage_clip->height == 0) return; if (!stage_glx->initialized_redraw_clip) { stage_glx->bounding_redraw_clip.x = stage_clip->x; stage_glx->bounding_redraw_clip.y = stage_clip->y; stage_glx->bounding_redraw_clip.width = stage_clip->width; stage_glx->bounding_redraw_clip.height = stage_clip->height; } else if (stage_glx->bounding_redraw_clip.width > 0) { _clutter_geometry_union (&stage_glx->bounding_redraw_clip, stage_clip, &stage_glx->bounding_redraw_clip); } #if 0 redraw_area = (stage_glx->bounding_redraw_clip.width * stage_glx->bounding_redraw_clip.height); stage_area = stage_x11->xwin_width * stage_x11->xwin_height; /* Redrawing and blitting >70% of the stage is assumed to be more * expensive than redrawing the additional 30% to avoid the blit. * * FIXME: This threshold was plucked out of thin air! */ if (redraw_area > (stage_area * 0.7f)) { g_print ("DEBUG: clipped redraw too big, forcing full redraw\n"); /* Set a degenerate clip to force a full redraw */ stage_glx->bounding_redraw_clip.width = 0; } #endif stage_glx->initialized_redraw_clip = TRUE; }
/* A redraw clip represents (in stage coordinates) the bounding box of * something that needs to be redraw. Typically they are added to the * StageWindow as a result of clutter_actor_queue_clipped_redraw() by * actors such as ClutterGLXTexturePixmap. All redraw clips are * discarded after the next paint. * * A NULL stage_clip means the whole stage needs to be redrawn. * * What we do with this information: * - we keep track of the bounding box for all redraw clips * - when we come to redraw; if the bounding box is smaller than the * stage we scissor the redraw to that box and use * GLX_MESA_copy_sub_buffer to present the redraw to the front * buffer. * * XXX - In theory, we should have some sort of heuristics to promote * a clipped redraw to a full screen redraw; in reality, it turns out * that promotion is fairly expensive. See the Clutter bug described * at: http://bugzilla.clutter-project.org/show_bug.cgi?id=2136 . * * TODO - we should use different heuristics depending on whether the * framebuffer is on screen and not redirected by a compositor VS * offscreen (either due to compositor redirection or because we are * rendering to a CoglOffscreen framebuffer) * * When not redirected glXCopySubBuffer (on intel hardware at least) * will block the GPU until the vertical trace is at the optimal point * so the copy can be done without tearing. In this case we don't want * to copy tall regions because they increase the average time spent * blocking the GPU. * * When rendering offscreen (CoglOffscreen or redirected by * compositor) then no extra synchronization is needed before the copy * can start. * * In all cases we need to consider that glXCopySubBuffer implies a * blit which may be avoided by promoting to a full stage redraw if: * - the framebuffer is redirected offscreen or a CoglOffscreen. * - the framebuffer is onscreen and fullscreen. * By promoting to a full stage redraw we trade off the cost involved * in rasterizing the extra pixels vs avoiding to use a blit to * present the back buffer. */ static void clutter_stage_glx_add_redraw_clip (ClutterStageWindow *stage_window, ClutterGeometry *stage_clip) { ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); /* If we are already forced to do a full stage redraw then bail early */ if (clutter_stage_glx_ignoring_redraw_clips (stage_window)) return; /* A NULL stage clip means a full stage redraw has been queued and * we keep track of this by setting a zero width * stage_glx->bounding_redraw_clip */ if (stage_clip == NULL) { stage_glx->bounding_redraw_clip.width = 0; stage_glx->initialized_redraw_clip = TRUE; return; } /* Ignore requests to add degenerate/empty clip rectangles */ if (stage_clip->width == 0 || stage_clip->height == 0) return; if (!stage_glx->initialized_redraw_clip) { stage_glx->bounding_redraw_clip.x = stage_clip->x; stage_glx->bounding_redraw_clip.y = stage_clip->y; stage_glx->bounding_redraw_clip.width = stage_clip->width; stage_glx->bounding_redraw_clip.height = stage_clip->height; } else if (stage_glx->bounding_redraw_clip.width > 0) { clutter_geometry_union (&stage_glx->bounding_redraw_clip, stage_clip, &stage_glx->bounding_redraw_clip); } #if 0 redraw_area = (stage_glx->bounding_redraw_clip.width * stage_glx->bounding_redraw_clip.height); stage_area = stage_x11->xwin_width * stage_x11->xwin_height; /* Redrawing and blitting >70% of the stage is assumed to be more * expensive than redrawing the additional 30% to avoid the blit. * * FIXME: This threshold was plucked out of thin air! * * The threshold has been disabled after verifying that it indeed * made redraws more expensive than intended; see bug reference: * * http://bugzilla.clutter-project.org/show_bug.cgi?id=2136 */ if (redraw_area > (stage_area * 0.7f)) { g_print ("DEBUG: clipped redraw too big, forcing full redraw\n"); /* Set a zero width clip to force a full redraw */ stage_glx->bounding_redraw_clip.width = 0; } #endif stage_glx->initialized_redraw_clip = TRUE; }