From 8f839b2f9ca34932eceaf4f85db607bfe005898f Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 10 May 2021 13:27:39 +0200 Subject: clutter: Move ClutterStageCogl[View] code to src/backends This is now MetaStageImpl in backend code. Part-of: --- clutter/clutter/cogl/clutter-stage-cogl.c | 918 ------------------------------ clutter/clutter/cogl/clutter-stage-cogl.h | 65 --- clutter/clutter/meson.build | 8 +- src/backends/meta-renderer-view.h | 2 +- src/backends/meta-stage-impl-private.h | 88 +++ src/backends/meta-stage-impl.c | 907 +++++++++++++++++++++++++++++ src/backends/native/meta-stage-native.h | 1 + src/meson.build | 2 + 8 files changed, 1001 insertions(+), 990 deletions(-) delete mode 100644 clutter/clutter/cogl/clutter-stage-cogl.c delete mode 100644 clutter/clutter/cogl/clutter-stage-cogl.h create mode 100644 src/backends/meta-stage-impl-private.h create mode 100644 src/backends/meta-stage-impl.c diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c deleted file mode 100644 index 6c4ddbfc0..000000000 --- a/clutter/clutter/cogl/clutter-stage-cogl.c +++ /dev/null @@ -1,918 +0,0 @@ -/* - * 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 . - - * Authors: - * Matthew Allum - * Robert Bragg - * Neil Roberts - * Emmanuele Bassi - */ - -#include "clutter-build-config.h" - -#include "clutter-config.h" - -#include "clutter-stage-cogl.h" - -#include -#include - -#include "clutter-actor-private.h" -#include "clutter-backend-private.h" -#include "clutter-damage-history.h" -#include "clutter-debug.h" -#include "clutter-event.h" -#include "clutter-enum-types.h" -#include "clutter-feature.h" -#include "clutter-frame.h" -#include "clutter-main.h" -#include "clutter-private.h" -#include "clutter-stage-private.h" -#include "clutter-stage-view-private.h" -#include "cogl.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; - 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; - } - } - - use_clipped_redraw = - use_clipped_redraw && - !(clutter_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 (clutter_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 (clutter_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 (clutter_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/clutter/clutter/cogl/clutter-stage-cogl.h b/clutter/clutter/cogl/clutter-stage-cogl.h deleted file mode 100644 index 11aeaefe1..000000000 --- a/clutter/clutter/cogl/clutter-stage-cogl.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef __CLUTTER_STAGE_COGL_H__ -#define __CLUTTER_STAGE_COGL_H__ - -#include -#include -#include - -#include "clutter/clutter-stage-window.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 /* __CLUTTER_STAGE_COGL_H__ */ diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build index ad5895fae..35a0afefc 100644 --- a/clutter/clutter/meson.build +++ b/clutter/clutter/meson.build @@ -232,13 +232,9 @@ clutter_deprecated_headers = [ ] clutter_backend_sources = [] -clutter_backend_nonintrospected_sources = [ - 'cogl/clutter-stage-cogl.c', -] +clutter_backend_nonintrospected_sources = [] clutter_backend_headers = [] -clutter_backend_private_headers = [ - 'cogl/clutter-stage-cogl.h', -] +clutter_backend_private_headers = [] if have_x11 clutter_x11_sources = [ 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 . + * + * Written by: + * Matthew Allum + * Robert Bragg + * Neil Roberts + * Emmanuele Bassi + * + */ + +#ifndef META_STAGE_IMPL_PRIVATE_H +#define META_STAGE_IMPL_PRIVATE_H + +#include + +#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 . + + * Authors: + * Matthew Allum + * Robert Bragg + * Neil Roberts + * Emmanuele Bassi + */ + +#include "config.h" + +#include "backends/meta-stage-impl-private.h" + +#include +#include + +#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', -- cgit v1.2.1