From 54dc10f890919b7eab6fc893776fa6cef5aefdc2 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 1 Aug 2016 02:44:57 +0200 Subject: [PATCH] clutter: Add infrastructure to render ClutterStageViews to offscreen The offscreen is given through the ::back-buffer property, the ClutterStageView will set up the the CoglPipeline used to render it back to the "onscreen" framebuffer. The pipeline can be altered through the setup_pipeline() vfunc, so ClutterStageView implementations can alter the default behavior of blitting from offscreen to onscreen with no transformations. All getters of "the framebuffer" that were expecting to get an onscreen have been updated to call the right clutter_stage_view_get_onscreen() function. https://bugzilla.gnome.org/show_bug.cgi?id=745079 --- clutter/clutter/clutter-stage-view.c | 168 ++++++++++++++++++++- clutter/clutter/clutter-stage-view.h | 18 +++ clutter/clutter/cogl/clutter-stage-cogl.c | 2 +- src/backends/native/meta-renderer-native.c | 8 +- src/backends/native/meta-stage-native.c | 2 +- 5 files changed, 191 insertions(+), 7 deletions(-) diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index 8eb17b842..a8b1d093b 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -27,6 +27,7 @@ enum PROP_LAYOUT, PROP_FRAMEBUFFER, + PROP_OFFSCREEN, PROP_LAST }; @@ -37,6 +38,10 @@ typedef struct _ClutterStageViewPrivate { cairo_rectangle_int_t layout; CoglFramebuffer *framebuffer; + + CoglOffscreen *offscreen; + CoglPipeline *pipeline; + guint dirty_viewport : 1; guint dirty_projection : 1; } ClutterStageViewPrivate; @@ -55,6 +60,18 @@ clutter_stage_view_get_layout (ClutterStageView *view, CoglFramebuffer * clutter_stage_view_get_framebuffer (ClutterStageView *view) +{ + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + + if (priv->offscreen) + return priv->offscreen; + else + return priv->framebuffer; +} + +CoglFramebuffer * +clutter_stage_view_get_onscreen (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); @@ -62,6 +79,108 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view) return priv->framebuffer; } +static void +clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view) +{ + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + ClutterStageViewClass *view_class = + CLUTTER_STAGE_VIEW_GET_CLASS (view); + + g_assert (priv->offscreen != NULL); + + if (priv->pipeline) + return; + + priv->pipeline = + cogl_pipeline_new (cogl_framebuffer_get_context (priv->offscreen)); + cogl_pipeline_set_layer_filters (priv->pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + cogl_pipeline_set_layer_texture (priv->pipeline, 0, + cogl_offscreen_get_texture (priv->offscreen)); + cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); + + if (view_class->setup_offscreen_blit_pipeline) + view_class->setup_offscreen_blit_pipeline (view, priv->pipeline); +} + +void +clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) +{ + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + + g_clear_pointer (&priv->pipeline, cogl_object_unref); +} + +static void +transform_rect_to_onscreen (ClutterStageView *view, + const cairo_rectangle_t *rect, + cairo_rectangle_t *rect_out) +{ + float x1, y1, x2, y2; + + x1 = rect->x; + y1 = rect->y; + clutter_stage_view_transform_to_onscreen (view, &x1, &y1); + + x2 = rect->x + rect->width; + y2 = rect->y + rect->height; + clutter_stage_view_transform_to_onscreen (view, &x2, &y2); + + *rect_out = (cairo_rectangle_t) { + .x = MIN (x1, x2), + .y = MIN (y1, y2), + .width = ABS (x2 - x1), + .height = ABS (y2 - y1) + }; +} + +void +clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *rect) +{ + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + cairo_rectangle_t texture_rect, onscreen_rect; + CoglMatrix matrix; + + clutter_stage_view_ensure_offscreen_blit_pipeline (view); + cogl_framebuffer_push_matrix (priv->framebuffer); + + /* Set transform so 0,0 is on the top left corner and 1,1 on + * the bottom right corner. + */ + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, -1, 1, 0); + cogl_matrix_scale (&matrix, 2, -2, 0); + cogl_framebuffer_set_projection_matrix (priv->framebuffer, &matrix); + + texture_rect = (cairo_rectangle_t) { + .x = (double) rect->x / cogl_framebuffer_get_width (priv->offscreen), + .y = (double) rect->y / cogl_framebuffer_get_height (priv->offscreen), + .width = (double) rect->width / cogl_framebuffer_get_width (priv->offscreen), + .height = (double) rect->height / cogl_framebuffer_get_height (priv->offscreen) + }; + + transform_rect_to_onscreen (view, &texture_rect, &onscreen_rect); + + cogl_framebuffer_draw_textured_rectangle (priv->framebuffer, + priv->pipeline, + onscreen_rect.x, + onscreen_rect.y, + onscreen_rect.x + onscreen_rect.width, + onscreen_rect.y + onscreen_rect.height, + texture_rect.x, + texture_rect.y, + texture_rect.x + texture_rect.width, + texture_rect.y + texture_rect.height); + + cogl_framebuffer_pop_matrix (priv->framebuffer); +} + gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view) { @@ -100,6 +219,27 @@ clutter_stage_view_set_dirty_projection (ClutterStageView *view, priv->dirty_projection = dirty; } +void +clutter_stage_view_transform_to_onscreen (ClutterStageView *view, + gfloat *x, + gfloat *y) +{ + ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view); + gfloat z = 0, w = 1; + CoglMatrix matrix; + + view_class->get_offscreen_transformation_matrix (view, &matrix); + cogl_matrix_get_inverse (&matrix, &matrix); + cogl_matrix_transform_point (&matrix, x, y, &z, &w); +} + +static void +clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *view, + CoglMatrix *matrix) +{ + cogl_matrix_init_identity (matrix); +} + static void clutter_stage_view_get_property (GObject *object, guint prop_id, @@ -118,6 +258,11 @@ clutter_stage_view_get_property (GObject *object, case PROP_FRAMEBUFFER: g_value_set_boxed (value, priv->framebuffer); break; + case PROP_OFFSCREEN: + g_value_set_boxed (value, priv->offscreen); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } @@ -141,6 +286,11 @@ clutter_stage_view_set_property (GObject *object, case PROP_FRAMEBUFFER: priv->framebuffer = g_value_dup_boxed (value); break; + case PROP_OFFSCREEN: + priv->offscreen = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } @@ -152,6 +302,10 @@ clutter_stage_view_dispose (GObject *object) clutter_stage_view_get_instance_private (view); g_clear_pointer (&priv->framebuffer, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->pipeline, cogl_object_unref); + + G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object); } static void @@ -169,6 +323,9 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + klass->get_offscreen_transformation_matrix = + clutter_stage_default_get_offscreen_transformation_matrix; + object_class->get_property = clutter_stage_view_get_property; object_class->set_property = clutter_stage_view_set_property; object_class->dispose = clutter_stage_view_dispose; @@ -184,10 +341,19 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) obj_props[PROP_FRAMEBUFFER] = g_param_spec_boxed ("framebuffer", "View framebuffer", - "The framebuffer of the view", + "The front buffer of the view", COGL_TYPE_HANDLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + obj_props[PROP_OFFSCREEN] = + g_param_spec_boxed ("offscreen", + "Offscreen buffer", + "Framebuffer used as intermediate buffer", + COGL_TYPE_HANDLE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, PROP_LAST, obj_props); } diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h index e9fe3a043..e820e9646 100644 --- a/clutter/clutter/clutter-stage-view.h +++ b/clutter/clutter/clutter-stage-view.h @@ -33,6 +33,12 @@ G_DECLARE_DERIVABLE_TYPE (ClutterStageView, clutter_stage_view, struct _ClutterStageViewClass { GObjectClass parent_class; + + void (* setup_offscreen_blit_pipeline) (ClutterStageView *view, + CoglPipeline *pipeline); + + void (* get_offscreen_transformation_matrix) (ClutterStageView *view, + CoglMatrix *matrix); }; CLUTTER_AVAILABLE_IN_MUTTER @@ -41,6 +47,18 @@ void clutter_stage_view_get_layout (ClutterStageView *view, CLUTTER_AVAILABLE_IN_MUTTER CoglFramebuffer *clutter_stage_view_get_framebuffer (ClutterStageView *view); +CLUTTER_AVAILABLE_IN_MUTTER +CoglFramebuffer *clutter_stage_view_get_onscreen (ClutterStageView *view); +CLUTTER_AVAILABLE_IN_MUTTER +void clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view); + +CLUTTER_AVAILABLE_IN_MUTTER +void clutter_stage_view_transform_to_onscreen (ClutterStageView *view, + gfloat *x, + gfloat *y); + +void clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *clip); gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c index 58979f11b..6bf2f7b0a 100644 --- a/clutter/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/clutter/cogl/clutter-stage-cogl.c @@ -357,7 +357,7 @@ swap_framebuffer (ClutterStageWindow *stage_window, cairo_rectangle_int_t *swap_region, gboolean swap_with_damage) { - CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); + CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); int damage[4], ndamage; damage[0] = swap_region->x; diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 895f8507c..bdcfc795a 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -379,7 +379,7 @@ on_crtc_flipped (GClosure *closure, { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); CoglFramebuffer *framebuffer = - clutter_stage_view_get_framebuffer (stage_view); + clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; @@ -397,7 +397,7 @@ flip_closure_destroyed (MetaRendererView *view) { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); CoglFramebuffer *framebuffer = - clutter_stage_view_get_framebuffer (stage_view); + clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; @@ -828,7 +828,7 @@ meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native) { ClutterStageView *stage_view = l->data; CoglFramebuffer *framebuffer = - clutter_stage_view_get_framebuffer (stage_view); + clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; @@ -861,7 +861,7 @@ meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native, MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); CoglFramebuffer *framebuffer = - clutter_stage_view_get_framebuffer (stage_view); + clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c index 617a7076d..82ede47cf 100644 --- a/src/backends/native/meta-stage-native.c +++ b/src/backends/native/meta-stage-native.c @@ -127,7 +127,7 @@ ensure_frame_callback (MetaStageNative *stage_native, if (closure) return; - framebuffer = clutter_stage_view_get_framebuffer (stage_view); + framebuffer = clutter_stage_view_get_onscreen (stage_view); onscreen = COGL_ONSCREEN (framebuffer); closure = cogl_onscreen_add_frame_callback (onscreen, frame_cb,