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,