diff --git a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch index 7d150d2..38a1213 100644 --- a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch +++ b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch @@ -1,4 +1,4 @@ -From 6108e0175932f74733c46ebe13db06998f26d4ba Mon Sep 17 00:00:00 2001 +From 8d7356fd7439f94f163438d55f2b2d3d918de96d Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Thu, 8 May 2014 18:44:15 -0400 Subject: [PATCH] Add support for quad-buffer stereo @@ -21,7 +21,7 @@ texture_from_pixmap. src/compositor/compositor-private.h | 9 ++ src/compositor/compositor.c | 125 +++++++++++++++ src/compositor/meta-shaped-texture-private.h | 5 +- - src/compositor/meta-shaped-texture.c | 85 +++++++++- + src/compositor/meta-shaped-texture.c | 84 +++++++++- src/compositor/meta-surface-actor-wayland.c | 2 +- src/compositor/meta-surface-actor-x11.c | 54 ++++++- src/compositor/meta-surface-actor-x11.h | 5 + @@ -32,7 +32,7 @@ texture_from_pixmap. src/core/stereo.h | 28 ++++ src/meson.build | 2 + src/wayland/meta-wayland-surface.c | 2 +- - 14 files changed, 482 insertions(+), 20 deletions(-) + 14 files changed, 481 insertions(+), 20 deletions(-) create mode 100644 src/core/stereo.c create mode 100644 src/core/stereo.h @@ -265,7 +265,7 @@ index a86a2bff0..d0efdd4dc 100644 gboolean is_y_inverted); void meta_shaped_texture_set_snippet (MetaShapedTexture *stex, diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c -index d64e214e5..332b4c814 100644 +index d64e214e5..e77a32109 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -88,8 +88,10 @@ struct _MetaShapedTexture @@ -287,7 +287,7 @@ index d64e214e5..332b4c814 100644 stex->texture = NULL; stex->mask_texture = NULL; -@@ -297,7 +300,11 @@ meta_shaped_texture_dispose (GObject *object) +@@ -297,6 +300,9 @@ meta_shaped_texture_dispose (GObject *object) meta_texture_tower_free (stex->paint_tower); stex->paint_tower = NULL; @@ -295,11 +295,9 @@ index d64e214e5..332b4c814 100644 + g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free); + g_clear_pointer (&stex->texture, cogl_object_unref); -+ g_clear_pointer (&stex->texture_right, cogl_object_unref); g_clear_pointer (&stex->opaque_region, cairo_region_destroy); - meta_shaped_texture_set_mask_texture (stex, NULL); -@@ -507,8 +514,9 @@ paint_clipped_rectangle (MetaShapedTexture *stex, +@@ -507,8 +513,9 @@ paint_clipped_rectangle (MetaShapedTexture *stex, } static void @@ -311,7 +309,7 @@ index d64e214e5..332b4c814 100644 { int width, height; -@@ -516,8 +524,11 @@ set_cogl_texture (MetaShapedTexture *stex, +@@ -516,8 +523,11 @@ set_cogl_texture (MetaShapedTexture *stex, if (stex->texture) cogl_object_unref (stex->texture); @@ -323,7 +321,7 @@ index d64e214e5..332b4c814 100644 if (cogl_tex != NULL) { -@@ -531,6 +542,9 @@ set_cogl_texture (MetaShapedTexture *stex, +@@ -531,6 +541,9 @@ set_cogl_texture (MetaShapedTexture *stex, height = 0; } @@ -333,7 +331,7 @@ index d64e214e5..332b4c814 100644 if (stex->tex_width != width || stex->tex_height != height) { -@@ -544,8 +558,23 @@ set_cogl_texture (MetaShapedTexture *stex, +@@ -544,8 +557,23 @@ set_cogl_texture (MetaShapedTexture *stex, * previous buffer. We only queue a redraw in response to surface * damage. */ @@ -358,7 +356,7 @@ index d64e214e5..332b4c814 100644 } static gboolean -@@ -779,7 +808,9 @@ meta_shaped_texture_paint (ClutterActor *actor) +@@ -779,7 +807,9 @@ meta_shaped_texture_paint (ClutterActor *actor) { MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor); CoglTexture *paint_tex; @@ -368,7 +366,7 @@ index d64e214e5..332b4c814 100644 if (!stex->texture) return; -@@ -841,7 +872,32 @@ meta_shaped_texture_paint (ClutterActor *actor) +@@ -841,7 +871,32 @@ meta_shaped_texture_paint (ClutterActor *actor) return; fb = cogl_get_draw_framebuffer (); @@ -402,7 +400,7 @@ index d64e214e5..332b4c814 100644 } static void -@@ -915,6 +971,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, +@@ -915,6 +970,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, stex->create_mipmaps = create_mipmaps; base_texture = create_mipmaps ? stex->texture : NULL; meta_texture_tower_set_base_texture (stex->paint_tower, base_texture); @@ -415,7 +413,7 @@ index d64e214e5..332b4c814 100644 } } -@@ -1046,6 +1108,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, +@@ -1046,6 +1107,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, clip.y, clip.width, clip.height); @@ -428,7 +426,7 @@ index d64e214e5..332b4c814 100644 stex->prev_invalidation = stex->last_invalidation; stex->last_invalidation = g_get_monotonic_time (); -@@ -1092,17 +1160,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, +@@ -1092,17 +1159,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, } /** @@ -903,5 +901,5 @@ index da0acfcbb..ddad1a45c 100644 meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted); g_clear_pointer (&snippet, cogl_object_unref); -- -2.28.0 +2.25.1 diff --git a/SOURCES/shadow-buffer-tile-damage.patch b/SOURCES/shadow-buffer-tile-damage.patch new file mode 100644 index 0000000..ad19f8e --- /dev/null +++ b/SOURCES/shadow-buffer-tile-damage.patch @@ -0,0 +1,3248 @@ +From e42c4e83283787062fb446a2aa698f227fe2db5f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 29 Apr 2020 16:26:52 +0200 +Subject: [PATCH 01/20] cogl/dma-buf-handle: Pass more metadata to handle + constructor + +Could be useful would one want to mmap the dmabuf and deal with its +content manually in CPU space. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit e656d0caf01d8b012d2b458676e5658c540525dc) +--- + cogl/cogl/cogl-dma-buf-handle.c | 45 +++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 46 +++++++++++++++++++++- + src/backends/native/meta-renderer-native.c | 14 +++++-- + 3 files changed, 101 insertions(+), 4 deletions(-) + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +index 4a8f709f2c..d8b4e57c55 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.c ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -40,6 +40,11 @@ struct _CoglDmaBufHandle + { + CoglFramebuffer *framebuffer; + int dmabuf_fd; ++ int width; ++ int height; ++ int stride; ++ int offset; ++ int bpp; + gpointer user_data; + GDestroyNotify destroy_func; + }; +@@ -47,6 +52,11 @@ struct _CoglDmaBufHandle + CoglDmaBufHandle * + cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + int dmabuf_fd, ++ int width, ++ int height, ++ int stride, ++ int offset, ++ int bpp, + gpointer user_data, + GDestroyNotify destroy_func) + { +@@ -61,6 +71,12 @@ cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + dmabuf_handle->user_data = user_data; + dmabuf_handle->destroy_func = destroy_func; + ++ dmabuf_handle->width = width; ++ dmabuf_handle->height = height; ++ dmabuf_handle->stride = stride; ++ dmabuf_handle->offset = offset; ++ dmabuf_handle->bpp = bpp; ++ + return dmabuf_handle; + } + +@@ -92,3 +108,32 @@ cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle) + return dmabuf_handle->dmabuf_fd; + } + ++int ++cogl_dma_buf_handle_get_width (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->width; ++} ++ ++int ++cogl_dma_buf_handle_get_height (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->height; ++} ++ ++int ++cogl_dma_buf_handle_get_stride (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->stride; ++} ++ ++int ++cogl_dma_buf_handle_get_offset (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->offset; ++} ++ ++int ++cogl_dma_buf_handle_get_bpp (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->bpp; ++} +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +index 25b9b0ccb5..f64a20678d 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.h ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -46,7 +46,12 @@ + CoglDmaBufHandle * + cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + int dmabuf_fd, +- gpointer data, ++ int width, ++ int height, ++ int stride, ++ int offset, ++ int bpp, ++ gpointer user_data, + GDestroyNotify destroy_func); + + /** +@@ -79,5 +84,44 @@ cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle); + int + cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle); + ++/** ++ * cogl_dmabuf_handle_get_width: (skip) ++ * ++ * Returns: the buffer width ++ */ ++int ++cogl_dma_buf_handle_get_width (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_height: (skip) ++ * ++ * Returns: the buffer height ++ */ ++int ++cogl_dma_buf_handle_get_height (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_stride: (skip) ++ * ++ * Returns: the buffer stride ++ */ ++int ++cogl_dma_buf_handle_get_stride (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_offset: (skip) ++ * ++ * Returns: the buffer offset ++ */ ++int ++cogl_dma_buf_handle_get_offset (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dmabuf_handle_get_bpp: (skip) ++ * ++ * Returns: the number of bytes per pixel ++ */ ++int ++cogl_dma_buf_handle_get_bpp (CoglDmaBufHandle *dmabuf_handle); + + #endif /* __COGL_DMA_BUF_HANDLE_H__ */ +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 25833b6cf6..c14cb5acda 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -2641,6 +2641,9 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + CoglFramebuffer *dmabuf_fb; + CoglDmaBufHandle *dmabuf_handle; + struct gbm_bo *new_bo; ++ int stride; ++ int offset; ++ int bpp; + int dmabuf_fd = -1; + + new_bo = gbm_bo_create (renderer_gpu_data->gbm.device, +@@ -2664,11 +2667,14 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + return NULL; + } + ++ stride = gbm_bo_get_stride (new_bo); ++ offset = gbm_bo_get_offset (new_bo, 0); ++ bpp = 4; + dmabuf_fb = create_dma_buf_framebuffer (renderer_native, + dmabuf_fd, + width, height, +- gbm_bo_get_stride (new_bo), +- gbm_bo_get_offset (new_bo, 0), ++ stride, ++ offset, + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_XRGB8888, + error); +@@ -2677,7 +2683,9 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + return NULL; + + dmabuf_handle = +- cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, ++ cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, ++ width, height, stride, offset, bpp, ++ new_bo, + (GDestroyNotify) gbm_bo_destroy); + cogl_object_unref (dmabuf_fb); + return dmabuf_handle; +-- +2.28.0 + + +From 2270f6dcf7b1e70386f5b4242f92bf5735bb88ba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 10:42:30 +0200 +Subject: [PATCH 02/20] clutter/stage-view: Add name property + +Will be used for logging to identify what view a log entry concerns. For +the native and nested backend this is the name of the output the CRTC is +assigned to drive; for X11 it's just "X11 screen", and for the legacy +"X11 screen" emulation mode of the nested backend it's called "legacy +nested". + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit c86367199febdee10ecd7ba24c69b6dda52cb896) +--- + clutter/clutter/clutter-stage-view.c | 18 ++++++++++++++++++ + src/backends/native/meta-renderer-native.c | 3 +++ + .../x11/nested/meta-renderer-x11-nested.c | 4 ++++ + 3 files changed, 25 insertions(+) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 0fad6fc446..6b543b5d51 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -27,6 +27,7 @@ enum + { + PROP_0, + ++ PROP_NAME, + PROP_LAYOUT, + PROP_FRAMEBUFFER, + PROP_OFFSCREEN, +@@ -40,6 +41,8 @@ static GParamSpec *obj_props[PROP_LAST]; + + typedef struct _ClutterStageViewPrivate + { ++ char *name; ++ + cairo_rectangle_int_t layout; + float scale; + CoglFramebuffer *framebuffer; +@@ -339,6 +342,9 @@ clutter_stage_view_get_property (GObject *object, + + switch (prop_id) + { ++ case PROP_NAME: ++ g_value_set_string (value, priv->name); ++ break; + case PROP_LAYOUT: + g_value_set_boxed (value, &priv->layout); + break; +@@ -372,6 +378,9 @@ clutter_stage_view_set_property (GObject *object, + + switch (prop_id) + { ++ case PROP_NAME: ++ priv->name = g_value_dup_string (value); ++ break; + case PROP_LAYOUT: + layout = g_value_get_boxed (value); + priv->layout = *layout; +@@ -414,6 +423,7 @@ clutter_stage_view_dispose (GObject *object) + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + ++ g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); + g_clear_pointer (&priv->shadowfb, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); +@@ -446,6 +456,14 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + object_class->set_property = clutter_stage_view_set_property; + object_class->dispose = clutter_stage_view_dispose; + ++ obj_props[PROP_NAME] = ++ g_param_spec_string ("name", ++ "Name", ++ "Name of view", ++ NULL, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS); + obj_props[PROP_LAYOUT] = + g_param_spec_boxed ("layout", + "View layout", +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index c14cb5acda..d3fb5b3c55 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3679,6 +3679,7 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + CoglContext *cogl_context = + cogl_context_from_renderer_native (renderer_native); + CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); ++ MetaMonitor *monitor; + CoglDisplayEGL *cogl_display_egl; + CoglOnscreenEGL *onscreen_egl; + MetaMonitorTransform view_transform; +@@ -3742,7 +3743,9 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + g_error ("Failed to allocate shadow buffer texture: %s", error->message); + } + ++ monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; + view = g_object_new (META_TYPE_RENDERER_VIEW, ++ "name", meta_monitor_get_connector (monitor), + "layout", &logical_monitor->rect, + "scale", scale, + "framebuffer", onscreen, +diff --git a/src/backends/x11/nested/meta-renderer-x11-nested.c b/src/backends/x11/nested/meta-renderer-x11-nested.c +index 5000bf3579..f3a5547dbb 100644 +--- a/src/backends/x11/nested/meta-renderer-x11-nested.c ++++ b/src/backends/x11/nested/meta-renderer-x11-nested.c +@@ -163,6 +163,7 @@ meta_renderer_x11_nested_ensure_legacy_view (MetaRendererX11Nested *renderer_x11 + .height = height + }; + legacy_view = g_object_new (META_TYPE_RENDERER_VIEW, ++ "name", "legacy nested", + "layout", &view_layout, + "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), + NULL); +@@ -179,6 +180,7 @@ meta_renderer_x11_nested_create_view (MetaRenderer *renderer, + meta_backend_get_monitor_manager (backend); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); ++ MetaMonitor *monitor; + MetaMonitorTransform view_transform; + float view_scale; + int width, height; +@@ -212,7 +214,9 @@ meta_renderer_x11_nested_create_view (MetaRenderer *renderer, + else + offscreen = NULL; + ++ monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; + return g_object_new (META_TYPE_RENDERER_VIEW, ++ "name", meta_monitor_get_connector (monitor), + "layout", &logical_monitor->rect, + "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), + "offscreen", COGL_FRAMEBUFFER (offscreen), +-- +2.28.0 + + +From 6716fde14c5b1a00a02a80b46db67d3f236a87c1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 17:53:30 +0200 +Subject: [PATCH 03/20] renderer-native: Move shadow fb construction to the + stage view + +The stage view will need a more involved approach to shadow buffers, in +order to implement things such double buffered shadow buffers and damage +detection. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 3ab89be574f0e02dc67e1b1f538bb24f94612bcf) +--- + clutter/clutter/clutter-stage-view.c | 115 ++++++++++++++++++--- + src/backends/native/meta-renderer-native.c | 25 +---- + 2 files changed, 106 insertions(+), 34 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 6b543b5d51..db0067297c 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -31,7 +31,7 @@ enum + PROP_LAYOUT, + PROP_FRAMEBUFFER, + PROP_OFFSCREEN, +- PROP_SHADOWFB, ++ PROP_USE_SHADOWFB, + PROP_SCALE, + + PROP_LAST +@@ -50,6 +50,7 @@ typedef struct _ClutterStageViewPrivate + CoglOffscreen *offscreen; + CoglPipeline *offscreen_pipeline; + ++ gboolean use_shadowfb; + CoglOffscreen *shadowfb; + CoglPipeline *shadowfb_pipeline; + +@@ -206,6 +207,80 @@ clutter_stage_view_copy_to_framebuffer (ClutterStageView *view, + cogl_framebuffer_pop_matrix (dst_framebuffer); + } + ++static CoglOffscreen * ++create_offscreen_framebuffer (CoglContext *context, ++ int width, ++ int height, ++ GError **error) ++{ ++ CoglOffscreen *framebuffer; ++ CoglTexture2D *texture; ++ ++ texture = cogl_texture_2d_new_with_size (context, width, height); ++ cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), ++ FALSE); ++ ++ if (!cogl_texture_allocate (COGL_TEXTURE (texture), error)) ++ { ++ cogl_object_unref (texture); ++ return FALSE; ++ } ++ ++ framebuffer = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture)); ++ cogl_object_unref (texture); ++ if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (framebuffer), error)) ++ { ++ cogl_object_unref (framebuffer); ++ return FALSE; ++ } ++ ++ return framebuffer; ++} ++ ++static gboolean ++init_offscreen_shadowfb (ClutterStageView *view, ++ CoglContext *cogl_context, ++ int width, ++ int height, ++ GError **error) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglOffscreen *offscreen; ++ ++ offscreen = create_offscreen_framebuffer (cogl_context, width, height, error); ++ if (!offscreen) ++ return FALSE; ++ ++ priv->shadowfb = offscreen; ++ return TRUE; ++} ++ ++static void ++init_shadowfb (ClutterStageView *view) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ g_autoptr (GError) error = NULL; ++ int width; ++ int height; ++ CoglContext *cogl_context; ++ ++ width = cogl_framebuffer_get_width (priv->framebuffer); ++ height = cogl_framebuffer_get_height (priv->framebuffer); ++ cogl_context = cogl_framebuffer_get_context (priv->framebuffer); ++ ++ if (!init_offscreen_shadowfb (view, cogl_context, width, height, &error)) ++ { ++ g_warning ("Failed to initialize single buffered shadow fb for %s: %s", ++ priv->name, error->message); ++ } ++ else ++ { ++ g_message ("Initialized single buffered shadow fb for %s", priv->name); ++ } ++} ++ + void + clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *rect) +@@ -354,8 +429,8 @@ clutter_stage_view_get_property (GObject *object, + case PROP_OFFSCREEN: + g_value_set_boxed (value, priv->offscreen); + break; +- case PROP_SHADOWFB: +- g_value_set_boxed (value, priv->shadowfb); ++ case PROP_USE_SHADOWFB: ++ g_value_set_boolean (value, priv->use_shadowfb); + break; + case PROP_SCALE: + g_value_set_float (value, priv->scale); +@@ -405,8 +480,8 @@ clutter_stage_view_set_property (GObject *object, + case PROP_OFFSCREEN: + priv->offscreen = g_value_dup_boxed (value); + break; +- case PROP_SHADOWFB: +- priv->shadowfb = g_value_dup_boxed (value); ++ case PROP_USE_SHADOWFB: ++ priv->use_shadowfb = g_value_get_boolean (value); + break; + case PROP_SCALE: + priv->scale = g_value_get_float (value); +@@ -416,6 +491,19 @@ clutter_stage_view_set_property (GObject *object, + } + } + ++static void ++clutter_stage_view_constructed (GObject *object) ++{ ++ ClutterStageView *view = CLUTTER_STAGE_VIEW (object); ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->use_shadowfb) ++ init_shadowfb (view); ++ ++ G_OBJECT_CLASS (clutter_stage_view_parent_class)->constructed (object); ++} ++ + static void + clutter_stage_view_dispose (GObject *object) + { +@@ -454,6 +542,7 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + + object_class->get_property = clutter_stage_view_get_property; + object_class->set_property = clutter_stage_view_set_property; ++ object_class->constructed = clutter_stage_view_constructed; + object_class->dispose = clutter_stage_view_dispose; + + obj_props[PROP_NAME] = +@@ -491,14 +580,14 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + +- obj_props[PROP_SHADOWFB] = +- g_param_spec_boxed ("shadowfb", +- "Shadow framebuffer", +- "Framebuffer used as intermediate shadow buffer", +- COGL_TYPE_HANDLE, +- G_PARAM_READWRITE | +- G_PARAM_CONSTRUCT_ONLY | +- G_PARAM_STATIC_STRINGS); ++ obj_props[PROP_USE_SHADOWFB] = ++ g_param_spec_boolean ("use-shadowfb", ++ "Use shadowfb", ++ "Whether to use one or more shadow framebuffers", ++ FALSE, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS); + + obj_props[PROP_SCALE] = + g_param_spec_float ("scale", +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index d3fb5b3c55..463dddd3a7 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3685,7 +3685,7 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + MetaMonitorTransform view_transform; + CoglOnscreen *onscreen = NULL; + CoglOffscreen *offscreen = NULL; +- CoglOffscreen *shadowfb = NULL; ++ gboolean use_shadowfb; + float scale; + int width, height; + MetaRendererView *view; +@@ -3724,24 +3724,8 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + + } + +- if (should_force_shadow_fb (renderer_native, +- renderer_native->primary_gpu_kms)) +- { +- int shadow_width; +- int shadow_height; +- +- /* The shadowfb must be the same size as the on-screen framebuffer */ +- shadow_width = cogl_framebuffer_get_width (COGL_FRAMEBUFFER (onscreen)); +- shadow_height = cogl_framebuffer_get_height (COGL_FRAMEBUFFER (onscreen)); +- +- shadowfb = meta_renderer_native_create_offscreen (renderer_native, +- cogl_context, +- shadow_width, +- shadow_height, +- &error); +- if (!shadowfb) +- g_error ("Failed to allocate shadow buffer texture: %s", error->message); +- } ++ use_shadowfb = should_force_shadow_fb (renderer_native, ++ renderer_native->primary_gpu_kms); + + monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; + view = g_object_new (META_TYPE_RENDERER_VIEW, +@@ -3750,12 +3734,11 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + "scale", scale, + "framebuffer", onscreen, + "offscreen", offscreen, +- "shadowfb", shadowfb, ++ "use-shadowfb", use_shadowfb, + "logical-monitor", logical_monitor, + "transform", view_transform, + NULL); + g_clear_pointer (&offscreen, cogl_object_unref); +- g_clear_pointer (&shadowfb, cogl_object_unref); + + meta_onscreen_native_set_view (onscreen, view); + +-- +2.28.0 + + +From f79b37583e575d34edb4b7965cb0e48eb2736749 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 18:19:30 +0200 +Subject: [PATCH 04/20] clutter/stage-view: Move shadowfb struct fields into + anonymous struct + +With the aim to collect shadow buffer related things in one place, place +them in an anonymous struct. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 310ca695d90b48074a06327e87bd7e924f49cb7f) +--- + clutter/clutter/clutter-stage-view.c | 32 +++++++++++++++------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index db0067297c..9bbe158f36 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -51,8 +51,10 @@ typedef struct _ClutterStageViewPrivate + CoglPipeline *offscreen_pipeline; + + gboolean use_shadowfb; +- CoglOffscreen *shadowfb; +- CoglPipeline *shadowfb_pipeline; ++ struct { ++ CoglOffscreen *framebuffer; ++ CoglPipeline *pipeline; ++ } shadow; + + guint dirty_viewport : 1; + guint dirty_projection : 1; +@@ -86,8 +88,8 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view) + + if (priv->offscreen) + return priv->offscreen; +- else if (priv->shadowfb) +- return priv->shadowfb; ++ else if (priv->shadow.framebuffer) ++ return priv->shadow.framebuffer; + else + return priv->framebuffer; + } +@@ -153,11 +155,11 @@ clutter_stage_view_ensure_shadowfb_blit_pipeline (ClutterStageView *view) + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- if (priv->shadowfb_pipeline) ++ if (priv->shadow.pipeline) + return; + +- priv->shadowfb_pipeline = +- clutter_stage_view_create_framebuffer_pipeline (priv->shadowfb); ++ priv->shadow.pipeline = ++ clutter_stage_view_create_framebuffer_pipeline (priv->shadow.framebuffer); + } + + void +@@ -252,7 +254,7 @@ init_offscreen_shadowfb (ClutterStageView *view, + if (!offscreen) + return FALSE; + +- priv->shadowfb = offscreen; ++ priv->shadow.framebuffer = offscreen; + return TRUE; + } + +@@ -297,13 +299,13 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); + can_blit = cogl_matrix_is_identity (&matrix); + +- if (priv->shadowfb) ++ if (priv->shadow.framebuffer) + { + clutter_stage_view_copy_to_framebuffer (view, + rect, + priv->offscreen_pipeline, + priv->offscreen, +- priv->shadowfb, ++ priv->shadow.framebuffer, + can_blit); + } + else +@@ -317,13 +319,13 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + } + } + +- if (priv->shadowfb) ++ if (priv->shadow.framebuffer) + { + clutter_stage_view_ensure_shadowfb_blit_pipeline (view); + clutter_stage_view_copy_to_framebuffer (view, + rect, +- priv->shadowfb_pipeline, +- priv->shadowfb, ++ priv->shadow.pipeline, ++ priv->shadow.framebuffer, + priv->framebuffer, + TRUE); + } +@@ -513,10 +515,10 @@ clutter_stage_view_dispose (GObject *object) + + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); +- g_clear_pointer (&priv->shadowfb, cogl_object_unref); ++ g_clear_pointer (&priv->shadow.framebuffer, cogl_object_unref); ++ g_clear_pointer (&priv->shadow.pipeline, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); +- g_clear_pointer (&priv->shadowfb_pipeline, cogl_object_unref); + + G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object); + } +-- +2.28.0 + + +From 7bf71e7b5f39fcf34c4a636640636f9452b4b06c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 21:51:10 +0200 +Subject: [PATCH 05/20] clutter/stage-view: Move fb viewport and projection + setting to here + +The stage would fetch the front framebuffer and set the viewport and +projection matrix, but if we are going to more than one front buffer, +that won't work, so let the stage just pass the viewport and projection +matrix to the view and have the view deal with the framebuffer(s). + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit c79bcf0d7e35cf9e85864cf72ea53659a6b8d8a7) +--- + clutter/clutter/clutter-stage-view-private.h | 8 ++++++ + clutter/clutter/clutter-stage-view.c | 29 ++++++++++++++++++++ + clutter/clutter/clutter-stage.c | 16 ++++------- + 3 files changed, 42 insertions(+), 11 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index 89c42599fc..78aa37c9f4 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -28,10 +28,18 @@ gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, + gboolean dirty); + ++void clutter_stage_view_set_viewport (ClutterStageView *view, ++ float x, ++ float y, ++ float width, ++ float height); ++ + gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); + + void clutter_stage_view_set_dirty_projection (ClutterStageView *view, + gboolean dirty); + ++void clutter_stage_view_set_projection (ClutterStageView *view, ++ const CoglMatrix *matrix); + + #endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 9bbe158f36..4d8bbddc9d 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -359,6 +359,22 @@ clutter_stage_view_set_dirty_viewport (ClutterStageView *view, + priv->dirty_viewport = dirty; + } + ++void ++clutter_stage_view_set_viewport (ClutterStageView *view, ++ float x, ++ float y, ++ float width, ++ float height) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglFramebuffer *framebuffer; ++ ++ priv->dirty_viewport = FALSE; ++ framebuffer = clutter_stage_view_get_framebuffer (view); ++ cogl_framebuffer_set_viewport (framebuffer, x, y, width, height); ++} ++ + gboolean + clutter_stage_view_is_dirty_projection (ClutterStageView *view) + { +@@ -378,6 +394,19 @@ clutter_stage_view_set_dirty_projection (ClutterStageView *view, + priv->dirty_projection = dirty; + } + ++void ++clutter_stage_view_set_projection (ClutterStageView *view, ++ const CoglMatrix *matrix) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglFramebuffer *framebuffer; ++ ++ priv->dirty_projection = FALSE; ++ framebuffer = clutter_stage_view_get_framebuffer (view); ++ cogl_framebuffer_set_projection_matrix (framebuffer, matrix); ++} ++ + void + clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, + CoglMatrix *matrix) +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 34c4e0119a..4bde234dbf 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -3687,7 +3687,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + ClutterStageView *view) + { + ClutterStagePrivate *priv = stage->priv; +- CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); + + if (clutter_stage_view_is_dirty_viewport (view)) + { +@@ -3716,9 +3715,10 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + viewport_y = roundf (priv->viewport[1] * fb_scale - viewport_offset_y); + viewport_width = roundf (priv->viewport[2] * fb_scale); + viewport_height = roundf (priv->viewport[3] * fb_scale); +- cogl_framebuffer_set_viewport (fb, +- viewport_x, viewport_y, +- viewport_width, viewport_height); ++ ++ clutter_stage_view_set_viewport (view, ++ viewport_x, viewport_y, ++ viewport_width, viewport_height); + + perspective = priv->perspective; + +@@ -3751,16 +3751,10 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + z_2d, + priv->viewport[2], + priv->viewport[3]); +- +- clutter_stage_view_set_dirty_viewport (view, FALSE); + } + + if (clutter_stage_view_is_dirty_projection (view)) +- { +- cogl_framebuffer_set_projection_matrix (fb, &priv->projection); +- +- clutter_stage_view_set_dirty_projection (view, FALSE); +- } ++ clutter_stage_view_set_projection (view, &priv->projection); + } + + #undef _DEG_TO_RAD +-- +2.28.0 + + +From 0b345dc3a108f12ebc00e831692b43291c84cd07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Apr 2020 21:59:49 +0200 +Subject: [PATCH 06/20] clutter/stage-view: Change set_dirty..() API to + invalidate..() + +The manual "cleaning" of the viewport and projection state is removed, +and we only ever try to invalidate the state so that it'll be updated +next time. Change the API used to reflect this. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 3080ee672a366a3a52d9f43523c40e3afd08e874) +--- + clutter/clutter/clutter-stage-view-private.h | 6 ++---- + clutter/clutter/clutter-stage-view.c | 10 ++++------ + clutter/clutter/clutter-stage.c | 4 ++-- + 3 files changed, 8 insertions(+), 12 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index 78aa37c9f4..e27f140b8a 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -25,8 +25,7 @@ void clutter_stage_view_blit_offscreen (ClutterStageView *view, + + gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + +-void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, +- gboolean dirty); ++void clutter_stage_view_invalidate_viewport (ClutterStageView *view); + + void clutter_stage_view_set_viewport (ClutterStageView *view, + float x, +@@ -36,8 +35,7 @@ void clutter_stage_view_set_viewport (ClutterStageView *view, + + gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); + +-void clutter_stage_view_set_dirty_projection (ClutterStageView *view, +- gboolean dirty); ++void clutter_stage_view_invalidate_projection (ClutterStageView *view); + + void clutter_stage_view_set_projection (ClutterStageView *view, + const CoglMatrix *matrix); +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 4d8bbddc9d..40edfad6e1 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -350,13 +350,12 @@ clutter_stage_view_is_dirty_viewport (ClutterStageView *view) + } + + void +-clutter_stage_view_set_dirty_viewport (ClutterStageView *view, +- gboolean dirty) ++clutter_stage_view_invalidate_viewport (ClutterStageView *view) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- priv->dirty_viewport = dirty; ++ priv->dirty_viewport = TRUE; + } + + void +@@ -385,13 +384,12 @@ clutter_stage_view_is_dirty_projection (ClutterStageView *view) + } + + void +-clutter_stage_view_set_dirty_projection (ClutterStageView *view, +- gboolean dirty) ++clutter_stage_view_invalidate_projection (ClutterStageView *view) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- priv->dirty_projection = dirty; ++ priv->dirty_projection = TRUE; + } + + void +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 4bde234dbf..aaa77d9ede 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -2636,7 +2636,7 @@ _clutter_stage_dirty_projection (ClutterStage *stage) + { + ClutterStageView *view = l->data; + +- clutter_stage_view_set_dirty_projection (view, TRUE); ++ clutter_stage_view_invalidate_projection (view); + } + } + +@@ -2725,7 +2725,7 @@ _clutter_stage_dirty_viewport (ClutterStage *stage) + { + ClutterStageView *view = l->data; + +- clutter_stage_view_set_dirty_viewport (view, TRUE); ++ clutter_stage_view_invalidate_viewport (view); + } + } + +-- +2.28.0 + + +From 32da7b5c31277c56089e4b3b8ccf43bc552e8974 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 17:05:36 +0200 +Subject: [PATCH 07/20] cogl: Make private BLIT_FRAMEBUFFER feature public + +Will be a requirement for enabling shadow buffers. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit b3153760bf81af07f5328ba07b0ff3009bd8305b) +--- + cogl/cogl/cogl-blit.c | 2 +- + cogl/cogl/cogl-context.h | 3 +++ + cogl/cogl/cogl-framebuffer.c | 2 +- + cogl/cogl/cogl-framebuffer.h | 2 +- + cogl/cogl/cogl-private.h | 1 - + cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 4 ++-- + cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 4 ++-- + cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 4 ++-- + 8 files changed, 12 insertions(+), 10 deletions(-) + +diff --git a/cogl/cogl/cogl-blit.c b/cogl/cogl/cogl-blit.c +index ae5a8a345d..dd5fffff37 100644 +--- a/cogl/cogl/cogl-blit.c ++++ b/cogl/cogl/cogl-blit.c +@@ -158,7 +158,7 @@ _cogl_blit_framebuffer_begin (CoglBlitData *data) + supported. */ + if ((_cogl_texture_get_format (data->src_tex) & COGL_PREMULT_BIT) != + (_cogl_texture_get_format (data->dst_tex) & COGL_PREMULT_BIT) || +- !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) ++ !cogl_has_feature (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)) + return FALSE; + + dst_offscreen = _cogl_offscreen_new_with_texture_full +diff --git a/cogl/cogl/cogl-context.h b/cogl/cogl/cogl-context.h +index d4104625e6..ec90491e94 100644 +--- a/cogl/cogl/cogl-context.h ++++ b/cogl/cogl/cogl-context.h +@@ -227,6 +227,8 @@ cogl_is_context (void *object); + * the depth buffer to a texture. + * @COGL_FEATURE_ID_PRESENTATION_TIME: Whether frame presentation + * time stamps will be recorded in #CoglFrameInfo objects. ++ * @COGL_FEATURE_ID_BLIT_FRAMEBUFFER: Whether blitting using ++ * cogl_blit_framebuffer() is supported. + * + * All the capabilities that can vary between different GPUs supported + * by Cogl. Applications that depend on any of these features should explicitly +@@ -261,6 +263,7 @@ typedef enum _CoglFeatureID + COGL_FEATURE_ID_TEXTURE_RG, + COGL_FEATURE_ID_BUFFER_AGE, + COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, ++ COGL_FEATURE_ID_BLIT_FRAMEBUFFER, + + /*< private >*/ + _COGL_N_FEATURE_IDS /*< skip >*/ +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index d64fc89fb6..fffac3f685 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1464,7 +1464,7 @@ cogl_blit_framebuffer (CoglFramebuffer *src, + int src_x1, src_y1, src_x2, src_y2; + int dst_x1, dst_y1, dst_x2, dst_y2; + +- if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) ++ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)) + { + g_set_error_literal (error, COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, +diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h +index 38ada9feb7..c347076919 100644 +--- a/cogl/cogl/cogl-framebuffer.h ++++ b/cogl/cogl/cogl-framebuffer.h +@@ -1863,7 +1863,7 @@ cogl_is_framebuffer (void *object); + * + * This blits a region of the color buffer of the source buffer + * to the destination buffer. This function should only be +- * called if the COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER feature is ++ * called if the COGL_FEATURE_ID_BLIT_FRAMEBUFFER feature is + * advertised. + * + * The source and destination rectangles are defined in offscreen +diff --git a/cogl/cogl/cogl-private.h b/cogl/cogl/cogl-private.h +index d9fbe68c76..07ac7eb2d8 100644 +--- a/cogl/cogl/cogl-private.h ++++ b/cogl/cogl/cogl-private.h +@@ -42,7 +42,6 @@ typedef enum + { + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, + COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, +- COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, + COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES, + COGL_PRIVATE_FEATURE_PBOS, + COGL_PRIVATE_FEATURE_VBOS, +diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +index 6466fd6bcf..2c0613462f 100644 +--- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c ++++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +@@ -401,8 +401,8 @@ _cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, + { + /* NB: Currently we only take advantage of binding separate + * read/write buffers for framebuffer blit purposes. */ +- _COGL_RETURN_IF_FAIL (_cogl_has_private_feature +- (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)); ++ _COGL_RETURN_IF_FAIL (cogl_has_feature ++ (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)); + + _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); + _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); +diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +index 716617b54b..f905267c53 100644 +--- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c ++++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +@@ -466,8 +466,8 @@ _cogl_driver_update_features (CoglContext *ctx, + } + + if (ctx->glBlitFramebuffer) +- COGL_FLAGS_SET (private_features, +- COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); ++ COGL_FLAGS_SET (ctx->features, ++ COGL_FEATURE_ID_BLIT_FRAMEBUFFER, TRUE); + + if (ctx->glRenderbufferStorageMultisampleIMG) + { +diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +index 902bd0bd3a..e55bb302c4 100644 +--- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c ++++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +@@ -325,8 +325,8 @@ _cogl_driver_update_features (CoglContext *context, + } + + if (context->glBlitFramebuffer) +- COGL_FLAGS_SET (private_features, +- COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); ++ COGL_FLAGS_SET (context->features, ++ COGL_FEATURE_ID_BLIT_FRAMEBUFFER, TRUE); + + if (_cogl_check_extension ("GL_OES_element_index_uint", gl_extensions)) + { +-- +2.28.0 + + +From 32aa92e50a12a5fd9652866937750a3c86c4845f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 17:06:35 +0200 +Subject: [PATCH 08/20] renderer/native: Only enable shadowfbs if we can blit + +There is no point in enabling shadow buffers if we can't as that'd be +even slower than not having them at all. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit f191c3b74f572547707fcb6522db76a88689eae2) +--- + src/backends/native/meta-renderer-native.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 463dddd3a7..62ca4bcbd4 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3649,6 +3649,9 @@ should_force_shadow_fb (MetaRendererNative *renderer_native, + break; + } + ++ if (!cogl_has_feature (cogl_context, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)) ++ return FALSE; ++ + kms_fd = meta_gpu_kms_get_fd (primary_gpu); + if (drmGetCap (kms_fd, DRM_CAP_DUMB_PREFER_SHADOW, &prefer_shadow) == 0) + { +-- +2.28.0 + + +From 5f247503e261f5bbb6baedc40c737c96b8144218 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 18:55:03 +0200 +Subject: [PATCH 09/20] clutter/stage-view: Always use cogl_blit_framebuffer() + from shadowfb + +It should only be used when direct blitting is supported, so there is no +reason we should have to deal with pipelines etc when blitting from the +shadow buffer to the onscreen. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 130f696f303a01d6d666ac967c53b4b5dc372f08) +--- + clutter/clutter/clutter-stage-view.c | 37 +++++++++++----------------- + 1 file changed, 15 insertions(+), 22 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 40edfad6e1..e7e33963a6 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -53,7 +53,6 @@ typedef struct _ClutterStageViewPrivate + gboolean use_shadowfb; + struct { + CoglOffscreen *framebuffer; +- CoglPipeline *pipeline; + } shadow; + + guint dirty_viewport : 1; +@@ -149,19 +148,6 @@ clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view) + view_class->setup_offscreen_blit_pipeline (view, priv->offscreen_pipeline); + } + +-static void +-clutter_stage_view_ensure_shadowfb_blit_pipeline (ClutterStageView *view) +-{ +- ClutterStageViewPrivate *priv = +- clutter_stage_view_get_instance_private (view); +- +- if (priv->shadow.pipeline) +- return; +- +- priv->shadow.pipeline = +- clutter_stage_view_create_framebuffer_pipeline (priv->shadow.framebuffer); +-} +- + void + clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + { +@@ -321,13 +307,21 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + + if (priv->shadow.framebuffer) + { +- clutter_stage_view_ensure_shadowfb_blit_pipeline (view); +- clutter_stage_view_copy_to_framebuffer (view, +- rect, +- priv->shadow.pipeline, +- priv->shadow.framebuffer, +- priv->framebuffer, +- TRUE); ++ int width, height; ++ g_autoptr (GError) error = NULL; ++ ++ width = cogl_framebuffer_get_width (priv->framebuffer); ++ height = cogl_framebuffer_get_height (priv->framebuffer); ++ if (!cogl_blit_framebuffer (priv->shadow.framebuffer, ++ priv->framebuffer, ++ 0, 0, ++ 0, 0, ++ width, height, ++ &error)) ++ { ++ g_warning ("Failed to blit shadow buffer: %s", error->message); ++ return; ++ } + } + } + +@@ -543,7 +537,6 @@ clutter_stage_view_dispose (GObject *object) + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); + g_clear_pointer (&priv->shadow.framebuffer, cogl_object_unref); +- g_clear_pointer (&priv->shadow.pipeline, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); + +-- +2.28.0 + + +From d20008aa8630c87d8607e64ff77188fc67b3d22a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 18:59:32 +0200 +Subject: [PATCH 10/20] clutter/stage-view: Simplify painting of offscreen + slightly + +We will only ever have an "offscreen" if we're painting transformed in +some way, so the 'can_blit' checking is unnecessary. Remove it. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 32d5e7d3d77c7ba29b8a7da45731aa31bd486056) +--- + clutter/clutter/clutter-stage-view.c | 49 +++++++--------------------- + 1 file changed, 12 insertions(+), 37 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index e7e33963a6..64fb20cb00 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -158,29 +158,13 @@ clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + } + + static void +-clutter_stage_view_copy_to_framebuffer (ClutterStageView *view, +- const cairo_rectangle_int_t *rect, +- CoglPipeline *pipeline, +- CoglFramebuffer *src_framebuffer, +- CoglFramebuffer *dst_framebuffer, +- gboolean can_blit) ++paint_transformed_framebuffer (ClutterStageView *view, ++ CoglPipeline *pipeline, ++ CoglFramebuffer *src_framebuffer, ++ CoglFramebuffer *dst_framebuffer) + { + CoglMatrix matrix; + +- /* First, try with blit */ +- if (can_blit) +- { +- if (cogl_blit_framebuffer (src_framebuffer, +- dst_framebuffer, +- 0, 0, +- 0, 0, +- cogl_framebuffer_get_width (dst_framebuffer), +- cogl_framebuffer_get_height (dst_framebuffer), +- NULL)) +- return; +- } +- +- /* If blit fails, fallback to the slower painting method */ + cogl_framebuffer_push_matrix (dst_framebuffer); + + cogl_matrix_init_identity (&matrix); +@@ -278,30 +262,21 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + + if (priv->offscreen) + { +- gboolean can_blit; +- CoglMatrix matrix; +- + clutter_stage_view_ensure_offscreen_blit_pipeline (view); +- clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); +- can_blit = cogl_matrix_is_identity (&matrix); + + if (priv->shadow.framebuffer) + { +- clutter_stage_view_copy_to_framebuffer (view, +- rect, +- priv->offscreen_pipeline, +- priv->offscreen, +- priv->shadow.framebuffer, +- can_blit); ++ paint_transformed_framebuffer (view, ++ priv->offscreen_pipeline, ++ priv->offscreen, ++ priv->shadow.framebuffer); + } + else + { +- clutter_stage_view_copy_to_framebuffer (view, +- rect, +- priv->offscreen_pipeline, +- priv->offscreen, +- priv->framebuffer, +- can_blit); ++ paint_transformed_framebuffer (view, ++ priv->offscreen_pipeline, ++ priv->offscreen, ++ priv->framebuffer); + } + } + +-- +2.28.0 + + +From 8fca65cc3ff989529bf08a47f20b80691f91f95f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:08:03 +0200 +Subject: [PATCH 11/20] region-utils: Make transform util const correct + +The input should be const, as it will not be altered. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 761bc64cdd4746389625173454b8861cf211cd79) +--- + src/compositor/region-utils.c | 2 +- + src/compositor/region-utils.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/compositor/region-utils.c b/src/compositor/region-utils.c +index 752af85c5c..8edb89322c 100644 +--- a/src/compositor/region-utils.c ++++ b/src/compositor/region-utils.c +@@ -376,7 +376,7 @@ meta_make_border_region (cairo_region_t *region, + } + + cairo_region_t * +-meta_region_transform (cairo_region_t *region, ++meta_region_transform (const cairo_region_t *region, + MetaMonitorTransform transform, + int width, + int height) +diff --git a/src/compositor/region-utils.h b/src/compositor/region-utils.h +index 84e4d83bc2..ca1b8b7b45 100644 +--- a/src/compositor/region-utils.h ++++ b/src/compositor/region-utils.h +@@ -106,7 +106,7 @@ cairo_region_t * meta_make_border_region (cairo_region_t *region, + int y_amount, + gboolean flip); + +-cairo_region_t * meta_region_transform (cairo_region_t *region, ++cairo_region_t * meta_region_transform (const cairo_region_t *region, + MetaMonitorTransform transform, + int width, + int height); +-- +2.28.0 + + +From 58331ff2f10aad87f537e3ebdaa5707c13c9e41b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:05:36 +0200 +Subject: [PATCH 12/20] clutter/stage-cogl: Use buffer age when view monitor is + rotated + +We failed to use the buffer age when monitors were rotated, as when they +are, we first composite to an offscreen framebuffer, then later again to +the onscreen. The buffer age checking happened on the offscreen, and an +offscreen being single buffered, they can't possible support buffer +ages. + +Instead, move the buffer age check to check the actual onscreen +framebuffer. The offscreen to onscreen painting is still always full +frame, but that will be fixed in a later commit. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 41c2c2c7d72a0bc8ac1970d35183345424642cf1) +--- + clutter/clutter/clutter-stage-view-private.h | 6 +++ + clutter/clutter/clutter-stage-view.c | 29 ++++++----- + clutter/clutter/clutter-stage-view.h | 11 ++-- + clutter/clutter/cogl/clutter-stage-cogl.c | 54 +++++++------------- + src/backends/meta-renderer-view.c | 22 ++++++++ + 5 files changed, 68 insertions(+), 54 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index e27f140b8a..10f9847b70 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -40,4 +40,10 @@ void clutter_stage_view_invalidate_projection (ClutterStageView *view); + void clutter_stage_view_set_projection (ClutterStageView *view, + const CoglMatrix *matrix); + ++void clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect); ++ + #endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 64fb20cb00..080bfd6669 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -157,6 +157,22 @@ clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); + } + ++void ++clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect) ++{ ++ ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view); ++ ++ return view_class->transform_rect_to_onscreen (view, ++ src_rect, ++ dst_width, ++ dst_height, ++ dst_rect); ++} ++ + static void + paint_transformed_framebuffer (ClutterStageView *view, + CoglPipeline *pipeline, +@@ -383,19 +399,6 @@ clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, + view_class->get_offscreen_transformation_matrix (view, matrix); + } + +-void +-clutter_stage_view_transform_to_onscreen (ClutterStageView *view, +- gfloat *x, +- gfloat *y) +-{ +- gfloat z = 0, w = 1; +- CoglMatrix matrix; +- +- clutter_stage_view_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) +diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h +index 26bf10e798..eb0184e9ab 100644 +--- a/clutter/clutter/clutter-stage-view.h ++++ b/clutter/clutter/clutter-stage-view.h +@@ -43,6 +43,12 @@ struct _ClutterStageViewClass + + void (* get_offscreen_transformation_matrix) (ClutterStageView *view, + CoglMatrix *matrix); ++ ++ void (* transform_rect_to_onscreen) (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect); + }; + + CLUTTER_EXPORT +@@ -56,11 +62,6 @@ CoglFramebuffer *clutter_stage_view_get_onscreen (ClutterStageView *view); + CLUTTER_EXPORT + void clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view); + +-CLUTTER_EXPORT +-void clutter_stage_view_transform_to_onscreen (ClutterStageView *view, +- gfloat *x, +- gfloat *y); +- + CLUTTER_EXPORT + float clutter_stage_view_get_scale (ClutterStageView *view); + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 005c6f6922..821f78ee7c 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -509,36 +509,17 @@ static void + transform_swap_region_to_onscreen (ClutterStageView *view, + cairo_rectangle_int_t *swap_region) + { +- CoglFramebuffer *framebuffer; +- cairo_rectangle_int_t layout; +- gfloat x1, y1, x2, y2; +- gint width, height; +- +- framebuffer = clutter_stage_view_get_onscreen (view); +- clutter_stage_view_get_layout (view, &layout); +- +- x1 = (float) swap_region->x / layout.width; +- y1 = (float) swap_region->y / layout.height; +- x2 = (float) (swap_region->x + swap_region->width) / layout.width; +- y2 = (float) (swap_region->y + swap_region->height) / layout.height; +- +- clutter_stage_view_transform_to_onscreen (view, &x1, &y1); +- clutter_stage_view_transform_to_onscreen (view, &x2, &y2); +- +- width = cogl_framebuffer_get_width (framebuffer); +- height = cogl_framebuffer_get_height (framebuffer); +- +- x1 = floor (x1 * width); +- y1 = floor (height - (y1 * height)); +- x2 = ceil (x2 * width); +- y2 = ceil (height - (y2 * height)); +- +- *swap_region = (cairo_rectangle_int_t) { +- .x = x1, +- .y = y1, +- .width = x2 - x1, +- .height = y2 - y1 +- }; ++ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); ++ int width, height; ++ ++ width = cogl_framebuffer_get_width (onscreen); ++ height = cogl_framebuffer_get_height (onscreen); ++ ++ clutter_stage_view_transform_rect_to_onscreen (view, ++ swap_region, ++ width, ++ height, ++ swap_region); + } + + static void +@@ -593,6 +574,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + 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 have_clip; + gboolean may_use_clipped_redraw; +@@ -618,10 +600,10 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + fb_height = cogl_framebuffer_get_height (fb); + + can_blit_sub_buffer = +- cogl_is_onscreen (fb) && ++ cogl_is_onscreen (onscreen) && + cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION); + +- has_buffer_age = cogl_is_onscreen (fb) && is_buffer_age_enabled (); ++ has_buffer_age = cogl_is_onscreen (onscreen) && is_buffer_age_enabled (); + + /* NB: a zero width redraw clip == full stage redraw */ + if (stage_cogl->bounding_redraw_clip.width == 0) +@@ -645,7 +627,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + have_clip && + /* some drivers struggle to get going and produce some junk + * frames when starting up... */ +- cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3) ++ cogl_onscreen_get_frame_counter (COGL_ONSCREEN (onscreen)) > 3) + { + ClutterRect rect; + +@@ -686,7 +668,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + cairo_rectangle_int_t *current_fb_damage = + &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)]; + +- age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb)); ++ age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (onscreen)); + + if (valid_buffer_age (view_cogl, age)) + { +@@ -961,9 +943,9 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, + int *x, + int *y) + { +- CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); ++ CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); + gboolean has_buffer_age = +- cogl_is_onscreen (framebuffer) && ++ cogl_is_onscreen (onscreen) && + is_buffer_age_enabled (); + float fb_scale; + gboolean scale_is_fractional; +diff --git a/src/backends/meta-renderer-view.c b/src/backends/meta-renderer-view.c +index cab1f5f483..4e45f2ef02 100644 +--- a/src/backends/meta-renderer-view.c ++++ b/src/backends/meta-renderer-view.c +@@ -34,6 +34,7 @@ + + #include "backends/meta-renderer.h" + #include "clutter/clutter-mutter.h" ++#include "compositor/region-utils.h" + + enum + { +@@ -125,6 +126,25 @@ meta_renderer_view_setup_offscreen_blit_pipeline (ClutterStageView *view, + cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); + } + ++static void ++meta_renderer_view_transform_rect_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *src_rect, ++ int dst_width, ++ int dst_height, ++ cairo_rectangle_int_t *dst_rect) ++{ ++ MetaRendererView *renderer_view = META_RENDERER_VIEW (view); ++ MetaMonitorTransform inverted_transform; ++ ++ inverted_transform = ++ meta_monitor_transform_invert (renderer_view->transform); ++ return meta_rectangle_transform (src_rect, ++ inverted_transform, ++ dst_width, ++ dst_height, ++ dst_rect); ++} ++ + static void + meta_renderer_view_set_transform (MetaRendererView *view, + MetaMonitorTransform transform) +@@ -195,6 +215,8 @@ meta_renderer_view_class_init (MetaRendererViewClass *klass) + meta_renderer_view_setup_offscreen_blit_pipeline; + view_class->get_offscreen_transformation_matrix = + meta_renderer_view_get_offscreen_transformation_matrix; ++ view_class->transform_rect_to_onscreen = ++ meta_renderer_view_transform_rect_to_onscreen; + + object_class->get_property = meta_renderer_view_get_property; + object_class->set_property = meta_renderer_view_set_property; +-- +2.28.0 + + +From 6fc1da9dd3ac2753771bb68adb780d1d55494cba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:22:10 +0200 +Subject: [PATCH 13/20] clutter/stage-view: Only paint redraw clip from + offscreen + +The rest didn't change, so only actually paint the part of the offscreen +that was composited as part of the stage painting. In practice, this +means that, unless a shadow buffer is used, we now only paint the +damaged part of the stage, and copy the damage part of the offscreen to +the onscreen. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit acf6b79e3a5b9d8d285886c471961e8c0bec48ce) +--- + clutter/clutter/clutter-stage-view.c | 85 ++++++++++++++++++++++++---- + 1 file changed, 73 insertions(+), 12 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 080bfd6669..b686272db0 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -19,6 +19,7 @@ + + #include "clutter/clutter-stage-view.h" + #include "clutter/clutter-stage-view-private.h" ++#include "clutter/clutter-private.h" + + #include + #include +@@ -174,23 +175,81 @@ clutter_stage_view_transform_rect_to_onscreen (ClutterStageView *view + } + + static void +-paint_transformed_framebuffer (ClutterStageView *view, +- CoglPipeline *pipeline, +- CoglFramebuffer *src_framebuffer, +- CoglFramebuffer *dst_framebuffer) ++paint_transformed_framebuffer (ClutterStageView *view, ++ CoglPipeline *pipeline, ++ CoglFramebuffer *src_framebuffer, ++ CoglFramebuffer *dst_framebuffer, ++ const cairo_rectangle_int_t *redraw_clip) + { + CoglMatrix matrix; ++ int dst_width, dst_height; ++ cairo_rectangle_int_t view_layout; ++ cairo_rectangle_int_t onscreen_layout; ++ float view_scale; ++ float *coordinates; ++ cairo_rectangle_int_t src_rect; ++ cairo_rectangle_int_t dst_rect; ++ ++ dst_width = cogl_framebuffer_get_width (dst_framebuffer); ++ dst_height = cogl_framebuffer_get_height (dst_framebuffer); ++ clutter_stage_view_get_layout (view, &view_layout); ++ clutter_stage_view_transform_rect_to_onscreen (view, ++ &(cairo_rectangle_int_t) { ++ .width = view_layout.width, ++ .height = view_layout.height, ++ }, ++ view_layout.width, ++ view_layout.height, ++ &onscreen_layout); ++ view_scale = clutter_stage_view_get_scale (view); + + cogl_framebuffer_push_matrix (dst_framebuffer); + + cogl_matrix_init_identity (&matrix); +- cogl_matrix_translate (&matrix, -1, 1, 0); +- cogl_matrix_scale (&matrix, 2, -2, 0); ++ cogl_matrix_scale (&matrix, ++ 1.0 / (dst_width / 2.0), ++ -1.0 / (dst_height / 2.0), 0); ++ cogl_matrix_translate (&matrix, ++ -(dst_width / 2.0), ++ -(dst_height / 2.0), 0); + cogl_framebuffer_set_projection_matrix (dst_framebuffer, &matrix); +- +- cogl_framebuffer_draw_rectangle (dst_framebuffer, +- pipeline, +- 0, 0, 1, 1); ++ cogl_framebuffer_set_viewport (dst_framebuffer, ++ 0, 0, dst_width, dst_height); ++ ++ coordinates = g_newa (float, 2 * 4); ++ ++ src_rect = *redraw_clip; ++ _clutter_util_rectangle_offset (&src_rect, ++ -view_layout.x, ++ -view_layout.y, ++ &src_rect); ++ ++ clutter_stage_view_transform_rect_to_onscreen (view, ++ &src_rect, ++ onscreen_layout.width, ++ onscreen_layout.height, ++ &dst_rect); ++ ++ coordinates[0] = (float) dst_rect.x * view_scale; ++ coordinates[1] = (float) dst_rect.y * view_scale; ++ coordinates[2] = ((float) (dst_rect.x + dst_rect.width) * ++ view_scale); ++ coordinates[3] = ((float) (dst_rect.y + dst_rect.height) * ++ view_scale); ++ ++ coordinates[4] = (((float) dst_rect.x / (float) dst_width) * ++ view_scale); ++ coordinates[5] = (((float) dst_rect.y / (float) dst_height) * ++ view_scale); ++ coordinates[6] = ((float) (dst_rect.x + dst_rect.width) / ++ (float) dst_width) * view_scale; ++ coordinates[7] = ((float) (dst_rect.y + dst_rect.height) / ++ (float) dst_height) * view_scale; ++ ++ cogl_framebuffer_draw_textured_rectangles (dst_framebuffer, ++ pipeline, ++ coordinates, ++ 1); + + cogl_framebuffer_pop_matrix (dst_framebuffer); + } +@@ -285,14 +344,16 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + paint_transformed_framebuffer (view, + priv->offscreen_pipeline, + priv->offscreen, +- priv->shadow.framebuffer); ++ priv->shadow.framebuffer, ++ rect); + } + else + { + paint_transformed_framebuffer (view, + priv->offscreen_pipeline, + priv->offscreen, +- priv->framebuffer); ++ priv->framebuffer, ++ rect); + } + } + +-- +2.28.0 + + +From ff3164440e6bbb3e845a1d4a23843a5792afc16f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 13 May 2020 17:18:50 +0200 +Subject: [PATCH 14/20] clutter/stage-cogl: Only construct damage array if + it'll be used + +It's only used when we actually swap buffers, which we only do if the +target framebuffer is an onscreen. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 95a80c442b6300ce5b41b4b3975a372f1eabd166) +--- + clutter/clutter/cogl/clutter-stage-cogl.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 821f78ee7c..fc6d0d031d 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -413,17 +413,6 @@ swap_framebuffer (ClutterStageWindow *stage_window, + gboolean swap_with_damage) + { + CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); +- int damage[4], ndamage; +- +- damage[0] = swap_region->x; +- damage[1] = swap_region->y; +- damage[2] = swap_region->width; +- damage[3] = swap_region->height; +- +- if (swap_region->width != 0) +- ndamage = 1; +- else +- ndamage = 0; + + if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))) + paint_damage_region (stage_window, view, swap_region); +@@ -431,6 +420,17 @@ swap_framebuffer (ClutterStageWindow *stage_window, + if (cogl_is_onscreen (framebuffer)) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); ++ int damage[4], ndamage; ++ ++ damage[0] = swap_region->x; ++ damage[1] = swap_region->y; ++ damage[2] = swap_region->width; ++ damage[3] = swap_region->height; ++ ++ if (swap_region->width != 0) ++ ndamage = 1; ++ else ++ ndamage = 0; + + /* push on the screen */ + if (ndamage == 1 && !swap_with_damage) +-- +2.28.0 + + +From f946746f5938e7d6c48b688827fb991f22dc1364 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 5 May 2020 19:25:23 +0200 +Subject: [PATCH 15/20] clutter/stage-view: Only blit the damage part of the + shadow buffer + +This fixes the last "copy everything" paths when clutter doesn't +directly paint onto the onscreen framebuffer. It adds a new hook into +the stage view called before the swap buffer, as at this point, we have +the swap buffer damag regions ready, which corresponds to the regions we +must blit according to the damage reported to clutter. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 851e7727ec6f3719139ab562ac2524cdc1bd64ae) +--- + clutter/clutter/clutter-stage-view-private.h | 3 +++ + clutter/clutter/clutter-stage-view.c | 25 ++++++++++++++++++-- + clutter/clutter/cogl/clutter-stage-cogl.c | 2 ++ + 3 files changed, 28 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +index 10f9847b70..bddc38ded6 100644 +--- a/clutter/clutter/clutter-stage-view-private.h ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -23,6 +23,9 @@ + void clutter_stage_view_blit_offscreen (ClutterStageView *view, + const cairo_rectangle_int_t *clip); + ++void clutter_stage_view_before_swap_buffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region); ++ + gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); + + void clutter_stage_view_invalidate_viewport (ClutterStageView *view); +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index b686272db0..21ab02c97b 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -356,11 +356,22 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + rect); + } + } ++} ++ ++void ++clutter_stage_view_before_swap_buffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ g_autoptr (GError) error = NULL; + +- if (priv->shadow.framebuffer) ++ if (!priv->shadow.framebuffer) ++ return; ++ ++ if (swap_region->width == 0 || swap_region->height == 0) + { + int width, height; +- g_autoptr (GError) error = NULL; + + width = cogl_framebuffer_get_width (priv->framebuffer); + height = cogl_framebuffer_get_height (priv->framebuffer); +@@ -370,6 +381,16 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + 0, 0, + width, height, + &error)) ++ g_warning ("Failed to blit shadow buffer: %s", error->message); ++ } ++ else ++ { ++ if (!cogl_blit_framebuffer (priv->shadow.framebuffer, ++ priv->framebuffer, ++ swap_region->x, swap_region->y, ++ swap_region->x, swap_region->y, ++ swap_region->width, swap_region->height, ++ &error)) + { + g_warning ("Failed to blit shadow buffer: %s", error->message); + return; +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index fc6d0d031d..884819ebd3 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -417,6 +417,8 @@ swap_framebuffer (ClutterStageWindow *stage_window, + if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))) + paint_damage_region (stage_window, view, swap_region); + ++ clutter_stage_view_before_swap_buffer (view, swap_region); ++ + if (cogl_is_onscreen (framebuffer)) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); +-- +2.28.0 + + +From 757dd09dc9b76a7654f087679db1c7f005b7653c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 09:11:34 +0200 +Subject: [PATCH 16/20] clutter/stage-cogl: Extract damage history logic + +Move the damage history tracking to a new ClutterDamageHistory helper +type. The aim is to be able to track damage history elsewhere without +reimplementing the data structure and tracking logic. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 09271bcfef8889022f15a3b2949843e55f3df9da) +--- + clutter/clutter/clutter-damage-history.c | 89 +++++++++++++++++ + clutter/clutter/clutter-damage-history.h | 42 ++++++++ + clutter/clutter/cogl/clutter-stage-cogl.c | 116 ++++++++++++---------- + clutter/clutter/meson.build | 2 + + 4 files changed, 195 insertions(+), 54 deletions(-) + create mode 100644 clutter/clutter/clutter-damage-history.c + create mode 100644 clutter/clutter/clutter-damage-history.h + +diff --git a/clutter/clutter/clutter-damage-history.c b/clutter/clutter/clutter-damage-history.c +new file mode 100644 +index 0000000000..78ab0f7b5e +--- /dev/null ++++ b/clutter/clutter/clutter-damage-history.c +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation. ++ * Copyright (C) 2020 Red Hat Inc ++ * ++ * 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 . ++ */ ++ ++#include "clutter-build-config.h" ++ ++#include "clutter-damage-history.h" ++ ++#define DAMAGE_HISTORY_LENGTH 0x10 ++ ++struct _ClutterDamageHistory ++{ ++ cairo_rectangle_int_t damages[DAMAGE_HISTORY_LENGTH]; ++ int index; ++}; ++ ++ClutterDamageHistory * ++clutter_damage_history_new (void) ++{ ++ ClutterDamageHistory *history; ++ ++ history = g_new0 (ClutterDamageHistory, 1); ++ ++ return history; ++} ++ ++void ++clutter_damage_history_free (ClutterDamageHistory *history) ++{ ++ g_free (history); ++} ++ ++gboolean ++clutter_damage_history_is_age_valid (ClutterDamageHistory *history, ++ int age) ++{ ++ const cairo_rectangle_int_t *damage; ++ ++ if (age >= DAMAGE_HISTORY_LENGTH || ++ age < 1) ++ return FALSE; ++ ++ damage = clutter_damage_history_lookup (history, age); ++ if (damage->width == 0 || damage->height == 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++void ++clutter_damage_history_record (ClutterDamageHistory *history, ++ const cairo_rectangle_int_t *damage) ++{ ++ history->damages[history->index] = *damage; ++} ++ ++static inline int ++step_damage_index (int current, ++ int diff) ++{ ++ return (current + diff) & (DAMAGE_HISTORY_LENGTH - 1); ++} ++ ++void ++clutter_damage_history_step (ClutterDamageHistory *history) ++{ ++ history->index = step_damage_index (history->index, 1); ++} ++ ++const cairo_rectangle_int_t * ++clutter_damage_history_lookup (ClutterDamageHistory *history, ++ int age) ++{ ++ return &history->damages[step_damage_index (history->index, -age)]; ++} +diff --git a/clutter/clutter/clutter-damage-history.h b/clutter/clutter/clutter-damage-history.h +new file mode 100644 +index 0000000000..6c483acab7 +--- /dev/null ++++ b/clutter/clutter/clutter-damage-history.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation. ++ * Copyright (C) 2020 Red Hat Inc ++ * ++ * 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 . ++ */ ++ ++#ifndef CLUTTER_DAMAGE_HISTORY_H ++#define CLUTTER_DAMAGE_HISTORY_H ++ ++#include ++#include ++ ++typedef struct _ClutterDamageHistory ClutterDamageHistory; ++ ++ClutterDamageHistory * clutter_damage_history_new (void); ++ ++void clutter_damage_history_free (ClutterDamageHistory *history); ++ ++gboolean clutter_damage_history_is_age_valid (ClutterDamageHistory *history, ++ int age); ++ ++void clutter_damage_history_record (ClutterDamageHistory *history, ++ const cairo_rectangle_int_t *damage); ++ ++void clutter_damage_history_step (ClutterDamageHistory *history); ++ ++const cairo_rectangle_int_t * clutter_damage_history_lookup (ClutterDamageHistory *history, ++ int age); ++ ++#endif /* CLUTTER_DAMAGE_HISTORY_H */ +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 884819ebd3..11273ec894 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -38,6 +38,7 @@ + + #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" +@@ -49,13 +50,9 @@ + + typedef struct _ClutterStageViewCoglPrivate + { +- /* +- * List of previous damaged areas in stage view framebuffer coordinate space. ++ /* Damage history, in stage view render target framebuffer coordinate space. + */ +-#define DAMAGE_HISTORY_MAX 16 +-#define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1)) +- cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX]; +- unsigned int damage_index; ++ ClutterDamageHistory *damage_history; + } ClutterStageViewCoglPrivate; + + G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl, +@@ -348,10 +345,7 @@ valid_buffer_age (ClutterStageViewCogl *view_cogl, + ClutterStageViewCoglPrivate *view_priv = + clutter_stage_view_cogl_get_instance_private (view_cogl); + +- if (age <= 0) +- return FALSE; +- +- return age < MIN (view_priv->damage_index, DAMAGE_HISTORY_MAX); ++ return clutter_damage_history_is_age_valid (view_priv->damage_history, age); + } + + static void +@@ -483,30 +477,6 @@ paint_stage (ClutterStageCogl *stage_cogl, + clutter_stage_view_blit_offscreen (view, clip); + } + +-static void +-fill_current_damage_history_and_step (ClutterStageView *view) +-{ +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); +- cairo_rectangle_int_t view_rect; +- float fb_scale; +- cairo_rectangle_int_t *current_fb_damage; +- +- current_fb_damage = +- &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index)]; +- clutter_stage_view_get_layout (view, &view_rect); +- fb_scale = clutter_stage_view_get_scale (view); +- +- *current_fb_damage = (cairo_rectangle_int_t) { +- .x = 0, +- .y = 0, +- .width = view_rect.width * fb_scale, +- .height = view_rect.height * fb_scale +- }; +- view_priv->damage_index++; +-} +- + static void + transform_swap_region_to_onscreen (ClutterStageView *view, + cairo_rectangle_int_t *swap_region) +@@ -567,6 +537,24 @@ scale_and_clamp_rect (const ClutterRect *rect, + _clutter_util_rectangle_int_extents (&tmp, dest); + } + ++static void ++record_full_damage (ClutterStageView *view) ++{ ++ 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); ++ int fb_width, fb_height; ++ ++ fb_width = cogl_framebuffer_get_width (fb); ++ fb_height = cogl_framebuffer_get_height (fb); ++ clutter_damage_history_record (view_priv->damage_history, ++ &(cairo_rectangle_int_t) { ++ .width = fb_width, ++ .height = fb_height ++ }); ++} ++ + static gboolean + clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + ClutterStageView *view) +@@ -666,9 +654,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + { + if (use_clipped_redraw && !clip_region_empty) + { +- int age, i; +- cairo_rectangle_int_t *current_fb_damage = +- &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)]; ++ int age; + + age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (onscreen)); + +@@ -676,16 +662,20 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + { + ClutterRect rect; + cairo_rectangle_int_t damage_region; ++ int i; + +- *current_fb_damage = fb_clip_region; ++ clutter_damage_history_record (view_priv->damage_history, ++ &fb_clip_region); + + for (i = 1; i <= age; i++) + { +- cairo_rectangle_int_t *fb_damage = +- &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - i - 1)]; ++ const cairo_rectangle_int_t *old_damage; ++ ++ old_damage = ++ clutter_damage_history_lookup (view_priv->damage_history, i); + + _clutter_util_rectangle_union (&fb_clip_region, +- fb_damage, ++ old_damage, + &fb_clip_region); + } + +@@ -713,18 +703,15 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, + { + CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age); + use_clipped_redraw = FALSE; +- *current_fb_damage = (cairo_rectangle_int_t) { +- .x = 0, +- .y = 0, +- .width = view_rect.width * fb_scale, +- .height = view_rect.height * fb_scale +- }; ++ record_full_damage (view); + } + } + else if (!use_clipped_redraw) + { +- fill_current_damage_history_and_step (view); ++ record_full_damage (view); + } ++ ++ clutter_damage_history_step (view_priv->damage_history); + } + + cogl_push_framebuffer (fb); +@@ -946,6 +933,9 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, + int *y) + { + CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); ++ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); ++ ClutterStageViewCoglPrivate *view_priv = ++ clutter_stage_view_cogl_get_instance_private (view_cogl); + gboolean has_buffer_age = + cogl_is_onscreen (onscreen) && + is_buffer_age_enabled (); +@@ -967,22 +957,21 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, + * For now, always use the (0, 0) pixel for picking when using fractional + * framebuffer scaling. + */ +- if (!has_buffer_age || scale_is_fractional) ++ if (!has_buffer_age || ++ scale_is_fractional || ++ !clutter_damage_history_is_age_valid (view_priv->damage_history, 0)) + { + *x = 0; + *y = 0; + } + else + { +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); + cairo_rectangle_int_t view_layout; +- cairo_rectangle_int_t *fb_damage; ++ const cairo_rectangle_int_t *fb_damage; + + clutter_stage_view_get_layout (view, &view_layout); + +- fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)]; ++ fb_damage = clutter_damage_history_lookup (view_priv->damage_history, 0); + *x = fb_damage->x / fb_scale; + *y = fb_damage->y / fb_scale; + } +@@ -1052,12 +1041,31 @@ _clutter_stage_cogl_init (ClutterStageCogl *stage) + stage->update_time = -1; + } + ++static void ++clutter_stage_view_cogl_finalize (GObject *object) ++{ ++ ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (object); ++ ClutterStageViewCoglPrivate *view_priv = ++ clutter_stage_view_cogl_get_instance_private (view_cogl); ++ ++ clutter_damage_history_free (view_priv->damage_history); ++ ++ G_OBJECT_CLASS (clutter_stage_view_cogl_parent_class)->finalize (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->finalize = clutter_stage_view_cogl_finalize; + } +diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build +index 8e0484453d..c9eab96d29 100644 +--- a/clutter/clutter/meson.build ++++ b/clutter/clutter/meson.build +@@ -116,6 +116,7 @@ clutter_sources = [ + 'clutter-constraint.c', + 'clutter-container.c', + 'clutter-content.c', ++ 'clutter-damage-history.c', + 'clutter-deform-effect.c', + 'clutter-desaturate-effect.c', + 'clutter-device-manager.c', +@@ -186,6 +187,7 @@ clutter_private_headers = [ + 'clutter-bezier.h', + 'clutter-constraint-private.h', + 'clutter-content-private.h', ++ 'clutter-damage-history.h', + 'clutter-debug.h', + 'clutter-device-manager-private.h', + 'clutter-easing.h', +-- +2.28.0 + + +From 5da1c8083784a351a7763a0c9a9ce4c8359522a4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 21:40:40 +0200 +Subject: [PATCH 17/20] cogl/dma-buf: Add API to synchronize reading + +Used before and after accessing DMA buffer content using mmap(). + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 2d972fc761b9e39f78e66dd84eab57309cdc8658) +--- + cogl/cogl/cogl-dma-buf-handle.c | 51 +++++++++++++++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 8 ++++++ + cogl/meson.build | 1 + + 3 files changed, 60 insertions(+) + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +index d8b4e57c55..7e86e2267b 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.c ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -34,6 +34,10 @@ + #include "cogl-dma-buf-handle.h" + #include "cogl-object.h" + ++#include ++#include ++#include ++#include + #include + + struct _CoglDmaBufHandle +@@ -96,6 +100,53 @@ cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle) + g_free (dmabuf_handle); + } + ++static gboolean ++sync_read (CoglDmaBufHandle *dmabuf_handle, ++ uint64_t start_or_end, ++ GError **error) ++{ ++ struct dma_buf_sync sync = { 0 }; ++ ++ sync.flags = start_or_end | DMA_BUF_SYNC_READ; ++ ++ while (TRUE) ++ { ++ int ret; ++ ++ ret = ioctl (dmabuf_handle->dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync); ++ if (ret == -1 && errno == EINTR) ++ { ++ continue; ++ } ++ else if (ret == -1) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "ioctl: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ else ++ { ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++cogl_dma_buf_handle_sync_read_start (CoglDmaBufHandle *dmabuf_handle, ++ GError **error) ++{ ++ return sync_read (dmabuf_handle, DMA_BUF_SYNC_START, error); ++} ++ ++gboolean ++cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, ++ GError **error) ++{ ++ return sync_read (dmabuf_handle, DMA_BUF_SYNC_END, error); ++} ++ + CoglFramebuffer * + cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) + { +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +index f64a20678d..63c5bab7b7 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.h ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -63,6 +63,14 @@ cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, + void + cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle); + ++gboolean ++cogl_dma_buf_handle_sync_read_start (CoglDmaBufHandle *dmabuf_handle, ++ GError **error); ++ ++gboolean ++cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, ++ GError **error); ++ + /** + * cogl_dma_buf_handle_get_framebuffer: (skip) + * +diff --git a/cogl/meson.build b/cogl/meson.build +index 356d596f56..47e6a3e0da 100644 +--- a/cogl/meson.build ++++ b/cogl/meson.build +@@ -23,6 +23,7 @@ cogl_mutter_config_h = configure_file( + + cogl_pkg_deps = [ + glib_dep, ++ gio_dep, + gobject_dep, + ] + +-- +2.28.0 + + +From 360a397c19046c6a914ee27e3e5104da3ad0c1c6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 22:12:46 +0200 +Subject: [PATCH 18/20] cogl/dma-buf: Add mmap/munmap helpers + +Avoids dealing directly with mmap() and munmap(). + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit e05a1a6c0b2526146c85ec9c381bb2b49d19b4b2) +--- + cogl/cogl/cogl-dma-buf-handle.c | 41 +++++++++++++++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 9 ++++++++ + 2 files changed, 50 insertions(+) + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +index 7e86e2267b..9724ac9c95 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.c ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + + struct _CoglDmaBufHandle +@@ -147,6 +148,46 @@ cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, + return sync_read (dmabuf_handle, DMA_BUF_SYNC_END, error); + } + ++gpointer ++cogl_dma_buf_handle_mmap (CoglDmaBufHandle *dmabuf_handle, ++ GError **error) ++{ ++ size_t size; ++ gpointer data; ++ ++ size = dmabuf_handle->height * dmabuf_handle->stride; ++ ++ data = mmap (NULL, size, PROT_READ, MAP_PRIVATE, ++ dmabuf_handle->dmabuf_fd, ++ dmabuf_handle->offset); ++ if (data == MAP_FAILED) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "mmap failed: %s", g_strerror (errno)); ++ return NULL; ++ } ++ ++ return data; ++} ++ ++gboolean ++cogl_dma_buf_handle_munmap (CoglDmaBufHandle *dmabuf_handle, ++ gpointer data, ++ GError **error) ++{ ++ size_t size; ++ ++ size = dmabuf_handle->height * dmabuf_handle->stride; ++ if (munmap (data, size) != 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "munmap failed: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + CoglFramebuffer * + cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) + { +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +index 63c5bab7b7..08f307c1db 100644 +--- a/cogl/cogl/cogl-dma-buf-handle.h ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -71,6 +71,15 @@ gboolean + cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle *dmabuf_handle, + GError **error); + ++gpointer ++cogl_dma_buf_handle_mmap (CoglDmaBufHandle *dmabuf_handle, ++ GError **error); ++ ++gboolean ++cogl_dma_buf_handle_munmap (CoglDmaBufHandle *dmabuf_handle, ++ gpointer data, ++ GError **error); ++ + /** + * cogl_dma_buf_handle_get_framebuffer: (skip) + * +-- +2.28.0 + + +From ff8a80137047a91ed27d90467b004d691428bac4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 6 May 2020 22:14:17 +0200 +Subject: [PATCH 19/20] clutter/stage-view: Add tile based shadow damage + detection + +Compare, tile by tile, whether actual damage actually changed any +pixels. While this requires mmap():ing DMA buffers and comparing their +content, we should only ever use shadow buffers when we're using the +software renderer, meaning mmap() is cheap as it doesn't involve any +downloading. + +This works by making the shadow framebuffer double buffered, while +keeping track of damage history. When we're about to swap the onscreen +buffer, we compare what part of the posted damage actually changed, +records that into a damage history, then given the onscreen buffer age, +collect all actual damage for that age. The intersection of these tiles, +and the actual damage, is then used when blitting the shadow buffer to +the onscreen framebuffer. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1157 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 068385df3a0cf545e5110378b59db56cbd1bdef3) +--- + clutter/clutter/clutter-private.h | 3 + + clutter/clutter/clutter-stage-view.c | 472 +++++++++++++++++++++++++-- + clutter/clutter/clutter-util.c | 22 ++ + 3 files changed, 465 insertions(+), 32 deletions(-) + +diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h +index a5cd1fa197..5a0fed85c9 100644 +--- a/clutter/clutter/clutter-private.h ++++ b/clutter/clutter/clutter-private.h +@@ -265,6 +265,9 @@ gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1 + const cairo_rectangle_int_t *src2, + cairo_rectangle_int_t *dest); + ++gboolean _clutter_util_rectangle_contains (const cairo_rectangle_int_t *src1, ++ const cairo_rectangle_int_t *src2); ++ + + struct _ClutterVertex4 + { +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 21ab02c97b..5e5966d06e 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -17,6 +17,7 @@ + + #include "clutter-build-config.h" + ++#include "clutter/clutter-damage-history.h" + #include "clutter/clutter-stage-view.h" + #include "clutter/clutter-stage-view-private.h" + #include "clutter/clutter-private.h" +@@ -53,6 +54,12 @@ typedef struct _ClutterStageViewPrivate + + gboolean use_shadowfb; + struct { ++ struct { ++ CoglDmaBufHandle *handles[2]; ++ int current_idx; ++ ClutterDamageHistory *damage_history; ++ } dma_buf; ++ + CoglOffscreen *framebuffer; + } shadow; + +@@ -254,6 +261,66 @@ paint_transformed_framebuffer (ClutterStageView *view, + cogl_framebuffer_pop_matrix (dst_framebuffer); + } + ++static gboolean ++is_shadowfb_double_buffered (ClutterStageView *view) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ return priv->shadow.dma_buf.handles[0] && priv->shadow.dma_buf.handles[1]; ++} ++ ++static gboolean ++init_dma_buf_shadowfbs (ClutterStageView *view, ++ CoglContext *cogl_context, ++ int width, ++ int height, ++ GError **error) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); ++ CoglFramebuffer *initial_shadowfb; ++ ++ if (!cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, ++ "Buffer age not supported"); ++ return FALSE; ++ } ++ ++ if (!cogl_is_onscreen (priv->framebuffer)) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, ++ "Tried to use shadow buffer without onscreen"); ++ return FALSE; ++ } ++ ++ priv->shadow.dma_buf.handles[0] = cogl_renderer_create_dma_buf (cogl_renderer, ++ width, height, ++ error); ++ if (!priv->shadow.dma_buf.handles[0]) ++ return FALSE; ++ ++ priv->shadow.dma_buf.handles[1] = cogl_renderer_create_dma_buf (cogl_renderer, ++ width, height, ++ error); ++ if (!priv->shadow.dma_buf.handles[1]) ++ { ++ g_clear_pointer (&priv->shadow.dma_buf.handles[0], ++ cogl_dma_buf_handle_free); ++ return FALSE; ++ } ++ ++ priv->shadow.dma_buf.damage_history = clutter_damage_history_new (); ++ ++ initial_shadowfb = ++ cogl_dma_buf_handle_get_framebuffer (priv->shadow.dma_buf.handles[0]); ++ priv->shadow.framebuffer = cogl_object_ref (initial_shadowfb); ++ ++ return TRUE; ++} ++ + static CoglOffscreen * + create_offscreen_framebuffer (CoglContext *context, + int width, +@@ -285,11 +352,11 @@ create_offscreen_framebuffer (CoglContext *context, + } + + static gboolean +-init_offscreen_shadowfb (ClutterStageView *view, +- CoglContext *cogl_context, +- int width, +- int height, +- GError **error) ++init_fallback_shadowfb (ClutterStageView *view, ++ CoglContext *cogl_context, ++ int width, ++ int height, ++ GError **error) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +@@ -317,7 +384,17 @@ init_shadowfb (ClutterStageView *view) + height = cogl_framebuffer_get_height (priv->framebuffer); + cogl_context = cogl_framebuffer_get_context (priv->framebuffer); + +- if (!init_offscreen_shadowfb (view, cogl_context, width, height, &error)) ++ if (init_dma_buf_shadowfbs (view, cogl_context, width, height, &error)) ++ { ++ g_message ("Initialized double buffered shadow fb for %s", priv->name); ++ return; ++ } ++ ++ g_warning ("Failed to initialize double buffered shadow fb for %s: %s", ++ priv->name, error->message); ++ g_clear_error (&error); ++ ++ if (!init_fallback_shadowfb (view, cogl_context, width, height, &error)) + { + g_warning ("Failed to initialize single buffered shadow fb for %s: %s", + priv->name, error->message); +@@ -358,44 +435,298 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + } + } + +-void +-clutter_stage_view_before_swap_buffer (ClutterStageView *view, +- const cairo_rectangle_int_t *swap_region) ++static gboolean ++is_tile_dirty (cairo_rectangle_int_t *tile, ++ uint8_t *current_data, ++ uint8_t *prev_data, ++ int bpp, ++ int stride) ++{ ++ int y; ++ ++ for (y = tile->y; y < tile->y + tile->height; y++) ++ { ++ if (memcmp (prev_data + y * stride + tile->x * bpp, ++ current_data + y * stride + tile->x * bpp, ++ tile->width * bpp) != 0) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static int ++flip_dma_buf_idx (int idx) ++{ ++ return (idx + 1) % 2; ++} ++ ++static cairo_region_t * ++find_damaged_tiles (ClutterStageView *view, ++ const cairo_region_t *damage_region, ++ GError **error) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +- g_autoptr (GError) error = NULL; ++ cairo_region_t *tile_damage_region; ++ cairo_rectangle_int_t damage_extents; ++ cairo_rectangle_int_t fb_rect; ++ int prev_dma_buf_idx; ++ CoglDmaBufHandle *prev_dma_buf_handle; ++ uint8_t *prev_data; ++ int current_dma_buf_idx; ++ CoglDmaBufHandle *current_dma_buf_handle; ++ uint8_t *current_data; ++ int width, height, stride, bpp; ++ int tile_x_min, tile_x_max; ++ int tile_y_min, tile_y_max; ++ int tile_x, tile_y; ++ const int tile_size = 16; ++ ++ prev_dma_buf_idx = flip_dma_buf_idx (priv->shadow.dma_buf.current_idx); ++ prev_dma_buf_handle = priv->shadow.dma_buf.handles[prev_dma_buf_idx]; ++ ++ current_dma_buf_idx = priv->shadow.dma_buf.current_idx; ++ current_dma_buf_handle = priv->shadow.dma_buf.handles[current_dma_buf_idx]; ++ ++ width = cogl_dma_buf_handle_get_width (current_dma_buf_handle); ++ height = cogl_dma_buf_handle_get_height (current_dma_buf_handle); ++ stride = cogl_dma_buf_handle_get_stride (current_dma_buf_handle); ++ bpp = cogl_dma_buf_handle_get_bpp (current_dma_buf_handle); ++ ++ cogl_framebuffer_finish (priv->shadow.framebuffer); ++ ++ if (!cogl_dma_buf_handle_sync_read_start (prev_dma_buf_handle, error)) ++ return NULL; ++ ++ if (!cogl_dma_buf_handle_sync_read_start (current_dma_buf_handle, error)) ++ goto err_sync_read_current; ++ ++ prev_data = cogl_dma_buf_handle_mmap (prev_dma_buf_handle, error); ++ if (!prev_data) ++ goto err_mmap_prev; ++ current_data = cogl_dma_buf_handle_mmap (current_dma_buf_handle, error); ++ if (!current_data) ++ goto err_mmap_current; ++ ++ fb_rect = (cairo_rectangle_int_t) { ++ .width = width, ++ .height = height, ++ }; ++ ++ cairo_region_get_extents (damage_region, &damage_extents); ++ ++ tile_x_min = damage_extents.x / tile_size; ++ tile_x_max = ((damage_extents.x + damage_extents.width + tile_size - 1) / ++ tile_size); ++ tile_y_min = damage_extents.y / tile_size; ++ tile_y_max = ((damage_extents.y + damage_extents.height + tile_size - 1) / ++ tile_size); ++ ++ tile_damage_region = cairo_region_create (); ++ ++ for (tile_y = tile_y_min; tile_y <= tile_y_max; tile_y++) ++ { ++ for (tile_x = tile_x_min; tile_x <= tile_x_max; tile_x++) ++ { ++ cairo_rectangle_int_t tile = { ++ .x = tile_x * tile_size, ++ .y = tile_y * tile_size, ++ .width = tile_size, ++ .height = tile_size, ++ }; + +- if (!priv->shadow.framebuffer) +- return; ++ if (cairo_region_contains_rectangle (damage_region, &tile) == ++ CAIRO_REGION_OVERLAP_OUT) ++ continue; + +- if (swap_region->width == 0 || swap_region->height == 0) ++ _clutter_util_rectangle_intersection (&tile, &fb_rect, &tile); ++ ++ if (is_tile_dirty (&tile, current_data, prev_data, bpp, stride)) ++ cairo_region_union_rectangle (tile_damage_region, &tile); ++ } ++ } ++ ++ if (!cogl_dma_buf_handle_sync_read_end (prev_dma_buf_handle, error)) + { +- int width, height; ++ g_warning ("Failed to end DMA buffer read synchronization: %s", ++ (*error)->message); ++ g_clear_error (error); ++ } + +- width = cogl_framebuffer_get_width (priv->framebuffer); +- height = cogl_framebuffer_get_height (priv->framebuffer); +- if (!cogl_blit_framebuffer (priv->shadow.framebuffer, +- priv->framebuffer, +- 0, 0, +- 0, 0, +- width, height, +- &error)) +- g_warning ("Failed to blit shadow buffer: %s", error->message); ++ if (!cogl_dma_buf_handle_sync_read_end (current_dma_buf_handle, error)) ++ { ++ g_warning ("Failed to end DMA buffer read synchronization: %s", ++ (*error)->message); ++ g_clear_error (error); ++ } ++ ++ cogl_dma_buf_handle_munmap (prev_dma_buf_handle, prev_data, NULL); ++ cogl_dma_buf_handle_munmap (current_dma_buf_handle, current_data, NULL); ++ ++ cairo_region_intersect (tile_damage_region, damage_region); ++ ++ return tile_damage_region; ++ ++err_mmap_current: ++ cogl_dma_buf_handle_munmap (prev_dma_buf_handle, prev_data, NULL); ++ ++err_mmap_prev: ++ cogl_dma_buf_handle_sync_read_end (current_dma_buf_handle, NULL); ++ ++err_sync_read_current: ++ cogl_dma_buf_handle_sync_read_end (prev_dma_buf_handle, NULL); ++ ++ return NULL; ++} ++ ++static void ++swap_dma_buf_framebuffer (ClutterStageView *view) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ int next_idx; ++ CoglDmaBufHandle *next_dma_buf_handle; ++ CoglOffscreen *next_framebuffer; ++ ++ next_idx = ((priv->shadow.dma_buf.current_idx + 1) % ++ G_N_ELEMENTS (priv->shadow.dma_buf.handles)); ++ priv->shadow.dma_buf.current_idx = next_idx; ++ ++ next_dma_buf_handle = priv->shadow.dma_buf.handles[next_idx]; ++ next_framebuffer = ++ cogl_dma_buf_handle_get_framebuffer (next_dma_buf_handle); ++ cogl_clear_object (&priv->shadow.framebuffer); ++ priv->shadow.framebuffer = cogl_object_ref (next_framebuffer); ++} ++ ++static void ++copy_shadowfb_to_onscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ClutterDamageHistory *damage_history = priv->shadow.dma_buf.damage_history; ++ cairo_region_t *damage_region; ++ int age; ++ int i; ++ ++ if (swap_region->width == 0 || swap_region->height == 0) ++ { ++ cairo_rectangle_int_t full_damage = { ++ .width = cogl_framebuffer_get_width (priv->framebuffer), ++ .height = cogl_framebuffer_get_height (priv->framebuffer), ++ }; ++ damage_region = cairo_region_create_rectangle (&full_damage); + } + else + { ++ damage_region = cairo_region_create_rectangle (swap_region); ++ } ++ ++ if (is_shadowfb_double_buffered (view)) ++ { ++ CoglOnscreen *onscreen = COGL_ONSCREEN (priv->framebuffer); ++ cairo_region_t *changed_region; ++ ++ if (cogl_onscreen_get_frame_counter (onscreen) >= 1) ++ { ++ g_autoptr (GError) error = NULL; ++ ++ changed_region = find_damaged_tiles (view, damage_region, &error); ++ if (!changed_region) ++ { ++ int other_dma_buf_idx; ++ ++ g_warning ("Disabling actual damage detection: %s", ++ error->message); ++ ++ other_dma_buf_idx = ++ flip_dma_buf_idx (priv->shadow.dma_buf.current_idx); ++ g_clear_pointer (&priv->shadow.dma_buf.handles[other_dma_buf_idx], ++ cogl_dma_buf_handle_free); ++ } ++ } ++ else ++ { ++ changed_region = cairo_region_copy (damage_region); ++ } ++ ++ if (changed_region) ++ { ++ cairo_rectangle_int_t changed_extents; ++ int buffer_age; ++ ++ cairo_region_get_extents (changed_region, &changed_extents); ++ clutter_damage_history_record (damage_history, &changed_extents); ++ ++ buffer_age = cogl_onscreen_get_buffer_age (onscreen); ++ if (clutter_damage_history_is_age_valid (damage_history, buffer_age)) ++ { ++ for (age = 1; age <= buffer_age; age++) ++ { ++ const cairo_rectangle_int_t *old_damage; ++ ++ old_damage = clutter_damage_history_lookup (damage_history, age); ++ cairo_region_union_rectangle (changed_region, old_damage); ++ } ++ ++ cairo_region_destroy (damage_region); ++ damage_region = g_steal_pointer (&changed_region); ++ } ++ else ++ { ++ cairo_region_destroy (changed_region); ++ } ++ ++ clutter_damage_history_step (damage_history); ++ } ++ } ++ ++ if (0) ++ { ++ CoglColor clear_color; ++ ++ cogl_color_init_from_4ub (&clear_color, ++ 0, 0, 0, 0); ++ cogl_framebuffer_clear (priv->framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); ++ } ++ ++ for (i = 0; i < cairo_region_num_rectangles (damage_region); i++) ++ { ++ g_autoptr (GError) error = NULL; ++ cairo_rectangle_int_t rect; ++ ++ cairo_region_get_rectangle (damage_region, i, &rect); ++ + if (!cogl_blit_framebuffer (priv->shadow.framebuffer, + priv->framebuffer, +- swap_region->x, swap_region->y, +- swap_region->x, swap_region->y, +- swap_region->width, swap_region->height, ++ rect.x, rect.y, ++ rect.x, rect.y, ++ rect.width, rect.height, + &error)) + { + g_warning ("Failed to blit shadow buffer: %s", error->message); ++ cairo_region_destroy (damage_region); + return; + } + } ++ ++ cairo_region_destroy (damage_region); ++ ++ if (is_shadowfb_double_buffered (view)) ++ swap_dma_buf_framebuffer (view); ++} ++ ++void ++clutter_stage_view_before_swap_buffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *swap_region) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->shadow.framebuffer) ++ copy_shadowfb_to_onscreen (view, swap_region); + } + + float +@@ -407,6 +738,47 @@ clutter_stage_view_get_scale (ClutterStageView *view) + return priv->scale; + } + ++typedef void (*FrontBufferCallback) (CoglFramebuffer *framebuffer, ++ gconstpointer user_data); ++ ++static void ++clutter_stage_view_foreach_front_buffer (ClutterStageView *view, ++ FrontBufferCallback callback, ++ gconstpointer user_data) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->offscreen) ++ { ++ callback (priv->offscreen, user_data); ++ } ++ else if (priv->shadow.framebuffer) ++ { ++ if (is_shadowfb_double_buffered (view)) ++ { ++ int i; ++ ++ for (i = 0; i < G_N_ELEMENTS (priv->shadow.dma_buf.handles); i++) ++ { ++ CoglDmaBufHandle *handle = priv->shadow.dma_buf.handles[i]; ++ CoglFramebuffer *framebuffer = ++ cogl_dma_buf_handle_get_framebuffer (handle); ++ ++ callback (framebuffer, user_data); ++ } ++ } ++ else ++ { ++ callback (priv->shadow.framebuffer, user_data); ++ } ++ } ++ else ++ { ++ callback (priv->framebuffer, user_data); ++ } ++} ++ + gboolean + clutter_stage_view_is_dirty_viewport (ClutterStageView *view) + { +@@ -425,6 +797,19 @@ clutter_stage_view_invalidate_viewport (ClutterStageView *view) + priv->dirty_viewport = TRUE; + } + ++static void ++set_framebuffer_viewport (CoglFramebuffer *framebuffer, ++ gconstpointer user_data) ++{ ++ const ClutterRect *rect = user_data; ++ ++ cogl_framebuffer_set_viewport (framebuffer, ++ rect->origin.x, ++ rect->origin.y, ++ rect->size.width, ++ rect->size.height); ++} ++ + void + clutter_stage_view_set_viewport (ClutterStageView *view, + float x, +@@ -434,11 +819,17 @@ clutter_stage_view_set_viewport (ClutterStageView *view, + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +- CoglFramebuffer *framebuffer; ++ ClutterRect rect; + + priv->dirty_viewport = FALSE; +- framebuffer = clutter_stage_view_get_framebuffer (view); +- cogl_framebuffer_set_viewport (framebuffer, x, y, width, height); ++ ++ rect = (ClutterRect) { ++ .origin = { .x = x, .y = y }, ++ .size = { .width = width, .height = height }, ++ }; ++ clutter_stage_view_foreach_front_buffer (view, ++ set_framebuffer_viewport, ++ &rect); + } + + gboolean +@@ -450,6 +841,13 @@ clutter_stage_view_is_dirty_projection (ClutterStageView *view) + return priv->dirty_projection; + } + ++static void ++set_framebuffer_projection_matrix (CoglFramebuffer *framebuffer, ++ gconstpointer user_data) ++{ ++ cogl_framebuffer_set_projection_matrix (framebuffer, user_data); ++} ++ + void + clutter_stage_view_invalidate_projection (ClutterStageView *view) + { +@@ -465,11 +863,11 @@ clutter_stage_view_set_projection (ClutterStageView *view, + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); +- CoglFramebuffer *framebuffer; + + priv->dirty_projection = FALSE; +- framebuffer = clutter_stage_view_get_framebuffer (view); +- cogl_framebuffer_set_projection_matrix (framebuffer, matrix); ++ clutter_stage_view_foreach_front_buffer (view, ++ set_framebuffer_projection_matrix, ++ matrix); + } + + void +@@ -593,10 +991,20 @@ clutter_stage_view_dispose (GObject *object) + ClutterStageView *view = CLUTTER_STAGE_VIEW (object); + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); ++ int i; + + g_clear_pointer (&priv->name, g_free); + g_clear_pointer (&priv->framebuffer, cogl_object_unref); ++ + g_clear_pointer (&priv->shadow.framebuffer, cogl_object_unref); ++ for (i = 0; i < G_N_ELEMENTS (priv->shadow.dma_buf.handles); i++) ++ { ++ g_clear_pointer (&priv->shadow.dma_buf.handles[i], ++ cogl_dma_buf_handle_free); ++ } ++ g_clear_pointer (&priv->shadow.dma_buf.damage_history, ++ clutter_damage_history_free); ++ + g_clear_pointer (&priv->offscreen, cogl_object_unref); + g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); + +diff --git a/clutter/clutter/clutter-util.c b/clutter/clutter/clutter-util.c +index ed52b69774..834adae39a 100644 +--- a/clutter/clutter/clutter-util.c ++++ b/clutter/clutter/clutter-util.c +@@ -210,6 +210,28 @@ _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1, + } + } + ++gboolean ++_clutter_util_rectangle_contains (const cairo_rectangle_int_t *src1, ++ const cairo_rectangle_int_t *src2) ++{ ++ int x1, y1, x2, y2; ++ ++ x1 = MAX (src1->x, src2->x); ++ y1 = MAX (src1->y, src2->y); ++ ++ x2 = MIN (src1->x + (int) src1->width, src2->x + (int) src2->width); ++ y2 = MIN (src1->y + (int) src1->height, src2->y + (int) src2->height); ++ ++ if (x1 >= x2 || y1 >= y2) ++ { ++ return FALSE; ++ } ++ else ++ { ++ return TRUE; ++ } ++} ++ + float + _clutter_util_matrix_determinant (const ClutterMatrix *matrix) + { +-- +2.28.0 + + +From 9968d4aeefc2c47a63e12f977dad031672a63abe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= +Date: Sat, 7 Mar 2020 20:29:09 +0100 +Subject: [PATCH 20/20] clutter/stage-cogl: Use view fb instead of onscreen fb + for debug-drawing + +We need to use the framebuffer of the view instead of the onscreen +framebuffer when painting the damage region, otherwise the redraw clips +on rotated monitors won't be shown correctly. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1237 +(cherry picked from commit 8e1bd64e05c3098fcce4f916f9e4468decb8f30c) +--- + clutter/clutter/cogl/clutter-stage-cogl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 11273ec894..3f1f609c4e 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -353,7 +353,7 @@ paint_damage_region (ClutterStageWindow *stage_window, + ClutterStageView *view, + cairo_rectangle_int_t *swap_region) + { +- CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); ++ 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); +-- +2.28.0 + diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec index dd96fb4..9d1f1fd 100644 --- a/SPECS/mutter.spec +++ b/SPECS/mutter.spec @@ -8,7 +8,7 @@ Name: mutter Version: 3.32.2 -Release: 48%{?dist} +Release: 49%{?dist} Summary: Window and compositing manager based on Clutter License: GPLv2+ @@ -159,6 +159,9 @@ Patch501: 0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch Patch502: 0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch Patch503: 0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch +# Add tile based shadow buffer damage tracking (#1670273) +Patch504: shadow-buffer-tile-damage.patch + BuildRequires: chrpath BuildRequires: pango-devel BuildRequires: startup-notification-devel @@ -300,9 +303,9 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %{_datadir}/mutter-%{mutter_api_version}/tests %changelog -* Mon Sep 21 2020 Jonas Ådahl - 3.32.2-48 -- Fix GLX stereo buffer rebase error - Resolves: #1880339 +* Tue Nov 10 2020 Jonas Ådahl - 3.32.2-49 +- Add tile based shadow buffer damage tracking + Resolves: #1670273 * Thu Sep 03 2020 Florian Müllner - 3.32.2-47 - Fix screen sharing on wayland