summaryrefslogtreecommitdiff
path: root/clutter/clutter-offscreen-effect.c
diff options
context:
space:
mode:
Diffstat (limited to 'clutter/clutter-offscreen-effect.c')
-rw-r--r--clutter/clutter-offscreen-effect.c480
1 files changed, 365 insertions, 115 deletions
diff --git a/clutter/clutter-offscreen-effect.c b/clutter/clutter-offscreen-effect.c
index 09443dc35..99130a0ed 100644
--- a/clutter/clutter-offscreen-effect.c
+++ b/clutter/clutter-offscreen-effect.c
@@ -76,26 +76,32 @@
#include "clutter-private.h"
#include "clutter-stage-private.h"
-struct _ClutterOffscreenEffectPrivate
+typedef struct _PerCameraState
{
+ const ClutterCamera *camera;
+ int valid_for_age;
+
CoglHandle offscreen;
- CoglPipeline *target;
- CoglHandle texture;
- ClutterActor *actor;
- ClutterActor *stage;
+ /* aka "target" for legacy reasons */
+ CoglPipeline *pipeline;
+ CoglHandle texture;
- gfloat x_offset;
- gfloat y_offset;
+ gfloat viewport_x_offset;
+ gfloat viewport_y_offset;
/* This is the calculated size of the fbo before being passed
- through create_texture(). This needs to be tracked separately so
- that we can detect when a different size is calculated and
- regenerate the fbo */
- int fbo_width;
- int fbo_height;
-
- gint old_opacity_override;
+ * through update_fbo() and create_texture(). This needs to be
+ * tracked separately from the final fbo_width/height so that we can
+ * detect when a different size is calculated and regenerate the
+ * fbo.
+ *
+ * NB: We can't just compare the fbo_width/height because some
+ * sub-classes may return a texture from create_texture() that has
+ * a different size from the calculated request size.
+ */
+ int request_width;
+ int request_height;
/* The matrix that was current the last time the fbo was updated. We
need to keep track of this to detect when we can reuse the
@@ -106,6 +112,18 @@ struct _ClutterOffscreenEffectPrivate
and it won't cause a redraw to be queued on the parent's
children. */
CoglMatrix last_matrix_drawn;
+
+} PerCameraState;
+
+struct _ClutterOffscreenEffectPrivate
+{
+ ClutterActor *actor;
+
+ PerCameraState *camera_state;
+ int n_cameras;
+ int cameras_age;
+
+ gint old_opacity_override;
};
G_DEFINE_ABSTRACT_TYPE (ClutterOffscreenEffect,
@@ -119,15 +137,20 @@ clutter_offscreen_effect_set_actor (ClutterActorMeta *meta,
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (meta);
ClutterOffscreenEffectPrivate *priv = self->priv;
ClutterActorMetaClass *meta_class;
+ int i;
meta_class = CLUTTER_ACTOR_META_CLASS (clutter_offscreen_effect_parent_class);
meta_class->set_actor (meta, actor);
/* clear out the previous state */
- if (priv->offscreen != NULL)
+ for (i = 0; i < priv->n_cameras; i++)
{
- cogl_handle_unref (priv->offscreen);
- priv->offscreen = NULL;
+ PerCameraState *camera_state = &priv->camera_state[i];
+ if (camera_state->offscreen != NULL)
+ {
+ cogl_object_unref (camera_state->offscreen);
+ camera_state->offscreen = NULL;
+ }
}
/* we keep a back pointer here, to avoid going through the ActorMeta */
@@ -144,72 +167,139 @@ clutter_offscreen_effect_real_create_texture (ClutterOffscreenEffect *effect,
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
}
-static gboolean
-update_fbo (ClutterEffect *effect, int fbo_width, int fbo_height)
+static void
+invalidate_per_camera_state (PerCameraState *camera_state)
+{
+ if (camera_state->pipeline)
+ {
+ cogl_object_unref (camera_state->pipeline);
+ camera_state->pipeline = NULL;
+ }
+
+ if (camera_state->offscreen)
+ {
+ cogl_object_unref (camera_state->offscreen);
+ camera_state->offscreen = NULL;
+ }
+}
+
+static PerCameraState *
+get_per_camera_state (ClutterOffscreenEffect *self, int camera_index)
{
- ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
ClutterOffscreenEffectPrivate *priv = self->priv;
+ ClutterStage *stage =
+ CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ int cameras_age = _clutter_stage_get_cameras_age (stage);
+ PerCameraState *camera_state;
- priv->stage = clutter_actor_get_stage (priv->actor);
- if (priv->stage == NULL)
+ /* 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 cached state associated with the old cameras. */
+
+ if (G_UNLIKELY (cameras_age != priv->cameras_age))
{
- CLUTTER_NOTE (MISC, "The actor '%s' is not part of a stage",
- clutter_actor_get_name (priv->actor) == NULL
- ? G_OBJECT_TYPE_NAME (priv->actor)
- : clutter_actor_get_name (priv->actor));
- return FALSE;
+ int i;
+ int n_cameras;
+
+ for (i = 0; i < priv->n_cameras; i++)
+ invalidate_per_camera_state (&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->pipeline = NULL;
+ camera_state->texture = NULL;
+ camera_state->offscreen = NULL;
+ }
+
+ priv->n_cameras = n_cameras;
+ priv->cameras_age = cameras_age;
}
- if (priv->fbo_width == fbo_width &&
- priv->fbo_height == fbo_height &&
- priv->offscreen != NULL)
+ camera_state = &priv->camera_state[camera_index];
+ if (camera_state->camera->age != camera_state->valid_for_age)
+ {
+ invalidate_per_camera_state (camera_state);
+ camera_state->valid_for_age = camera_state->camera->age;
+ }
+
+ return camera_state;
+}
+
+static gboolean
+update_fbo (ClutterEffect *effect,
+ int camera_index,
+ int request_width,
+ int request_height)
+{
+ ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
+ PerCameraState *camera_state;
+
+ camera_state = get_per_camera_state (self, camera_index);
+ if (camera_state->request_width == request_width &&
+ camera_state->request_height == request_height &&
+ camera_state->offscreen != NULL)
return TRUE;
- if (priv->target == NULL)
+ if (camera_state->pipeline == NULL)
{
CoglContext *ctx =
clutter_backend_get_cogl_context (clutter_get_default_backend ());
- priv->target = cogl_pipeline_new (ctx);
+ camera_state->pipeline = cogl_pipeline_new (ctx);
/* We're always going to render the texture at a 1:1 texel:pixel
ratio so we can use 'nearest' filtering to decrease the
effects of rounding errors in the geometry calculation */
- cogl_pipeline_set_layer_filters (priv->target,
+ cogl_pipeline_set_layer_filters (camera_state->pipeline,
0, /* layer_index */
COGL_PIPELINE_FILTER_NEAREST,
COGL_PIPELINE_FILTER_NEAREST);
}
- if (priv->texture != NULL)
+ if (camera_state->texture != NULL)
{
- cogl_handle_unref (priv->texture);
- priv->texture = NULL;
+ cogl_object_unref (camera_state->texture);
+ camera_state->texture = NULL;
}
- priv->texture =
- clutter_offscreen_effect_create_texture (self, fbo_width, fbo_height);
- if (priv->texture == NULL)
+ camera_state->texture =
+ clutter_offscreen_effect_create_texture (self,
+ request_width,
+ request_height);
+ if (camera_state->texture == NULL)
return FALSE;
- cogl_pipeline_set_layer_texture (priv->target, 0, priv->texture);
+ cogl_pipeline_set_layer_texture (camera_state->pipeline, 0, camera_state->texture);
- priv->fbo_width = fbo_width;
- priv->fbo_height = fbo_height;
+ camera_state->request_width = request_width;
+ camera_state->request_height = request_height;
- if (priv->offscreen != NULL)
- cogl_handle_unref (priv->offscreen);
+ if (camera_state->offscreen != NULL)
+ cogl_object_unref (camera_state->offscreen);
- priv->offscreen = cogl_offscreen_new_to_texture (priv->texture);
- if (priv->offscreen == NULL)
+ camera_state->offscreen = cogl_offscreen_new_to_texture (camera_state->texture);
+ if (camera_state->offscreen == NULL)
{
g_warning ("%s: Unable to create an Offscreen buffer", G_STRLOC);
- cogl_handle_unref (priv->target);
- priv->target = NULL;
+ cogl_object_unref (camera_state->pipeline);
+ camera_state->pipeline = NULL;
- priv->fbo_width = 0;
- priv->fbo_height = 0;
+ camera_state->request_width = 0;
+ camera_state->request_height = 0;
return FALSE;
}
@@ -225,10 +315,14 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect)
ClutterActorBox box;
CoglMatrix projection;
CoglColor transparent;
- gfloat fbo_width, fbo_height;
- gfloat width, height;
+ gfloat fbo_request_width, fbo_request_height;
+ gfloat stage_viewport_x, stage_viewport_y;
+ gfloat stage_viewport_width, stage_viewport_height;
gfloat xexpand, yexpand;
int texture_width, texture_height;
+ const ClutterCamera *camera;
+ PerCameraState *camera_state;
+ ClutterStage *stage;
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
return FALSE;
@@ -236,71 +330,108 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect)
if (priv->actor == NULL)
return FALSE;
+ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+
+ camera = _clutter_stage_get_current_camera (stage);
+ camera_state = get_per_camera_state (self, camera->index);
+
+ stage_viewport_x = camera->viewport[0];
+ stage_viewport_y = camera->viewport[1];
+ stage_viewport_width = camera->viewport[2];
+ stage_viewport_height = camera->viewport[3];
+
/* The paint box is the bounding box of the actor's paint volume in
- * stage coordinates. This will give us the size for the framebuffer
- * we need to redirect its rendering offscreen and its position will
- * be used to setup an offset viewport */
+ * screen coordinates (i.e. stage coordinates that have been
+ * projected by the current camera and had the camera's viewport
+ * transform applied). This will give us the size for the
+ * framebuffer we need to redirect its rendering offscreen.
+ *
+ * The position will be used to setup an offset viewport so we need
+ * an offset that is relative to the top-left of the stage's
+ * viewport rectangle not relative to the screen.
+ *
+ * NB: We can't assume the stage viewport has an origin of (0,0) or
+ * that the viewport size matches the geometry of the stage because
+ * for example when we are running in stereoscopic rendering mode
+ * the stage size might map to half the width or height in screen
+ * coordinates if a horizonal or vertical split screen is being
+ * used.
+ */
if (clutter_actor_get_paint_box (priv->actor, &box))
{
- clutter_actor_box_get_size (&box, &fbo_width, &fbo_height);
- clutter_actor_box_get_origin (&box, &priv->x_offset, &priv->y_offset);
+ clutter_actor_box_get_size (&box,
+ &fbo_request_width, &fbo_request_height);
+ clutter_actor_box_get_origin (&box,
+ &camera_state->viewport_x_offset,
+ &camera_state->viewport_y_offset);
+ camera_state->viewport_x_offset -= stage_viewport_x;
+ camera_state->viewport_y_offset -= stage_viewport_y;
}
else
{
/* If we can't get a valid paint box then we fallback to
- * creating a full stage size fbo. */
- ClutterActor *stage = _clutter_actor_get_stage_internal (priv->actor);
- clutter_actor_get_size (stage, &fbo_width, &fbo_height);
- priv->x_offset = 0.0f;
- priv->y_offset = 0.0f;
+ * creating a full stage size fbo.
+ *
+ * Note: as mentioned above the stage's viewport might not
+ * match the stage's geometry so if we want to know the stages
+ * size in screen coordinates we should look at the viewport
+ * geometry.
+ *
+ * Note: we may need to change how we determine the screen-space
+ * size of the stage if we add support for sliced stages in the
+ * future.
+ */
+ fbo_request_width = stage_viewport_width;
+ fbo_request_height = stage_viewport_height;
+ camera_state->viewport_x_offset = 0;
+ camera_state->viewport_y_offset = 0;
}
/* First assert that the framebuffer is the right size... */
- if (!update_fbo (effect, fbo_width, fbo_height))
+ if (!update_fbo (effect, camera->index,
+ fbo_request_width, fbo_request_height))
return FALSE;
- texture_width = cogl_texture_get_width (priv->texture);
- texture_height = cogl_texture_get_height (priv->texture);
+ texture_width = cogl_texture_get_width (camera_state->texture);
+ texture_height = cogl_texture_get_height (camera_state->texture);
/* get the current modelview matrix so that we can copy it to the
* framebuffer. We also store the matrix that was last used when we
* updated the FBO so that we can detect when we don't need to
* update the FBO to paint a second time */
- cogl_get_modelview_matrix (&priv->last_matrix_drawn);
+ cogl_get_modelview_matrix (&camera_state->last_matrix_drawn);
/* let's draw offscreen */
- cogl_push_framebuffer (priv->offscreen);
+ cogl_push_framebuffer (camera_state->offscreen);
/* Copy the modelview that would have been used if rendering onscreen */
- cogl_set_modelview_matrix (&priv->last_matrix_drawn);
-
- /* Set up the viewport so that it has the same size as the stage,
- * but offset it so that the actor of interest lands on our
- * framebuffer. */
- clutter_actor_get_size (priv->stage, &width, &height);
+ cogl_set_modelview_matrix (&camera_state->last_matrix_drawn);
/* Expand the viewport if the actor is partially off-stage,
* otherwise the actor will end up clipped to the stage viewport
*/
xexpand = 0.f;
- if (priv->x_offset < 0.f)
- xexpand = -priv->x_offset;
- if (priv->x_offset + texture_width > width)
- xexpand = MAX (xexpand, (priv->x_offset + texture_width) - width);
+ if (camera_state->viewport_x_offset < 0.f)
+ xexpand = -camera_state->viewport_x_offset;
+ if (camera_state->viewport_x_offset + texture_width > stage_viewport_width)
+ xexpand = MAX (xexpand, (camera_state->viewport_x_offset +
+ texture_width) - stage_viewport_width);
yexpand = 0.f;
- if (priv->y_offset < 0.f)
- yexpand = -priv->y_offset;
- if (priv->y_offset + texture_height > height)
- yexpand = MAX (yexpand, (priv->y_offset + texture_height) - height);
+ if (camera_state->viewport_y_offset < 0.f)
+ yexpand = -camera_state->viewport_y_offset;
+ if (camera_state->viewport_y_offset + texture_height > stage_viewport_height)
+ yexpand = MAX (yexpand, (camera_state->viewport_y_offset +
+ texture_height) - stage_viewport_height);
/* Set the viewport */
- cogl_set_viewport (-(priv->x_offset + xexpand), -(priv->y_offset + yexpand),
- width + (2 * xexpand), height + (2 * yexpand));
+ cogl_set_viewport (-(camera_state->viewport_x_offset + xexpand),
+ -(camera_state->viewport_y_offset + yexpand),
+ stage_viewport_width + (2 * xexpand),
+ stage_viewport_height + (2 * yexpand));
/* Copy the stage's projection matrix across to the framebuffer */
- _clutter_stage_get_projection_matrix (CLUTTER_STAGE (priv->stage),
- &projection);
+ _clutter_stage_get_projection_matrix (stage, &projection);
/* If we've expanded the viewport, make sure to scale the projection
* matrix accordingly (as it's been initialised to work with the
@@ -310,12 +441,12 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect)
{
gfloat new_width, new_height;
- new_width = width + (2 * xexpand);
- new_height = height + (2 * yexpand);
+ new_width = stage_viewport_width + (2 * xexpand);
+ new_height = stage_viewport_height + (2 * yexpand);
cogl_matrix_scale (&projection,
- width / new_width,
- height / new_height,
+ stage_viewport_width / new_width,
+ stage_viewport_height / new_height,
1);
}
@@ -344,24 +475,32 @@ clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect)
{
ClutterOffscreenEffectPrivate *priv = effect->priv;
guint8 paint_opacity;
+ ClutterStage *stage =
+ CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ const ClutterCamera *camera = _clutter_stage_get_current_camera (stage);
+ PerCameraState *camera_state = get_per_camera_state (effect, camera->index);
+ int texture_width;
+ int texture_height;
paint_opacity = clutter_actor_get_paint_opacity (priv->actor);
- cogl_pipeline_set_color4ub (priv->target,
+ cogl_pipeline_set_color4ub (camera_state->pipeline,
paint_opacity,
paint_opacity,
paint_opacity,
paint_opacity);
- cogl_set_source (priv->target);
+ cogl_set_source (camera_state->pipeline);
/* At this point we are in stage coordinates translated so if
* we draw our texture using a textured quad the size of the paint
* box then we will overlay where the actor would have drawn if it
* hadn't been redirected offscreen.
*/
+ texture_width = cogl_texture_get_width (camera_state->texture);
+ texture_height = cogl_texture_get_height (camera_state->texture);
cogl_rectangle_with_texture_coords (0, 0,
- cogl_texture_get_width (priv->texture),
- cogl_texture_get_height (priv->texture),
+ texture_width,
+ texture_height,
0.0, 0.0,
1.0, 1.0);
}
@@ -371,16 +510,62 @@ clutter_offscreen_effect_paint_texture (ClutterOffscreenEffect *effect)
{
ClutterOffscreenEffectPrivate *priv = effect->priv;
CoglMatrix modelview;
+ ClutterStage *stage =
+ CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ const ClutterCamera *camera = _clutter_stage_get_current_camera (stage);
+ PerCameraState *camera_state = get_per_camera_state (effect, camera->index);
+ CoglMatrix saved_projection;
+ gfloat scale_x, scale_y;
+ gfloat stage_x, stage_y;
+ gfloat stage_width, stage_height;
+
+ /* Now reset the modelview/projection to put us in orthographic
+ * stage coordinates so we can draw the result of our offscreen render as
+ * a textured quad...
+ *
+ * XXX: Note we don't use _apply_transform (stage, &modelview) to
+ * put is in regular stage coordinates because that might include a
+ * stereoscopic camera view transform and we don't want our
+ * rectangle to be affected by that.
+ *
+ * XXX: since clutter-stage.c should be free to play tricks with the
+ * viewport and projection matrix to support different forms of
+ * stereoscopic rendering it might make sense at some point to add
+ * some internal _clutter_stage api something like
+ * _clutter_stage_push/pop_orthographic() that can handle the
+ * details of giving us an orthographic projection without
+ * clobbering any of the transforms in place for stereo rendering.
+ *
+ * XXX: For now we are assuming that clutter-stage.c only plays with
+ * the viewport for vertical and horizontal split stereo rendering
+ * but at some point if we start using the projection matrix instead
+ * then this code will conflict with that!
+ */
+
+ cogl_get_projection_matrix (&saved_projection);
+ clutter_actor_get_size (CLUTTER_ACTOR (stage), &stage_width, &stage_height);
+ cogl_ortho (0, stage_width, /* left, right */
+ stage_height, 0, /* bottom, top */
+ -1, 100 /* z near, far */);
cogl_push_matrix ();
- /* Now reset the modelview to put us in stage coordinates so
- * we can drawn the result of our offscreen render as a textured
- * quad... */
+ /* NB: camera_state->viewport_x/y_offset are in screen coordinates
+ * relative to the stage's viewport rectangle but here we need a
+ * position in stage coordinates.
+ *
+ * Also our texture size was measured in screen coordinates but we
+ * want to paint the texture in actor coordinates.
+ */
+ scale_x = (stage_width / camera->viewport[2]);
+ scale_y = (stage_height / camera->viewport[3]);
+
+ stage_x = camera_state->viewport_x_offset * scale_x;
+ stage_y = camera_state->viewport_y_offset * scale_y;
cogl_matrix_init_identity (&modelview);
- _clutter_actor_apply_modelview_transform (priv->stage, &modelview);
- cogl_matrix_translate (&modelview, priv->x_offset, priv->y_offset, 0.0f);
+ cogl_matrix_translate (&modelview, stage_x, stage_y, 0.0f);
+ cogl_matrix_scale (&modelview, scale_x, scale_y, 1);
cogl_set_modelview_matrix (&modelview);
/* paint the target material; this is virtualized for
@@ -389,6 +574,7 @@ clutter_offscreen_effect_paint_texture (ClutterOffscreenEffect *effect)
clutter_offscreen_effect_paint_target (effect);
cogl_pop_matrix ();
+ cogl_set_projection_matrix (&saved_projection);
}
static void
@@ -396,9 +582,13 @@ clutter_offscreen_effect_post_paint (ClutterEffect *effect)
{
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
ClutterOffscreenEffectPrivate *priv = self->priv;
+ ClutterStage *stage =
+ CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ const ClutterCamera *camera = _clutter_stage_get_current_camera (stage);
+ PerCameraState *camera_state = get_per_camera_state (self, camera->index);
- if (priv->offscreen == NULL ||
- priv->target == NULL ||
+ if (camera_state->offscreen == NULL ||
+ camera_state->pipeline == NULL ||
priv->actor == NULL)
return;
@@ -418,20 +608,27 @@ clutter_offscreen_effect_paint (ClutterEffect *effect,
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
ClutterOffscreenEffectPrivate *priv = self->priv;
CoglMatrix matrix;
+ ClutterStage *stage =
+ CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ const ClutterCamera *camera = _clutter_stage_get_current_camera (stage);
+ PerCameraState *camera_state = get_per_camera_state (self, camera->index);
cogl_get_modelview_matrix (&matrix);
/* If we've already got a cached image for the same matrix and the
- actor hasn't been redrawn then we can just use the cached image
- in the fbo */
- if (priv->offscreen == NULL ||
+ * actor hasn't been redrawn then we can just use the cached image
+ * in the fbo
+ */
+ if (camera_state->offscreen == NULL ||
(flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) ||
- !cogl_matrix_equal (&matrix, &priv->last_matrix_drawn))
+ !cogl_matrix_equal (&matrix, &camera_state->last_matrix_drawn) ||
+ camera_state->valid_for_age != camera_state->camera->age)
{
/* Chain up to the parent paint method which will call the pre and
post paint functions to update the image */
CLUTTER_EFFECT_CLASS (clutter_offscreen_effect_parent_class)->
paint (effect, flags);
+ camera_state->valid_for_age = camera_state->camera->age;
}
else
clutter_offscreen_effect_paint_texture (self);
@@ -442,15 +639,29 @@ clutter_offscreen_effect_finalize (GObject *gobject)
{
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (gobject);
ClutterOffscreenEffectPrivate *priv = self->priv;
+ int i;
+
+ for (i = 0; i < priv->n_cameras; i++)
+ {
+ PerCameraState *camera_state = &priv->camera_state[i];
+
+ if (camera_state->offscreen)
+ cogl_object_unref (camera_state->offscreen);
- if (priv->offscreen)
- cogl_handle_unref (priv->offscreen);
+ if (camera_state->pipeline)
+ cogl_object_unref (camera_state->pipeline);
- if (priv->target)
- cogl_handle_unref (priv->target);
+ if (camera_state->texture)
+ cogl_object_unref (camera_state->texture);
+ }
- if (priv->texture)
- cogl_handle_unref (priv->texture);
+ if (priv->camera_state)
+ {
+ g_slice_free1 (sizeof (PerCameraState) * priv->n_cameras,
+ priv->camera_state);
+ priv->camera_state = NULL;
+ priv->n_cameras = 0;
+ }
G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->finalize (gobject);
}
@@ -482,6 +693,8 @@ clutter_offscreen_effect_init (ClutterOffscreenEffect *self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
CLUTTER_TYPE_OFFSCREEN_EFFECT,
ClutterOffscreenEffectPrivate);
+
+ self->priv->cameras_age = -1;
}
/**
@@ -507,10 +720,23 @@ clutter_offscreen_effect_init (ClutterOffscreenEffect *self)
CoglHandle
clutter_offscreen_effect_get_texture (ClutterOffscreenEffect *effect)
{
+ ClutterOffscreenEffect *self;
+ ClutterOffscreenEffectPrivate *priv;
+ ClutterStage *stage;
+ const ClutterCamera *camera;
+ PerCameraState *camera_state;
+
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
NULL);
- return effect->priv->texture;
+ self = CLUTTER_OFFSCREEN_EFFECT (effect);
+ priv = self->priv;
+
+ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ camera = _clutter_stage_get_current_camera (stage);
+ camera_state = get_per_camera_state (self, camera->index);
+
+ return camera_state->texture;
}
/**
@@ -532,10 +758,23 @@ clutter_offscreen_effect_get_texture (ClutterOffscreenEffect *effect)
CoglMaterial *
clutter_offscreen_effect_get_target (ClutterOffscreenEffect *effect)
{
+ ClutterOffscreenEffect *self;
+ ClutterOffscreenEffectPrivate *priv;
+ ClutterStage *stage;
+ const ClutterCamera *camera;
+ PerCameraState *camera_state;
+
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
NULL);
- return (CoglMaterial *)effect->priv->target;
+ self = CLUTTER_OFFSCREEN_EFFECT (effect);
+ priv = self->priv;
+
+ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ camera = _clutter_stage_get_current_camera (stage);
+ camera_state = get_per_camera_state (self, camera->index);
+
+ return (CoglMaterial *)camera_state->pipeline;
}
/**
@@ -594,6 +833,10 @@ clutter_offscreen_effect_create_texture (ClutterOffscreenEffect *effect,
* implementations, from within the <function>paint_target()</function>
* virtual function.
*
+ * <note>If stereoscopic rendering has been enabled then this function
+ * returns the size according to the eye currently being
+ * rendered.</note>
+ *
* Return value: %TRUE if the offscreen buffer has a valid size,
* and %FALSE otherwise
*
@@ -605,19 +848,26 @@ clutter_offscreen_effect_get_target_size (ClutterOffscreenEffect *effect,
gfloat *height)
{
ClutterOffscreenEffectPrivate *priv;
+ ClutterStage *stage;
+ const ClutterCamera *camera;
+ PerCameraState *camera_state;
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), FALSE);
priv = effect->priv;
- if (priv->texture == NULL)
+ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (priv->actor));
+ camera = _clutter_stage_get_current_camera (stage);
+ camera_state = get_per_camera_state (effect, camera->index);
+
+ if (camera_state->texture == NULL)
return FALSE;
if (width)
- *width = cogl_texture_get_width (priv->texture);
+ *width = cogl_texture_get_width (camera_state->texture);
if (height)
- *height = cogl_texture_get_height (priv->texture);
+ *height = cogl_texture_get_height (camera_state->texture);
return TRUE;
}