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 <[email protected]> > > 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 > [email protected] > https://lists.freedesktop.org/mailman/listinfo/wayland-devel _______________________________________________ wayland-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/wayland-devel
