Track the view a plane is currently displaying, so we can avoid
needlessly recreating buffers.

Signed-off-by: Daniel Stone <dani...@collabora.com>
---
 src/compositor-drm.c | 123 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 104 insertions(+), 19 deletions(-)

diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index b3053c0..bb909b9 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -284,6 +284,9 @@ struct drm_plane {
 
        enum wdrm_plane_type type;
 
+       struct weston_view *view;
+       struct wl_listener view_destroy;
+
        struct drm_fb *current, *next;
        struct drm_output *output;
        struct drm_compositor *compositor;
@@ -1220,6 +1223,17 @@ drm_output_check_scanout_format(struct drm_output 
*output,
        return 0;
 }
 
+/**
+ * Try to use the primary DRM plane to directly display a full-screen view
+ *
+ * If a surface covers an entire output and is unoccluded, attempt to directly
+ * pageflip the primary DRM plane (not to be confused with the primary Weston
+ * plane) to the buffer. In legacy DRM usage, this will use drmModePageFlip;
+ * in atomic, this is just another plane.
+ *
+ * @param output Output to target
+ * @param ev View to prospectively use the primary plane
+ */
 static struct weston_plane *
 drm_output_prepare_scanout_view(struct drm_output *output,
                                struct weston_view *ev)
@@ -1920,6 +1934,7 @@ drm_output_prepare_overlay_view(struct drm_output *output,
        struct drm_compositor *c = (struct drm_compositor *)ec;
        struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
        struct drm_plane *p;
+       struct drm_plane *cur = NULL;
        int found = 0;
        struct gbm_bo *bo;
        pixman_region32_t dest_rect, src_rect;
@@ -1954,42 +1969,87 @@ drm_output_prepare_overlay_view(struct drm_output 
*output,
        if (!drm_view_transform_supported(ev))
                return NULL;
 
+       /* Find the current view this plane is assigned to, if any. */
+       wl_list_for_each(p, &c->plane_list, link) {
+               if (p->view != ev || p->output != output)
+                       continue;
+
+               cur = p;
+               break;
+       }
+
+       bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
+                          ev->surface->buffer_ref.buffer->resource,
+                          GBM_BO_USE_SCANOUT);
+
        wl_list_for_each(p, &c->plane_list, link) {
+               if (!bo)
+                       continue;
+
                if (!drm_plane_crtc_supported(output, p->possible_crtcs))
                        continue;
 
                if (p->type != WDRM_PLANE_TYPE_OVERLAY)
                        continue;
 
-               if (!p->next) {
+               format = drm_output_check_plane_format(p, ev, bo);
+               if (format == 0)
+                       continue;
+
+               /* XXX: At this point, we need two runs through assign_planes;
+                *      one to prepare any necessary views, and see if there
+                *      are any currently-unused planes. This process may
+                *      actually free up some planes for other views to use;
+                *      if any planes have been freed up, we should do another
+                *      pass to see if any planeless views can use any planes
+                *      which have just been freed. But we want to cache what
+                *      we can, so we're not, e.g., calling gbm_bo_import
+                *      twice. */
+               if (!p->current && !p->next && !p->view) {
                        found = 1;
+                       assert(p->output == NULL);
                        break;
                }
-       }
-
-       /* No sprites available */
-       if (!found)
-               return NULL;
 
-       bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
-                          ev->surface->buffer_ref.buffer->resource,
-                          GBM_BO_USE_SCANOUT);
-       if (!bo)
-               return NULL;
+               /* XXX: Factor out all the above to see if we can just use the
+                *      current plane still. */
+               if (p == cur) {
+                       found = 1;
+                       break;
+               }
+       }
 
-       format = drm_output_check_plane_format(p, ev, bo);
-       if (format == 0) {
-               gbm_bo_destroy(bo);
-               return NULL;
+       /* If this buffer (view) was previously on a plane, but is being moved
+        * off, then get rid of it. */
+       if (cur && (!found || cur != p)) {
+               assert(cur->next == NULL);
+               cur->output = NULL;
+               wl_list_remove(&cur->view_destroy.link);
+               cur->view = NULL;
+               cur = NULL;
        }
 
-       p->next = drm_fb_get_from_bo(bo, c, format);
-       if (!p->next) {
-               gbm_bo_destroy(bo);
+       /* No planes available. */
+       if (!found) {
+               if (bo)
+                       gbm_bo_destroy(bo);
                return NULL;
        }
 
-       drm_fb_set_buffer(p->next, ev->surface->buffer_ref.buffer);
+       if (cur && cur->current &&
+           cur->current->buffer_ref.buffer == ev->surface->buffer_ref.buffer) {
+               p->next = p->current;
+       } else {
+               p->next = drm_fb_get_from_bo(bo, c, format);
+               if (!p->next) {
+                       gbm_bo_destroy(bo);
+                       return NULL;
+               }
+               drm_fb_set_buffer(p->next, ev->surface->buffer_ref.buffer);
+       }
+       p->output = output;
+       p->view = ev;
+       wl_signal_add(&ev->destroy_signal, &p->view_destroy);
 
        box = pixman_region32_extents(&ev->transform.boundingbox);
        p->base.x = box->x1;
@@ -2273,6 +2333,8 @@ drm_assign_planes(struct weston_output *output_base)
                                        plane->next = NULL;
                                }
                                plane->output = NULL;
+                               plane->view = NULL;
+                               wl_list_remove(&plane->view_destroy.link);
                                next_plane = NULL;
                        }
                }
@@ -3675,6 +3737,27 @@ err_free:
 }
 
 /**
+ * Handle destruction of a view active on a KMS plane
+ *
+ * Called when the weston_view currently being displayed on a KMS
+ * plane has been destroyed. The very act of destroying a view on
+ * an active plane will cause a repaint to be scheduled, but we
+ * still need to disable the plane itself.
+ *
+ * @param listener view_destroy listener struct from the plane
+ * @param data The view being destroyed
+ */
+static void
+drm_plane_view_destroyed(struct wl_listener *listener, void *data)
+{
+       struct drm_plane *plane =
+               container_of(listener, struct drm_plane, view_destroy);
+
+       assert(plane->view == data);
+       plane->view = NULL;
+}
+
+/**
  * Create a drm_plane for a hardware plane
  *
  * Creates one drm_plane structure for a hardware plane, and initialises its
@@ -3700,6 +3783,7 @@ drm_plane_create(struct drm_compositor *ec, const 
drmModePlane *kplane)
                return NULL;
        }
 
+       weston_plane_init(&plane->base, &ec->base, 0, 0);
        plane->possible_crtcs = kplane->possible_crtcs;
        plane->plane_id = kplane->plane_id;
        plane->current = NULL;
@@ -3707,6 +3791,7 @@ drm_plane_create(struct drm_compositor *ec, const 
drmModePlane *kplane)
        plane->output = NULL;
        plane->compositor = ec;
        plane->count_formats = kplane->count_formats;
+       plane->view_destroy.notify = drm_plane_view_destroyed;
        memcpy(plane->formats, kplane->formats,
               kplane->count_formats * sizeof(kplane->formats[0]));
 
-- 
2.4.3

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to