Currently this doesn't actually really do anything, but will be used in
the future to track the state for both modeset and repaint requests.
Completion of the request gives us a single request-completion path for
both pageflip and vblank events.

Differential Revision: https://phabricator.freedesktop.org/D1497

Signed-off-by: Daniel Stone <dani...@collabora.com>
---
 libweston/compositor-drm.c | 319 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 273 insertions(+), 46 deletions(-)

v9: Adapt to drm_pending_state introduction.

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2d293fd..85c9dba 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -146,6 +146,19 @@ struct drm_property_info {
        struct drm_property_enum_info *enum_values; /**< array of enum values */
 };
 
+/**
+ * Mode for drm_output_state_duplicate.
+ */
+enum drm_output_state_duplicate_mode {
+       DRM_OUTPUT_STATE_CLEAR_PLANES, /**< reset all planes to off */
+       DRM_OUTPUT_STATE_PRESERVE_PLANES, /**< preserve plane state */
+};
+
+enum drm_output_state_update_mode {
+       DRM_OUTPUT_STATE_UPDATE_SYNCHRONOUS, /**< state already applied */
+       DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS, /**< pending event delivery */
+};
+
 struct drm_backend {
        struct weston_backend base;
        struct weston_compositor *compositor;
@@ -245,6 +258,23 @@ struct drm_edid {
  */
 struct drm_pending_state {
        struct drm_backend *backend;
+       struct wl_list output_list;
+};
+
+/*
+ * Output state holds the dynamic state for one Weston output, i.e. a KMS CRTC,
+ * plus >= 1 each of encoder/connector/plane. Since everything but the planes
+ * is currently statically assigned per-output, we mainly use this to track
+ * plane state.
+ *
+ * pending_state is set when the output state is owned by a pending_state,
+ * i.e. when it is being constructed and has not yet been applied. When the
+ * output state has been applied, the owning pending_state is freed.
+ */
+struct drm_output_state {
+       struct drm_pending_state *pending_state;
+       struct drm_output *output;
+       struct wl_list link;
 };
 
 /**
@@ -315,6 +345,9 @@ struct drm_output {
        struct weston_plane scanout_plane;
        struct drm_fb *fb_current, *fb_pending, *fb_last;
 
+       struct drm_output_state *state_last;
+       struct drm_output_state *state_cur;
+
        struct drm_fb *dumb[2];
        pixman_image_t *image[2];
        int current_image;
@@ -602,6 +635,9 @@ drm_output_set_cursor(struct drm_output *output);
 static void
 drm_output_update_msc(struct drm_output *output, unsigned int seq);
 
+static void
+drm_output_destroy(struct weston_output *output_base);
+
 static int
 drm_plane_crtc_supported(struct drm_output *output, struct drm_plane *plane)
 {
@@ -902,11 +938,64 @@ drm_fb_unref(struct drm_fb *fb)
        }
 }
 
-static int
-drm_view_transform_supported(struct weston_view *ev)
+/**
+ * Allocate a new, empty drm_output_state. This should not generally be used
+ * in the repaint cycle; see drm_output_state_duplicate.
+ */
+static struct drm_output_state *
+drm_output_state_alloc(struct drm_output *output,
+                      struct drm_pending_state *pending_state)
 {
-       return !ev->transform.enabled ||
-               (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
+       struct drm_output_state *state = calloc(1, sizeof(*state));
+
+       assert(state);
+       state->output = output;
+       state->pending_state = pending_state;
+       if (pending_state)
+               wl_list_insert(&pending_state->output_list, &state->link);
+       else
+               wl_list_init(&state->link);
+
+       return state;
+}
+
+/**
+ * Duplicate an existing drm_output_state into a new one. This is generally
+ * used during the repaint cycle, to capture the existing state of an output
+ * and modify it to create a new state to be used.
+ *
+ * The mode determines whether the output will be reset to an a blank state,
+ * or an exact mirror of the current state.
+ */
+static struct drm_output_state *
+drm_output_state_duplicate(struct drm_output_state *src,
+                          struct drm_pending_state *pending_state,
+                          enum drm_output_state_duplicate_mode plane_mode)
+{
+       struct drm_output_state *dst = malloc(sizeof(*dst));
+
+       assert(dst);
+       memcpy(dst, src, sizeof(*dst));
+       dst->pending_state = pending_state;
+       if (pending_state)
+               wl_list_insert(&pending_state->output_list, &dst->link);
+       else
+               wl_list_init(&dst->link);
+
+       return dst;
+}
+
+/**
+ * Free an unused drm_output_state.
+ */
+static void
+drm_output_state_free(struct drm_output_state *state)
+{
+       if (!state)
+               return;
+
+       wl_list_remove(&state->link);
+       free(state);
 }
 
 /**
@@ -928,6 +1017,7 @@ drm_pending_state_alloc(struct drm_backend *backend)
                return NULL;
 
        ret->backend = backend;
+       wl_list_init(&ret->output_list);
 
        return ret;
 }
@@ -945,6 +1035,98 @@ drm_pending_state_free(struct drm_pending_state 
*pending_state)
        free(pending_state);
 }
 
+/**
+ * Find an output state in a pending state
+ *
+ * Given a pending_state structure, find the output_state for a particular
+ * output.
+ *
+ * @param pending_state Pending state structure to search
+ * @param output Output to find state for
+ * @returns Output state if present, or NULL if not
+ */
+static struct drm_output_state *
+drm_pending_state_get_output(struct drm_pending_state *pending_state,
+                            struct drm_output *output)
+{
+       struct drm_output_state *output_state;
+
+       wl_list_for_each(output_state, &pending_state->output_list, link) {
+               if (output_state->output == output)
+                       return output_state;
+       }
+
+       return NULL;
+}
+
+/**
+ * Mark a drm_output_state (the output's last state) as complete. This handles
+ * any post-completion actions such as updating the repaint timer, disabling 
the
+ * output, and finally freeing the state.
+ */
+static void
+drm_output_update_complete(struct drm_output *output, uint32_t flags,
+                          unsigned int sec, unsigned int usec)
+{
+       struct timespec ts;
+
+       drm_output_state_free(output->state_last);
+       output->state_last = NULL;
+
+       if (output->destroy_pending) {
+               drm_output_destroy(&output->base);
+               goto out;
+       } else if (output->disable_pending) {
+               weston_output_disable(&output->base);
+               goto out;
+       }
+
+       ts.tv_sec = sec;
+       ts.tv_nsec = usec * 1000;
+       weston_output_finish_frame(&output->base, &ts, flags);
+
+       /* We can't call this from frame_notify, because the output's
+        * repaint needed flag is cleared just after that */
+       if (output->recorder)
+               weston_output_schedule_repaint(&output->base);
+
+out:
+       output->destroy_pending = 0;
+       output->disable_pending = 0;
+}
+
+/**
+ * Mark an output state as current on the output, i.e. it has been
+ * submitted to the kernel. The mode argument determines whether this
+ * update will be applied synchronously (e.g. when calling drmModeSetCrtc),
+ * or asynchronously (in which case we wait for events to complete).
+ */
+static void
+drm_output_assign_state(struct drm_output_state *state,
+                       enum drm_output_state_update_mode mode)
+{
+       struct drm_output *output = state->output;
+
+       assert(!output->state_last);
+
+       if (mode == DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+               output->state_last = output->state_cur;
+       else
+               drm_output_state_free(output->state_cur);
+
+       wl_list_remove(&state->link);
+       wl_list_init(&state->link);
+       output->state_cur = state;
+}
+
+
+static int
+drm_view_transform_supported(struct weston_view *ev)
+{
+       return !ev->transform.enabled ||
+               (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
+}
+
 static uint32_t
 drm_output_check_scanout_format(struct drm_output *output,
                                struct weston_surface *es, struct gbm_bo *bo)
@@ -976,9 +1158,10 @@ drm_output_check_scanout_format(struct drm_output *output,
 }
 
 static struct weston_plane *
-drm_output_prepare_scanout_view(struct drm_output *output,
+drm_output_prepare_scanout_view(struct drm_output_state *output_state,
                                struct weston_view *ev)
 {
+       struct drm_output *output = output_state->output;
        struct drm_backend *b = to_drm_backend(output->base.compositor);
        struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
        struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
@@ -1045,8 +1228,9 @@ drm_output_prepare_scanout_view(struct drm_output *output,
 }
 
 static struct drm_fb *
-drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
+drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
 {
+       struct drm_output *output = state->output;
        struct drm_backend *b = to_drm_backend(output->base.compositor);
        struct gbm_bo *bo;
        struct drm_fb *ret;
@@ -1072,8 +1256,10 @@ drm_output_render_gl(struct drm_output *output, 
pixman_region32_t *damage)
 }
 
 static struct drm_fb *
-drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)
+drm_output_render_pixman(struct drm_output_state *state,
+                        pixman_region32_t *damage)
 {
+       struct drm_output *output = state->output;
        struct weston_compositor *ec = output->base.compositor;
        pixman_region32_t total_damage, previous_damage;
 
@@ -1099,8 +1285,9 @@ drm_output_render_pixman(struct drm_output *output, 
pixman_region32_t *damage)
 }
 
 static void
-drm_output_render(struct drm_output *output, pixman_region32_t *damage)
+drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
 {
+       struct drm_output *output = state->output;
        struct weston_compositor *c = output->base.compositor;
        struct drm_backend *b = to_drm_backend(c);
        struct drm_fb *fb;
@@ -1111,9 +1298,9 @@ drm_output_render(struct drm_output *output, 
pixman_region32_t *damage)
                return;
 
        if (b->use_pixman)
-               fb = drm_output_render_pixman(output, damage);
+               fb = drm_output_render_pixman(state, damage);
        else
-               fb = drm_output_render_gl(output, damage);
+               fb = drm_output_render_gl(state, damage);
 
        if (!fb)
                return;
@@ -1175,6 +1362,8 @@ drm_output_repaint(struct weston_output *output_base,
                   pixman_region32_t *damage,
                   void *repaint_data)
 {
+       struct drm_pending_state *pending_state = repaint_data;
+       struct drm_output_state *state;
        struct drm_output *output = to_drm_output(output_base);
        struct drm_backend *backend =
                to_drm_backend(output->base.compositor);
@@ -1183,13 +1372,24 @@ drm_output_repaint(struct weston_output *output_base,
        int ret = 0;
 
        if (output->disable_pending || output->destroy_pending)
-               return -1;
+               goto err;
+
+       assert(!output->state_last);
+
+       /* If planes have been disabled in the core, we might not have
+        * hit assign_planes at all, so might not have valid output state
+        * here. */
+       state = drm_pending_state_get_output(pending_state, output);
+       if (!state)
+               state = drm_output_state_duplicate(output->state_cur,
+                                                  pending_state,
+                                                  
DRM_OUTPUT_STATE_CLEAR_PLANES);
 
        assert(!output->fb_last);
 
-       drm_output_render(output, damage);
+       drm_output_render(state, damage);
        if (!output->fb_pending)
-               return -1;
+               goto err;
 
        mode = container_of(output->base.current_mode, struct drm_mode, base);
        if (!output->fb_current ||
@@ -1200,7 +1400,7 @@ drm_output_repaint(struct weston_output *output_base,
                                     &mode->mode_info);
                if (ret) {
                        weston_log("set mode failed: %m\n");
-                       goto err_pageflip;
+                       goto err;
                }
                output_base->set_dpms(output_base, WESTON_DPMS_ON);
        }
@@ -1209,7 +1409,7 @@ drm_output_repaint(struct weston_output *output_base,
                            output->fb_pending->fb_id,
                            DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
                weston_log("queueing pageflip failed: %m\n");
-               goto err_pageflip;
+               goto err;
        }
 
        output->fb_last = output->fb_current;
@@ -1275,12 +1475,13 @@ drm_output_repaint(struct weston_output *output_base,
 
        return 0;
 
-err_pageflip:
+err:
        output->cursor_view = NULL;
        if (output->fb_pending) {
                drm_fb_unref(output->fb_pending);
                output->fb_pending = NULL;
        }
+       drm_output_state_free(state);
 
        return -1;
 }
@@ -1289,6 +1490,8 @@ static void
 drm_output_start_repaint_loop(struct weston_output *output_base)
 {
        struct drm_output *output = to_drm_output(output_base);
+       struct drm_pending_state *pending_state;
+       struct drm_output_state *state;
        struct drm_backend *backend =
                to_drm_backend(output_base->compositor);
        uint32_t fb_id;
@@ -1343,16 +1546,25 @@ drm_output_start_repaint_loop(struct weston_output 
*output_base)
 
        assert(!output->page_flip_pending);
        assert(!output->fb_last);
+       assert(!output->state_last);
+
+       pending_state = drm_pending_state_alloc(backend);
+       state = drm_output_state_duplicate(output->state_cur, pending_state,
+                                          DRM_OUTPUT_STATE_PRESERVE_PLANES);
 
        if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,
                            DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
                weston_log("queueing pageflip failed: %m\n");
+               drm_output_state_free(state);
                goto finish_frame;
        }
 
        output->fb_last = drm_fb_ref(output->fb_current);
        output->page_flip_pending = 1;
 
+       drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+       free(pending_state);
+
        return;
 
 finish_frame:
@@ -1378,7 +1590,6 @@ vblank_handler(int fd, unsigned int frame, unsigned int 
sec, unsigned int usec,
 {
        struct drm_plane *s = (struct drm_plane *)data;
        struct drm_output *output = s->output;
-       struct timespec ts;
        uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
                         WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
 
@@ -1390,11 +1601,10 @@ vblank_handler(int fd, unsigned int frame, unsigned int 
sec, unsigned int usec,
        drm_fb_unref(s->fb_last);
        s->fb_last = NULL;
 
-       if (!output->page_flip_pending && !output->vblank_pending) {
-               ts.tv_sec = sec;
-               ts.tv_nsec = usec * 1000;
-               weston_output_finish_frame(&output->base, &ts, flags);
-       }
+       if (output->page_flip_pending || output->vblank_pending)
+               return;
+
+       drm_output_update_complete(output, flags, sec, usec);
 }
 
 static void
@@ -1405,7 +1615,6 @@ page_flip_handler(int fd, unsigned int frame,
                  unsigned int sec, unsigned int usec, void *data)
 {
        struct drm_output *output = data;
-       struct timespec ts;
        uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
                         WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
                         WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
@@ -1418,26 +1627,18 @@ page_flip_handler(int fd, unsigned int frame,
        drm_fb_unref(output->fb_last);
        output->fb_last = NULL;
 
-       if (output->destroy_pending)
-               drm_output_destroy(&output->base);
-       else if (output->disable_pending)
-               weston_output_disable(&output->base);
-       else if (!output->vblank_pending) {
-               ts.tv_sec = sec;
-               ts.tv_nsec = usec * 1000;
-               weston_output_finish_frame(&output->base, &ts, flags);
-
-               /* We can't call this from frame_notify, because the output's
-                * repaint needed flag is cleared just after that */
-               if (output->recorder)
-                       weston_output_schedule_repaint(&output->base);
-       }
+       if (output->vblank_pending)
+               return;
+
+       drm_output_update_complete(output, flags, sec, usec);
 }
 
 /**
  * Begin a new repaint cycle
  *
- * Called by the core compositor at the beginning of a repaint cycle.
+ * Called by the core compositor at the beginning of a repaint cycle. Creates
+ * a new pending_state structure to own any output state created by individual
+ * output repaint functions until the repaint is flushed or cancelled.
  */
 static void *
 drm_repaint_begin(struct weston_compositor *compositor)
@@ -1455,13 +1656,23 @@ drm_repaint_begin(struct weston_compositor *compositor)
  * Flush a repaint set
  *
  * Called by the core compositor when a repaint cycle has been completed
- * and should be flushed.
+ * and should be flushed. Frees the pending state, transitioning ownership
+ * of the output state from the pending state, to the update itself. When
+ * the update completes (see drm_output_update_complete), the output
+ * state will be freed.
  */
 static void
 drm_repaint_flush(struct weston_compositor *compositor, void *repaint_data)
 {
        struct drm_backend *b = to_drm_backend(compositor);
        struct drm_pending_state *pending_state = repaint_data;
+       struct drm_output_state *output_state, *tmp;
+
+       wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+                             link) {
+               drm_output_assign_state(output_state,
+                                       DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+       }
 
        drm_pending_state_free(pending_state);
        b->repaint_data = NULL;
@@ -1513,9 +1724,10 @@ drm_output_check_plane_format(struct drm_plane *p,
 }
 
 static struct weston_plane *
-drm_output_prepare_overlay_view(struct drm_output *output,
+drm_output_prepare_overlay_view(struct drm_output_state *output_state,
                                struct weston_view *ev)
 {
+       struct drm_output *output = output_state->output;
        struct weston_compositor *ec = output->base.compositor;
        struct drm_backend *b = to_drm_backend(ec);
        struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
@@ -1699,9 +1911,10 @@ drm_output_prepare_overlay_view(struct drm_output 
*output,
 }
 
 static struct weston_plane *
-drm_output_prepare_cursor_view(struct drm_output *output,
+drm_output_prepare_cursor_view(struct drm_output_state *output_state,
                               struct weston_view *ev)
 {
+       struct drm_output *output = output_state->output;
        struct drm_backend *b = to_drm_backend(output->base.compositor);
        struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
        struct wl_shm_buffer *shmbuf;
@@ -1832,11 +2045,18 @@ static void
 drm_assign_planes(struct weston_output *output_base, void *repaint_data)
 {
        struct drm_backend *b = to_drm_backend(output_base->compositor);
+       struct drm_pending_state *pending_state = repaint_data;
        struct drm_output *output = to_drm_output(output_base);
+       struct drm_output_state *state;
        struct weston_view *ev, *next;
        pixman_region32_t overlap, surface_overlap;
        struct weston_plane *primary, *next_plane;
 
+       assert(!output->state_last);
+       state = drm_output_state_duplicate(output->state_cur,
+                                          pending_state,
+                                          DRM_OUTPUT_STATE_CLEAR_PLANES);
+
        /*
         * Find a surface for each sprite in the output using some heuristics:
         * 1) size
@@ -1885,11 +2105,11 @@ drm_assign_planes(struct weston_output *output_base, 
void *repaint_data)
                if (pixman_region32_not_empty(&surface_overlap))
                        next_plane = primary;
                if (next_plane == NULL)
-                       next_plane = drm_output_prepare_cursor_view(output, ev);
+                       next_plane = drm_output_prepare_cursor_view(state, ev);
                if (next_plane == NULL)
-                       next_plane = drm_output_prepare_scanout_view(output, 
ev);
+                       next_plane = drm_output_prepare_scanout_view(state, ev);
                if (next_plane == NULL)
-                       next_plane = drm_output_prepare_overlay_view(output, 
ev);
+                       next_plane = drm_output_prepare_overlay_view(state, ev);
                if (next_plane == NULL)
                        next_plane = primary;
 
@@ -3192,7 +3412,7 @@ drm_output_destroy(struct weston_output *base)
        struct drm_mode *drm_mode, *next;
        drmModeCrtcPtr origcrtc = output->original_crtc;
 
-       if (output->page_flip_pending) {
+       if (output->page_flip_pending || output->vblank_pending) {
                output->destroy_pending = 1;
                weston_log("destroy output while page flip pending\n");
                return;
@@ -3222,6 +3442,9 @@ drm_output_destroy(struct weston_output *base)
        if (output->backlight)
                backlight_destroy(output->backlight);
 
+       assert(!output->state_last);
+       drm_output_state_free(output->state_cur);
+
        free(output);
 }
 
@@ -3231,7 +3454,7 @@ drm_output_disable(struct weston_output *base)
        struct drm_output *output = to_drm_output(base);
        struct drm_backend *b = to_drm_backend(base->compositor);
 
-       if (output->page_flip_pending) {
+       if (output->page_flip_pending || output->vblank_pending) {
                output->disable_pending = 1;
                return -1;
        }
@@ -3244,6 +3467,8 @@ drm_output_disable(struct weston_output *base)
        weston_log("Disabling output %s\n", output->base.name);
        drmModeSetCrtc(b->drm.fd, output->crtc_id,
                       0, 0, 0, 0, 0, NULL);
+       drm_output_state_free(output->state_cur);
+       output->state_cur = NULL;
 
        return 0;
 }
@@ -3302,6 +3527,8 @@ create_output_for_connector(struct drm_backend *b,
 
        find_and_parse_output_edid(b, output, props);
 
+       output->state_cur = drm_output_state_alloc(output, NULL);
+
        weston_output_init(&output->base, b->compositor);
 
        wl_list_init(&output->base.mode_list);
-- 
2.9.3

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

Reply via email to