From 965907deb3f9e69cb459dd28af4074364a7b72f9 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 18 Aug 2010 16:48:55 +0100 Subject: [PATCH] Cache a full pick render if we have a static stage The idea is that if we see multiple picks per frame then that implies the visible scene has become static. In this case we can promote the next pick render to be unclipped so we have valid pick values for the entire stage. Now we can continue to read from this cached buffer until the stage contents do visibly change. Thanks to Luca Bruno on #clutter for this idea! --- clutter/clutter-actor.c | 12 ++++++++ clutter/clutter-main.c | 61 ++++++++++++++++++++++++++++++++++++--- clutter/clutter-private.h | 3 ++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index ac577ac48..3620ca87a 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -1780,6 +1780,7 @@ clutter_actor_real_queue_redraw (ClutterActor *self, ClutterActor *origin) { ClutterActor *parent; + ClutterMainContext *context; CLUTTER_NOTE (PAINT, "Redraw queued on '%s' (from: '%s')", get_actor_debug_name (self), @@ -1797,6 +1798,17 @@ clutter_actor_real_queue_redraw (ClutterActor *self, if (!CLUTTER_ACTOR_IS_VISIBLE (self)) return; + /* We have an optimization in _clutter_do_pick to detect when the scene is + * static so we can cache a full, un-clipped pick buffer to avoid continuous + * pick renders. + * + * Currently the assumption is that actors queue a redraw when some state + * changes that affects painting *or* picking so we can use this point + * to invalidate any currently cached pick buffer. + */ + context = _clutter_context_get_default (); + context->have_complete_pick_buffer = FALSE; + /* Although we could determine here that a full stage redraw * has already been queued and immediately bail out, we actually * guarantee that we will propagate a queue-redraw signal to our diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 46fa1440e..06d58eb26 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -301,6 +301,13 @@ _clutter_do_redraw (ClutterStage *stage) ctx = _clutter_context_get_default (); + /* We have an optimization to avoid pick renders while scene is static. + * This is implied by seeing multiple picks per frame so we have to + * reset a pick counter each frame and also invalidate any current pick + * buffer. */ + ctx->picks_per_frame = 0; + ctx->have_complete_pick_buffer = FALSE; + /* Before we can paint, we have to be sure we have the latest layout */ _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage)); @@ -577,6 +584,7 @@ _clutter_do_pick (ClutterStage *stage, guint32 id; GLboolean dither_was_on; ClutterActor *actor; + gboolean is_clipped; CLUTTER_STATIC_COUNTER (do_pick_counter, "_clutter_do_pick counter", "Increments for each full pick run", @@ -617,13 +625,52 @@ _clutter_do_pick (ClutterStage *stage, context = _clutter_context_get_default (); + /* It's possible that we currently have a static scene and have renderered a + * full, unclipped pick buffer. If so we can simply continue to read from + * this cached buffer until the scene next changes. */ + if (context->have_complete_pick_buffer) + { + CLUTTER_TIMER_START (_clutter_uprof_context, pick_read); + cogl_read_pixels (x, y, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read); + + /* FIXME: This is a lazy copy and paste of the logic at the end of this + * function used when we actually do a pick render. It should be + * consolidated somehow. + */ + if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) + { + actor = CLUTTER_ACTOR (stage); + goto result; + } + + id = _clutter_pixel_to_id (pixel); + actor = clutter_get_actor_by_gid (id); + goto result; + } + + context->picks_per_frame++; + _clutter_backend_ensure_context (context->backend, stage); /* needed for when a context switch happens */ _clutter_stage_maybe_setup_viewport (stage); - if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) - cogl_clip_push_window_rectangle (x, y, 1, 1); + /* If we are seeing multiple picks per frame that means the scene is static + * so we promote to doing a non-scissored pick render so that all subsequent + * picks for the same static scene won't require additional renders */ + if (context->picks_per_frame >= 2) + { + if (G_LIKELY (!(clutter_pick_debug_flags & + CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) + cogl_clip_push_window_rectangle (x, y, 1, 1); + is_clipped = TRUE; + } + else + is_clipped = FALSE; cogl_disable_fog (); cogl_color_set_from_4ub (&stage_pick_id, 255, 255, 255, 255); @@ -647,8 +694,14 @@ _clutter_do_pick (ClutterStage *stage, context->pick_mode = CLUTTER_PICK_NONE; CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint); - if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) - cogl_clip_pop (); + if (is_clipped) + { + if (G_LIKELY (!(clutter_pick_debug_flags & + CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) + cogl_clip_pop (); + } + else + context->have_complete_pick_buffer = TRUE; /* Make sure Cogl flushes any batched geometry to the GPU driver */ cogl_flush (); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index fc50b0dc1..3f7277c80 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -188,6 +188,9 @@ struct _ClutterMainContext GList *repaint_funcs; ClutterSettings *settings; + + gint picks_per_frame; + gboolean have_complete_pick_buffer; }; #define CLUTTER_CONTEXT() (_clutter_context_get_default ())