diff options
Diffstat (limited to 'clutter/clutter-actor.c')
-rw-r--r-- | clutter/clutter-actor.c | 687 |
1 files changed, 506 insertions, 181 deletions
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f84d7cf9d..92aa1b907 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -373,6 +373,21 @@ typedef enum { */ } MapStateChange; +typedef struct +{ + const ClutterCamera *camera; + + /* NB: This volume isn't relative to this actor, it is in eye + * coordinates so that it can remain valid after the actor changes. + */ + ClutterPaintVolume eye_volume; + gboolean eye_volume_valid; + + /* If this doesn't match camera->age then the above paint-volume + * is invalid. */ + int valid_for_age; +} PerCameraState; + /* 3 entries should be a good compromise, few layout managers * will ask for 3 different preferred size in each allocation cycle */ #define N_CACHED_SIZE_REQUESTS 3 @@ -396,6 +411,12 @@ struct _ClutterActorPrivate ClutterActorBox allocation; ClutterAllocationFlags allocation_flags; + /* State we cache that's specific to a camera view. We only currently + * consider their may be two cameras for stereo rendering. */ + PerCameraState *camera_state; + int n_cameras; + int cameras_age; + /* depth */ gfloat z; @@ -415,6 +436,7 @@ struct _ClutterActorPrivate ClutterEffect *flatten_effect; /* scene graph */ + ClutterStage *stage_cache; ClutterActor *parent; ClutterActor *prev_sibling; ClutterActor *next_sibling; @@ -449,6 +471,13 @@ struct _ClutterActorPrivate /* a counter used to toggle the CLUTTER_INTERNAL_CHILD flag */ gint internal_child; + /* XXX: These are a workaround for not being able to break the ABI + * of the QUEUE_REDRAW signal. They are out-of-band arguments. + * See clutter_actor_queue_clipped_redraw() for details. + */ + ClutterPaintVolume *oob_queue_redraw_clip; + int oob_queue_redraw_camera_index; + /* meta classes */ ClutterMetaGroup *actions; ClutterMetaGroup *constraints; @@ -476,11 +505,6 @@ struct _ClutterActorPrivate ClutterPaintVolume paint_volume; - /* NB: This volume isn't relative to this actor, it is in eye - * coordinates so that it can remain valid after the actor changes. - */ - ClutterPaintVolume last_paint_volume; - ClutterStageQueueRedrawEntry *queue_redraw_entry; ClutterColor bg_color; @@ -1096,11 +1120,40 @@ clutter_actor_update_map_state (ClutterActor *self, #endif } +static ClutterStage * +_clutter_actor_get_stage_real (ClutterActor *actor) +{ + ClutterActor *self; + + if (G_LIKELY (actor->priv->stage_cache)) + return actor->priv->stage_cache; + + self = actor; + + /* Check to see if the actor is associated with a stage yet... */ + while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor)) + actor = actor->priv->parent; + + /* Note: we never want to have a type check when we cast here + * since this is function can be used very heavily. */ + self->priv->stage_cache = (ClutterStage *)actor; + return self->priv->stage_cache; +} + +ClutterActor * +_clutter_actor_get_stage_internal (ClutterActor *actor) +{ + /* Note: we never want to have a type check when we cast here + * since this is function can be used very heavily. */ + return (ClutterActor *)_clutter_actor_get_stage_real (actor); +} + static void clutter_actor_real_map (ClutterActor *self) { + ClutterStage *stage = _clutter_actor_get_stage_real (self); ClutterActorPrivate *priv = self->priv; - ClutterActor *stage, *iter; + ClutterActor *iter; g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); @@ -1109,9 +1162,7 @@ clutter_actor_real_map (ClutterActor *self) CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED); - stage = _clutter_actor_get_stage_internal (self); - priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self); - + priv->pick_id = _clutter_stage_acquire_pick_id (stage, self); CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'", priv->pick_id, _clutter_actor_get_debug_name (self)); @@ -1165,6 +1216,7 @@ clutter_actor_real_unmap (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; ClutterActor *iter; + int i; g_assert (CLUTTER_ACTOR_IS_MAPPED (self)); @@ -1180,11 +1232,29 @@ clutter_actor_real_unmap (ClutterActor *self) CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED); - /* clear the contents of the last paint volume, so that hiding + moving + - * showing will not result in the wrong area being repainted + /* clear the contents of the - per camera - eye coordinate paint + * volumes, so that if we later show the actor again we won't + * redundantly also redraw the old location of the actor. + * + * Note: We only do this if the actor doesn't already have redraw + * queued for it that may depend on the last eye_volume to clear + * its old location. For example if you were to hide, move and + * re-show an actor in preparing for a single frame then in that + * case we would have queued a redraw for the hide and and do + * need to make sure that the actors old location is redrawn. */ - _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL); - priv->last_paint_volume_valid = TRUE; + if (priv->queue_redraw_entry != NULL) + { + for (i = 0; i < priv->n_cameras; i++) + { + PerCameraState *camera_state = &priv->camera_state[i]; + + if (!camera_state->eye_volume_valid) + clutter_paint_volume_free (&camera_state->eye_volume); + _clutter_paint_volume_init_static (&camera_state->eye_volume, NULL); + camera_state->eye_volume_valid = TRUE; + } + } /* notify on parent mapped after potentially unmapping * children, so apps see a bottom-up notification. @@ -1194,10 +1264,7 @@ clutter_actor_real_unmap (ClutterActor *self) /* relinquish keyboard focus if we were unmapped while owning it */ if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) { - ClutterStage *stage; - - stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); - + ClutterStage *stage = _clutter_actor_get_stage_real (self); if (stage != NULL) _clutter_stage_release_pick_id (stage, priv->pick_id); @@ -1340,6 +1407,14 @@ clutter_actor_show (ClutterActor *self) g_signal_emit (self, actor_signals[SHOW], 0); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]); + /* XXX: shouldn't this be: + * if (_clutter_actor_get_stage_real (self)) + * clutter_actor_queue_redraw (self); + * + * XXX: actually shouldn't we queue redraws from map/unmap changes + * instead since there's no point queueing a redraw for an actor if + * one of its ancestors is unmapped. + */ if (priv->parent != NULL) clutter_actor_queue_redraw (priv->parent); @@ -1435,6 +1510,14 @@ clutter_actor_hide (ClutterActor *self) g_signal_emit (self, actor_signals[HIDE], 0); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]); + /* XXX: shouldn't this be: + * if (_clutter_actor_get_stage_real (self)) + * clutter_actor_queue_redraw (self); + * + * XXX: actually shouldn't we queue redraws from map/unmap changes + * instead since there's no point queueing a redraw for an actor if + * one of its ancestors is unmapped. + */ if (priv->parent != NULL) clutter_actor_queue_redraw (priv->parent); @@ -2176,9 +2259,8 @@ clutter_actor_real_queue_redraw (ClutterActor *self, */ if (self->priv->propagated_one_redraw) { - ClutterActor *stage = _clutter_actor_get_stage_internal (self); - if (stage != NULL && - _clutter_stage_has_full_redraw_queued (CLUTTER_STAGE (stage))) + ClutterStage *stage = _clutter_actor_get_stage_real (self); + if (stage != NULL && _clutter_stage_has_full_redraw_queued (stage)) return; } @@ -2256,7 +2338,7 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, w = 1.0; if (ancestor == NULL) - ancestor = _clutter_actor_get_stage_internal (self); + ancestor = CLUTTER_ACTOR (_clutter_actor_get_stage_real (self)); if (ancestor == NULL) { @@ -2270,40 +2352,32 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, static gboolean _clutter_actor_fully_transform_vertices (ClutterActor *self, + int camera_index, const ClutterVertex *vertices_in, ClutterVertex *vertices_out, int n_vertices) { ClutterActor *stage; + const ClutterCamera *camera; CoglMatrix modelview; - CoglMatrix projection; - float viewport[4]; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); - stage = _clutter_actor_get_stage_internal (self); - /* We really can't do anything meaningful in this case so don't try * to do any transform */ + stage = CLUTTER_ACTOR (_clutter_actor_get_stage_real (self)); if (stage == NULL) return FALSE; - /* Note: we pass NULL as the ancestor because we don't just want the modelview - * that gets us to stage coordinates, we want to go all the way to eye - * coordinates */ - _clutter_actor_apply_relative_transformation_matrix (self, NULL, &modelview); + camera = _clutter_stage_get_camera (CLUTTER_STAGE (stage), camera_index); + cogl_matrix_init_from_array (&modelview, (float *)&camera->view); - /* Fetch the projection and viewport */ - _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection); - _clutter_stage_get_viewport (CLUTTER_STAGE (stage), - &viewport[0], - &viewport[1], - &viewport[2], - &viewport[3]); + _clutter_actor_apply_relative_transformation_matrix (self, stage, &modelview); + /* Fetch the projection and viewport */ _clutter_util_fully_transform_vertices (&modelview, - &projection, - viewport, + &camera->projection, + camera->viewport, vertices_in, vertices_out, n_vertices); @@ -2321,6 +2395,9 @@ _clutter_actor_fully_transform_vertices (ClutterActor *self, * into screen-relative coordinates with the current actor * transformation (i.e. scale, rotation, etc) * + * <note>If clutter is being used for stereo rendering then this will + * simply transform the point according the left eye's view</note> + * * Since: 0.4 **/ void @@ -2330,14 +2407,13 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, { g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - _clutter_actor_fully_transform_vertices (self, point, vertex, 1); + _clutter_actor_fully_transform_vertices (self, 0, point, vertex, 1); } /* * _clutter_actor_get_relative_transformation_matrix: * @self: The actor whose coordinate space you want to transform from. - * @ancestor: The ancestor actor whose coordinate space you want to transform too - * or %NULL if you want to transform all the way to eye coordinates. + * @ancestor: The ancestor actor whose coordinate space you want to transform too. * @matrix: A #CoglMatrix to store the transformation * * This gets a transformation @matrix that will transform coordinates from the @@ -2347,13 +2423,6 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, * coordinates of @self into stage coordinates you would pass the actor's stage * pointer as the @ancestor. * - * If you pass %NULL then the transformation will take you all the way through - * to eye coordinates. This can be useful if you want to extract the entire - * modelview transform that Clutter applies before applying the projection - * transformation. If you want to explicitly set a modelview on a CoglFramebuffer - * using cogl_set_modelview_matrix() for example then you would want a matrix - * that transforms into eye coordinates. - * * <note><para>This function explicitly initializes the given @matrix. If you just * want clutter to multiply a relative transformation with an existing matrix * you can use clutter_actor_apply_relative_transformation_matrix() @@ -2367,6 +2436,8 @@ _clutter_actor_get_relative_transformation_matrix (ClutterActor *self, ClutterActor *ancestor, CoglMatrix *matrix) { + g_return_if_fail (ancestor != NULL); + cogl_matrix_init_identity (matrix); _clutter_actor_apply_relative_transformation_matrix (self, ancestor, matrix); @@ -2376,6 +2447,7 @@ _clutter_actor_get_relative_transformation_matrix (ClutterActor *self, * transformed vertices to @verts[]. */ static gboolean _clutter_actor_transform_and_project_box (ClutterActor *self, + int camera_index, const ClutterActorBox *box, ClutterVertex verts[]) { @@ -2395,7 +2467,8 @@ _clutter_actor_transform_and_project_box (ClutterActor *self, box_vertices[3].z = 0; return - _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4); + _clutter_actor_fully_transform_vertices (self, camera_index, + box_vertices, verts, 4); } /** @@ -2432,25 +2505,26 @@ clutter_actor_get_allocation_vertices (ClutterActor *self, ClutterActorBox box; ClutterVertex vertices[4]; CoglMatrix modelview; + ClutterStage *stage; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); + priv = self->priv; + stage = _clutter_actor_get_stage_real (self); + if (ancestor == NULL) - ancestor = _clutter_actor_get_stage_internal (self); + ancestor = CLUTTER_ACTOR (stage); /* Fallback to a NOP transform if the actor isn't parented under a * stage. */ if (ancestor == NULL) ancestor = self; - priv = self->priv; - /* if the actor needs to be allocated we force a relayout, so that * we will have valid values to use in the transformations */ if (priv->needs_allocation) { - ClutterActor *stage = _clutter_actor_get_stage_internal (self); if (stage) _clutter_stage_maybe_relayout (stage); else @@ -2505,6 +2579,9 @@ clutter_actor_get_allocation_vertices (ClutterActor *self, * <listitem><para>v[3] contains (x2, y2)</para></listitem> * </itemizedlist> * + * <note>If clutter is being used for stereo rendering then this will + * simply return a box according the left eye's view.</note> + * * Since: 0.4 */ void @@ -2524,7 +2601,7 @@ clutter_actor_get_abs_allocation_vertices (ClutterActor *self, */ if (priv->needs_allocation) { - ClutterActor *stage = _clutter_actor_get_stage_internal (self); + ClutterStage *stage = _clutter_actor_get_stage_real (self); /* There's nothing meaningful we can do now */ if (!stage) return; @@ -2539,6 +2616,7 @@ clutter_actor_get_abs_allocation_vertices (ClutterActor *self, actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1; actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1; _clutter_actor_transform_and_project_box (self, + 0, &actor_space_allocation, verts); } @@ -2630,8 +2708,7 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self, /* * clutter_actor_apply_relative_transformation_matrix: * @self: The actor whose coordinate space you want to transform from. - * @ancestor: The ancestor actor whose coordinate space you want to transform too - * or %NULL if you want to transform all the way to eye coordinates. + * @ancestor: The ancestor actor whose coordinate space you want to transform too. * @matrix: A #CoglMatrix to apply the transformation too. * * This multiplies a transform with @matrix that will transform coordinates @@ -2641,13 +2718,6 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self, * coordinates of @self into stage coordinates you would pass the actor's stage * pointer as the @ancestor. * - * If you pass %NULL then the transformation will take you all the way through - * to eye coordinates. This can be useful if you want to extract the entire - * modelview transform that Clutter applies before applying the projection - * transformation. If you want to explicitly set a modelview on a CoglFramebuffer - * using cogl_set_modelview_matrix() for example then you would want a matrix - * that transforms into eye coordinates. - * * <note>This function doesn't initialize the given @matrix, it simply * multiplies the requested transformation matrix with the existing contents of * @matrix. You can use cogl_matrix_init_identity() to initialize the @matrix @@ -2661,6 +2731,8 @@ _clutter_actor_apply_relative_transformation_matrix (ClutterActor *self, { ClutterActor *parent; + g_return_if_fail (ancestor != NULL); + /* Note we terminate before ever calling stage->apply_transform() * since that would conceptually be relative to the underlying * window OpenGL coordinates so we'd need a special @ancestor @@ -2754,9 +2826,9 @@ _clutter_actor_draw_paint_volume (ClutterActor *self) { gfloat width, height; ClutterPaintVolume fake_pv; + ClutterStage *stage = _clutter_actor_get_stage_real (self); - ClutterActor *stage = _clutter_actor_get_stage_internal (self); - _clutter_paint_volume_init_static (&fake_pv, stage); + _clutter_paint_volume_init_static (&fake_pv, CLUTTER_ACTOR (stage)); clutter_actor_get_size (self, &width, &height); clutter_paint_volume_set_width (&fake_pv, width); @@ -2780,6 +2852,7 @@ _clutter_actor_draw_paint_volume (ClutterActor *self) static void _clutter_actor_paint_cull_result (ClutterActor *self, + const ClutterCamera *camera, gboolean success, ClutterCullResult result) { @@ -2851,32 +2924,36 @@ static gboolean cull_actor (ClutterActor *self, ClutterCullResult *result_out) { ClutterActorPrivate *priv = self->priv; - ClutterActor *stage; const ClutterPlane *stage_clip; + const ClutterCamera *camera; + PerCameraState *camera_state; + ClutterStage *stage = _clutter_actor_get_stage_real (self); - if (!priv->last_paint_volume_valid) + if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING)) + return FALSE; + + stage_clip = _clutter_stage_get_clip (stage); + if (G_UNLIKELY (!stage_clip)) { CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): " - "->last_paint_volume_valid == FALSE", + "No stage clip set", _clutter_actor_get_debug_name (self)); return FALSE; } - if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING)) - return FALSE; + camera = _clutter_stage_get_current_camera (stage); + camera_state = &priv->camera_state[camera->index]; - stage = _clutter_actor_get_stage_internal (self); - stage_clip = _clutter_stage_get_clip (CLUTTER_STAGE (stage)); - if (G_UNLIKELY (!stage_clip)) + if (!camera_state->eye_volume_valid) { CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): " - "No stage clip set", + "->paint_volume_valid == FALSE", _clutter_actor_get_debug_name (self)); return FALSE; } if (cogl_get_draw_framebuffer () != - _clutter_stage_get_active_framebuffer (CLUTTER_STAGE (stage))) + _clutter_stage_get_active_framebuffer (stage)) { CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): " "Current framebuffer doesn't correspond to stage", @@ -2885,37 +2962,107 @@ cull_actor (ClutterActor *self, ClutterCullResult *result_out) } *result_out = - _clutter_paint_volume_cull (&priv->last_paint_volume, stage_clip); + _clutter_paint_volume_cull (&camera_state->eye_volume, stage_clip); return TRUE; } static void -_clutter_actor_update_last_paint_volume (ClutterActor *self) +invalidate_per_camera_eye_volume (PerCameraState *camera_state) +{ + if (camera_state->eye_volume_valid) + { + clutter_paint_volume_free (&camera_state->eye_volume); + camera_state->eye_volume_valid = FALSE; + } +} + +static PerCameraState * +_clutter_actor_get_per_camera_state (ClutterActor *self, + int camera_index) { ClutterActorPrivate *priv = self->priv; + ClutterStage *stage = _clutter_actor_get_stage_real (self); + int cameras_age = _clutter_stage_get_cameras_age (stage); + PerCameraState *camera_state; + + /* Whenever there are additions or removals of cameras associated with the + * stage then the stage's 'cameras_age' is bumped and we throw away any + * per-actor cached state associated with the old cameras. */ + + if (G_UNLIKELY (cameras_age != priv->cameras_age)) + { + int i; + int n_cameras; + + for (i = 0; i < priv->n_cameras; i++) + invalidate_per_camera_eye_volume (&priv->camera_state[i]); + + if (priv->camera_state) + g_slice_free1 (sizeof (PerCameraState) * priv->n_cameras, + priv->camera_state); + + /* NB: We always allocate for the total number of cameras since + * we expect that each camera is likely going to be painted each + * frame so we should save having to re-allocate later. */ + n_cameras = _clutter_stage_get_n_cameras (stage); + priv->camera_state = g_slice_alloc (sizeof (PerCameraState) * n_cameras); + + for (i = 0; i < n_cameras; i++) + { + camera_state = &priv->camera_state[i]; + + camera_state->camera = _clutter_stage_get_camera (stage, i); + camera_state->eye_volume_valid = FALSE; + camera_state->valid_for_age = camera_state->camera->age; + } + + priv->n_cameras = n_cameras; + priv->cameras_age = cameras_age; + } + + camera_state = &priv->camera_state[camera_index]; + if (camera_state->camera->age != camera_state->valid_for_age) + { + invalidate_per_camera_eye_volume (camera_state); + camera_state->valid_for_age = camera_state->camera->age; + } + + return camera_state; +} + +/* NB: This updates the eye coordinates paint volume ("eye_volume") for the + * current camera and it's assumed that this is only used during painting where + * the current camera is meaningful. */ +static void +_clutter_actor_update_eye_volume (ClutterActor *self) +{ const ClutterPaintVolume *pv; + ClutterStage *stage = _clutter_actor_get_stage_real (self); + const ClutterCamera *camera = _clutter_stage_get_current_camera (stage); + PerCameraState *camera_state = + _clutter_actor_get_per_camera_state (self, camera->index); - if (priv->last_paint_volume_valid) + if (camera_state->eye_volume_valid) { - clutter_paint_volume_free (&priv->last_paint_volume); - priv->last_paint_volume_valid = FALSE; + clutter_paint_volume_free (&camera_state->eye_volume); + camera_state->eye_volume_valid = FALSE; } pv = clutter_actor_get_paint_volume (self); if (!pv) { - CLUTTER_NOTE (CLIPPING, "Bail from update_last_paint_volume (%s): " + CLUTTER_NOTE (CLIPPING, "Bail from update_paint_volume (%s): " "Actor failed to report a paint volume", _clutter_actor_get_debug_name (self)); return; } - _clutter_paint_volume_copy_static (pv, &priv->last_paint_volume); + _clutter_paint_volume_copy_static (pv, &camera_state->eye_volume); - _clutter_paint_volume_transform_relative (&priv->last_paint_volume, - NULL); /* eye coordinates */ + _clutter_paint_volume_transform_relative_to_camera (&camera_state->eye_volume, + camera); - priv->last_paint_volume_valid = TRUE; + camera_state->eye_volume_valid = TRUE; } static inline gboolean @@ -3095,6 +3242,9 @@ clutter_actor_paint (ClutterActor *self) ClutterPickMode pick_mode; gboolean clip_set = FALSE; gboolean shader_applied = FALSE; + ClutterStage *stage; + const ClutterCamera *camera; + gboolean set_current_camera; CLUTTER_STATIC_COUNTER (actor_paint_counter, "Actor real-paint counter", @@ -3137,6 +3287,39 @@ clutter_actor_paint (ClutterActor *self) /* mark that we are in the paint process */ CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT); + stage = _clutter_actor_get_stage_real (self); + camera = _clutter_stage_get_current_camera (stage); + + /* Although not ideal, we have to support toolkits that may + * manually paint actors outside of a standard paint-cycle + * (such as as MxOffscreen which may paint individual actors + * to an offscreen fbo) + * + * In this situation we won't have setup a current camera and so, + * for compatibility, we make the left_eye camera current so code + * relying on this capability won't simply crash. + * + * It should be noted though that code relying on this behaviour + * won't work with stereoscopic rendering. + * + * XXX: This code should stay very near the beginning of + * clutter_actor_paint() to ensure that we do have a valid camera for + * subsequent code. + */ + if (!camera) + { + camera = _clutter_stage_get_camera (stage, 0); + + /* XXX: code relying on this really should be encourage to + * switch to a solution that works within the paint-cycle not + * least because the state of the current camera is basically + * un-defined and may change before the next paint. */ + _clutter_stage_set_current_camera (stage, camera); + set_current_camera = TRUE; + } + else + set_current_camera = FALSE; + cogl_push_matrix(); if (priv->enable_model_view_transform) @@ -3249,7 +3432,9 @@ clutter_actor_paint (ClutterActor *self) * paint then the last-paint-volume would likely represent the new * actor position not the old. */ - if (!in_clone_paint () && pick_mode == CLUTTER_PICK_NONE) + if (!set_current_camera && + !in_clone_paint () && + pick_mode == CLUTTER_PICK_NONE) { gboolean success; /* annoyingly gcc warns if uninitialized even though @@ -3261,12 +3446,12 @@ clutter_actor_paint (ClutterActor *self) CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) != (CLUTTER_DEBUG_DISABLE_CULLING | CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS))) - _clutter_actor_update_last_paint_volume (self); + _clutter_actor_update_eye_volume (self); success = cull_actor (self, &result); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)) - _clutter_actor_paint_cull_result (self, success, result); + _clutter_actor_paint_cull_result (self, camera, success, result); else if (result == CLUTTER_CULL_RESULT_OUT && success) goto done; } @@ -3301,6 +3486,9 @@ done: if (pick_mode == CLUTTER_PICK_NONE) priv->is_dirty = FALSE; + if (set_current_camera) + _clutter_stage_set_current_camera (stage, NULL); + if (clip_set) cogl_clip_pop(); @@ -3439,6 +3627,7 @@ remove_child (ClutterActor *self, if (self->priv->last_child == child) self->priv->last_child = prev_sibling; + child->priv->stage_cache = NULL; child->priv->parent = NULL; child->priv->prev_sibling = NULL; child->priv->next_sibling = NULL; @@ -4611,6 +4800,21 @@ clutter_actor_dispose (GObject *object) priv->layout_manager = NULL; } + if (priv->n_cameras > 0) + { + int i; + for (i = 0; i < priv->n_cameras; i++) + { + PerCameraState *camera_state = &priv->camera_state[i]; + if (camera_state->eye_volume_valid) + clutter_paint_volume_free (&camera_state->eye_volume); + } + g_slice_free1 (sizeof (PerCameraState) * priv->n_cameras, + priv->camera_state); + priv->camera_state = NULL; + priv->n_cameras = -1; + } + G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); } @@ -6455,6 +6659,8 @@ clutter_actor_init (ClutterActor *self) self->priv = priv = CLUTTER_ACTOR_GET_PRIVATE (self); + priv->stage_cache = NULL; + priv->id = _clutter_context_acquire_id (self); priv->pick_id = -1; @@ -6471,9 +6677,16 @@ clutter_actor_init (ClutterActor *self) priv->opacity_override = -1; priv->enable_model_view_transform = TRUE; - /* Initialize an empty paint volume to start with */ - _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL); - priv->last_paint_volume_valid = TRUE; + priv->camera_state = NULL; + priv->n_cameras = 0; + + /* When an actor first gets associated with a stage we make sure to + * initialize this to a value not matching the + * stage's->priv->cameras_age, but for the stage itself we need to + * make sure the age is initialized to a value other than 0 so that + * get_per_camera_state() will correctly initialize the per-camera + * state. */ + priv->cameras_age = -1; priv->transform_valid = FALSE; } @@ -6529,13 +6742,44 @@ clutter_actor_destroy (ClutterActor *self) g_object_unref (self); } +/* XXX: This is a workaround for not being able to break the ABI of + * the QUEUE_REDRAW signal. It is an out-of-band argument. See + * clutter_actor_queue_clipped_redraw() for details. + */ +ClutterPaintVolume * +_clutter_actor_get_queue_redraw_clip (ClutterActor *self) +{ + return self->priv->oob_queue_redraw_clip; +} + +static void +_clutter_actor_set_queue_redraw_clip (ClutterActor *self, + ClutterPaintVolume *clip) +{ + self->priv->oob_queue_redraw_clip = clip; +} + +int +_clutter_actor_get_queue_redraw_camera_index (ClutterActor *self) +{ + return self->priv->oob_queue_redraw_camera_index; +} + +static void +_clutter_actor_set_queue_redraw_camera_index (ClutterActor *self, + int camera_index) +{ + self->priv->oob_queue_redraw_camera_index = camera_index; +} + void _clutter_actor_finish_queue_redraw (ClutterActor *self, ClutterPaintVolume *clip) { ClutterActorPrivate *priv = self->priv; - ClutterPaintVolume *pv; - gboolean clipped; + ClutterStage *stage = _clutter_actor_get_stage_real (self); + int n_cameras = _clutter_stage_get_n_cameras (stage); + int i; /* Remove queue entry early in the process, otherwise a new queue_redraw() during signal handling could put back this @@ -6563,46 +6807,65 @@ _clutter_actor_finish_queue_redraw (ClutterActor *self, if (clip) { _clutter_actor_set_queue_redraw_clip (self, clip); - clipped = TRUE; + for (i = 0; i < n_cameras; i++) + { + _clutter_actor_set_queue_redraw_camera_index (self, i); + _clutter_actor_signal_queue_redraw (self, self); + } } - else if (G_LIKELY (priv->last_paint_volume_valid)) + else { - pv = _clutter_actor_get_paint_volume_mutable (self); - if (pv) + for (i = 0; i < n_cameras; i++) { - ClutterActor *stage = _clutter_actor_get_stage_internal (self); + PerCameraState *camera_state = + _clutter_actor_get_per_camera_state (self, i); + ClutterPaintVolume *pv; - /* make sure we redraw the actors old position... */ - _clutter_actor_set_queue_redraw_clip (stage, - &priv->last_paint_volume); - _clutter_actor_signal_queue_redraw (stage, stage); - _clutter_actor_set_queue_redraw_clip (stage, NULL); + if (G_LIKELY (camera_state->eye_volume_valid)) + { + pv = _clutter_actor_get_paint_volume_mutable (self); + if (pv) + { + ClutterActor *stage_actor = CLUTTER_ACTOR (stage); + + /* make sure we redraw the actors old position... */ + _clutter_actor_set_queue_redraw_clip (stage_actor, + &camera_state->eye_volume); + _clutter_actor_signal_queue_redraw (stage_actor, + stage_actor); + _clutter_actor_set_queue_redraw_clip (stage_actor, NULL); + } + } + else + pv = NULL; - /* XXX: Ideally the redraw signal would take a clip volume - * argument, but that would be an ABI break. Until we can - * break the ABI we pass the argument out-of-band + /* XXX: Ideally the redraw signal would take clip volume and + * camera arguments, but that would be an ABI break. Until + * we can break the ABI we pass these arguments out-of-band + * via actor->priv members... */ - /* setup the clip for the actors new position... */ + /* Setup the clip for the actor's new position. + * Note: pv could be NULL here which will result in a full + * redraw. */ _clutter_actor_set_queue_redraw_clip (self, pv); - clipped = TRUE; + _clutter_actor_set_queue_redraw_camera_index (self, i); + _clutter_actor_signal_queue_redraw (self, self); } - else - clipped = FALSE; } - else - clipped = FALSE; - - _clutter_actor_signal_queue_redraw (self, self); /* Just in case anyone is manually firing redraw signals without * using the public queue_redraw() API we are careful to ensure that - * our out-of-band clip member is cleared before returning... + * our out-of-band clip member is cleared before returning and + * the out-of-band camera index reset to zero. * - * Note: A NULL clip denotes a full-stage, un-clipped redraw + * Note: A NULL clip denotes a full-stage, un-clipped redraw and + * camera index 0 corresponds to the main stage camera or the + * left eye camera while stereoscopic rendering is enabled. */ - if (G_LIKELY (clipped)) - _clutter_actor_set_queue_redraw_clip (self, NULL); + _clutter_actor_set_queue_redraw_clip (self, NULL); + + _clutter_actor_set_queue_redraw_camera_index (self, 0); } static void @@ -6638,10 +6901,10 @@ _clutter_actor_queue_redraw_full (ClutterActor *self, ClutterEffect *effect) { ClutterActorPrivate *priv = self->priv; + ClutterStage *stage; ClutterPaintVolume allocation_pv; ClutterPaintVolume *pv; gboolean should_free_pv; - ClutterActor *stage; /* Here's an outline of the actor queue redraw mechanism: * @@ -6716,9 +6979,8 @@ _clutter_actor_queue_redraw_full (ClutterActor *self, if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; - stage = _clutter_actor_get_stage_internal (self); - /* Ignore queueing a redraw for actors not descended from a stage */ + stage = _clutter_actor_get_stage_real (self); if (stage == NULL) return; @@ -6726,6 +6988,8 @@ _clutter_actor_queue_redraw_full (ClutterActor *self, if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return; + /* FIXME: in this case if a clip was explicitly passed we should + * intersect the clip with the allocation. */ if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION) { ClutterActorBox allocation_clip; @@ -6736,9 +7000,12 @@ _clutter_actor_queue_redraw_full (ClutterActor *self, if (priv->needs_allocation) { /* NB: NULL denotes an undefined clip which will result in a - * full redraw... */ - _clutter_actor_set_queue_redraw_clip (self, NULL); - _clutter_actor_signal_queue_redraw (self, self); + * full redraw according to the stage paint-volume. */ + priv->queue_redraw_entry = + _clutter_stage_queue_actor_redraw (stage, + priv->queue_redraw_entry, + self, + NULL); return; } @@ -6764,8 +7031,8 @@ _clutter_actor_queue_redraw_full (ClutterActor *self, should_free_pv = FALSE; } - self->priv->queue_redraw_entry = - _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage), + priv->queue_redraw_entry = + _clutter_stage_queue_actor_redraw (stage, priv->queue_redraw_entry, self, pv); @@ -7557,8 +7824,12 @@ void clutter_actor_get_allocation_box (ClutterActor *self, ClutterActorBox *box) { + ClutterActorPrivate *priv; + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + priv = self->priv; + /* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail, * which limits calling get_allocation to inside paint() basically; or * we can 2) force a layout, which could be expensive if someone calls @@ -7572,16 +7843,15 @@ clutter_actor_get_allocation_box (ClutterActor *self, */ /* this implements 2) */ - if (G_UNLIKELY (self->priv->needs_allocation)) + if (G_UNLIKELY (priv->needs_allocation)) { - ClutterActor *stage = _clutter_actor_get_stage_internal (self); - + ClutterStage *stage = _clutter_actor_get_stage_real (self); /* do not queue a relayout on an unparented actor */ if (stage) _clutter_stage_maybe_relayout (stage); } - /* commenting out the code above and just keeping this assigment + /* commenting out the code above and just keeping this assignment * implements 3) */ *box = self->priv->allocation; @@ -7780,9 +8050,13 @@ clutter_actor_allocate (ClutterActor *self, ClutterActorBox old_allocation, real_allocation; gboolean origin_changed, child_moved, size_changed; gboolean stage_allocation_changed; + ClutterStage *stage; g_return_if_fail (CLUTTER_IS_ACTOR (self)); - if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL)) + priv = self->priv; + stage = _clutter_actor_get_stage_real (self); + + if (G_UNLIKELY (stage == NULL)) { g_warning ("Spurious clutter_actor_allocate called for actor %p/%s " "which isn't a descendent of the stage!\n", @@ -7790,8 +8064,6 @@ clutter_actor_allocate (ClutterActor *self, return; } - priv = self->priv; - old_allocation = priv->allocation; real_allocation = *box; @@ -8633,6 +8905,9 @@ clutter_actor_get_transformed_position (ClutterActor *self, * information, you need to use clutter_actor_get_abs_allocation_vertices() * to get the coords of the actual quadrangle.</note> * + * <note>If clutter is being used for stereo rendering then this will + * simply return a size according the left eye's view.</note> + * * Since: 0.8 */ void @@ -8673,7 +8948,7 @@ clutter_actor_get_transformed_size (ClutterActor *self, box.x2 = natural_width; box.y2 = natural_height; - _clutter_actor_transform_and_project_box (self, &box, v); + _clutter_actor_transform_and_project_box (self, 0, &box, v); } else clutter_actor_get_abs_allocation_vertices (self, v); @@ -9892,6 +10167,53 @@ clutter_actor_get_clip (ClutterActor *self, *height = priv->clip.height; } +typedef struct +{ + ClutterStage *stage; + int stage_n_cameras; + int stage_cameras_age; +} InitPerCameraStateClosure; + +static ClutterActorTraverseVisitFlags +init_per_camera_state_cb (ClutterActor *self, + int depth, + gpointer user_data) +{ + ClutterActorPrivate *priv = self->priv; + InitPerCameraStateClosure *closure = user_data; + int n_cameras = closure->stage_n_cameras; + int i; + + /* This is the first point at which this actor has been + * associated with a specific stage and now that we have been + * associated with a set of cameras we need to initialize the + * actor's per-camera paint-volume to be empty so when it first + * gets shown we will only redraw the new area of the actor. + * + * XXX: it could be nice if re-parenting an actor within the + * same stage didn't hit this path too. + */ + + /* Make sure our camera state doesn't have the same age as the + * stage's camera state so we can be sure it will be invalidated + * during _clutter_actor_get_per_camera_state() */ + priv->cameras_age = closure->stage_cameras_age - 1; + + /* XXX: note we don't just rely on the initialization of per camera + * state by _clutter_actor_get_per_camera_state() since that will + * mark the initial paint_volume as invalid. + */ + for (i = 0; i < n_cameras; i++) + { + PerCameraState *camera_state = + _clutter_actor_get_per_camera_state (self, i); + _clutter_paint_volume_init_static (&camera_state->eye_volume, NULL); + camera_state->eye_volume_valid = TRUE; + } + + return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; +} + /** * clutter_actor_get_children: * @self: a #ClutterActor @@ -10185,6 +10507,7 @@ clutter_actor_add_child_internal (ClutterActor *self, gboolean check_state; gboolean notify_first_last; ClutterActor *old_first_child, *old_last_child; + ClutterStage *stage; if (child->priv->parent != NULL) { @@ -10289,6 +10612,25 @@ clutter_actor_add_child_internal (ClutterActor *self, if (self->priv->internal_child) CLUTTER_SET_PRIVATE_FLAGS (child, CLUTTER_INTERNAL_CHILD); + /* Check to see if the actor is associated with a stage yet... */ + stage = _clutter_actor_get_stage_real (self); + + if (stage) + { + InitPerCameraStateClosure init_per_camera_state_closure; + + init_per_camera_state_closure.stage_n_cameras = + _clutter_stage_get_n_cameras (stage); + init_per_camera_state_closure.stage_cameras_age = + _clutter_stage_get_cameras_age (stage); + + _clutter_actor_traverse (self, + CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, + init_per_camera_state_cb, + NULL, + &init_per_camera_state_closure); + } + /* clutter_actor_reparent() will emit ::parent-set for us */ if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child)) g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL); @@ -12628,15 +12970,6 @@ clutter_actor_is_scaled (ClutterActor *self) return FALSE; } -ClutterActor * -_clutter_actor_get_stage_internal (ClutterActor *actor) -{ - while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor)) - actor = actor->priv->parent; - - return actor; -} - /** * clutter_actor_get_stage: * @actor: a #ClutterActor @@ -12653,7 +12986,7 @@ clutter_actor_get_stage (ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); - return _clutter_actor_get_stage_internal (actor); + return CLUTTER_ACTOR (_clutter_actor_get_stage_real (actor)); } /** @@ -12966,13 +13299,13 @@ out: void clutter_actor_grab_key_focus (ClutterActor *self) { - ClutterActor *stage; + ClutterStage *stage; g_return_if_fail (CLUTTER_IS_ACTOR (self)); - stage = _clutter_actor_get_stage_internal (self); + stage = _clutter_actor_get_stage_real (self); if (stage != NULL) - clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self); + clutter_stage_set_key_focus (stage, self); } /** @@ -13710,26 +14043,6 @@ clutter_actor_has_pointer (ClutterActor *self) return self->priv->has_pointer; } -/* XXX: This is a workaround for not being able to break the ABI of - * the QUEUE_REDRAW signal. It is an out-of-band argument. See - * clutter_actor_queue_clipped_redraw() for details. - */ -ClutterPaintVolume * -_clutter_actor_get_queue_redraw_clip (ClutterActor *self) -{ - return g_object_get_data (G_OBJECT (self), - "-clutter-actor-queue-redraw-clip"); -} - -void -_clutter_actor_set_queue_redraw_clip (ClutterActor *self, - ClutterPaintVolume *clip) -{ - g_object_set_data (G_OBJECT (self), - "-clutter-actor-queue-redraw-clip", - clip); -} - /** * clutter_actor_has_allocation: * @self: a #ClutterActor @@ -14425,15 +14738,15 @@ clutter_actor_clear_effects (ClutterActor *self) gboolean clutter_actor_has_key_focus (ClutterActor *self) { - ClutterActor *stage; + ClutterStage *stage; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); - stage = _clutter_actor_get_stage_internal (self); + stage = _clutter_actor_get_stage_real (self); if (stage == NULL) return FALSE; - return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self; + return clutter_stage_get_key_focus (stage) == self; } static gboolean @@ -14561,10 +14874,12 @@ _clutter_actor_get_paint_volume_real (ClutterActor *self, static ClutterPaintVolume * _clutter_actor_get_paint_volume_mutable (ClutterActor *self) { - ClutterActorPrivate *priv; - - priv = self->priv; + ClutterActorPrivate *priv = self->priv; + /* NB: the paint volume isn't slice/heap allocated; it's actually + * just a member of priv, but we still have to call _free incase + * cogl-paint-volume.c associates object/memory references with + * the volume... */ if (priv->paint_volume_valid) clutter_paint_volume_free (&priv->paint_volume); @@ -14639,29 +14954,30 @@ clutter_actor_get_paint_volume (ClutterActor *self) * not guaranteed to be valid across multiple frames; if you wish to * keep it, you will have to copy it using clutter_paint_volume_copy(). * + * <note>If stereoscopic rendering has been enabled then the paint + * volume is only valid for the eye currently being rendered.</note> + * * Since: 1.6 */ const ClutterPaintVolume * clutter_actor_get_transformed_paint_volume (ClutterActor *self, ClutterActor *relative_to_ancestor) { + ClutterStage *stage = _clutter_actor_get_stage_real (self); const ClutterPaintVolume *volume; - ClutterActor *stage; ClutterPaintVolume *transformed_volume; - stage = _clutter_actor_get_stage_internal (self); if (G_UNLIKELY (stage == NULL)) return NULL; if (relative_to_ancestor == NULL) - relative_to_ancestor = stage; + relative_to_ancestor = CLUTTER_ACTOR (stage); volume = clutter_actor_get_paint_volume (self); if (volume == NULL) return NULL; - transformed_volume = - _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage)); + transformed_volume = _clutter_stage_paint_volume_stack_allocate (stage); _clutter_paint_volume_copy_static (volume, transformed_volume); @@ -14689,30 +15005,39 @@ clutter_actor_get_transformed_paint_volume (ClutterActor *self, * because the actor isn't yet parented under a stage or because * the actor is unable to determine a paint volume. * + * This function may only be called during a paint cycle. + * * Return value: %TRUE if a 2D paint box could be determined, else * %FALSE. * + * <note>If stereoscopic rendering has been enabled then the paint box + * returned will only be valid for the current eye being + * rendered</note> + * * Since: 1.6 */ gboolean clutter_actor_get_paint_box (ClutterActor *self, ClutterActorBox *box) { - ClutterActor *stage; ClutterPaintVolume *pv; + const ClutterCamera *camera; + ClutterStage *stage; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); g_return_val_if_fail (box != NULL, FALSE); - stage = _clutter_actor_get_stage_internal (self); + stage = _clutter_actor_get_stage_real (self); if (G_UNLIKELY (!stage)) return FALSE; + camera = _clutter_stage_get_current_camera (stage); + pv = _clutter_actor_get_paint_volume_mutable (self); if (G_UNLIKELY (!pv)) return FALSE; - _clutter_paint_volume_get_stage_paint_box (pv, CLUTTER_STAGE (stage), box); + _clutter_paint_volume_get_camera_paint_box (pv, camera, box); return TRUE; } |