summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2021-05-10 13:27:39 +0200
committerCarlos Garnacho <carlosg@gnome.org>2021-07-16 19:08:06 +0200
commit8f839b2f9ca34932eceaf4f85db607bfe005898f (patch)
tree954141fef704a7f7a23e8aa11fcc7ad62d25c3f3 /src
parent0ac257212e7a26e3346b24930d5356bd7982b5b8 (diff)
downloadmutter-8f839b2f9ca34932eceaf4f85db607bfe005898f.tar.gz
clutter: Move ClutterStageCogl[View] code to src/backends
This is now MetaStageImpl in backend code. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1862>
Diffstat (limited to 'src')
-rw-r--r--src/backends/meta-renderer-view.h2
-rw-r--r--src/backends/meta-stage-impl-private.h88
-rw-r--r--src/backends/meta-stage-impl.c907
-rw-r--r--src/backends/native/meta-stage-native.h1
-rw-r--r--src/meson.build2
5 files changed, 999 insertions, 1 deletions
diff --git a/src/backends/meta-renderer-view.h b/src/backends/meta-renderer-view.h
index 0e19dc2f2..668b4e7e8 100644
--- a/src/backends/meta-renderer-view.h
+++ b/src/backends/meta-renderer-view.h
@@ -19,7 +19,7 @@
#define META_RENDERER_VIEW_H
#include "backends/meta-monitor-manager-private.h"
-#include "clutter/clutter-mutter.h"
+#include "backends/meta-stage-impl-private.h"
#define META_TYPE_RENDERER_VIEW (meta_renderer_view_get_type ())
G_DECLARE_FINAL_TYPE (MetaRendererView, meta_renderer_view,
diff --git a/src/backends/meta-stage-impl-private.h b/src/backends/meta-stage-impl-private.h
new file mode 100644
index 000000000..4b01fc1c2
--- /dev/null
+++ b/src/backends/meta-stage-impl-private.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation.
+ * Copyright (C) 2021 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Matthew Allum
+ * Robert Bragg
+ * Neil Roberts
+ * Emmanuele Bassi
+ *
+ */
+
+#ifndef META_STAGE_IMPL_PRIVATE_H
+#define META_STAGE_IMPL_PRIVATE_H
+
+#include <cairo.h>
+
+#include "clutter/clutter.h"
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_STAGE_COGL (_clutter_stage_cogl_get_type ())
+#define CLUTTER_STAGE_COGL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_COGL, ClutterStageCogl))
+#define CLUTTER_IS_STAGE_COGL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_COGL))
+#define CLUTTER_STAGE_COGL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_COGL, ClutterStageCoglClass))
+#define CLUTTER_IS_STAGE_COGL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_COGL))
+#define CLUTTER_STAGE_COGL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_COGL, ClutterStageCoglClass))
+
+typedef struct _ClutterStageCogl ClutterStageCogl;
+typedef struct _ClutterStageCoglClass ClutterStageCoglClass;
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterStageCogl, g_object_unref)
+
+#define CLUTTER_TYPE_STAGE_VIEW_COGL (clutter_stage_view_cogl_get_type ())
+CLUTTER_EXPORT
+G_DECLARE_DERIVABLE_TYPE (ClutterStageViewCogl, clutter_stage_view_cogl,
+ CLUTTER, STAGE_VIEW_COGL,
+ ClutterStageView)
+
+struct _ClutterStageViewCoglClass
+{
+ ClutterStageViewClass parent_class;
+};
+
+struct _ClutterStageCogl
+{
+ GObject parent_instance;
+
+ /* the stage wrapper */
+ ClutterStage *wrapper;
+
+ /* back pointer to the backend */
+ ClutterBackend *backend;
+};
+
+struct _ClutterStageCoglClass
+{
+ GObjectClass parent_class;
+};
+
+CLUTTER_EXPORT
+GType _clutter_stage_cogl_get_type (void) G_GNUC_CONST;
+
+CLUTTER_EXPORT
+void _clutter_stage_cogl_presented (ClutterStageCogl *stage_cogl,
+ CoglFrameEvent frame_event,
+ ClutterFrameInfo *frame_info);
+
+CLUTTER_EXPORT
+void clutter_stage_cogl_add_onscreen_frame_info (ClutterStageCogl *stage_cogl,
+ ClutterStageView *view);
+
+G_END_DECLS
+
+#endif /* META_STAGE_IMPL_PRIVATE_H */
diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c
new file mode 100644
index 000000000..950710d07
--- /dev/null
+++ b/src/backends/meta-stage-impl.c
@@ -0,0 +1,907 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+ * Authors:
+ * Matthew Allum
+ * Robert Bragg
+ * Neil Roberts
+ * Emmanuele Bassi
+ */
+
+#include "config.h"
+
+#include "backends/meta-stage-impl-private.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "clutter/clutter-mutter.h"
+#include "cogl/cogl.h"
+#include "core/util-private.h"
+
+#define MAX_STACK_RECTS 256
+
+typedef struct _ClutterStageViewCoglPrivate
+{
+ /* Damage history, in stage view render target framebuffer coordinate space.
+ */
+ ClutterDamageHistory *damage_history;
+
+ guint notify_presented_handle_id;
+
+ CoglFrameClosure *frame_cb_closure;
+} ClutterStageViewCoglPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl,
+ CLUTTER_TYPE_STAGE_VIEW)
+
+typedef struct _ClutterStageCoglPrivate
+{
+ int64_t global_frame_counter;
+} ClutterStageCoglPrivate;
+
+static void
+clutter_stage_window_iface_init (ClutterStageWindowInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ClutterStageCogl,
+ _clutter_stage_cogl,
+ G_TYPE_OBJECT,
+ G_ADD_PRIVATE (ClutterStageCogl)
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
+ clutter_stage_window_iface_init));
+
+enum
+{
+ PROP_0,
+ PROP_WRAPPER,
+ PROP_BACKEND,
+ PROP_LAST
+};
+
+static void
+clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
+{
+ g_debug ("Unrealizing Cogl stage [%p]", stage_window);
+}
+
+static gboolean
+clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
+{
+ ClutterBackend *backend;
+
+ g_debug ("Realizing stage '%s' [%p]",
+ G_OBJECT_TYPE_NAME (stage_window),
+ stage_window);
+
+ backend = clutter_get_default_backend ();
+
+ if (backend->cogl_context == NULL)
+ {
+ g_warning ("Failed to realize stage: missing Cogl context");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int64_t
+clutter_stage_cogl_get_frame_counter (ClutterStageWindow *stage_window)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+ ClutterStageCoglPrivate *priv =
+ _clutter_stage_cogl_get_instance_private (stage_cogl);
+
+ return priv->global_frame_counter;
+}
+
+static ClutterActor *
+clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window)
+{
+ return CLUTTER_ACTOR (CLUTTER_STAGE_COGL (stage_window)->wrapper);
+}
+
+static void
+clutter_stage_cogl_show (ClutterStageWindow *stage_window,
+ gboolean do_raise)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+
+ clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper));
+}
+
+static void
+clutter_stage_cogl_hide (ClutterStageWindow *stage_window)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+
+ clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper));
+}
+
+static void
+clutter_stage_cogl_resize (ClutterStageWindow *stage_window,
+ gint width,
+ gint height)
+{
+}
+
+static void
+paint_damage_region (ClutterStageWindow *stage_window,
+ ClutterStageView *view,
+ cairo_region_t *swap_region,
+ cairo_region_t *queued_redraw_clip)
+{
+ CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
+ CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
+ static CoglPipeline *overlay_blue = NULL;
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+ ClutterActor *actor = CLUTTER_ACTOR (stage_cogl->wrapper);
+ graphene_matrix_t transform;
+ int n_rects, i;
+
+ cogl_framebuffer_push_matrix (framebuffer);
+ clutter_actor_get_transform (actor, &transform);
+ cogl_framebuffer_transform (framebuffer, &transform);
+
+ /* Blue for the swap region */
+ if (G_UNLIKELY (overlay_blue == NULL))
+ {
+ overlay_blue = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_color4ub (overlay_blue, 0x00, 0x00, 0x33, 0x33);
+ }
+
+ n_rects = cairo_region_num_rectangles (swap_region);
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+ float x_1, x_2, y_1, y_2;
+
+ cairo_region_get_rectangle (swap_region, i, &rect);
+ x_1 = rect.x;
+ x_2 = rect.x + rect.width;
+ y_1 = rect.y;
+ y_2 = rect.y + rect.height;
+
+ cogl_framebuffer_draw_rectangle (framebuffer, overlay_blue, x_1, y_1, x_2, y_2);
+ }
+
+ /* Red for the clip */
+ if (queued_redraw_clip)
+ {
+ static CoglPipeline *overlay_red = NULL;
+
+ if (G_UNLIKELY (overlay_red == NULL))
+ {
+ overlay_red = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_color4ub (overlay_red, 0x33, 0x00, 0x00, 0x33);
+ }
+
+ n_rects = cairo_region_num_rectangles (queued_redraw_clip);
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+ float x_1, x_2, y_1, y_2;
+
+ cairo_region_get_rectangle (queued_redraw_clip, i, &rect);
+ x_1 = rect.x;
+ x_2 = rect.x + rect.width;
+ y_1 = rect.y;
+ y_2 = rect.y + rect.height;
+
+ cogl_framebuffer_draw_rectangle (framebuffer, overlay_red, x_1, y_1, x_2, y_2);
+ }
+ }
+
+ cogl_framebuffer_pop_matrix (framebuffer);
+}
+
+typedef struct _NotifyPresentedClosure
+{
+ ClutterStageView *view;
+ ClutterFrameInfo frame_info;
+} NotifyPresentedClosure;
+
+static gboolean
+notify_presented_idle (gpointer user_data)
+{
+ NotifyPresentedClosure *closure = user_data;
+ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (closure->view);
+ ClutterStageViewCoglPrivate *view_priv =
+ clutter_stage_view_cogl_get_instance_private (view_cogl);
+
+ view_priv->notify_presented_handle_id = 0;
+ clutter_stage_view_notify_presented (closure->view, &closure->frame_info);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+swap_framebuffer (ClutterStageWindow *stage_window,
+ ClutterStageView *view,
+ cairo_region_t *swap_region,
+ gboolean swap_with_damage,
+ ClutterFrame *frame)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+ ClutterStageCoglPrivate *priv =
+ _clutter_stage_cogl_get_instance_private (stage_cogl);
+ CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
+ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
+
+ clutter_stage_view_before_swap_buffer (view, swap_region);
+
+ if (COGL_IS_ONSCREEN (framebuffer))
+ {
+ CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+ int *damage, n_rects, i;
+ CoglFrameInfo *frame_info;
+
+ n_rects = cairo_region_num_rectangles (swap_region);
+ damage = g_newa (int, n_rects * 4);
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (swap_region, i, &rect);
+ damage[i * 4] = rect.x;
+ damage[i * 4 + 1] = rect.y;
+ damage[i * 4 + 2] = rect.width;
+ damage[i * 4 + 3] = rect.height;
+ }
+
+ frame_info =
+ cogl_frame_info_new (cogl_context, priv->global_frame_counter);
+ priv->global_frame_counter++;
+
+ /* push on the screen */
+ if (n_rects > 0 && !swap_with_damage)
+ {
+ g_debug ("cogl_onscreen_swap_region (onscreen: %p)", onscreen);
+
+ cogl_onscreen_swap_region (onscreen,
+ damage, n_rects,
+ frame_info,
+ frame);
+ }
+ else
+ {
+ g_debug ("cogl_onscreen_swap_buffers (onscreen: %p)", onscreen);
+
+ cogl_onscreen_swap_buffers_with_damage (onscreen,
+ damage, n_rects,
+ frame_info,
+ frame);
+ }
+ }
+ else
+ {
+ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
+ ClutterStageViewCoglPrivate *view_priv =
+ clutter_stage_view_cogl_get_instance_private (view_cogl);
+ NotifyPresentedClosure *closure;
+
+ g_debug ("fake offscreen swap (framebuffer: %p)", framebuffer);
+
+ closure = g_new0 (NotifyPresentedClosure, 1);
+ closure->view = view;
+ closure->frame_info = (ClutterFrameInfo) {
+ .frame_counter = priv->global_frame_counter,
+ .refresh_rate = clutter_stage_view_get_refresh_rate (view),
+ .presentation_time = g_get_monotonic_time (),
+ .flags = CLUTTER_FRAME_INFO_FLAG_NONE,
+ .sequence = 0,
+ };
+ priv->global_frame_counter++;
+
+ g_warn_if_fail (view_priv->notify_presented_handle_id == 0);
+ view_priv->notify_presented_handle_id =
+ g_idle_add_full (G_PRIORITY_DEFAULT,
+ notify_presented_idle,
+ closure, g_free);
+ }
+}
+
+static cairo_region_t *
+offset_scale_and_clamp_region (const cairo_region_t *region,
+ int offset_x,
+ int offset_y,
+ float scale)
+{
+ int n_rects, i;
+ cairo_rectangle_int_t *rects;
+ g_autofree cairo_rectangle_int_t *freeme = NULL;
+
+ n_rects = cairo_region_num_rectangles (region);
+
+ if (n_rects == 0)
+ return cairo_region_create ();
+
+ if (n_rects < MAX_STACK_RECTS)
+ rects = g_newa (cairo_rectangle_int_t, n_rects);
+ else
+ rects = freeme = g_new (cairo_rectangle_int_t, n_rects);
+
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t *rect = &rects[i];
+ graphene_rect_t tmp;
+
+ cairo_region_get_rectangle (region, i, rect);
+
+ _clutter_util_rect_from_rectangle (rect, &tmp);
+ graphene_rect_offset (&tmp, offset_x, offset_y);
+ graphene_rect_scale (&tmp, scale, scale, &tmp);
+ _clutter_util_rectangle_int_extents (&tmp, rect);
+ }
+
+ return cairo_region_create_rectangles (rects, n_rects);
+}
+
+static cairo_region_t *
+scale_offset_and_clamp_region (const cairo_region_t *region,
+ float scale,
+ int offset_x,
+ int offset_y)
+{
+ int n_rects, i;
+ cairo_rectangle_int_t *rects;
+ g_autofree cairo_rectangle_int_t *freeme = NULL;
+
+ n_rects = cairo_region_num_rectangles (region);
+
+ if (n_rects == 0)
+ return cairo_region_create ();
+
+ if (n_rects < MAX_STACK_RECTS)
+ rects = g_newa (cairo_rectangle_int_t, n_rects);
+ else
+ rects = freeme = g_new (cairo_rectangle_int_t, n_rects);
+
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t *rect = &rects[i];
+ graphene_rect_t tmp;
+
+ cairo_region_get_rectangle (region, i, rect);
+
+ _clutter_util_rect_from_rectangle (rect, &tmp);
+ graphene_rect_scale (&tmp, scale, scale, &tmp);
+ graphene_rect_offset (&tmp, offset_x, offset_y);
+ _clutter_util_rectangle_int_extents (&tmp, rect);
+ }
+
+ return cairo_region_create_rectangles (rects, n_rects);
+}
+
+static void
+paint_stage (ClutterStageCogl *stage_cogl,
+ ClutterStageView *view,
+ cairo_region_t *redraw_clip)
+{
+ ClutterStage *stage = stage_cogl->wrapper;
+
+ _clutter_stage_maybe_setup_viewport (stage, view);
+ clutter_stage_paint_view (stage, view, redraw_clip);
+
+ clutter_stage_view_after_paint (view, redraw_clip);
+}
+
+static cairo_region_t *
+transform_swap_region_to_onscreen (ClutterStageView *view,
+ cairo_region_t *swap_region)
+{
+ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view);
+ int n_rects, i;
+ cairo_rectangle_int_t *rects;
+ cairo_region_t *transformed_region;
+ int width, height;
+
+ width = cogl_framebuffer_get_width (onscreen);
+ height = cogl_framebuffer_get_height (onscreen);
+
+ n_rects = cairo_region_num_rectangles (swap_region);
+ rects = g_newa (cairo_rectangle_int_t, n_rects);
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_region_get_rectangle (swap_region, i, &rects[i]);
+ clutter_stage_view_transform_rect_to_onscreen (view,
+ &rects[i],
+ width,
+ height,
+ &rects[i]);
+ }
+ transformed_region = cairo_region_create_rectangles (rects, n_rects);
+
+ return transformed_region;
+}
+
+static void
+clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
+ ClutterStageView *view,
+ ClutterFrame *frame)
+{
+ ClutterStageWindow *stage_window = CLUTTER_STAGE_WINDOW (stage_cogl);
+ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
+ ClutterStageViewCoglPrivate *view_priv =
+ clutter_stage_view_cogl_get_instance_private (view_cogl);
+ CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
+ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view);
+ cairo_rectangle_int_t view_rect;
+ gboolean is_full_redraw;
+ gboolean use_clipped_redraw = TRUE;
+ gboolean can_blit_sub_buffer;
+ gboolean has_buffer_age;
+ gboolean swap_with_damage;
+ cairo_region_t *redraw_clip;
+ cairo_region_t *queued_redraw_clip = NULL;
+ cairo_region_t *fb_clip_region;
+ cairo_region_t *swap_region;
+ ClutterDrawDebugFlag paint_debug_flags;
+ float fb_scale;
+ int fb_width, fb_height;
+ int buffer_age = 0;
+
+ clutter_stage_view_get_layout (view, &view_rect);
+ fb_scale = clutter_stage_view_get_scale (view);
+ fb_width = cogl_framebuffer_get_width (fb);
+ fb_height = cogl_framebuffer_get_height (fb);
+
+ can_blit_sub_buffer =
+ COGL_IS_ONSCREEN (onscreen) &&
+ cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
+
+ has_buffer_age =
+ COGL_IS_ONSCREEN (onscreen) &&
+ cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
+
+ redraw_clip = clutter_stage_view_take_redraw_clip (view);
+
+ /* NB: a NULL redraw clip == full stage redraw */
+ if (!redraw_clip)
+ is_full_redraw = TRUE;
+ else
+ is_full_redraw = FALSE;
+
+ if (has_buffer_age)
+ {
+ buffer_age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (onscreen));
+ if (!clutter_damage_history_is_age_valid (view_priv->damage_history,
+ buffer_age))
+ {
+ g_debug ("Invalid back buffer(age=%d): forcing full redraw",
+ buffer_age);
+ use_clipped_redraw = FALSE;
+ }
+ }
+
+ meta_get_clutter_debug_flags (NULL, &paint_debug_flags, NULL);
+
+ use_clipped_redraw =
+ use_clipped_redraw &&
+ !(paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) &&
+ _clutter_stage_window_can_clip_redraws (stage_window) &&
+ (can_blit_sub_buffer || has_buffer_age) &&
+ !is_full_redraw &&
+ /* some drivers struggle to get going and produce some junk
+ * frames when starting up... */
+ cogl_onscreen_get_frame_counter (COGL_ONSCREEN (onscreen)) > 3;
+
+ if (use_clipped_redraw)
+ {
+ fb_clip_region = offset_scale_and_clamp_region (redraw_clip,
+ -view_rect.x,
+ -view_rect.y,
+ fb_scale);
+
+ if (G_UNLIKELY (paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))
+ {
+ queued_redraw_clip =
+ scale_offset_and_clamp_region (fb_clip_region,
+ 1.0 / fb_scale,
+ view_rect.x,
+ view_rect.y);
+ }
+ }
+ else
+ {
+ cairo_rectangle_int_t fb_rect;
+
+ fb_rect = (cairo_rectangle_int_t) {
+ .width = fb_width,
+ .height = fb_height,
+ };
+ fb_clip_region = cairo_region_create_rectangle (&fb_rect);
+
+ g_clear_pointer (&redraw_clip, cairo_region_destroy);
+ redraw_clip = cairo_region_create_rectangle (&view_rect);
+
+ if (G_UNLIKELY (paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))
+ queued_redraw_clip = cairo_region_reference (redraw_clip);
+ }
+
+ g_return_if_fail (!cairo_region_is_empty (fb_clip_region));
+
+ swap_with_damage = FALSE;
+ if (has_buffer_age)
+ {
+ clutter_damage_history_record (view_priv->damage_history,
+ fb_clip_region);
+
+ if (use_clipped_redraw)
+ {
+ int age;
+
+ for (age = 1; age <= buffer_age; age++)
+ {
+ const cairo_region_t *old_damage;
+
+ old_damage =
+ clutter_damage_history_lookup (view_priv->damage_history, age);
+ cairo_region_union (fb_clip_region, old_damage);
+ }
+
+ g_debug ("Reusing back buffer(age=%d) - repairing region: num rects: %d",
+ buffer_age,
+ cairo_region_num_rectangles (fb_clip_region));
+
+ swap_with_damage = TRUE;
+ }
+
+ clutter_damage_history_step (view_priv->damage_history);
+ }
+
+ if (use_clipped_redraw)
+ {
+ /* Regenerate redraw_clip because:
+ * 1. It's missing the regions added from damage_history above; and
+ * 2. If using fractional scaling then it might be a fraction of a
+ * logical pixel (or one physical pixel) smaller than
+ * fb_clip_region, due to the clamping from
+ * offset_scale_and_clamp_region. So we need to ensure redraw_clip
+ * is a superset of fb_clip_region to avoid such gaps.
+ */
+ cairo_region_destroy (redraw_clip);
+ redraw_clip = scale_offset_and_clamp_region (fb_clip_region,
+ 1.0 / fb_scale,
+ view_rect.x,
+ view_rect.y);
+ }
+
+ if (paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)
+ {
+ cairo_region_t *debug_redraw_clip;
+
+ debug_redraw_clip = cairo_region_create_rectangle (&view_rect);
+ paint_stage (stage_cogl, view, debug_redraw_clip);
+ cairo_region_destroy (debug_redraw_clip);
+ }
+ else if (use_clipped_redraw)
+ {
+ cogl_framebuffer_push_region_clip (fb, fb_clip_region);
+
+ paint_stage (stage_cogl, view, redraw_clip);
+
+ cogl_framebuffer_pop_clip (fb);
+ }
+ else
+ {
+ g_debug ("Unclipped stage paint");
+
+ paint_stage (stage_cogl, view, redraw_clip);
+ }
+
+ /* XXX: It seems there will be a race here in that the stage
+ * window may be resized before the cogl_onscreen_swap_region
+ * is handled and so we may copy the wrong region. I can't
+ * really see how we can handle this with the current state of X
+ * but at least in this case a full redraw should be queued by
+ * the resize anyway so it should only exhibit temporary
+ * artefacts.
+ */
+ if (use_clipped_redraw)
+ swap_region = cairo_region_reference (fb_clip_region);
+ else
+ swap_region = cairo_region_create ();
+
+ g_clear_pointer (&redraw_clip, cairo_region_destroy);
+ g_clear_pointer (&fb_clip_region, cairo_region_destroy);
+
+ COGL_TRACE_BEGIN_SCOPED (ClutterStageCoglRedrawViewSwapFramebuffer,
+ "Paint (swap framebuffer)");
+
+ if (clutter_stage_view_get_onscreen (view) !=
+ clutter_stage_view_get_framebuffer (view))
+ {
+ cairo_region_t *transformed_swap_region;
+
+ transformed_swap_region =
+ transform_swap_region_to_onscreen (view, swap_region);
+ cairo_region_destroy (swap_region);
+ swap_region = transformed_swap_region;
+ }
+
+ if (queued_redraw_clip)
+ {
+ cairo_region_t *swap_region_in_stage_space;
+
+ swap_region_in_stage_space =
+ scale_offset_and_clamp_region (swap_region,
+ 1.0f / fb_scale,
+ view_rect.x,
+ view_rect.y);
+
+ cairo_region_subtract (swap_region_in_stage_space, queued_redraw_clip);
+
+ paint_damage_region (stage_window, view,
+ swap_region_in_stage_space, queued_redraw_clip);
+
+ cairo_region_destroy (queued_redraw_clip);
+ cairo_region_destroy (swap_region_in_stage_space);
+ }
+
+ swap_framebuffer (stage_window,
+ view,
+ swap_region,
+ swap_with_damage,
+ frame);
+
+ cairo_region_destroy (swap_region);
+}
+
+static gboolean
+clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl,
+ ClutterStageView *view,
+ CoglScanout *scanout,
+ ClutterFrame *frame,
+ GError **error)
+{
+ ClutterStageCoglPrivate *priv =
+ _clutter_stage_cogl_get_instance_private (stage_cogl);
+ CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
+ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
+ CoglOnscreen *onscreen;
+ CoglFrameInfo *frame_info;
+
+ g_assert (COGL_IS_ONSCREEN (framebuffer));
+
+ onscreen = COGL_ONSCREEN (framebuffer);
+
+ frame_info = cogl_frame_info_new (cogl_context, priv->global_frame_counter);
+
+ if (!cogl_onscreen_direct_scanout (onscreen,
+ scanout,
+ frame_info,
+ frame,
+ error))
+ {
+ cogl_object_unref (frame_info);
+ return FALSE;
+ }
+
+ priv->global_frame_counter++;
+
+ return TRUE;
+}
+
+static void
+clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
+ ClutterStageView *view,
+ ClutterFrame *frame)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+ g_autoptr (CoglScanout) scanout = NULL;
+
+ scanout = clutter_stage_view_take_scanout (view);
+ if (scanout)
+ {
+ g_autoptr (GError) error = NULL;
+
+ if (clutter_stage_cogl_scanout_view (stage_cogl,
+ view,
+ scanout,
+ frame,
+ &error))
+ return;
+
+ if (!g_error_matches (error,
+ COGL_SCANOUT_ERROR,
+ COGL_SCANOUT_ERROR_INHIBITED))
+ g_warning ("Failed to scan out client buffer: %s", error->message);
+ }
+
+ clutter_stage_cogl_redraw_view_primary (stage_cogl, view, frame);
+}
+
+void
+clutter_stage_cogl_add_onscreen_frame_info (ClutterStageCogl *stage_cogl,
+ ClutterStageView *view)
+{
+ ClutterStageCoglPrivate *priv =
+ _clutter_stage_cogl_get_instance_private (stage_cogl);
+ CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
+ CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
+ CoglFrameInfo *frame_info;
+
+ frame_info = cogl_frame_info_new (cogl_context, priv->global_frame_counter);
+ priv->global_frame_counter++;
+
+ cogl_onscreen_add_frame_info (COGL_ONSCREEN (framebuffer), frame_info);
+}
+
+static void
+clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
+{
+ iface->realize = clutter_stage_cogl_realize;
+ iface->unrealize = clutter_stage_cogl_unrealize;
+ iface->get_wrapper = clutter_stage_cogl_get_wrapper;
+ iface->resize = clutter_stage_cogl_resize;
+ iface->show = clutter_stage_cogl_show;
+ iface->hide = clutter_stage_cogl_hide;
+ iface->get_frame_counter = clutter_stage_cogl_get_frame_counter;
+ iface->redraw_view = clutter_stage_cogl_redraw_view;
+}
+
+static void
+clutter_stage_cogl_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterStageCogl *self = CLUTTER_STAGE_COGL (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_WRAPPER:
+ self->wrapper = g_value_get_object (value);
+ break;
+
+ case PROP_BACKEND:
+ self->backend = g_value_get_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+_clutter_stage_cogl_class_init (ClutterStageCoglClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = clutter_stage_cogl_set_property;
+
+ g_object_class_override_property (gobject_class, PROP_WRAPPER, "wrapper");
+ g_object_class_override_property (gobject_class, PROP_BACKEND, "backend");
+}
+
+static void
+_clutter_stage_cogl_init (ClutterStageCogl *stage)
+{
+}
+
+static void
+frame_cb (CoglOnscreen *onscreen,
+ CoglFrameEvent frame_event,
+ CoglFrameInfo *frame_info,
+ void *user_data)
+{
+ ClutterStageView *view = user_data;
+
+ if (frame_event == COGL_FRAME_EVENT_SYNC)
+ return;
+
+ if (cogl_frame_info_get_is_symbolic (frame_info))
+ {
+ clutter_stage_view_notify_ready (view);
+ }
+ else
+ {
+ ClutterFrameInfo clutter_frame_info;
+ ClutterFrameInfoFlag flags = CLUTTER_FRAME_INFO_FLAG_NONE;
+
+ if (cogl_frame_info_is_hw_clock (frame_info))
+ flags |= CLUTTER_FRAME_INFO_FLAG_HW_CLOCK;
+
+ if (cogl_frame_info_is_zero_copy (frame_info))
+ flags |= CLUTTER_FRAME_INFO_FLAG_ZERO_COPY;
+
+ if (cogl_frame_info_is_vsync (frame_info))
+ flags |= CLUTTER_FRAME_INFO_FLAG_VSYNC;
+
+ clutter_frame_info = (ClutterFrameInfo) {
+ .frame_counter = cogl_frame_info_get_global_frame_counter (frame_info),
+ .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info),
+ .presentation_time =
+ cogl_frame_info_get_presentation_time_us (frame_info),
+ .flags = flags,
+ .sequence = cogl_frame_info_get_sequence (frame_info),
+ .gpu_rendering_duration_ns =
+ cogl_frame_info_get_rendering_duration_ns (frame_info),
+ .cpu_time_before_buffer_swap_us =
+ cogl_frame_info_get_time_before_buffer_swap_us (frame_info),
+ };
+ clutter_stage_view_notify_presented (view, &clutter_frame_info);
+ }
+}
+
+static void
+clutter_stage_view_cogl_dispose (GObject *object)
+{
+ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (object);
+ ClutterStageViewCoglPrivate *view_priv =
+ clutter_stage_view_cogl_get_instance_private (view_cogl);
+ ClutterStageView *view = CLUTTER_STAGE_VIEW (view_cogl);
+
+ g_clear_handle_id (&view_priv->notify_presented_handle_id, g_source_remove);
+ g_clear_pointer (&view_priv->damage_history, clutter_damage_history_free);
+
+ if (view_priv->frame_cb_closure)
+ {
+ CoglFramebuffer *framebuffer;
+
+ framebuffer = clutter_stage_view_get_onscreen (view);
+ cogl_onscreen_remove_frame_callback (COGL_ONSCREEN (framebuffer),
+ view_priv->frame_cb_closure);
+ view_priv->frame_cb_closure = NULL;
+ }
+
+ G_OBJECT_CLASS (clutter_stage_view_cogl_parent_class)->dispose (object);
+}
+
+static void
+clutter_stage_view_cogl_constructed (GObject *object)
+{
+ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (object);
+ ClutterStageViewCoglPrivate *view_priv =
+ clutter_stage_view_cogl_get_instance_private (view_cogl);
+ ClutterStageView *view = CLUTTER_STAGE_VIEW (view_cogl);
+ CoglFramebuffer *framebuffer;
+
+ framebuffer = clutter_stage_view_get_onscreen (view);
+ if (framebuffer && COGL_IS_ONSCREEN (framebuffer))
+ {
+ view_priv->frame_cb_closure =
+ cogl_onscreen_add_frame_callback (COGL_ONSCREEN (framebuffer),
+ frame_cb,
+ view,
+ NULL);
+ }
+
+ G_OBJECT_CLASS (clutter_stage_view_cogl_parent_class)->constructed (object);
+}
+
+static void
+clutter_stage_view_cogl_init (ClutterStageViewCogl *view_cogl)
+{
+ ClutterStageViewCoglPrivate *view_priv =
+ clutter_stage_view_cogl_get_instance_private (view_cogl);
+
+ view_priv->damage_history = clutter_damage_history_new ();
+}
+
+static void
+clutter_stage_view_cogl_class_init (ClutterStageViewCoglClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = clutter_stage_view_cogl_constructed;
+ object_class->dispose = clutter_stage_view_cogl_dispose;
+}
diff --git a/src/backends/native/meta-stage-native.h b/src/backends/native/meta-stage-native.h
index f33743f3e..37fbc100b 100644
--- a/src/backends/native/meta-stage-native.h
+++ b/src/backends/native/meta-stage-native.h
@@ -25,6 +25,7 @@
#ifndef META_STAGE_NATIVE_H
#define META_STAGE_NATIVE_H
+#include "backends/meta-stage-impl-private.h"
#include "clutter/clutter-mutter.h"
#define META_TYPE_STAGE_NATIVE (meta_stage_native_get_type ())
diff --git a/src/meson.build b/src/meson.build
index 36a339960..9968a277f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -233,6 +233,8 @@ mutter_sources = [
'backends/meta-settings.c',
'backends/meta-settings-private.h',
'backends/meta-stage.c',
+ 'backends/meta-stage-impl.c',
+ 'backends/meta-stage-impl-private.h',
'backends/meta-stage-private.h',
'backends/meta-viewport-info.c',
'backends/meta-viewport-info.h',