[PATCH weston v5 30/42] compositor-drm: Introduce drm_output_state structure

Fabien DESSENNE fabien.dessenne at st.com
Mon Nov 21 14:58:33 UTC 2016


Hi Daniel



On 11/16/2016 03:25 PM, Daniel Stone wrote:
> 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.
>
> Signed-off-by: Daniel Stone <daniels at collabora.com>
>
> Differential Revision: https://phabricator.freedesktop.org/D1497
> ---
>   libweston/compositor-drm.c | 230 ++++++++++++++++++++++++++++++++++++++-------
>   1 file changed, 197 insertions(+), 33 deletions(-)
>
> diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
> index c24129b..1089d77 100644
> --- a/libweston/compositor-drm.c
> +++ b/libweston/compositor-drm.c
> @@ -120,6 +120,19 @@ struct plane_properties {
>          uint64_t value;
>   };
>
> +/**
> + * 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;
> @@ -206,6 +219,16 @@ struct drm_edid {
>   };
>
>   /**
> + * 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.
> + */
> +struct drm_output_state {
> +       struct drm_output *output;
> +};
> +
> +/**
>    * A plane represents one buffer, positioned within a CRTC, and stacked
>    * relative to other planes on the same CRTC.
>    *
> @@ -274,6 +297,10 @@ struct drm_output {
>          struct drm_fb *fb_current, *fb_pending, *fb_last;
>          struct backlight *backlight;
>
> +       struct drm_output_state *state_last;
> +       struct drm_output_state *state_cur;
> +       struct drm_output_state *state_pending;
> +
>          struct drm_fb *dumb[2];
>          pixman_image_t *image[2];
>          int current_image;
> @@ -641,6 +668,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)
>   {
> @@ -890,6 +920,113 @@ drm_fb_unref(struct drm_fb *fb)
>          }
>   }
>
> +/**
> + * 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_output_state *state = calloc(1, sizeof(*state));
> +
> +       state->output = output;
> +
> +       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,
> +                          enum drm_output_state_duplicate_mode plane_mode)
> +{
> +       struct drm_output_state *dst = malloc(sizeof(*dst));
> +
> +       assert(dst);
> +       memcpy(dst, src, sizeof(*dst));
> +
> +       return dst;
> +}
> +
> +/**
> + * Free an unused drm_output_state.
> + */
> +static void
> +drm_output_state_free(struct drm_output_state *state)
> +{
> +       if (!state)
> +               return;
> +
> +       free(state);
> +}
> +
> +/**
> + * 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) {

Remove 'else'

> +               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);
> +
> +       output->state_cur = state;
> +       output->state_pending = NULL;
> +}
> +
> +
>   static int
>   drm_view_transform_supported(struct weston_view *ev)
>   {
> @@ -928,9 +1065,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;
> @@ -1135,13 +1273,19 @@ 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 (!output->state_pending)
> +               output->state_pending =
> +                       drm_output_state_duplicate(output->state_cur,
> +                                                  DRM_OUTPUT_STATE_CLEAR_PLANES);
>
>          assert(!output->fb_last);
>
>          drm_output_render(output, damage);
>          if (!output->fb_pending)
> -               return -1;
> +               goto err;
>
>          mode = container_of(output->base.current_mode, struct drm_mode, base);
>          if (!output->fb_current ||
> @@ -1152,7 +1296,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);
>          }
> @@ -1161,7 +1305,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;
> @@ -1225,14 +1369,19 @@ drm_output_repaint(struct weston_output *output_base,
>                  output->vblank_pending++;
>          }
>
> +       drm_output_assign_state(output->state_pending,
> +                               DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
> +
>          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(output->state_pending);
> +       output->state_pending = NULL;
>
>          return -1;
>   }
> @@ -1241,6 +1390,7 @@ static void
>   drm_output_start_repaint_loop(struct weston_output *output_base)
>   {
>          struct drm_output *output = to_drm_output(output_base);
> +       struct drm_output_state *output_state;
>          struct drm_backend *backend =
>                  to_drm_backend(output_base->compositor);
>          uint32_t fb_id;
> @@ -1295,15 +1445,24 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
>
>          assert(!output->page_flip_pending);
>          assert(!output->fb_last);
> +       assert(!output->state_last);
> +       assert(!output->state_pending);
> +
> +       output_state =
> +               drm_output_state_duplicate(output->state_cur,
> +                                          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(output_state);
>                  goto finish_frame;
>          }
>
>          output->fb_last = drm_fb_ref(output->fb_current);
>          output->page_flip_pending = 1;
> +       drm_output_assign_state(output_state,
> +                               DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
>
>          return;
>
> @@ -1331,7 +1490,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;
>
> @@ -1343,11 +1501,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
> @@ -1358,7 +1515,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;
> @@ -1371,20 +1527,10 @@ 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);
> +       if (output->vblank_pending)
> +               return;
>
> -               /* 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);
> -       }
> +       drm_output_update_complete(output, flags, sec, usec);
>   }
>
>   static uint32_t
> @@ -1417,9 +1563,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;
> @@ -1593,9 +1740,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;
> @@ -1727,10 +1875,16 @@ drm_assign_planes(struct weston_output *output_base)
>   {
>          struct drm_backend *b = to_drm_backend(output_base->compositor);
>          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);
> +       assert(!output->state_pending);
> +       state = drm_output_state_duplicate(output->state_cur,
> +                                          DRM_OUTPUT_STATE_CLEAR_PLANES);
> +
>          /*
>           * Find a surface for each sprite in the output using some heuristics:
>           * 1) size
> @@ -1779,11 +1933,11 @@ drm_assign_planes(struct weston_output *output_base)
>                  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;
>
> @@ -1807,6 +1961,8 @@ drm_assign_planes(struct weston_output *output_base)
>                  pixman_region32_fini(&surface_overlap);
>          }
>          pixman_region32_fini(&overlap);
> +
> +       output->state_pending = state;
>   }
>
>   /**
> @@ -3107,7 +3263,7 @@ drm_output_destroy(struct weston_output *base)
>          struct drm_backend *b = to_drm_backend(base->compositor);
>          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;
> @@ -3134,6 +3290,10 @@ drm_output_destroy(struct weston_output *base)
>          b->crtc_allocator &= ~(1 << output->crtc_id);
>          b->connector_allocator &= ~(1 << output->connector_id);
>
> +       assert(!output->state_last);
> +       drm_output_state_free(output->state_cur);
> +       assert(!output->state_pending);
> +
>          free(output);
>   }
>
> @@ -3143,7 +3303,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;
>          }
> @@ -3156,6 +3316,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;
>   }
> @@ -3209,6 +3371,8 @@ create_output_for_connector(struct drm_backend *b,
>          output->disable_pending = 0;
>          output->original_crtc = NULL;
>
> +       output->state_cur = drm_output_state_alloc(output);
> +
>          b->crtc_allocator |= (1 << output->crtc_id);
>          b->connector_allocator |= (1 << output->connector_id);
>
> --
> 2.9.3
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list