Compare commits

...

12 Commits

Author SHA1 Message Date
Georges Basile Stavracas Neto
4a69e70000 Introduce ClutterBlitNode
It is not possible to express a blit operation using paint
nodes as of now. This is a requirement for GNOME Shell, e.g.,
to implement its blur effect.

Add a new ClutterBlitNode node that takes two framebuffers as
input, and blits source into dest according to added rectangles.

Because this paint node uses the rectangles in a different way
compared to all the other nodes, add an auxiliary method to
ensure all blit operations are valid.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
bc81f17d5f clutter/paint-nodes: Add serialization to layer node
It's useful to know which framebuffer the layer node is holding,
so serialize that too.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
3b64f56c5f clutter/paint-nodes: Push and pop framebuffer even without ops
ClutterLayerNode currently skips pushing and popping its framebuffer
to the paint context when no rectangles are added to it. However,
it's still useful to do that, since we might want to render child
nodes to the offscreen and reuse the pipeline in a later node.

Make ClutterLayerNode push and pop its framebuffer even without
rectangles.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
31dc74dfd3 clutter/paint-nodes: Add new ClutterLayerNode API
ClutterLayerNode is the "offscreen framebuffer" node, that paints
it's child nodes in a separate framebuffer, and then copies that
framebuffer to the parent one.

It'll be useful to hand ClutterLayerNode which framebuffer to use,
as this is a requirement for porting e.g. ClutterOffscreenEffect
and subclasses.

Add a new clutter_layer_node_new_with_framebuffer() API.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
26a225f675 clutter/paint-nodes: Don't skip pipeline node constructor
ClutterPipelineNode will be used by GNOME Shell in the future.
Fortunately for us, CoglPipeline is already usable from GJS,
so we don't need to skip the constructor for the pipeline node.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
0caca4fc49 clutter/paint-nodes: Add opacity overriding to ClutterActorNode
Some effects, such as ShellBlurEffect and ClutterOffscreenEffect, need
to make sure the actor is painted fully opaque. With ClutterActorNode,
however, that is currently not possible.

Add a new 'opacity' parameter to clutter_actor_node_new(). It follows
the opacity override heuristic, where -1 means disable, and anything
else is clamped to [0, 255].

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
5d11104a0a clutter/offscreen-effect: Simplify paint
Simply chain up to get the pre and post paint methods,
instead of reimplementing ClutterEffect.paint()

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
5186f0e3ef clutter/offscreen-effect: Clear framebuffer on pre_paint
Move the framebuffer cleanup to ClutterOffscreenEffect.pre_paint().
This will allow us to properly chain up ClutterOffscreenEffect.paint()
and not reimplement exactly what ClutterEffect does by default.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
61d247f399 clutter/effect: Add paint_node vfunc
Introduce a new paint_node vfunc that, if implemented, allows
the effect to add nodes to a transient paint node that is
immediately painted. This is a transitional step until we
have fully delegated paint node rendering.

The most basic implementation of a ClutterEffect.paint_node
vfunc, and also the default implementation, is with an actor
node, as follows:

```
static void
foo_bar_paint_node (ClutterEffect           *effect,
                    ClutterPaintNode        *node,
                    ClutterPaintContext     *paint_context,
                    ClutterEffectPaintFlags  flags)
{
  g_autoptr (ClutterPaintNode) actor_node = NULL;

  actor_node = clutter_actor_node_new (effect->actor);
  clutter_paint_node_add_child (node, actor_node);
}
```

This example gives the exact same behavior of simply calling
clutter_actor_continue_paint(). In the future, the paint node
itself will be a parameter of clutter_actor_continue_paint()
and we'll be able to simplify it event more.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
19bf3751eb Introduce ClutterEffectNode
ClutterEffectNode is a private ClutterPaintNode implementation
that does effectively nothing, but helps organizing the paint
node tree. It also helps debugging, since it can output the
effect class and name to the JSON debugging routines.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
963df6f61c clutter/effect: Don't expose pre and post paint helpers
They're not used outside ClutterEffect.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:17 +00:00
Georges Basile Stavracas Neto
a510339970 clutter/paint-node: Walk up paint node tree to find framebuffer
The idea of having a paint node tree is that we don't really need
to retrieve the framebuffer from ClutterPaintContext. For example,
ClutterLayerNode draws into an offscreen framebuffer; if any child
of a layer node needs to retrieve a framebuffer to draw, the layer
node's offscreen framebuffer should be used.

However, clutter_paint_node_get_framebuffer() goes straight to the
root node of the tree, skipping any potential paint nodes with a
custom framebuffer.

Modify clutter_paint_node_get_framebuffer() to walk up the paint
node tree until a node with a custom framebuffer appears. In many
cases, this will end up either in dummy or layer node's custom
framebuffer implementations.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340
2020-08-14 16:04:16 +00:00
9 changed files with 498 additions and 85 deletions

View File

@ -3746,7 +3746,7 @@ clutter_actor_paint (ClutterActor *self,
if (!CLUTTER_ACTOR_IS_MAPPED (self))
return;
actor_node = clutter_actor_node_new (self);
actor_node = clutter_actor_node_new (self, -1);
root_node = clutter_paint_node_ref (actor_node);
if (priv->has_clip)

View File

@ -5,10 +5,6 @@
G_BEGIN_DECLS
gboolean _clutter_effect_pre_paint (ClutterEffect *effect,
ClutterPaintContext *paint_context);
void _clutter_effect_post_paint (ClutterEffect *effect,
ClutterPaintContext *paint_context);
gboolean _clutter_effect_modify_paint_volume (ClutterEffect *effect,
ClutterPaintVolume *volume);
gboolean _clutter_effect_has_custom_paint_volume (ClutterEffect *effect);

View File

@ -169,6 +169,8 @@
#include "clutter-effect-private.h"
#include "clutter-enum-types.h"
#include "clutter-marshal.h"
#include "clutter-paint-node-private.h"
#include "clutter-paint-nodes.h"
#include "clutter-private.h"
#include "clutter-actor-private.h"
@ -196,26 +198,46 @@ clutter_effect_real_modify_paint_volume (ClutterEffect *effect,
return TRUE;
}
static void
clutter_effect_real_paint_node (ClutterEffect *effect,
ClutterPaintNode *node,
ClutterPaintContext *paint_context,
ClutterEffectPaintFlags flags)
{
ClutterPaintNode *actor_node;
ClutterActor *actor;
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
actor_node = clutter_actor_node_new (actor, -1);
clutter_paint_node_add_child (node, actor_node);
clutter_paint_node_unref (actor_node);
}
static void
clutter_effect_real_paint (ClutterEffect *effect,
ClutterPaintContext *paint_context,
ClutterEffectPaintFlags flags)
{
ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect);
ClutterActor *actor;
ClutterEffectClass *effect_class = CLUTTER_EFFECT_GET_CLASS (effect);
ClutterPaintNode *node;
gboolean pre_paint_succeeded;
/* The default implementation provides a compatibility wrapper for
effects that haven't migrated to use the 'paint' virtual yet. This
just calls the old pre and post virtuals before chaining on */
pre_paint_succeeded = _clutter_effect_pre_paint (effect, paint_context);
pre_paint_succeeded = effect_class->pre_paint (effect, paint_context);
actor = clutter_actor_meta_get_actor (actor_meta);
clutter_actor_continue_paint (actor, paint_context);
node = clutter_effect_node_new (effect);
effect_class->paint_node (effect, node, paint_context, flags);
clutter_paint_node_paint (node, paint_context);
if (pre_paint_succeeded)
_clutter_effect_post_paint (effect, paint_context);
effect_class->post_paint (effect, paint_context);
clutter_paint_node_unref (node);
}
static void
@ -255,6 +277,7 @@ clutter_effect_class_init (ClutterEffectClass *klass)
klass->post_paint = clutter_effect_real_post_paint;
klass->modify_paint_volume = clutter_effect_real_modify_paint_volume;
klass->paint = clutter_effect_real_paint;
klass->paint_node = clutter_effect_real_paint_node;
klass->pick = clutter_effect_real_pick;
}
@ -263,24 +286,6 @@ clutter_effect_init (ClutterEffect *self)
{
}
gboolean
_clutter_effect_pre_paint (ClutterEffect *effect,
ClutterPaintContext *paint_context)
{
g_return_val_if_fail (CLUTTER_IS_EFFECT (effect), FALSE);
return CLUTTER_EFFECT_GET_CLASS (effect)->pre_paint (effect, paint_context);
}
void
_clutter_effect_post_paint (ClutterEffect *effect,
ClutterPaintContext *paint_context)
{
g_return_if_fail (CLUTTER_IS_EFFECT (effect));
CLUTTER_EFFECT_GET_CLASS (effect)->post_paint (effect, paint_context);
}
void
_clutter_effect_paint (ClutterEffect *effect,
ClutterPaintContext *paint_context,

View File

@ -87,6 +87,10 @@ struct _ClutterEffectClass
void (* paint) (ClutterEffect *effect,
ClutterPaintContext *paint_context,
ClutterEffectPaintFlags flags);
void (* paint_node) (ClutterEffect *effect,
ClutterPaintNode *node,
ClutterPaintContext *paint_context,
ClutterEffectPaintFlags flags);
void (* pick) (ClutterEffect *effect,
ClutterPickContext *pick_context);

View File

@ -272,10 +272,10 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect,
local_offset = GRAPHENE_POINT3D_INIT (0.0f, 0.0f, 0.0f);
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
return FALSE;
goto disable_effect;
if (priv->actor == NULL)
return FALSE;
goto disable_effect;
stage = _clutter_actor_get_stage_internal (priv->actor);
clutter_actor_get_size (stage, &stage_width, &stage_height);
@ -320,7 +320,7 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect,
/* First assert that the framebuffer is the right size... */
if (!update_fbo (effect, target_width, target_height, resource_scale))
return FALSE;
goto disable_effect;
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
cogl_framebuffer_get_modelview_matrix (framebuffer, &old_modelview);
@ -387,6 +387,10 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect,
clutter_actor_set_opacity_override (priv->actor, 0xff);
return TRUE;
disable_effect:
cogl_clear_object (&priv->offscreen);
return FALSE;
}
static void
@ -505,17 +509,10 @@ clutter_offscreen_effect_paint (ClutterEffect *effect,
*/
if (priv->offscreen == NULL || (flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY))
{
ClutterEffectClass *effect_class = CLUTTER_EFFECT_GET_CLASS (effect);
gboolean pre_paint_succeeded;
ClutterEffectClass *parent_class =
CLUTTER_EFFECT_CLASS (clutter_offscreen_effect_parent_class);
pre_paint_succeeded = effect_class->pre_paint (effect, paint_context);
clutter_actor_continue_paint (priv->actor, paint_context);
if (pre_paint_succeeded)
effect_class->post_paint (effect, paint_context);
else
g_clear_pointer (&priv->offscreen, cogl_object_unref);
parent_class->paint (effect, paint_context, flags);
}
else
clutter_offscreen_effect_paint_texture (self, paint_context);

View File

@ -141,6 +141,26 @@ ClutterPaintNode * clutter_paint_node_get_last_child (Clutter
G_GNUC_INTERNAL
ClutterPaintNode * clutter_paint_node_get_parent (ClutterPaintNode *node);
#define CLUTTER_TYPE_EFFECT_NODE (clutter_effect_node_get_type ())
#define CLUTTER_EFFECT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_EFFECT_NODE, ClutterEffectNode))
#define CLUTTER_IS_EFFECT_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_EFFECT_NODE))
/**
* ClutterEffectNode:
*
* The #ClutterEffectNode structure is an opaque
* type whose members cannot be directly accessed.
*/
typedef struct _ClutterEffectNode ClutterEffectNode;
typedef struct _ClutterEffectNode ClutterEffectNodeClass;
CLUTTER_EXPORT
GType clutter_effect_node_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterPaintNode * clutter_effect_node_new (ClutterEffect *actor);
G_END_DECLS
#endif /* __CLUTTER_PAINT_NODE_PRIVATE_H__ */

View File

@ -1149,24 +1149,14 @@ _clutter_paint_node_create (GType gtype)
return (gpointer) g_type_create_instance (gtype);
}
static ClutterPaintNode *
clutter_paint_node_get_root (ClutterPaintNode *node)
{
ClutterPaintNode *iter;
iter = node;
while (iter != NULL && iter->parent != NULL)
iter = iter->parent;
return iter;
}
/**
* clutter_paint_node_get_framebuffer:
* @node: a #ClutterPaintNode
*
* Retrieves the #CoglFramebuffer that @node will draw
* into, if it the root node has a custom framebuffer set.
* into. If @node doesn't specify a custom framebuffer,
* the first parent node with a custom framebuffer will
* be used.
*
* Returns: (transfer none): a #CoglFramebuffer or %NULL if no custom one is
* set.
@ -1174,12 +1164,19 @@ clutter_paint_node_get_root (ClutterPaintNode *node)
CoglFramebuffer *
clutter_paint_node_get_framebuffer (ClutterPaintNode *node)
{
ClutterPaintNode *root = clutter_paint_node_get_root (node);
ClutterPaintNodeClass *klass;
ClutterPaintNode *iter;
klass = CLUTTER_PAINT_NODE_GET_CLASS (root);
if (klass->get_framebuffer != NULL)
return klass->get_framebuffer (root);
else
return NULL;
iter = node;
while (iter != NULL && iter->parent != NULL)
{
klass = CLUTTER_PAINT_NODE_GET_CLASS (iter);
if (klass->get_framebuffer != NULL)
return klass->get_framebuffer (iter);
iter = iter->parent;
}
return NULL;
}

View File

@ -551,7 +551,7 @@ clutter_pipeline_node_init (ClutterPipelineNode *self)
}
/**
* clutter_pipeline_node_new: (skip)
* clutter_pipeline_node_new:
* @pipeline: (allow-none): a Cogl pipeline state object, or %NULL
*
* Creates a new #ClutterPaintNode that will use the @pipeline to
@ -1115,6 +1115,8 @@ struct _ClutterActorNode
ClutterPaintNode parent_instance;
ClutterActor *actor;
int opacity_override;
int saved_opacity_override;
};
struct _ClutterActorNodeClass
@ -1130,6 +1132,14 @@ clutter_actor_node_pre_draw (ClutterPaintNode *node,
{
ClutterActorNode *actor_node = CLUTTER_ACTOR_NODE (node);
if (actor_node->opacity_override != -1)
{
actor_node->saved_opacity_override =
clutter_actor_get_opacity_override (actor_node->actor);
clutter_actor_set_opacity_override (actor_node->actor,
actor_node->opacity_override);
}
CLUTTER_SET_PRIVATE_FLAGS (actor_node->actor, CLUTTER_IN_PAINT);
return TRUE;
@ -1151,6 +1161,12 @@ clutter_actor_node_post_draw (ClutterPaintNode *node,
ClutterActorNode *actor_node = CLUTTER_ACTOR_NODE (node);
CLUTTER_UNSET_PRIVATE_FLAGS (actor_node->actor, CLUTTER_IN_PAINT);
if (actor_node->opacity_override != -1)
{
clutter_actor_set_opacity_override (actor_node->actor,
actor_node->saved_opacity_override);
}
}
static JsonNode *
@ -1192,6 +1208,7 @@ clutter_actor_node_init (ClutterActorNode *self)
/*
* clutter_actor_node_new:
* @actor: the actor to paint
* @opacity: opacity to draw the actor with, or -1 to use the actor's opacity
*
* Creates a new #ClutterActorNode.
*
@ -1203,7 +1220,8 @@ clutter_actor_node_init (ClutterActorNode *self)
* Use clutter_paint_node_unref() when done.
*/
ClutterPaintNode *
clutter_actor_node_new (ClutterActor *actor)
clutter_actor_node_new (ClutterActor *actor,
int opacity)
{
ClutterActorNode *res;
@ -1211,11 +1229,96 @@ clutter_actor_node_new (ClutterActor *actor)
res = _clutter_paint_node_create (CLUTTER_TYPE_ACTOR_NODE);
res->actor = actor;
res->opacity_override = CLAMP (opacity, -1, 255);
return (ClutterPaintNode *) res;
}
/*
* ClutterEffectNode
*/
struct _ClutterEffectNode
{
ClutterPaintNode parent_instance;
ClutterEffect *effect;
};
struct _ClutterEffectNodeClass
{
ClutterPaintNodeClass parent_class;
};
G_DEFINE_TYPE (ClutterEffectNode, clutter_effect_node, CLUTTER_TYPE_PAINT_NODE)
static JsonNode *
clutter_effect_node_serialize (ClutterPaintNode *node)
{
ClutterEffectNode *effect_node = CLUTTER_EFFECT_NODE (node);
ClutterActorMeta *effect_meta = CLUTTER_ACTOR_META (effect_node->effect);
g_autoptr (JsonBuilder) builder = NULL;
g_autoptr (GString) string = NULL;
const char *meta_name;
meta_name = clutter_actor_meta_get_name (effect_meta);
string = g_string_new (NULL);
g_string_append (string, G_OBJECT_TYPE_NAME (effect_node->effect));
g_string_append (string, " (");
if (meta_name)
g_string_append_printf (string, "\"%s\"", meta_name);
else
g_string_append (string, "unnamed");
g_string_append (string, ")");
builder = json_builder_new ();
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "effect");
json_builder_add_string_value (builder, string->str);
json_builder_end_object (builder);
return json_builder_get_root (builder);
}
static void
clutter_effect_node_class_init (ClutterEffectNodeClass *klass)
{
ClutterPaintNodeClass *node_class;
node_class = CLUTTER_PAINT_NODE_CLASS (klass);
node_class->serialize = clutter_effect_node_serialize;
}
static void
clutter_effect_node_init (ClutterEffectNode *self)
{
}
/*
* clutter_effect_node_new:
* @actor: the actor to paint
*
* Creates a new #ClutterEffectNode.
*
* Return value: (transfer full): the newly created #ClutterEffectNode.
* Use clutter_paint_node_unref() when done.
*/
ClutterPaintNode *
clutter_effect_node_new (ClutterEffect *effect)
{
ClutterEffectNode *res;
g_assert (effect != NULL);
res = _clutter_paint_node_create (CLUTTER_TYPE_EFFECT_NODE);
res->effect = effect;
return (ClutterPaintNode *) res;
}
/*
* ClutterLayerNode
*/
@ -1235,6 +1338,7 @@ struct _ClutterLayerNode
CoglFramebuffer *offscreen;
guint8 opacity;
gboolean update_modelview;
};
struct _ClutterLayerNodeClass
@ -1249,8 +1353,6 @@ clutter_layer_node_pre_draw (ClutterPaintNode *node,
ClutterPaintContext *paint_context)
{
ClutterLayerNode *lnode = (ClutterLayerNode *) node;
CoglFramebuffer *framebuffer;
CoglMatrix matrix;
/* if we were unable to create an offscreen buffer for this node, then
* we simply ignore it
@ -1258,29 +1360,30 @@ clutter_layer_node_pre_draw (ClutterPaintNode *node,
if (lnode->offscreen == NULL)
return FALSE;
/* if no geometry was submitted for this node then we simply ignore it */
if (node->operations == NULL)
return FALSE;
if (lnode->update_modelview)
{
CoglFramebuffer *framebuffer;
CoglMatrix matrix;
/* copy the same modelview from the current framebuffer to the one we
* are going to use
*/
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
cogl_framebuffer_get_modelview_matrix (framebuffer, &matrix);
/* copy the same modelview from the current framebuffer to the one we
* are going to use
*/
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
cogl_framebuffer_get_modelview_matrix (framebuffer, &matrix);
cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix);
cogl_framebuffer_set_viewport (lnode->offscreen,
lnode->viewport.x,
lnode->viewport.y,
lnode->viewport.width,
lnode->viewport.height);
cogl_framebuffer_set_projection_matrix (lnode->offscreen,
&lnode->projection);
}
clutter_paint_context_push_framebuffer (paint_context, lnode->offscreen);
cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix);
cogl_framebuffer_set_viewport (lnode->offscreen,
lnode->viewport.x,
lnode->viewport.y,
lnode->viewport.width,
lnode->viewport.height);
cogl_framebuffer_set_projection_matrix (lnode->offscreen,
&lnode->projection);
/* clear out the target framebuffer */
cogl_framebuffer_clear4f (lnode->offscreen,
COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH,
@ -1307,6 +1410,10 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
cogl_framebuffer_pop_matrix (lnode->offscreen);
clutter_paint_context_pop_framebuffer (paint_context);
/* if no geometry was submitted for this node then we simply ignore it */
if (node->operations == NULL)
return;
fb = clutter_paint_context_get_framebuffer (paint_context);
for (i = 0; i < node->operations->len; i++)
@ -1367,6 +1474,25 @@ clutter_layer_node_finalize (ClutterPaintNode *node)
CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node);
}
static JsonNode *
clutter_layer_node_serialize (ClutterPaintNode *node)
{
ClutterLayerNode *layer_node = CLUTTER_LAYER_NODE (node);
g_autoptr (JsonBuilder) builder = NULL;
g_autofree char *framebuffer_ptr = NULL;
builder = json_builder_new ();
framebuffer_ptr = g_strdup_printf ("%p", layer_node->offscreen);
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "framebuffer");
json_builder_add_string_value (builder, framebuffer_ptr);
json_builder_end_object (builder);
return json_builder_get_root (builder);
}
static void
clutter_layer_node_class_init (ClutterLayerNodeClass *klass)
{
@ -1376,6 +1502,7 @@ clutter_layer_node_class_init (ClutterLayerNodeClass *klass)
node_class->pre_draw = clutter_layer_node_pre_draw;
node_class->post_draw = clutter_layer_node_post_draw;
node_class->finalize = clutter_layer_node_finalize;
node_class->serialize = clutter_layer_node_serialize;
}
static void
@ -1418,6 +1545,7 @@ clutter_layer_node_new (const CoglMatrix *projection,
res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
res->update_modelview = TRUE;
res->projection = *projection;
res->viewport = *viewport;
res->fbo_width = width;
@ -1458,3 +1586,233 @@ out:
return (ClutterPaintNode *) res;
}
/**
* clutter_layer_node_new_with_framebuffer:
* @framebuffer: a #CoglFramebuffer
* @pipeline: a #CoglPipeline
* @opacity: the opacity to be used when drawing the layer
*
* Creates a new #ClutterLayerNode with @framebuffer and
* @pipeline. @framebuffer will then be painted using the
* given @opacity.
*
* When using this constructor, the caller is responsible
* for setting up the framebuffer's modelview and projection
* matrices.
*
* Return value: (transfer full)(nullable): the newly created #ClutterLayerNode.
* Use clutter_paint_node_unref() when done.
*/
ClutterPaintNode *
clutter_layer_node_new_with_framebuffer (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
guint8 opacity)
{
ClutterLayerNode *res;
CoglColor color;
g_return_val_if_fail (cogl_is_framebuffer (framebuffer), NULL);
res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
res->update_modelview = FALSE;
res->offscreen = cogl_object_ref (framebuffer);
res->pipeline = cogl_pipeline_copy (pipeline);
cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_pipeline_set_color (res->pipeline, &color);
return (ClutterPaintNode *) res;
}
/*
* ClutterBlitNode
*/
struct _ClutterBlitNode
{
ClutterPaintNode parent_instance;
CoglFramebuffer *src;
CoglFramebuffer *dst;
};
G_DEFINE_TYPE (ClutterBlitNode, clutter_blit_node, CLUTTER_TYPE_PAINT_NODE)
static gboolean
clutter_blit_node_pre_draw (ClutterPaintNode *node,
ClutterPaintContext *paint_context)
{
return TRUE;
}
static void
clutter_blit_node_draw (ClutterPaintNode *node,
ClutterPaintContext *paint_context)
{
ClutterBlitNode *blit_node = CLUTTER_BLIT_NODE (node);
g_autoptr (GError) error = NULL;
guint i;
if (node->operations == NULL)
return;
for (i = 0; i < node->operations->len; i++)
{
const ClutterPaintOperation *op;
float op_width, op_height;
op = &g_array_index (node->operations, ClutterPaintOperation, i);
switch (op->opcode)
{
case PAINT_OP_INVALID:
break;
case PAINT_OP_TEX_RECT:
op_width = op->op.texrect[6] - op->op.texrect[4];
op_height = op->op.texrect[7] - op->op.texrect[5];
cogl_blit_framebuffer (blit_node->src,
blit_node->dst,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[4],
op->op.texrect[5],
op_width,
op_height,
&error);
if (error)
{
g_warning ("Error blitting framebuffers: %s", error->message);
return;
}
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PRIMITIVE:
break;
}
}
}
static void
clutter_blit_node_finalize (ClutterPaintNode *node)
{
ClutterBlitNode *blit_node = CLUTTER_BLIT_NODE (node);
cogl_clear_object (&blit_node->src);
cogl_clear_object (&blit_node->dst);
CLUTTER_PAINT_NODE_CLASS (clutter_blit_node_parent_class)->finalize (node);
}
static JsonNode *
clutter_blit_node_serialize (ClutterPaintNode *node)
{
ClutterBlitNode *blit_node = CLUTTER_BLIT_NODE (node);
g_autoptr (JsonBuilder) builder = NULL;
g_autofree char *src_ptr = NULL;
g_autofree char *dst_ptr = NULL;
builder = json_builder_new ();
src_ptr = g_strdup_printf ("%p", blit_node->src);
dst_ptr = g_strdup_printf ("%p", blit_node->dst);
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "source");
json_builder_add_string_value (builder, src_ptr);
json_builder_end_object (builder);
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "dst");
json_builder_add_string_value (builder, dst_ptr);
json_builder_end_object (builder);
return json_builder_get_root (builder);
}
static void
clutter_blit_node_class_init (ClutterTransformNodeClass *klass)
{
ClutterPaintNodeClass *node_class;
node_class = CLUTTER_PAINT_NODE_CLASS (klass);
node_class->pre_draw = clutter_blit_node_pre_draw;
node_class->draw = clutter_blit_node_draw;
node_class->finalize = clutter_blit_node_finalize;
node_class->serialize = clutter_blit_node_serialize;
}
static void
clutter_blit_node_init (ClutterBlitNode *self)
{
}
/**
* clutter_blit_node_new:
* @src: the source #CoglFramebuffer
* @dst: the destination #CoglFramebuffer
*
* Creates a new #ClutterBlitNode that blits @src into @dst.
*
* You must only add rectangles using clutter_blit_node_add_blit_rectangle().
*
* Return value: (transfer full): the newly created #ClutterBlitNode.
* Use clutter_paint_node_unref() when done.
*/
ClutterPaintNode *
clutter_blit_node_new (CoglFramebuffer *src,
CoglFramebuffer *dst)
{
ClutterBlitNode *res;
g_return_val_if_fail (cogl_is_framebuffer (src), NULL);
g_return_val_if_fail (cogl_is_framebuffer (dst), NULL);
res = _clutter_paint_node_create (CLUTTER_TYPE_BLIT_NODE);
res->src = cogl_object_ref (src);
res->dst = cogl_object_ref (dst);
return (ClutterPaintNode *) res;
}
/**
* clutter_blit_node_add_blit_rectangle:
* @blit_node: a #ClutterBlitNode
* @src_x: Source x position
* @src_y: Source y position
* @dst_x: Destination x position
* @dst_y: Destination y position
* @width: Width of region to copy
* @height: Height of region to copy
*
* Adds a new blit rectangle to the stack of rectangles. All the
* constraints of cogl_blit_framebuffer() apply here.
*/
void
clutter_blit_node_add_blit_rectangle (ClutterBlitNode *blit_node,
int src_x,
int src_y,
int dst_x,
int dst_y,
int width,
int height)
{
g_return_if_fail (CLUTTER_IS_BLIT_NODE (blit_node));
clutter_paint_node_add_texture_rectangle (CLUTTER_PAINT_NODE (blit_node),
&(ClutterActorBox) {
.x1 = src_x,
.y1 = src_y,
.x2 = src_x + width,
.y2 = src_y + height,
},
dst_x,
dst_y,
dst_x + width,
dst_y + height);
}

View File

@ -160,7 +160,8 @@ CLUTTER_EXPORT
GType clutter_actor_node_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterPaintNode * clutter_actor_node_new (ClutterActor *actor);
ClutterPaintNode * clutter_actor_node_new (ClutterActor *actor,
int opacity);
#define CLUTTER_TYPE_ROOT_NODE (clutter_root_node_get_type ())
#define CLUTTER_ROOT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ROOT_NODE, ClutterRootNode))
@ -208,6 +209,11 @@ ClutterPaintNode * clutter_layer_node_new (const CoglMatrix
float height,
guint8 opacity);
CLUTTER_EXPORT
ClutterPaintNode * clutter_layer_node_new_with_framebuffer (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
guint8 opacity);
#define CLUTTER_TYPE_TRANSFORM_NODE (clutter_transform_node_get_type ())
#define CLUTTER_TRANSFORM_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TRANSFORM_NODE, ClutterTransformNode))
@ -230,6 +236,36 @@ GType clutter_transform_node_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterPaintNode * clutter_transform_node_new (const CoglMatrix *projection);
#define CLUTTER_TYPE_BLIT_NODE (clutter_blit_node_get_type ())
#define CLUTTER_BLIT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BLIT_NODE, ClutterBlitNode))
#define CLUTTER_IS_BLIT_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BLIT_NODE))
/*
* ClutterBlitNode:
*
* The #ClutterLayerNode structure is an opaque
* type whose members cannot be directly accessed.
*/
typedef struct _ClutterBlitNode ClutterBlitNode;
typedef struct _ClutterPaintNodeClass ClutterBlitNodeClass;
CLUTTER_EXPORT
GType clutter_blit_node_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterPaintNode * clutter_blit_node_new (CoglFramebuffer *src,
CoglFramebuffer *dst);
CLUTTER_EXPORT
void clutter_blit_node_add_blit_rectangle (ClutterBlitNode *blit_node,
int src_x,
int src_y,
int dst_x,
int dst_y,
int width,
int height);
G_END_DECLS
#endif /* __CLUTTER_PAINT_NODES_H__ */