shaped-texture-get-image-via-offscreen.patch

This commit is contained in:
Ray Strode 2019-01-14 10:06:58 -05:00
parent baf11d2471
commit af61b66953
23 changed files with 338 additions and 78 deletions

View File

@ -1043,5 +1043,6 @@ cogl_atlas_texture_vtable =
_cogl_atlas_texture_get_gl_format,
_cogl_atlas_texture_get_type,
NULL, /* is_foreign */
NULL /* set_auto_mipmap */
NULL, /* set_auto_mipmap */
NULL /* is_get_data_supported */
};

View File

@ -210,6 +210,9 @@ struct _CoglDriverVtable
int rowstride,
uint8_t *data);
CoglBool
(* texture_2d_is_get_data_supported) (CoglTexture2D *tex_2d);
/* Prepares for drawing by flushing the journal, framebuffer state,
* pipeline state and attribute state.
*/

View File

@ -454,6 +454,14 @@ _cogl_sub_texture_get_type (CoglTexture *tex)
return _cogl_texture_get_type (sub_tex->full_texture);
}
static CoglBool
_cogl_sub_texture_is_get_data_supported (CoglTexture *tex)
{
CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
return cogl_texture_is_get_data_supported (sub_tex->full_texture);
}
static const CoglTextureVtable
cogl_sub_texture_vtable =
{
@ -476,5 +484,6 @@ cogl_sub_texture_vtable =
_cogl_sub_texture_get_gl_format,
_cogl_sub_texture_get_type,
NULL, /* is_foreign */
NULL /* set_auto_mipmap */
NULL, /* set_auto_mipmap */
_cogl_sub_texture_is_get_data_supported
};

View File

@ -1542,5 +1542,6 @@ cogl_texture_2d_sliced_vtable =
_cogl_texture_2d_sliced_get_gl_format,
_cogl_texture_2d_sliced_get_type,
_cogl_texture_2d_sliced_is_foreign,
NULL /* set_auto_mipmap */
NULL, /* set_auto_mipmap */
NULL /* is_get_data_supported */
};

View File

@ -94,6 +94,15 @@ _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex,
tex_2d->auto_mipmap = value;
}
static CoglBool
_cogl_texture_2d_is_get_data_supported (CoglTexture *tex)
{
CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
CoglContext *ctx = tex->context;
return ctx->driver_vtable->texture_2d_is_get_data_supported (tex_2d);
}
CoglTexture2D *
_cogl_texture_2d_create_base (CoglContext *ctx,
int width,
@ -693,5 +702,6 @@ cogl_texture_2d_vtable =
_cogl_texture_2d_get_gl_format,
_cogl_texture_2d_get_type,
_cogl_texture_2d_is_foreign,
_cogl_texture_2d_set_auto_mipmap
_cogl_texture_2d_set_auto_mipmap,
_cogl_texture_2d_is_get_data_supported
};

View File

@ -755,5 +755,6 @@ cogl_texture_3d_vtable =
_cogl_texture_3d_get_gl_format,
_cogl_texture_3d_get_type,
NULL, /* is_foreign */
_cogl_texture_3d_set_auto_mipmap
_cogl_texture_3d_set_auto_mipmap,
NULL /* is_get_data_supported */
};

View File

@ -149,6 +149,8 @@ struct _CoglTextureVtable
/* Only needs to be implemented if is_primitive == TRUE */
void (* set_auto_mipmap) (CoglTexture *texture,
CoglBool value);
CoglBool (* is_get_data_supported) (CoglTexture *texture);
};
typedef enum _CoglTextureSoureType {

View File

@ -773,5 +773,6 @@ cogl_texture_rectangle_vtable =
_cogl_texture_rectangle_get_gl_format,
_cogl_texture_rectangle_get_type,
_cogl_texture_rectangle_is_foreign,
_cogl_texture_rectangle_set_auto_mipmap
_cogl_texture_rectangle_set_auto_mipmap,
NULL /* is_get_data_supported */
};

View File

@ -205,6 +205,15 @@ _cogl_texture_is_foreign (CoglTexture *texture)
return FALSE;
}
CoglBool
cogl_texture_is_get_data_supported (CoglTexture *texture)
{
if (texture->vtable->is_get_data_supported)
return texture->vtable->is_get_data_supported (texture);
else
return TRUE;
}
unsigned int
cogl_texture_get_width (CoglTexture *texture)
{

View File

@ -511,6 +511,12 @@ CoglBool
cogl_texture_allocate (CoglTexture *texture,
CoglError **error);
/**
* cogl_texture_is_get_data_supported: (skip)
*/
CoglBool
cogl_texture_is_get_data_supported (CoglTexture *texture);
COGL_END_DECLS
#endif /* __COGL_TEXTURE_H__ */

View File

@ -116,4 +116,7 @@ _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d,
int rowstride,
uint8_t *data);
CoglBool
_cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d);
#endif /* _COGL_TEXTURE_2D_GL_PRIVATE_H_ */

View File

@ -470,7 +470,12 @@ allocate_custom_egl_image_external (CoglTexture2D *tex_2d,
{
CoglTexture *tex = COGL_TEXTURE (tex_2d);
CoglContext *ctx = tex->context;
CoglPixelFormat internal_format = loader->src.egl_image_external.format;
CoglPixelFormat external_format;
CoglPixelFormat internal_format;
external_format = loader->src.egl_image_external.format;
internal_format = _cogl_texture_determine_internal_format (tex,
external_format);
_cogl_gl_util_clear_gl_errors (ctx);
@ -854,13 +859,22 @@ _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d,
width,
bpp);
_cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
_cogl_bind_gl_texture_transient (tex_2d->gl_target,
tex_2d->gl_texture,
tex_2d->is_foreign);
ctx->texture_driver->gl_get_tex_image (ctx,
GL_TEXTURE_2D,
tex_2d->gl_target,
gl_format,
gl_type,
data);
}
CoglBool
_cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d)
{
if (tex_2d->gl_target == GL_TEXTURE_EXTERNAL_OES)
return FALSE;
else
return TRUE;
}

View File

@ -714,6 +714,7 @@ _cogl_driver_gl =
_cogl_texture_2d_gl_generate_mipmap,
_cogl_texture_2d_gl_copy_from_bitmap,
_cogl_texture_2d_gl_get_data,
_cogl_texture_2d_gl_is_get_data_supported,
_cogl_gl_flush_attributes_state,
_cogl_clip_stack_gl_flush,
_cogl_buffer_gl_create,

View File

@ -493,6 +493,7 @@ _cogl_driver_gles =
_cogl_texture_2d_gl_generate_mipmap,
_cogl_texture_2d_gl_copy_from_bitmap,
NULL, /* texture_2d_get_data */
NULL, /* texture_2d_is_get_data_supported */
_cogl_gl_flush_attributes_state,
_cogl_clip_stack_gl_flush,
_cogl_buffer_gl_create,

View File

@ -82,6 +82,7 @@ _cogl_driver_nop =
_cogl_texture_2d_nop_generate_mipmap,
_cogl_texture_2d_nop_copy_from_bitmap,
NULL, /* texture_2d_get_data */
NULL, /* texture_2d_is_get_data_supported */
_cogl_nop_flush_attributes_state,
_cogl_clip_stack_nop_flush,
};

View File

@ -1180,5 +1180,6 @@ cogl_texture_pixmap_x11_vtable =
_cogl_texture_pixmap_x11_get_gl_format,
_cogl_texture_pixmap_x11_get_type,
NULL, /* is_foreign */
NULL /* set_auto_mipmap */
NULL, /* set_auto_mipmap */
NULL /* is_get_data_supported */
};

View File

@ -143,7 +143,8 @@ meta_actor_is_untransformed (ClutterActor *actor,
* transform.
*/
gboolean
meta_actor_painting_untransformed (int paint_width,
meta_actor_painting_untransformed (CoglFramebuffer *fb,
int paint_width,
int paint_height,
int *x_origin,
int *y_origin)
@ -153,8 +154,8 @@ meta_actor_painting_untransformed (int paint_width,
float viewport[4];
int i;
cogl_get_modelview_matrix (&modelview);
cogl_get_projection_matrix (&projection);
cogl_framebuffer_get_modelview_matrix (fb, &modelview);
cogl_framebuffer_get_projection_matrix (fb, &projection);
cogl_matrix_multiply (&modelview_projection,
&projection,
@ -173,7 +174,7 @@ meta_actor_painting_untransformed (int paint_width,
vertices[3].y = paint_height;
vertices[3].z = 0;
cogl_get_viewport (viewport);
cogl_framebuffer_get_viewport4fv (fb, viewport);
for (i = 0; i < 4; i++)
{

View File

@ -31,9 +31,10 @@ gboolean meta_actor_is_untransformed (ClutterActor *actor,
int *x_origin,
int *y_origin);
gboolean meta_actor_painting_untransformed (int paint_width,
int paint_height,
int *x_origin,
int *y_origin);
gboolean meta_actor_painting_untransformed (CoglFramebuffer *fb,
int paint_width,
int paint_height,
int *x_origin,
int *y_origin);
#endif /* __META_CLUTTER_UTILS_H__ */

View File

@ -325,6 +325,7 @@ setup_pipeline (MetaBackgroundActor *self,
PipelineFlags pipeline_flags = 0;
guint8 opacity;
float color_component;
CoglFramebuffer *fb;
CoglPipelineFilter filter;
opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self));
@ -417,8 +418,12 @@ setup_pipeline (MetaBackgroundActor *self,
color_component,
opacity / 255.);
fb = cogl_get_draw_framebuffer ();
if (!priv->force_bilinear &&
meta_actor_painting_untransformed (actor_pixel_rect->width, actor_pixel_rect->height, NULL, NULL))
meta_actor_painting_untransformed (fb,
actor_pixel_rect->width,
actor_pixel_rect->height,
NULL, NULL))
filter = COGL_PIPELINE_FILTER_NEAREST;
else
filter = COGL_PIPELINE_FILTER_LINEAR;

View File

@ -35,6 +35,7 @@
#include "clutter-utils.h"
#include "meta-texture-tower.h"
#include "core/boxes-private.h"
#include "meta-cullable.h"
@ -373,47 +374,18 @@ set_cogl_texture (MetaShapedTexture *stex,
}
static void
meta_shaped_texture_paint (ClutterActor *actor)
do_paint (MetaShapedTexture *stex,
CoglFramebuffer *fb,
CoglTexture *paint_tex,
cairo_region_t *clip_region)
{
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
MetaShapedTexturePrivate *priv = stex->priv;
guint tex_width, tex_height;
guchar opacity;
CoglContext *ctx;
CoglFramebuffer *fb;
CoglTexture *paint_tex;
ClutterActorBox alloc;
CoglPipelineFilter filter;
if (priv->clip_region && cairo_region_is_empty (priv->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
* are distinct performance implications from:
*
* - Updating mipmaps that we don't need
* - Having to reallocate pixmaps on the server into larger buffers
*
* So, we just unconditionally use our mipmap emulation code. If we
* wanted to use SGIS_generate_mipmap, we'd have to query COGL to
* see if it was supported (no API currently), and then if and only
* if that was the case, set the clutter texture quality to HIGH.
* Setting the texture quality to high without SGIS_generate_mipmap
* support for TFP textures will result in fallbacks to XGetImage.
*/
if (priv->create_mipmaps)
paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
else
paint_tex = COGL_TEXTURE (priv->texture);
if (paint_tex == NULL)
return;
tex_width = priv->tex_width;
tex_height = priv->tex_height;
@ -428,14 +400,15 @@ meta_shaped_texture_paint (ClutterActor *actor)
filter = COGL_PIPELINE_FILTER_LINEAR;
if (meta_actor_painting_untransformed (tex_width, tex_height, NULL, NULL))
if (meta_actor_painting_untransformed (fb,
tex_width, tex_height,
NULL, NULL))
filter = COGL_PIPELINE_FILTER_NEAREST;
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
fb = cogl_get_draw_framebuffer ();
opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_allocation_box (actor, &alloc);
opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (stex));
clutter_actor_get_allocation_box (CLUTTER_ACTOR (stex), &alloc);
cairo_region_t *blended_region;
gboolean use_opaque_region = (priv->opaque_region != NULL && opacity == 255);
@ -573,6 +546,52 @@ meta_shaped_texture_paint (ClutterActor *actor)
cairo_region_destroy (blended_region);
}
static void
meta_shaped_texture_paint (ClutterActor *actor)
{
MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
MetaShapedTexturePrivate *priv = stex->priv;
CoglTexture *paint_tex = NULL;
CoglFramebuffer *fb;
if (!priv->texture)
return;
if (priv->clip_region && cairo_region_is_empty (priv->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
* are distinct performance implications from:
*
* - Updating mipmaps that we don't need
* - Having to reallocate pixmaps on the server into larger buffers
*
* So, we just unconditionally use our mipmap emulation code. If we
* wanted to use SGIS_generate_mipmap, we'd have to query COGL to
* see if it was supported (no API currently), and then if and only
* if that was the case, set the clutter texture quality to HIGH.
* Setting the texture quality to high without SGIS_generate_mipmap
* support for TFP textures will result in fallbacks to XGetImage.
*/
if (priv->create_mipmaps)
paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
if (!paint_tex)
paint_tex = COGL_TEXTURE (priv->texture);
if (cogl_texture_get_width (paint_tex) == 0 ||
cogl_texture_get_height (paint_tex) == 0)
return;
fb = cogl_get_draw_framebuffer ();
do_paint (META_SHAPED_TEXTURE (actor), fb, paint_tex, priv->clip_region);
}
static void
meta_shaped_texture_get_preferred_width (ClutterActor *self,
gfloat for_height,
@ -888,6 +907,121 @@ meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex)
return priv->opaque_region;
}
static gboolean
should_get_via_offscreen (MetaShapedTexture *stex)
{
MetaShapedTexturePrivate *priv = stex->priv;
if (!cogl_texture_is_get_data_supported (priv->texture))
return TRUE;
return FALSE;
}
static cairo_surface_t *
get_image_via_offscreen (MetaShapedTexture *stex,
cairo_rectangle_int_t *clip)
{
MetaShapedTexturePrivate *priv = stex->priv;
ClutterBackend *clutter_backend = clutter_get_default_backend ();
CoglContext *cogl_context =
clutter_backend_get_cogl_context (clutter_backend);
CoglTexture *image_texture;
GError *error = NULL;
CoglOffscreen *offscreen;
CoglFramebuffer *fb;
CoglMatrix projection_matrix;
unsigned int fb_width, fb_height;
cairo_rectangle_int_t fallback_clip;
CoglColor clear_color;
cairo_surface_t *surface;
if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT))
{
fb_width = priv->tex_width;
fb_height = priv->tex_height;
}
else
{
fb_width = clutter_util_next_p2 (priv->tex_width);
fb_height = clutter_util_next_p2 (priv->tex_height);
}
if (!clip)
{
fallback_clip = (cairo_rectangle_int_t) {
.width = priv->tex_width,
.height = priv->tex_height,
};
clip = &fallback_clip;
}
image_texture =
COGL_TEXTURE (cogl_texture_2d_new_with_size (cogl_context,
fb_width, fb_height));
cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (image_texture),
FALSE);
if (!cogl_texture_allocate (COGL_TEXTURE (image_texture), &error))
{
g_error_free (error);
cogl_object_unref (image_texture);
return FALSE;
}
if (fb_width != priv->tex_width || fb_height != priv->tex_height)
{
CoglSubTexture *sub_texture;
sub_texture = cogl_sub_texture_new (cogl_context,
image_texture,
0, 0,
priv->tex_width, priv->tex_height);
cogl_object_unref (image_texture);
image_texture = COGL_TEXTURE (sub_texture);
}
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (image_texture));
fb = COGL_FRAMEBUFFER (offscreen);
cogl_object_unref (image_texture);
if (!cogl_framebuffer_allocate (fb, &error))
{
g_error_free (error);
cogl_object_unref (fb);
return FALSE;
}
cogl_framebuffer_push_matrix (fb);
cogl_matrix_init_identity (&projection_matrix);
cogl_matrix_scale (&projection_matrix,
1.0 / (priv->tex_width / 2.0),
-1.0 / (priv->tex_height / 2.0), 0);
cogl_matrix_translate (&projection_matrix,
-(priv->tex_width / 2.0),
-(priv->tex_height / 2.0), 0);
cogl_framebuffer_set_projection_matrix (fb, &projection_matrix);
cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
do_paint (stex, fb, priv->texture, NULL);
cogl_framebuffer_pop_matrix (fb);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
clip->width, clip->height);
cogl_framebuffer_read_pixels (fb,
clip->x, clip->y,
clip->width, clip->height,
CLUTTER_CAIRO_FORMAT_ARGB32,
cairo_image_surface_get_data (surface));
cogl_object_unref (fb);
cairo_surface_mark_dirty (surface);
return surface;
}
/**
* meta_shaped_texture_get_image:
* @stex: A #MetaShapedTexture
@ -906,34 +1040,52 @@ cairo_surface_t *
meta_shaped_texture_get_image (MetaShapedTexture *stex,
cairo_rectangle_int_t *clip)
{
MetaShapedTexturePrivate *priv = stex->priv;
cairo_rectangle_int_t *transformed_clip = NULL;
CoglTexture *texture, *mask_texture;
cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 };
cairo_surface_t *surface;
g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
texture = COGL_TEXTURE (stex->priv->texture);
texture = COGL_TEXTURE (priv->texture);
if (texture == NULL)
return NULL;
texture_rect.width = cogl_texture_get_width (texture);
texture_rect.height = cogl_texture_get_height (texture);
if (priv->tex_width == 0 || priv->tex_height == 0)
return NULL;
if (clip != NULL)
{
/* GdkRectangle is just a typedef of cairo_rectangle_int_t,
* so we can use the gdk_rectangle_* APIs on these. */
if (!gdk_rectangle_intersect (&texture_rect, clip, clip))
double tex_scale;
cairo_rectangle_int_t tex_rect;
transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
*transformed_clip = *clip;
clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
meta_rectangle_scale_double (transformed_clip, 1.0 / tex_scale,
META_ROUNDING_STRATEGY_GROW);
tex_rect = (cairo_rectangle_int_t) {
.width = priv->tex_width,
.height = priv->tex_height,
};
if (!meta_rectangle_intersect (&tex_rect, transformed_clip,
transformed_clip))
return NULL;
}
if (clip != NULL)
if (should_get_via_offscreen (stex))
return get_image_via_offscreen (stex, transformed_clip);
if (transformed_clip)
texture = cogl_texture_new_from_sub_texture (texture,
clip->x,
clip->y,
clip->width,
clip->height);
transformed_clip->x,
transformed_clip->y,
transformed_clip->width,
transformed_clip->height);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
cogl_texture_get_width (texture),
@ -945,21 +1097,22 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex,
cairo_surface_mark_dirty (surface);
if (clip != NULL)
if (transformed_clip)
cogl_object_unref (texture);
mask_texture = stex->priv->mask_texture;
mask_texture = priv->mask_texture;
if (mask_texture != NULL)
{
cairo_t *cr;
cairo_surface_t *mask_surface;
if (clip != NULL)
mask_texture = cogl_texture_new_from_sub_texture (mask_texture,
clip->x,
clip->y,
clip->width,
clip->height);
if (transformed_clip)
mask_texture =
cogl_texture_new_from_sub_texture (mask_texture,
transformed_clip->x,
transformed_clip->y,
transformed_clip->width,
transformed_clip->height);
mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
cogl_texture_get_width (mask_texture),
@ -979,7 +1132,7 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex,
cairo_surface_destroy (mask_surface);
if (clip != NULL)
if (transformed_clip)
cogl_object_unref (mask_texture);
}

View File

@ -81,7 +81,11 @@ meta_window_group_paint (ClutterActor *actor)
*/
if (clutter_actor_is_in_clone_paint (actor))
{
if (!meta_actor_painting_untransformed (screen_width,
CoglFramebuffer *fb;
fb = cogl_get_draw_framebuffer ();
if (!meta_actor_painting_untransformed (fb,
screen_width,
screen_height,
&paint_x_origin,
&paint_y_origin) ||

View File

@ -38,6 +38,12 @@ typedef enum
FIXED_DIRECTION_Y = 1 << 1,
} FixedDirections;
typedef enum _MetaRoundingStrategy
{
META_ROUNDING_STRATEGY_SHRINK,
META_ROUNDING_STRATEGY_GROW,
} MetaRoundingStrategy;
/* Output functions -- note that the output buffer had better be big enough:
* rect_to_string: RECT_LENGTH
* region_to_string: (RECT_LENGTH+strlen(separator_string)) *
@ -218,6 +224,10 @@ GList* meta_rectangle_find_nonintersected_monitor_edges (
gboolean meta_rectangle_is_adjecent_to (MetaRectangle *rect,
MetaRectangle *other);
void meta_rectangle_scale_double (MetaRectangle *rect,
double scale,
MetaRoundingStrategy rounding_strategy);
static inline ClutterRect
meta_rectangle_to_clutter_rect (MetaRectangle *rect)
{

View File

@ -2036,3 +2036,25 @@ meta_rectangle_is_adjecent_to (MetaRectangle *rect,
else
return FALSE;
}
void
meta_rectangle_scale_double (MetaRectangle *rect,
double scale,
MetaRoundingStrategy rounding_strategy)
{
switch (rounding_strategy)
{
case META_ROUNDING_STRATEGY_SHRINK:
rect->x = (int) ceil (rect->x * scale);
rect->y = (int) ceil (rect->y * scale);
rect->width = (int) floor (rect->width * scale);
rect->height = (int) floor (rect->height * scale);
break;
case META_ROUNDING_STRATEGY_GROW:
rect->x = (int) floor (rect->x * scale);
rect->y = (int) floor (rect->y * scale);
rect->width = (int) ceil (rect->width * scale);
rect->height = (int) ceil (rect->height * scale);
break;
}
}