diff --git a/src/compositor/cogl-utils.c b/src/compositor/cogl-utils.c index 5dbe3dfae..509d2d014 100644 --- a/src/compositor/cogl-utils.c +++ b/src/compositor/cogl-utils.c @@ -23,47 +23,8 @@ #include "cogl-utils.h" -/** - * meta_create_color_texture_4ub: - * @red: - * @green: - * @blue: - * @alpha: - * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE; - * %COGL_TEXTURE_NO_SLICING is useful if the texture will be - * repeated to create a constant color fill, since hardware - * repeat can't be used for a sliced texture. - * - * Creates a texture that is a single pixel with the specified - * unpremultiplied color components. - * - * Return value: (transfer full): a newly created Cogl texture - */ -CoglHandle -meta_create_color_texture_4ub (guint8 red, - guint8 green, - guint8 blue, - guint8 alpha, - CoglTextureFlags flags) -{ - CoglColor color; - guint8 pixel[4]; - - cogl_color_set_from_4ub (&color, red, green, blue, alpha); - cogl_color_premultiply (&color); - - pixel[0] = cogl_color_get_red_byte (&color); - pixel[1] = cogl_color_get_green_byte (&color); - pixel[2] = cogl_color_get_blue_byte (&color); - pixel[3] = cogl_color_get_alpha_byte (&color); - - return cogl_texture_new_from_data (1, 1, - flags, - COGL_PIXEL_FORMAT_RGBA_8888_PRE, - COGL_PIXEL_FORMAT_ANY, - 4, pixel); -} - +#define CLUTTER_ENABLE_EXPERIMENTAL_API +#include /* Based on gnome-shell/src/st/st-private.c:_st_create_texture_material.c */ @@ -79,32 +40,69 @@ meta_create_color_texture_4ub (guint8 red, * * Return value: (transfer full): a newly created Cogl material */ -CoglHandle +CoglPipeline * meta_create_texture_material (CoglHandle src_texture) { - static CoglHandle texture_material_template = COGL_INVALID_HANDLE; - CoglHandle material; + static CoglPipeline *texture_material_template = NULL; + CoglPipeline *material; - /* We use a material that has a dummy texture as a base for all - texture materials. The idea is that only the Cogl texture object - would be different in the children so it is likely that Cogl will - be able to share GL programs between all the textures. */ - if (G_UNLIKELY (texture_material_template == COGL_INVALID_HANDLE)) + if (G_UNLIKELY (texture_material_template == NULL)) { - CoglHandle dummy_texture; + ClutterBackend *backend = clutter_get_default_backend (); + CoglContext *context = clutter_backend_get_cogl_context (backend); - dummy_texture = meta_create_color_texture_4ub (0xff, 0xff, 0xff, 0xff, - COGL_TEXTURE_NONE); - - texture_material_template = cogl_material_new (); - cogl_material_set_layer (texture_material_template, 0, dummy_texture); - cogl_handle_unref (dummy_texture); + texture_material_template = cogl_pipeline_new (context); + cogl_pipeline_set_layer_null_texture (texture_material_template, + 0, COGL_TEXTURE_TYPE_2D); } - material = cogl_material_copy (texture_material_template); + material = cogl_pipeline_copy (texture_material_template); if (src_texture != COGL_INVALID_HANDLE) - cogl_material_set_layer (material, 0, src_texture); + cogl_pipeline_set_layer_texture (material, 0, src_texture); + + return material; +} + +/** + * meta_create_crossfade_material: + * @src_texture_0: (allow-none): the texture to crossfade from + * @src_texture_1: (allow-none): the texture to crossfade to + * + * Creates a material with two layers, using a combine constant to + * crossfade between them. + * + * Return value: (transfer full): a newly created Cogl material + */ +CoglPipeline * +meta_create_crossfade_material (CoglHandle src_texture_0, + CoglHandle src_texture_1) +{ + static CoglPipeline *texture_material_template = NULL; + CoglPipeline *material; + + if (G_UNLIKELY (texture_material_template == NULL)) + { + ClutterBackend *backend = clutter_get_default_backend (); + CoglContext *context = clutter_backend_get_cogl_context (backend); + + texture_material_template = cogl_pipeline_new (context); + + cogl_pipeline_set_layer_null_texture (texture_material_template, + 0, COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_null_texture (texture_material_template, + 1, COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_combine (texture_material_template, + 1, "RGBA = INTERPOLATE (TEXTURE, PREVIOUS, CONSTANT[A])", + NULL); + } + + material = cogl_pipeline_copy (texture_material_template); + + if (src_texture_0 != COGL_INVALID_HANDLE) + cogl_pipeline_set_layer_texture (material, 0, src_texture_0); + if (src_texture_1 != COGL_INVALID_HANDLE) + cogl_pipeline_set_layer_texture (material, 1, src_texture_1); return material; } diff --git a/src/compositor/cogl-utils.h b/src/compositor/cogl-utils.h index 8d6742e66..b13b16fb8 100644 --- a/src/compositor/cogl-utils.h +++ b/src/compositor/cogl-utils.h @@ -23,13 +23,11 @@ #ifndef __META_COGL_UTILS_H__ #define __META_COGL_UTILS_H__ +#define COGL_ENABLE_EXPERIMENTAL_API #include -CoglHandle meta_create_color_texture_4ub (guint8 red, - guint8 green, - guint8 blue, - guint8 alpha, - CoglTextureFlags flags); -CoglHandle meta_create_texture_material (CoglHandle src_texture); +CoglPipeline *meta_create_texture_material (CoglHandle src_texture); +CoglPipeline *meta_create_crossfade_material (CoglHandle src_texture_0, + CoglHandle src_texture_1); #endif /* __META_COGL_UTILS_H__ */ diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index dabe59ec8..f8a58cecf 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -117,21 +117,6 @@ process_property_notify (MetaCompositor *compositor, { MetaWindowActor *window_actor; - if (event->atom == compositor->atom_x_root_pixmap) - { - GSList *l; - - for (l = meta_display_get_screens (compositor->display); l; l = l->next) - { - MetaScreen *screen = l->data; - if (event->window == meta_screen_get_xroot (screen)) - { - meta_background_actor_update (screen); - return; - } - } - } - if (window == NULL) return; diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c index 558e95383..3c2134a38 100644 --- a/src/compositor/meta-background-actor.c +++ b/src/compositor/meta-background-actor.c @@ -34,6 +34,8 @@ #include #include "meta-background-actor-private.h" +#define CROSSFADE_DURATION 1000 + /* We allow creating multiple MetaBackgroundActors for the same MetaScreen to * allow different rendering options to be set for different copies. * But we want to share the same underlying CoglTexture for efficiency and @@ -55,6 +57,7 @@ struct _MetaScreenBackground float texture_width; float texture_height; + CoglTexture *old_texture; CoglTexture *texture; CoglMaterialWrapMode wrap_mode; @@ -64,10 +67,14 @@ struct _MetaScreenBackground struct _MetaBackgroundActorPrivate { MetaScreenBackground *background; + CoglPipeline *single_pipeline; + CoglPipeline *crossfade_pipeline; CoglPipeline *pipeline; cairo_region_t *visible_region; float dim_factor; + float crossfade_progress; + guint is_crossfading : 1; }; enum @@ -75,6 +82,7 @@ enum PROP_0, PROP_DIM_FACTOR, + PROP_CROSSFADE_PROGRESS, PROP_LAST }; @@ -83,6 +91,7 @@ static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR); +static void clear_old_texture (MetaScreenBackground *background); static void set_texture (MetaScreenBackground *background, CoglHandle texture); @@ -150,19 +159,86 @@ meta_screen_background_get (MetaScreen *screen) } static void -update_wrap_mode_of_actor (MetaBackgroundActor *self) +update_actor_pipeline (MetaBackgroundActor *self, + gboolean crossfade) { MetaBackgroundActorPrivate *priv = self->priv; - cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode); + if (crossfade) + { + priv->pipeline = priv->crossfade_pipeline; + priv->is_crossfading = TRUE; + + cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->old_texture); + cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode); + + cogl_pipeline_set_layer_texture (priv->pipeline, 1, priv->background->texture); + cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 1, priv->background->wrap_mode); + } + else + { + priv->pipeline = priv->single_pipeline; + priv->is_crossfading = FALSE; + + cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->texture); + cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode); + } + + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } static void -update_wrap_mode (MetaScreenBackground *background) +crossfade_completed (ClutterTimeline *timeline, + MetaBackgroundActor *actor) +{ + clear_old_texture (actor->priv->background); + update_actor_pipeline (actor, FALSE); +} + +static void +clear_old_texture (MetaScreenBackground *background) +{ + if (background->old_texture != COGL_INVALID_HANDLE) + { + cogl_handle_unref (background->old_texture); + background->old_texture = COGL_INVALID_HANDLE; + } +} + +static void +set_texture (MetaScreenBackground *background, + CoglHandle texture) { GSList *l; + gboolean crossfade; int width, height; + if (background->old_texture != COGL_INVALID_HANDLE) + { + cogl_handle_unref (background->old_texture); + background->old_texture = COGL_INVALID_HANDLE; + } + + if (texture != COGL_INVALID_HANDLE) + { + background->old_texture = background->texture; + background->texture = cogl_handle_ref (texture); + } + else if (background->texture != COGL_INVALID_HANDLE) + { + cogl_handle_unref (background->texture); + background->texture = COGL_INVALID_HANDLE; + } + + if (texture != COGL_INVALID_HANDLE && + background->old_texture != COGL_INVALID_HANDLE) + crossfade = TRUE; + else + crossfade = FALSE; + + background->texture_width = cogl_texture_get_width (background->texture); + background->texture_height = cogl_texture_get_height (background->texture); + meta_screen_get_size (background->screen, &width, &height); /* We turn off repeating when we have a full-screen pixmap to keep from @@ -175,53 +251,55 @@ update_wrap_mode (MetaScreenBackground *background) background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT; for (l = background->actors; l; l = l->next) - update_wrap_mode_of_actor (l->data); -} - -static void -set_texture_on_actor (MetaBackgroundActor *self) -{ - MetaBackgroundActorPrivate *priv = self->priv; - MetaDisplay *display = meta_screen_get_display (priv->background->screen); - - /* This may trigger destruction of an old texture pixmap, which, if - * the underlying X pixmap is already gone has the tendency to trigger - * X errors inside DRI. For safety, trap errors */ - meta_error_trap_push (display); - cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->texture); - meta_error_trap_pop (display); - - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); -} - -static void -set_texture (MetaScreenBackground *background, - CoglHandle texture) -{ - MetaDisplay *display = meta_screen_get_display (background->screen); - GSList *l; - - /* This may trigger destruction of an old texture pixmap, which, if - * the underlying X pixmap is already gone has the tendency to trigger - * X errors inside DRI. For safety, trap errors */ - meta_error_trap_push (display); - if (background->texture != COGL_INVALID_HANDLE) { - cogl_handle_unref (background->texture); - background->texture = COGL_INVALID_HANDLE; + MetaBackgroundActor *actor = l->data; + + update_actor_pipeline (actor, crossfade); + + if (crossfade) + { + ClutterTransition *transition; + ClutterInterval *interval; + + interval = clutter_interval_new (G_TYPE_FLOAT, 0.0, 1.0); + transition = g_object_new (CLUTTER_TYPE_PROPERTY_TRANSITION, + "animatable", actor, + "property-name", "crossfade-progress", + "interval", interval, + "remove-on-complete", TRUE, + "duration", CROSSFADE_DURATION, + "progress-mode", CLUTTER_EASE_OUT_QUAD, + NULL); + + g_signal_connect_object (transition, "completed", + G_CALLBACK (crossfade_completed), actor, 0); + + clutter_actor_remove_transition (CLUTTER_ACTOR (actor), "crossfade"); + clutter_actor_add_transition (CLUTTER_ACTOR (actor), "crossfade", + transition); + } } - meta_error_trap_pop (display); +} - if (texture != COGL_INVALID_HANDLE) - background->texture = cogl_handle_ref (texture); +static void +update_wrap_mode (MetaScreenBackground *background) +{ + GSList *l; + int width, height; - background->texture_width = cogl_texture_get_width (background->texture); - background->texture_height = cogl_texture_get_height (background->texture); + meta_screen_get_size (background->screen, &width, &height); - for (l = background->actors; l; l = l->next) - set_texture_on_actor (l->data); + if (width == background->texture_width && height == background->texture_height) + background->wrap_mode = COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE; + else + background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT; - update_wrap_mode (background); + for (l = background->actors; l != NULL; l++) + { + MetaBackgroundActor *actor = l->data; + + update_actor_pipeline (actor, actor->priv->is_crossfading); + } } static inline void @@ -248,7 +326,8 @@ meta_background_actor_dispose (GObject *object) priv->background = NULL; } - g_clear_pointer(&priv->pipeline, cogl_object_unref); + g_clear_pointer(&priv->single_pipeline, cogl_object_unref); + g_clear_pointer(&priv->crossfade_pipeline, cogl_object_unref); G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object); } @@ -298,6 +377,7 @@ meta_background_actor_paint (ClutterActor *actor) guint8 opacity = clutter_actor_get_paint_opacity (actor); guint8 color_component; int width, height; + CoglColor crossfade_color; meta_background_ensure_rendered (priv->background); @@ -311,6 +391,17 @@ meta_background_actor_paint (ClutterActor *actor) color_component, opacity); + if (priv->is_crossfading) + { + cogl_color_init_from_4f (&crossfade_color, + priv->crossfade_progress, + priv->crossfade_progress, + priv->crossfade_progress, + priv->crossfade_progress); + cogl_pipeline_set_layer_combine_constant (priv->pipeline, + 1, &crossfade_color); + } + cogl_set_source (priv->pipeline); if (priv->visible_region) @@ -357,6 +448,22 @@ meta_background_actor_get_paint_volume (ClutterActor *actor, return TRUE; } +static void +meta_background_actor_set_crossfade_progress (MetaBackgroundActor *self, + gfloat crossfade_progress) +{ + MetaBackgroundActorPrivate *priv = self->priv; + + if (priv->crossfade_progress == crossfade_progress) + return; + + priv->crossfade_progress = crossfade_progress; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CROSSFADE_PROGRESS]); +} + static void meta_background_actor_set_dim_factor (MetaBackgroundActor *self, gfloat dim_factor) @@ -387,6 +494,8 @@ meta_background_actor_get_property(GObject *object, case PROP_DIM_FACTOR: g_value_set_float (value, priv->dim_factor); break; + case PROP_CROSSFADE_PROGRESS: + g_value_set_float (value, priv->crossfade_progress); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -406,6 +515,9 @@ meta_background_actor_set_property(GObject *object, case PROP_DIM_FACTOR: meta_background_actor_set_dim_factor (self, g_value_get_float (value)); break; + case PROP_CROSSFADE_PROGRESS: + meta_background_actor_set_crossfade_progress (self, g_value_get_float (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -443,7 +555,18 @@ meta_background_actor_class_init (MetaBackgroundActorClass *klass) 1.0, G_PARAM_READWRITE); obj_props[PROP_DIM_FACTOR] = pspec; - g_object_class_install_property (object_class, PROP_DIM_FACTOR, pspec); + + /** + * MetaBackgroundActor:crossfade-progress: (skip) + */ + pspec = g_param_spec_float ("crossfade-progress", + "", "", + 0.0, 1.0, + 1.0, + G_PARAM_READWRITE); + obj_props[PROP_CROSSFADE_PROGRESS] = pspec; + + g_object_class_install_properties (object_class, PROP_LAST, obj_props); } static void @@ -479,11 +602,12 @@ meta_background_actor_new_for_screen (MetaScreen *screen) priv->background = meta_screen_background_get (screen); priv->background->actors = g_slist_prepend (priv->background->actors, self); - /* A CoglMaterial and a CoglPipeline are the same thing */ - priv->pipeline = (CoglPipeline*) meta_create_texture_material (NULL); + priv->single_pipeline = meta_create_texture_material (priv->background->texture); + priv->crossfade_pipeline = meta_create_crossfade_material (priv->background->old_texture, + priv->background->texture); - set_texture_on_actor (self); - update_wrap_mode_of_actor (self); + if (priv->background->texture != COGL_INVALID_HANDLE) + update_actor_pipeline (self, FALSE); return CLUTTER_ACTOR (self); } @@ -520,7 +644,7 @@ on_background_drawn (GObject *object, } else { - g_warning ("Failed to create background texture from pixmap: %s", + g_warning ("Failed to render background: %s", error->message); g_error_free (error); } @@ -650,9 +774,17 @@ meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor, if (hook == META_SNIPPET_HOOK_VERTEX || hook == META_SNIPPET_HOOK_FRAGMENT) - cogl_pipeline_add_snippet (priv->pipeline, snippet); + { + cogl_pipeline_add_snippet (priv->single_pipeline, snippet); + cogl_pipeline_add_snippet (priv->crossfade_pipeline, snippet); + } else - cogl_pipeline_add_layer_snippet (priv->pipeline, 0, snippet); + { + /* In case of crossfading, apply the snippet only to the new texture. + We can't apply it to both because declarations would be doubled. */ + cogl_pipeline_add_layer_snippet (priv->single_pipeline, 0, snippet); + cogl_pipeline_add_layer_snippet (priv->crossfade_pipeline, 1, snippet); + } cogl_object_unref (snippet); } @@ -686,8 +818,12 @@ meta_background_actor_set_uniform_float (MetaBackgroundActor *actor, priv = actor->priv; - cogl_pipeline_set_uniform_float (priv->pipeline, - cogl_pipeline_get_uniform_location (priv->pipeline, + cogl_pipeline_set_uniform_float (priv->single_pipeline, + cogl_pipeline_get_uniform_location (priv->single_pipeline, + uniform_name), + n_components, count, uniform); + cogl_pipeline_set_uniform_float (priv->crossfade_pipeline, + cogl_pipeline_get_uniform_location (priv->crossfade_pipeline, uniform_name), n_components, count, uniform); }