Compare commits

...

5 Commits

Author SHA1 Message Date
Georges Basile Stavracas Neto
dd39d7a060 shaped-texture: Implement ClutterContent
MetaWindowActor is the compositor-side representative of a
MetaWindow. Specifically it represents the geometry of the
window under Clutter scene graph. MetaWindowActors are backed
by MetaSurfaceActors, that represent the windowing system's
surfaces themselves. Naturally, these surfaces have textures
with the pixel content of the clients associated with them.
These textures are represented by MetaShapedTexture.

MetaShapedTextures are currently implemented as ClutterActor
subclasses that override the paint function to paint the
textures it holds.

Conceptually, however, Clutter has an abstraction layer for
contents of actors: ClutterContent. Which MetaShapedTexture
fits nicely, in fact.

Make MetaShapedTexture a ClutterContent implementation. This
forces a few changes in the stack:

 * MetaShapedTexture now manually tracks the content scale.

 * We now paint into ClutterPaintNode instead of the direct
   framebuffer.

 * Various pieces of Wayland code now use MetaSurfaceActor
   instead of MetaShapedTexture.

 * MetaSurfaceActorWayland doesn't override size negotiation
   vfuncs anymore
2019-01-27 16:22:42 -02:00
Georges Basile Stavracas Neto
e1c272e3d7 clutter/image: Also invalidate size
ClutterImage is a ClutterContent implementation that
has an internally managed CoglTexture. This texture
is recreated when new image data is set.

ClutterContent implementations may have control over
the allocation of the widgets they're attached to,
through CLUTTER_REQUEST_CONTENT_SIZE. On those cases,
if the new image data differs in size from the previous
data, it is important to notify those actors about the
size change. However, currently ClutterImage does not
notify them.

With the introduction of clutter_content_invalidate_size(),
it is possible to report the size changes to attached
actors.

Adapt ClutterImage to invalidate_size() when image data
has different sizes.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/405
2019-01-27 15:20:58 -02:00
Georges Basile Stavracas Neto
41ece851d6 clutter/paint-nodes: Expose ClutterLayerNode
ClutterLayerNode is a private paint node that paints the
contents of its subnodes into an offscreen buffer, then
paints this entire offscreen buffer at the previous draw
framebuffer with the given opacity.

It is specially interesting to the future ClutterContent
implementation of MetaShapedTexture, for the NVidia and
rotation cases where the contents are painted on an
offscreen.

Expose ClutterLayerNode as public API.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/405
2019-01-27 15:20:58 -02:00
Georges Basile Stavracas Neto
d5262f0f4d clutter/paint-node: Add multitexture API
The multitexture API is not a shortcut for multiple calls
to the single texture API. It is meant to wrap calls to
cogl_framebuffer_draw_multitexture_rectangle(), which
uses the passed texture coordinates at different layers of
the pipeline.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/405
2019-01-27 15:20:58 -02:00
Georges Basile Stavracas Neto
fd99a56fd6 clutter/content: Add clutter_content_invalidate_size()
ClutterContent has the ability to dictate the layout of any
given actor, through the CLUTTER_REQUEST_CONTENT_SIZE request
mode.

However, there is no way for ClutterContent implementations
to notify their attached actors that the content size changed.

Add a new optional ClutterContent.invalidate_size() vfunc and
clutter_content_invalidate_size().

https://gitlab.gnome.org/GNOME/mutter/merge_requests/405
2019-01-27 15:15:01 -02:00
18 changed files with 630 additions and 253 deletions

View File

@ -38,6 +38,7 @@
#include "clutter-build-config.h"
#include "clutter-actor-private.h"
#include "clutter-content-private.h"
#include "clutter-debug.h"
@ -91,6 +92,11 @@ clutter_content_real_invalidate (ClutterContent *content)
{
}
static void
clutter_content_real_invalidate_size (ClutterContent *content)
{
}
static void
clutter_content_real_paint_content (ClutterContent *content,
ClutterActor *actor,
@ -108,6 +114,7 @@ clutter_content_default_init (ClutterContentInterface *iface)
iface->attached = clutter_content_real_attached;
iface->detached = clutter_content_real_detached;
iface->invalidate = clutter_content_real_invalidate;
iface->invalidate_size = clutter_content_real_invalidate_size;
/**
* ClutterContent::attached:
@ -188,6 +195,45 @@ clutter_content_invalidate (ClutterContent *content)
}
}
/**
* clutter_content_invalidate_size:
* @content: a #ClutterContent
*
* Signals that @content's size changed. Attached actors with request mode
* set to %CLUTTER_REQUEST_CONTENT_SIZE will have a relayout queued.
*
* Attached actors with other request modes are not redrawn. To redraw them
* too, use clutter_content_invalidate().
*/
void
clutter_content_invalidate_size (ClutterContent *content)
{
ClutterActor *actor;
GHashTable *actors;
GHashTableIter iter;
g_return_if_fail (CLUTTER_IS_CONTENT (content));
CLUTTER_CONTENT_GET_IFACE (content)->invalidate_size (content);
actors = g_object_get_qdata (G_OBJECT (content), quark_content_actors);
if (actors == NULL)
return;
g_hash_table_iter_init (&iter, actors);
while (g_hash_table_iter_next (&iter, (gpointer *) &actor, NULL))
{
ClutterRequestMode request_mode;
g_assert (actor != NULL);
request_mode = clutter_actor_get_request_mode (actor);
if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
_clutter_actor_queue_only_relayout (actor);
}
}
/*< private >
* _clutter_content_attached:
* @content: a #ClutterContent

View File

@ -86,6 +86,8 @@ struct _ClutterContentIface
ClutterActor *actor);
void (* invalidate) (ClutterContent *content);
void (* invalidate_size) (ClutterContent *content);
};
CLUTTER_EXPORT
@ -98,6 +100,9 @@ gboolean clutter_content_get_preferred_size (ClutterContent *content
CLUTTER_EXPORT
void clutter_content_invalidate (ClutterContent *content);
CLUTTER_EXPORT
void clutter_content_invalidate_size (ClutterContent *content);
G_END_DECLS
#endif /* __CLUTTER_CONTENT_H__ */

View File

@ -53,6 +53,8 @@
struct _ClutterImagePrivate
{
CoglTexture *texture;
gint width;
gint height;
};
static void clutter_content_iface_init (ClutterContentIface *iface);
@ -68,6 +70,27 @@ clutter_image_error_quark (void)
return g_quark_from_static_string ("clutter-image-error-quark");
}
static void
update_image_size (ClutterImage *self)
{
gint width, height;
if (self->priv->texture == NULL)
return;
width = cogl_texture_get_width (self->priv->texture);
height = cogl_texture_get_height (self->priv->texture);
if (self->priv->width == width &&
self->priv->height == height)
return;
self->priv->width = width;
self->priv->height = height;
clutter_content_invalidate_size (CLUTTER_CONTENT (self));
}
static void
clutter_image_finalize (GObject *gobject)
{
@ -238,6 +261,7 @@ clutter_image_set_data (ClutterImage *image,
}
clutter_content_invalidate (CLUTTER_CONTENT (image));
update_image_size (image);
return TRUE;
}
@ -306,6 +330,7 @@ clutter_image_set_bytes (ClutterImage *image,
}
clutter_content_invalidate (CLUTTER_CONTENT (image));
update_image_size (image);
return TRUE;
}
@ -399,6 +424,7 @@ clutter_image_set_area (ClutterImage *image,
}
clutter_content_invalidate (CLUTTER_CONTENT (image));
update_image_size (image);
return TRUE;
}

View File

@ -77,6 +77,7 @@ struct _ClutterPaintNodeClass
typedef enum {
PAINT_OP_INVALID = 0,
PAINT_OP_TEX_RECT,
PAINT_OP_MULTITEX_RECT,
PAINT_OP_PATH,
PAINT_OP_PRIMITIVE
} PaintOpCode;
@ -85,6 +86,8 @@ struct _ClutterPaintOperation
{
PaintOpCode opcode;
GArray *multitex_coords;
union {
float texrect[8];
@ -142,30 +145,6 @@ ClutterPaintNode * clutter_paint_node_get_parent (Clutter
G_GNUC_INTERNAL
CoglFramebuffer * clutter_paint_node_get_framebuffer (ClutterPaintNode *node);
#define CLUTTER_TYPE_LAYER_NODE (_clutter_layer_node_get_type ())
#define CLUTTER_LAYER_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYER_NODE, ClutterLayerNode))
#define CLUTTER_IS_LAYER_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYER_NODE))
/*
* ClutterLayerNode:
*
* The #ClutterLayerNode structure is an opaque
* type whose members cannot be directly accessed.
*
* Since: 1.10
*/
typedef struct _ClutterLayerNode ClutterLayerNode;
typedef struct _ClutterLayerNodeClass ClutterLayerNodeClass;
GType _clutter_layer_node_get_type (void) G_GNUC_CONST;
ClutterPaintNode * _clutter_layer_node_new (const CoglMatrix *projection,
const cairo_rectangle_t *viewport,
float width,
float height,
guint8 opacity);
G_END_DECLS
#endif /* __CLUTTER_PAINT_NODE_PRIVATE_H__ */

View File

@ -761,6 +761,11 @@ clutter_paint_operation_clear (ClutterPaintOperation *op)
case PAINT_OP_TEX_RECT:
break;
case PAINT_OP_MULTITEX_RECT:
if (op->multitex_coords != NULL)
g_array_unref (op->multitex_coords);
break;
case PAINT_OP_PATH:
if (op->op.path != NULL)
cogl_object_unref (op->op.path);
@ -794,6 +799,27 @@ clutter_paint_op_init_tex_rect (ClutterPaintOperation *op,
op->op.texrect[7] = y_2;
}
static inline void
clutter_paint_op_init_multitex_rect (ClutterPaintOperation *op,
const ClutterActorBox *rect,
const float *tex_coords,
unsigned int tex_coords_len)
{
clutter_paint_operation_clear (op);
op->opcode = PAINT_OP_MULTITEX_RECT;
op->multitex_coords = g_array_sized_new (FALSE, FALSE,
sizeof (float),
tex_coords_len);
g_array_append_vals (op->multitex_coords, tex_coords, tex_coords_len);
op->op.texrect[0] = rect->x1;
op->op.texrect[1] = rect->y1;
op->op.texrect[2] = rect->x2;
op->op.texrect[3] = rect->y2;
}
static inline void
clutter_paint_op_init_path (ClutterPaintOperation *op,
CoglPath *path)
@ -881,6 +907,33 @@ clutter_paint_node_add_texture_rectangle (ClutterPaintNode *node,
g_array_append_val (node->operations, operation);
}
/**
* clutter_paint_node_add_multitexture_rectangle:
* @node: a #ClutterPaintNode
* @rect: a #ClutterActorBox
* @text_coords: array of multitexture values
* @text_coords_len: number of items of @text_coords
*
* Adds a rectangle region to the @node, with multitexture coordinates.
*/
void
clutter_paint_node_add_multitexture_rectangle (ClutterPaintNode *node,
const ClutterActorBox *rect,
const float *text_coords,
unsigned int text_coords_len)
{
ClutterPaintOperation operation = PAINT_OP_INIT;
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
g_return_if_fail (rect != NULL);
clutter_paint_node_maybe_init_operations (node);
clutter_paint_op_init_multitex_rect (&operation, rect, text_coords, text_coords_len);
g_array_append_val (node->operations, operation);
}
/**
* clutter_paint_node_add_path: (skip)
* @node: a #ClutterPaintNode
@ -1006,7 +1059,7 @@ clutter_paint_node_to_json (ClutterPaintNode *node)
if (node->operations != NULL)
{
guint i;
guint i, j;
for (i = 0; i < node->operations->len; i++)
{
@ -1031,6 +1084,19 @@ clutter_paint_node_to_json (ClutterPaintNode *node)
json_builder_end_array (builder);
break;
case PAINT_OP_MULTITEX_RECT:
json_builder_set_member_name (builder, "texrect");
json_builder_begin_array (builder);
for (j = 0; i < op->multitex_coords->len; j++)
{
float coord = g_array_index (op->multitex_coords, float, j);
json_builder_add_double_value (builder, coord);
}
json_builder_end_array (builder);
break;
case PAINT_OP_PATH:
json_builder_set_member_name (builder, "path");
json_builder_add_int_value (builder, (gint64) op->op.path);

View File

@ -67,6 +67,12 @@ void clutter_paint_node_add_texture_rectangle (Clutter
float x_2,
float y_2);
CLUTTER_EXPORT
void clutter_paint_node_add_multitexture_rectangle (ClutterPaintNode *node,
const ClutterActorBox *rect,
const float *text_coords,
unsigned int text_coords_len);
CLUTTER_EXPORT
void clutter_paint_node_add_path (ClutterPaintNode *node,
CoglPath *path);

View File

@ -431,6 +431,17 @@ clutter_pipeline_node_draw (ClutterPaintNode *node)
op->op.texrect[7]);
break;
case PAINT_OP_MULTITEX_RECT:
cogl_framebuffer_draw_multitextured_rectangle (cogl_get_draw_framebuffer (),
pnode->pipeline,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[2],
op->op.texrect[3],
(float*) op->multitex_coords->data,
op->multitex_coords->len);
break;
case PAINT_OP_PATH:
cogl_path_fill (op->op.path);
break;
@ -827,6 +838,7 @@ clutter_text_node_draw (ClutterPaintNode *node)
cogl_framebuffer_pop_clip (fb);
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PATH:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
@ -992,6 +1004,7 @@ clutter_clip_node_pre_draw (ClutterPaintNode *node)
retval = TRUE;
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
break;
@ -1025,6 +1038,7 @@ clutter_clip_node_post_draw (ClutterPaintNode *node)
cogl_framebuffer_pop_clip (fb);
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
break;
@ -1065,11 +1079,9 @@ clutter_clip_node_new (void)
}
/*
* ClutterLayerNode (private)
* ClutterLayerNode
*/
#define clutter_layer_node_get_type _clutter_layer_node_get_type
struct _ClutterLayerNode
{
ClutterPaintNode parent_instance;
@ -1180,6 +1192,17 @@ clutter_layer_node_post_draw (ClutterPaintNode *node)
cogl_pop_source ();
break;
case PAINT_OP_MULTITEX_RECT:
cogl_framebuffer_draw_multitextured_rectangle (cogl_get_draw_framebuffer (),
lnode->state,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[2],
op->op.texrect[3],
(float*) op->multitex_coords->data,
op->multitex_coords->len);
break;
case PAINT_OP_PATH:
cogl_push_source (lnode->state);
cogl_path_fill (op->op.path);
@ -1244,11 +1267,11 @@ clutter_layer_node_init (ClutterLayerNode *self)
* Since: 1.10
*/
ClutterPaintNode *
_clutter_layer_node_new (const CoglMatrix *projection,
const cairo_rectangle_t *viewport,
float width,
float height,
guint8 opacity)
clutter_layer_node_new (const CoglMatrix *projection,
const cairo_rectangle_t *viewport,
float width,
float height,
guint8 opacity)
{
ClutterLayerNode *res;
CoglColor color;

View File

@ -143,6 +143,29 @@ CLUTTER_EXPORT
ClutterPaintNode * clutter_text_node_new (PangoLayout *layout,
const ClutterColor *color);
#define CLUTTER_TYPE_LAYER_NODE (clutter_layer_node_get_type ())
#define CLUTTER_LAYER_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYER_NODE, ClutterLayerNode))
#define CLUTTER_IS_LAYER_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYER_NODE))
/*
* ClutterLayerNode:
*
* The #ClutterLayerNode structure is an opaque
* type whose members cannot be directly accessed.
*/
typedef struct _ClutterLayerNode ClutterLayerNode;
typedef struct _ClutterLayerNodeClass ClutterLayerNodeClass;
CLUTTER_EXPORT
GType clutter_layer_node_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterPaintNode * clutter_layer_node_new (const CoglMatrix *projection,
const cairo_rectangle_t *viewport,
float width,
float height,
guint8 opacity);
G_END_DECLS
#endif /* __CLUTTER_PAINT_NODES_H__ */

View File

@ -44,5 +44,13 @@ gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self);
cairo_region_t * meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex);
void meta_shaped_texture_set_transform (MetaShapedTexture *stex,
MetaMonitorTransform transform);
void meta_shaped_texture_cull_out (MetaShapedTexture *stex,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region,
uint8_t opacity);
void meta_shaped_texture_reset_culling (MetaShapedTexture *stex);
void meta_shaped_texture_set_scale (MetaShapedTexture *stex,
double scale);
double meta_shaped_texture_get_scale (MetaShapedTexture *stex);
#endif

View File

@ -57,21 +57,7 @@
static void meta_shaped_texture_dispose (GObject *object);
static void meta_shaped_texture_paint (ClutterActor *actor);
static void meta_shaped_texture_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p);
static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p);
static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
static void cullable_iface_init (MetaCullableInterface *iface);
static void clutter_content_iface_init (ClutterContentIface *iface);
enum {
SIZE_CHANGED,
@ -83,7 +69,7 @@ static guint signals[LAST_SIGNAL];
struct _MetaShapedTexture
{
ClutterActor parent;
GObject parent;
MetaTextureTower *paint_tower;
@ -116,25 +102,21 @@ struct _MetaShapedTexture
guint remipmap_timeout_id;
gint64 earliest_remipmap;
double scale;
guint create_mipmaps : 1;
};
G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR,
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init));
static void
meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
gobject_class->dispose = meta_shaped_texture_dispose;
actor_class->get_preferred_width = meta_shaped_texture_get_preferred_width;
actor_class->get_preferred_height = meta_shaped_texture_get_preferred_height;
actor_class->paint = meta_shaped_texture_paint;
actor_class->get_paint_volume = meta_shaped_texture_get_paint_volume;
signals[SIZE_CHANGED] = g_signal_new ("size-changed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
@ -154,16 +136,12 @@ meta_shaped_texture_init (MetaShapedTexture *stex)
{
stex->paint_tower = meta_texture_tower_new ();
stex->scale = 1.0;
stex->texture = NULL;
stex->mask_texture = NULL;
stex->create_mipmaps = TRUE;
stex->is_y_inverted = TRUE;
stex->transform = META_MONITOR_TRANSFORM_NORMAL;
g_signal_connect (stex,
"notify::scale-x",
G_CALLBACK (invalidate_size),
stex);
}
static void
@ -207,7 +185,7 @@ update_size (MetaShapedTexture *stex)
stex->dst_width = dst_width;
stex->dst_height = dst_height;
meta_shaped_texture_set_mask_texture (stex, NULL);
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
clutter_content_invalidate_size (CLUTTER_CONTENT (stex));
g_signal_emit (stex, signals[SIZE_CHANGED], 0);
}
}
@ -436,6 +414,40 @@ paint_clipped_rectangle (CoglFramebuffer *fb,
&coords[0], 8);
}
static void
paint_clipped_rectangle_node (ClutterPaintNode *root_node,
CoglPipeline *pipeline,
cairo_rectangle_int_t *rect,
ClutterActorBox *alloc)
{
g_autoptr(ClutterPaintNode) node = NULL;
float coords[8];
coords[0] = rect->x / (alloc->x2 - alloc->x1);
coords[1] = rect->y / (alloc->y2 - alloc->y1);
coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);
coords[4] = coords[0];
coords[5] = coords[1];
coords[6] = coords[2];
coords[7] = coords[3];
node = clutter_pipeline_node_new (pipeline);
clutter_paint_node_set_name (node, "MetaShapedTexture (clipped)");
clutter_paint_node_add_child (root_node, node);
clutter_paint_node_add_multitexture_rectangle (node,
&(ClutterActorBox)
{
.x1 = rect->x,
.x2 = rect->x + rect->width,
.y1 = rect->y,
.y2 = rect->y + rect->height,
},
coords, 8);
}
static void
set_cogl_texture (MetaShapedTexture *stex,
CoglTexture *cogl_tex)
@ -476,6 +488,8 @@ set_cogl_texture (MetaShapedTexture *stex,
if (stex->create_mipmaps)
meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
clutter_content_invalidate (CLUTTER_CONTENT (stex));
}
static gboolean
@ -486,12 +500,31 @@ texture_is_idle_and_not_mipmapped (gpointer user_data)
if ((g_get_monotonic_time () - stex->earliest_remipmap) < 0)
return G_SOURCE_CONTINUE;
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
clutter_content_invalidate (CLUTTER_CONTENT (stex));
stex->remipmap_timeout_id = 0;
return G_SOURCE_REMOVE;
}
static cairo_region_t *
effective_unobscured_region (MetaShapedTexture *stex)
{
/*
ClutterActor *actor;
actor = CLUTTER_ACTOR (stex);
do
{
if (clutter_actor_has_mapped_clones (actor))
return NULL;
actor = clutter_actor_get_parent (actor);
}
while (actor != NULL);
*/
return stex->unobscured_region;
}
static void
do_paint (MetaShapedTexture *stex,
CoglFramebuffer *fb,
@ -696,21 +729,262 @@ do_paint (MetaShapedTexture *stex,
}
static void
meta_shaped_texture_paint (ClutterActor *actor)
{
MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
CoglTexture *paint_tex;
CoglFramebuffer *fb;
do_paint_content (MetaShapedTexture *stex,
ClutterPaintNode *root_node,
CoglTexture *paint_tex,
ClutterActorBox *alloc,
double tex_scale,
guchar opacity)
if (!stex->texture)
{
int dst_width, dst_height;
cairo_rectangle_int_t tex_rect;
gboolean use_opaque_region;
cairo_region_t *clip_tex_region;
cairo_region_t *opaque_tex_region;
cairo_region_t *blended_tex_region;
CoglContext *ctx;
CoglPipelineFilter filter;
ensure_size_valid (stex);
dst_width = stex->dst_width;
dst_height = stex->dst_height;
if (dst_width == 0 || dst_height == 0) /* no contents yet */
return;
tex_rect = (cairo_rectangle_int_t) { 0, 0, dst_width, dst_height };
/* Use nearest-pixel interpolation if the texture is unscaled. This
* improves performance, especially with software rendering.
*/
filter = COGL_PIPELINE_FILTER_LINEAR;
/* FIXME: evil cogl_get_draw_framebuffer() */
if (meta_actor_painting_untransformed (cogl_get_draw_framebuffer (),
dst_width, dst_height,
NULL, NULL))
filter = COGL_PIPELINE_FILTER_NEAREST;
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
if (stex->opaque_region && opacity == 255)
{
opaque_tex_region =
meta_region_scale_double (stex->opaque_region,
1.0 / tex_scale,
META_ROUNDING_STRATEGY_SHRINK);
use_opaque_region = TRUE;
}
else
{
opaque_tex_region = NULL;
use_opaque_region = FALSE;
}
if (stex->clip_region)
{
clip_tex_region =
meta_region_scale_double (stex->clip_region,
1.0 / tex_scale,
META_ROUNDING_STRATEGY_GROW);
}
else
{
clip_tex_region = NULL;
}
if (use_opaque_region)
{
if (clip_tex_region)
blended_tex_region = cairo_region_copy (clip_tex_region);
else
blended_tex_region = cairo_region_create_rectangle (&tex_rect);
cairo_region_subtract (blended_tex_region, opaque_tex_region);
}
else
{
if (clip_tex_region)
blended_tex_region = cairo_region_reference (clip_tex_region);
else
blended_tex_region = NULL;
}
/* Limit to how many separate rectangles we'll draw; beyond this just
* fall back and draw the whole thing */
#define MAX_RECTS 16
if (blended_tex_region)
{
int n_rects = cairo_region_num_rectangles (blended_tex_region);
if (n_rects > MAX_RECTS)
{
/* Fall back to taking the fully blended path. */
use_opaque_region = FALSE;
g_clear_pointer (&blended_tex_region, cairo_region_destroy);
}
}
/* First, paint the unblended parts, which are part of the opaque region. */
if (use_opaque_region)
{
cairo_region_t *region;
int n_rects;
int i;
if (clip_tex_region)
{
region = cairo_region_copy (clip_tex_region);
cairo_region_intersect (region, opaque_tex_region);
}
else
{
region = cairo_region_reference (opaque_tex_region);
}
if (!cairo_region_is_empty (region))
{
CoglPipeline *opaque_pipeline;
opaque_pipeline = get_unblended_pipeline (stex, ctx);
cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
n_rects = cairo_region_num_rectangles (region);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle_node (root_node, opaque_pipeline, &rect, alloc);
}
}
cairo_region_destroy (region);
}
/* Now, go ahead and paint the blended parts. */
/* We have three cases:
* 1) blended_tex_region has rectangles - paint the rectangles.
* 2) blended_tex_region is empty - don't paint anything
* 3) blended_tex_region is NULL - paint fully-blended.
*
* 1) and 3) are the times where we have to paint stuff. This tests
* for 1) and 3).
*/
if (!blended_tex_region || !cairo_region_is_empty (blended_tex_region))
{
CoglPipeline *blended_pipeline;
if (stex->mask_texture == NULL)
{
blended_pipeline = get_unmasked_pipeline (stex, ctx);
}
else
{
blended_pipeline = get_masked_pipeline (stex, ctx);
cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture);
cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
}
cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
CoglColor color;
cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_pipeline_set_color (blended_pipeline, &color);
if (blended_tex_region)
{
/* 1) blended_tex_region is not empty. Paint the rectangles. */
int i;
int n_rects = cairo_region_num_rectangles (blended_tex_region);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (blended_tex_region, i, &rect);
if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
continue;
paint_clipped_rectangle_node (root_node, blended_pipeline, &rect, alloc);
}
}
else
{
g_autoptr(ClutterPaintNode) node = NULL;
node = clutter_pipeline_node_new (blended_pipeline);
clutter_paint_node_set_name (node, "MetaShapedTexture (unclipped)");
clutter_paint_node_add_child (root_node, node);
/* 3) blended_tex_region is NULL. Do a full paint. */
clutter_paint_node_add_rectangle (node, alloc);
}
}
g_clear_pointer (&clip_tex_region, cairo_region_destroy);
g_clear_pointer (&opaque_tex_region, cairo_region_destroy);
g_clear_pointer (&blended_tex_region, cairo_region_destroy);
}
static CoglTexture *
select_texture_for_paint (MetaShapedTexture *stex)
{
CoglTexture *texture = NULL;
gint64 now = g_get_monotonic_time ();
if (stex->create_mipmaps && stex->last_invalidation)
{
gint64 age = now - stex->last_invalidation;
if (age >= MIN_MIPMAP_AGE_USEC ||
stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP)
texture = meta_texture_tower_get_paint_texture (stex->paint_tower);
}
if (texture == NULL)
{
texture = COGL_TEXTURE (stex->texture);
if (texture == NULL)
return NULL;
if (stex->create_mipmaps)
{
/* Minus 1000 to ensure we don't fail the age test in timeout */
stex->earliest_remipmap = now + MIN_MIPMAP_AGE_USEC - 1000;
if (!stex->remipmap_timeout_id)
stex->remipmap_timeout_id =
g_timeout_add (MIN_MIPMAP_AGE_USEC / 1000,
texture_is_idle_and_not_mipmapped,
stex);
}
}
return texture;
}
static void
meta_shaped_texture_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *root_node)
{
MetaShapedTexture *stex = META_SHAPED_TEXTURE (content);
ClutterActorBox alloc;
CoglTexture *paint_tex = NULL;
double tex_scale;
guchar opacity;
if (stex->clip_region && cairo_region_is_empty (stex->clip_region))
return;
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
clutter_actor_realize (CLUTTER_ACTOR (stex));
/* The GL EXT_texture_from_pixmap extension does allow for it to be
* used together with SGIS_generate_mipmap, however this is very
* rarely supported. Also, even when it is supported there
@ -726,100 +1000,40 @@ meta_shaped_texture_paint (ClutterActor *actor)
* Setting the texture quality to high without SGIS_generate_mipmap
* support for TFP textures will result in fallbacks to XGetImage.
*/
if (stex->create_mipmaps)
{
int64_t now = g_get_monotonic_time ();
int64_t age = now - stex->last_invalidation;
if (age >= MIN_MIPMAP_AGE_USEC ||
stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP)
{
paint_tex = meta_texture_tower_get_paint_texture (stex->paint_tower);
if (!paint_tex)
paint_tex = stex->texture;
}
else
{
paint_tex = stex->texture;
/* Minus 1000 to ensure we don't fail the age test in timeout */
stex->earliest_remipmap = now + MIN_MIPMAP_AGE_USEC - 1000;
if (!stex->remipmap_timeout_id)
stex->remipmap_timeout_id =
g_timeout_add (MIN_MIPMAP_AGE_USEC / 1000,
texture_is_idle_and_not_mipmapped,
stex);
}
}
else
{
paint_tex = COGL_TEXTURE (stex->texture);
}
if (cogl_texture_get_width (paint_tex) == 0 ||
cogl_texture_get_height (paint_tex) == 0)
paint_tex = select_texture_for_paint (stex);
if (!paint_tex)
return;
fb = cogl_get_draw_framebuffer ();
do_paint (META_SHAPED_TEXTURE (actor), fb, paint_tex, stex->clip_region);
}
clutter_actor_get_scale (actor, &tex_scale, NULL);
opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_content_box (actor, &alloc);
static void
meta_shaped_texture_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
MetaShapedTexture *stex = META_SHAPED_TEXTURE (self);
ensure_size_valid (stex);
if (min_width_p)
*min_width_p = stex->dst_width;
if (natural_width_p)
*natural_width_p = stex->dst_width;
}
static void
meta_shaped_texture_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
MetaShapedTexture *stex = META_SHAPED_TEXTURE (self);
ensure_size_valid (stex);
if (min_height_p)
*min_height_p = stex->dst_height;
if (natural_height_p)
*natural_height_p = stex->dst_height;
}
static cairo_region_t *
effective_unobscured_region (MetaShapedTexture *stex)
{
ClutterActor *actor;
/* Fail if we have any mapped clones. */
actor = CLUTTER_ACTOR (stex);
do
{
if (clutter_actor_has_mapped_clones (actor))
return NULL;
actor = clutter_actor_get_parent (actor);
}
while (actor != NULL);
return stex->unobscured_region;
do_paint_content (stex, root_node, paint_tex, &alloc, tex_scale, opacity);
}
static gboolean
meta_shaped_texture_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume)
meta_shaped_texture_get_preferred_size (ClutterContent *content,
float *width,
float *height)
{
return clutter_paint_volume_set_from_allocation (volume, actor);
MetaShapedTexture *stex = META_SHAPED_TEXTURE (content);
ensure_size_valid (stex);
if (width)
*width = stex->dst_width * stex->scale;
if (height)
*height = stex->dst_height * stex->scale;
return TRUE;
}
static void
clutter_content_iface_init (ClutterContentIface *iface)
{
iface->paint_content = meta_shaped_texture_paint_content;
iface->get_preferred_size = meta_shaped_texture_get_preferred_size;
}
void
@ -853,7 +1067,7 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
cogl_object_ref (stex->mask_texture);
}
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
clutter_content_invalidate (CLUTTER_CONTENT (stex));
}
gboolean
@ -944,7 +1158,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
{
cairo_rectangle_int_t damage_rect;
cairo_region_get_extents (intersection, &damage_rect);
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect);
clutter_content_invalidate (CLUTTER_CONTENT (stex));
//clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect);
cairo_region_destroy (intersection);
return TRUE;
}
@ -954,7 +1169,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
}
else
{
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
clutter_content_invalidate (CLUTTER_CONTENT (stex));
//clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
return TRUE;
}
}
@ -1230,7 +1446,7 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex,
transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
tex_scale = stex->scale;
meta_rectangle_scale_double (clip, 1.0 / tex_scale,
META_ROUNDING_STRATEGY_GROW,
transformed_clip);
@ -1318,17 +1534,47 @@ meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex,
invalidate_size (stex);
}
static void
meta_shaped_texture_cull_out (MetaCullable *cullable,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region)
ClutterActor *
meta_shaped_texture_new (void)
{
MetaShapedTexture *stex = META_SHAPED_TEXTURE (cullable);
return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
}
void
meta_shaped_texture_set_scale (MetaShapedTexture *stex,
double scale)
{
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
if (scale == stex->scale)
return;
stex->scale = scale;
invalidate_size (stex);
clutter_content_invalidate_size (CLUTTER_CONTENT (stex));
}
double
meta_shaped_texture_get_scale (MetaShapedTexture *stex)
{
g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0.0);
return stex->scale;
}
void
meta_shaped_texture_cull_out (MetaShapedTexture *stex,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region,
uint8_t opacity)
{
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
set_unobscured_region (stex, unobscured_region);
set_clip_region (stex, clip_region);
if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (stex)) == 0xff)
if (opacity == 0xff)
{
if (stex->opaque_region)
{
@ -1340,22 +1586,10 @@ meta_shaped_texture_cull_out (MetaCullable *cullable,
}
}
static void
meta_shaped_texture_reset_culling (MetaCullable *cullable)
void
meta_shaped_texture_reset_culling (MetaShapedTexture *stex)
{
MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable);
set_clip_region (self, NULL);
}
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
static void
cullable_iface_init (MetaCullableInterface *iface)
{
iface->cull_out = meta_shaped_texture_cull_out;
iface->reset_culling = meta_shaped_texture_reset_culling;
}
ClutterActor *
meta_shaped_texture_new (void)
{
return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
set_clip_region (stex, NULL);
}

View File

@ -110,54 +110,6 @@ meta_surface_actor_wayland_get_window (MetaSurfaceActor *actor)
return surface->window;
}
static void
meta_surface_actor_wayland_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor);
MetaShapedTexture *stex;
double scale;
stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
clutter_actor_get_scale (CLUTTER_ACTOR (stex), &scale, NULL);
clutter_actor_get_preferred_width (CLUTTER_ACTOR (stex),
for_height,
min_width_p,
natural_width_p);
if (min_width_p)
*min_width_p *= scale;
if (natural_width_p)
*natural_width_p *= scale;
}
static void
meta_surface_actor_wayland_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor);
MetaShapedTexture *stex;
double scale;
stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
clutter_actor_get_scale (CLUTTER_ACTOR (stex), NULL, &scale);
clutter_actor_get_preferred_height (CLUTTER_ACTOR (stex),
for_width,
min_height_p,
natural_height_p);
if (min_height_p)
*min_height_p *= scale;
if (natural_height_p)
*natural_height_p *= scale;
}
static void
meta_surface_actor_wayland_paint (ClutterActor *actor)
{
@ -203,8 +155,6 @@ meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass)
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
actor_class->get_preferred_width = meta_surface_actor_wayland_get_preferred_width;
actor_class->get_preferred_height = meta_surface_actor_wayland_get_preferred_height;
actor_class->paint = meta_surface_actor_wayland_paint;
surface_actor_class->process_damage = meta_surface_actor_wayland_process_damage;

View File

@ -158,13 +158,22 @@ meta_surface_actor_cull_out (MetaCullable *cullable,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region)
{
meta_cullable_cull_out_children (cullable, unobscured_region, clip_region);
MetaSurfaceActor *surface_actor = META_SURFACE_ACTOR (cullable);
MetaSurfaceActorPrivate *priv =
meta_surface_actor_get_instance_private (surface_actor);
uint8_t opacity = clutter_actor_get_opacity (CLUTTER_ACTOR (cullable));
meta_shaped_texture_cull_out (priv->texture, unobscured_region, clip_region, opacity);
}
static void
meta_surface_actor_reset_culling (MetaCullable *cullable)
{
meta_cullable_reset_culling_children (cullable);
MetaSurfaceActor *surface_actor = META_SURFACE_ACTOR (cullable);
MetaSurfaceActorPrivate *priv =
meta_surface_actor_get_instance_private (surface_actor);
meta_shaped_texture_reset_culling (priv->texture);
}
static void
@ -191,7 +200,8 @@ meta_surface_actor_init (MetaSurfaceActor *self)
priv->texture = META_SHAPED_TEXTURE (meta_shaped_texture_new ());
g_signal_connect_object (priv->texture, "size-changed",
G_CALLBACK (texture_size_changed), self, 0);
clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->texture));
clutter_actor_set_content (CLUTTER_ACTOR (self), CLUTTER_CONTENT (priv->texture));
clutter_actor_set_request_mode (CLUTTER_ACTOR (self), CLUTTER_REQUEST_CONTENT_SIZE);
}
cairo_surface_t *

View File

@ -829,14 +829,14 @@ meta_window_actor_get_meta_window (MetaWindowActor *self)
*
* Return value: (transfer none): the #ClutterActor for the contents
*/
ClutterActor *
MetaShapedTexture *
meta_window_actor_get_texture (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv =
meta_window_actor_get_instance_private (self);
if (priv->surface)
return CLUTTER_ACTOR (meta_surface_actor_get_texture (priv->surface));
return meta_surface_actor_get_texture (priv->surface);
else
return NULL;
}
@ -1875,19 +1875,19 @@ meta_window_actor_get_frame_bounds (MetaScreenCastWindow *screen_cast_window,
MetaShapedTexture *stex;
MetaRectangle buffer_rect;
MetaRectangle frame_rect;
double scale_x, scale_y;
double scale;
stex = meta_surface_actor_get_texture (priv->surface);
clutter_actor_get_scale (CLUTTER_ACTOR (stex), &scale_x, &scale_y);
scale = meta_shaped_texture_get_scale (stex);
window = priv->window;
meta_window_get_buffer_rect (window, &buffer_rect);
meta_window_get_frame_rect (window, &frame_rect);
bounds->x = (int) floor ((frame_rect.x - buffer_rect.x) / scale_x);
bounds->y = (int) floor ((frame_rect.y - buffer_rect.y) / scale_y);
bounds->width = (int) ceil (frame_rect.width / scale_x);
bounds->height = (int) ceil (frame_rect.height / scale_y);
bounds->x = (int) floor ((frame_rect.x - buffer_rect.x) / scale);
bounds->y = (int) floor ((frame_rect.y - buffer_rect.y) / scale);
bounds->width = (int) ceil (frame_rect.width / scale);
bounds->height = (int) ceil (frame_rect.height / scale);
}
static void

View File

@ -27,6 +27,7 @@
#include "clutter/clutter.h"
#include "meta/compositor.h"
#include "meta/meta-shaped-texture.h"
#define META_TYPE_WINDOW_ACTOR (meta_window_actor_get_type ())
@ -43,7 +44,7 @@ META_EXPORT
MetaWindow * meta_window_actor_get_meta_window (MetaWindowActor *self);
META_EXPORT
ClutterActor * meta_window_actor_get_texture (MetaWindowActor *self);
MetaShapedTexture *meta_window_actor_get_texture (MetaWindowActor *self);
META_EXPORT
void meta_window_actor_sync_visibility (MetaWindowActor *self);

View File

@ -163,7 +163,7 @@ meta_wayland_actor_surface_real_sync_actor_state (MetaWaylandActorSurface *actor
stex = meta_surface_actor_get_texture (surface_actor);
actor_scale = meta_wayland_actor_surface_calculate_scale (actor_surface);
clutter_actor_set_scale (CLUTTER_ACTOR (stex), actor_scale, actor_scale);
meta_shaped_texture_set_scale (stex, actor_scale);
/* Wayland surface coordinate space -> stage coordinate space */
geometry_scale = meta_wayland_actor_surface_get_geometry_scale (actor_surface);

View File

@ -1123,7 +1123,7 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
surface_actor = meta_wayland_surface_get_actor (surface);
clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface_actor)),
clutter_actor_transform_stage_point (CLUTTER_ACTOR (surface_actor),
seat->pointer->grab_x,
seat->pointer->grab_y,
&surface_pos.x, &surface_pos.y);

View File

@ -1514,7 +1514,7 @@ meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
else
{
ClutterActor *actor =
CLUTTER_ACTOR (meta_surface_actor_get_texture (meta_wayland_surface_get_actor (surface)));
CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface));
clutter_actor_transform_stage_point (actor, abs_x, abs_y, sx, sy);
*sx /= surface->scale;
@ -1530,7 +1530,7 @@ meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface,
float *y)
{
ClutterActor *actor =
CLUTTER_ACTOR (meta_surface_actor_get_texture (meta_wayland_surface_get_actor (surface)));
CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface));
ClutterVertex sv = {
.x = sx * surface->scale,
.y = sy * surface->scale,

View File

@ -640,7 +640,7 @@ meta_wayland_tablet_tool_get_relative_coordinates (MetaWaylandTabletTool *tool,
surface_actor = meta_wayland_surface_get_actor (surface);
clutter_event_get_coords (event, &xf, &yf);
clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface_actor)),
clutter_actor_transform_stage_point (CLUTTER_ACTOR (surface_actor),
xf, yf, &xf, &yf);
*sx = wl_fixed_from_double (xf) / surface->scale;