diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 8d2ef6030..b2a402e74 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -2478,6 +2478,38 @@ _clutter_actor_draw_paint_volume (ClutterActor *self) clutter_paint_volume_free (&fake_pv); } +/* Returns TRUE if the actor can be ignored */ +static gboolean +cull_actor (ClutterActor *self) +{ + ClutterActorPrivate *priv = self->priv; + ClutterActor *stage; + const ClutterGeometry *stage_clip; + ClutterActorBox *box; + ClutterGeometry paint_geom; + + if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING)) + return FALSE; + + stage = _clutter_actor_get_stage_internal (self); + stage_clip = _clutter_stage_get_clip (CLUTTER_STAGE (stage)); + if (G_UNLIKELY (!stage_clip)) + return FALSE; + + /* XXX: It might be better if _get_paint_box returned a + * ClutterGeometry instead. */ + box = &priv->last_paint_box; + paint_geom.x = box->x1; + paint_geom.y = box->y1; + paint_geom.width = box->x2 - box->x1; + paint_geom.height = box->y2 - box->y1; + + if (!clutter_geometry_intersects (stage_clip, &paint_geom)) + return TRUE; + else + return FALSE; +} + /** * clutter_actor_paint: * @self: A #ClutterActor @@ -2621,9 +2653,27 @@ clutter_actor_paint (ClutterActor *self) * XXX: We are starting to do a lot of vertex transforms on * the CPU in a typical paint, so at some point we should * audit these and consider caching some things. + * + * XXX: We should consider doing all our culling in the + * stage's model space using PaintVolumes so we don't have + * to project actor paint volumes all the way into window + * coordinates! + * XXX: To do this we also need a way to store an + * "absolute paint volume" in some way. Currently the + * paint volumes are defined relative to a referenced + * actor's coordinates, but we'd need to be able to cache + * the last paint volume used with the actor's *current* + * modelview and either with a specific projection matrix + * or we'd need to be able to invalidate paint-volumes on + * projection changes. */ if (clutter_actor_get_paint_box (self, &priv->last_paint_box)) - priv->last_paint_box_valid = TRUE; + { + priv->last_paint_box_valid = TRUE; + + if (cull_actor (self)) + goto done; + } else priv->last_paint_box_valid = FALSE; } @@ -2659,6 +2709,7 @@ clutter_actor_paint (ClutterActor *self) g_signal_emit (self, actor_signals[PICK], 0, &col); } +done: if (clip_set) cogl_clip_pop(); diff --git a/clutter/clutter-debug.h b/clutter/clutter-debug.h index 3a10c4685..920000515 100644 --- a/clutter/clutter-debug.h +++ b/clutter/clutter-debug.h @@ -36,7 +36,8 @@ typedef enum { CLUTTER_DEBUG_DISABLE_SWAP_EVENTS = 1 << 0, CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS = 1 << 1, CLUTTER_DEBUG_REDRAWS = 1 << 2, - CLUTTER_DEBUG_PAINT_VOLUMES = 1 << 3 + CLUTTER_DEBUG_PAINT_VOLUMES = 1 << 3, + CLUTTER_DEBUG_DISABLE_CULLING = 1 << 4 } ClutterDrawDebugFlag; #ifdef CLUTTER_ENABLE_DEBUG diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 32e209b3a..10535c260 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -181,6 +181,7 @@ static const GDebugKey clutter_paint_debug_keys[] = { { "disable-clipped-redraws", CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS }, { "redraws", CLUTTER_DEBUG_REDRAWS }, { "paint-volumes", CLUTTER_DEBUG_PAINT_VOLUMES }, + { "disable-culling", CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS } }; #ifdef CLUTTER_ENABLE_PROFILE @@ -622,7 +623,7 @@ _clutter_do_pick (ClutterStage *stage, */ CLUTTER_TIMER_START (_clutter_uprof_context, pick_paint); context->pick_mode = mode; - _clutter_stage_do_paint (stage); + _clutter_stage_do_paint (stage, NULL); context->pick_mode = CLUTTER_PICK_NONE; CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index d955bf931..340990b9f 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -329,7 +329,8 @@ void _clutter_stage_manager_set_default_stage (ClutterStageManager *stage_manage ClutterStage *stage); /* stage */ -void _clutter_stage_do_paint (ClutterStage *stage); +void _clutter_stage_do_paint (ClutterStage *stage, + const ClutterGeometry *clip); void _clutter_stage_set_window (ClutterStage *stage, ClutterStageWindow *stage_window); ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); @@ -377,6 +378,9 @@ guint _clutter_stage_get_picks_per_frame_counter (ClutterStage *stage); ClutterPaintVolume *_clutter_stage_paint_volume_stack_allocate (ClutterStage *stage); void _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage); +const ClutterGeometry *_clutter_stage_get_clip (ClutterStage *stage); + + /* vfuncs implemented by backend */ GType _clutter_backend_impl_get_type (void); diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 881944cbd..6ed7c3918 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -120,6 +120,8 @@ struct _ClutterStagePrivate GArray *paint_volume_stack; + const ClutterGeometry *current_paint_clip; + guint relayout_pending : 1; guint redraw_pending : 1; guint is_fullscreen : 1; @@ -314,12 +316,20 @@ clutter_stage_allocate (ClutterActor *self, /* This provides a common point of entry for painting the scenegraph * for picking or painting... + * + * XXX: Instead of having a toplevel 2D clip region, it might be + * better to have a clip volume within the view frustum. This could + * allow us to avoid projecting actors into window coordinates to + * be able to cull them. */ void -_clutter_stage_do_paint (ClutterStage *stage) +_clutter_stage_do_paint (ClutterStage *stage, const ClutterGeometry *clip) { + ClutterStagePrivate *priv = stage->priv; + priv->current_paint_clip = clip; _clutter_stage_paint_volume_stack_free_all (stage); clutter_actor_paint (CLUTTER_ACTOR (stage)); + priv->current_paint_clip = NULL; } static void @@ -3100,3 +3110,11 @@ _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage) g_array_set_size (paint_volume_stack, 0); } +/* The is an out-of-band paramater available while painting that + * can be used to cull actors. */ +const ClutterGeometry * +_clutter_stage_get_clip (ClutterStage *stage) +{ + return stage->priv->current_paint_clip; +} + diff --git a/clutter/egl/clutter-stage-egl.c b/clutter/egl/clutter-stage-egl.c index 7d95c71ce..ca5d3c283 100644 --- a/clutter/egl/clutter-stage-egl.c +++ b/clutter/egl/clutter-stage-egl.c @@ -359,7 +359,7 @@ _clutter_stage_egl_redraw (ClutterStageEGL *stage_egl, egl_surface = backend_egl->egl_surface; #endif - _clutter_stage_do_paint (CLUTTER_STAGE (wrapper)); + _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL); cogl_flush (); eglSwapBuffers (backend_egl->edpy, egl_surface); diff --git a/clutter/fruity/clutter-backend-fruity.c b/clutter/fruity/clutter-backend-fruity.c index 68226548b..d9279d91d 100644 --- a/clutter/fruity/clutter-backend-fruity.c +++ b/clutter/fruity/clutter-backend-fruity.c @@ -71,7 +71,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend, stage_egl = CLUTTER_STAGE_EGL (impl); eglWaitNative (EGL_CORE_NATIVE_ENGINE); - _clutter_stage_do_paint (stage); + _clutter_stage_do_paint (stage, NULL); cogl_flush (); eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index d144ced3e..5dbe45ea5 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -544,11 +544,11 @@ _clutter_stage_glx_redraw (ClutterStageGLX *stage_glx, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); - _clutter_stage_do_paint (stage); + _clutter_stage_do_paint (stage, &stage_glx->bounding_redraw_clip); cogl_clip_pop (); } else - _clutter_stage_do_paint (stage); + _clutter_stage_do_paint (stage, NULL); if (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS && may_use_clipped_redraw) diff --git a/clutter/osx/clutter-stage-osx.c b/clutter/osx/clutter-stage-osx.c index 6b966a44d..12c20aeed 100644 --- a/clutter/osx/clutter-stage-osx.c +++ b/clutter/osx/clutter-stage-osx.c @@ -147,7 +147,7 @@ clutter_stage_osx_get_wrapper (ClutterStageWindow *stage_window); - (void) drawRect: (NSRect) bounds { - _clutter_stage_do_paint (CLUTTER_STAGE (self->stage_osx->wrapper)); + _clutter_stage_do_paint (CLUTTER_STAGE (self->stage_osx->wrapper), NULL); cogl_flush (); [[self openGLContext] flushBuffer]; } diff --git a/clutter/win32/clutter-backend-win32.c b/clutter/win32/clutter-backend-win32.c index cc5abe3c5..6613468ea 100644 --- a/clutter/win32/clutter-backend-win32.c +++ b/clutter/win32/clutter-backend-win32.c @@ -494,7 +494,7 @@ clutter_backend_win32_redraw (ClutterBackend *backend, stage_win32 = CLUTTER_STAGE_WIN32 (impl); /* this will cause the stage implementation to be painted */ - _clutter_stage_do_paint (stage); + _clutter_stage_do_paint (stage, NULL); cogl_flush (); if (stage_win32->client_dc)