diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2021-05-10 13:27:39 +0200 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2021-07-16 19:08:06 +0200 |
commit | 8f839b2f9ca34932eceaf4f85db607bfe005898f (patch) | |
tree | 954141fef704a7f7a23e8aa11fcc7ad62d25c3f3 /src | |
parent | 0ac257212e7a26e3346b24930d5356bd7982b5b8 (diff) | |
download | mutter-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.h | 2 | ||||
-rw-r--r-- | src/backends/meta-stage-impl-private.h | 88 | ||||
-rw-r--r-- | src/backends/meta-stage-impl.c | 907 | ||||
-rw-r--r-- | src/backends/native/meta-stage-native.h | 1 | ||||
-rw-r--r-- | src/meson.build | 2 |
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', |