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 | 229 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 196 insertions(+), 33 deletions(-) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 3dd2924..106d851 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -121,6 +121,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; @@ -210,6 +223,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. * @@ -278,6 +301,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; @@ -645,6 +672,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) { @@ -905,6 +935,112 @@ 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) { + 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) { @@ -943,9 +1079,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; @@ -1149,13 +1286,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 || @@ -1166,7 +1309,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); } @@ -1175,7 +1318,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; @@ -1239,14 +1382,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; } @@ -1255,6 +1403,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; @@ -1309,15 +1458,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; @@ -1345,7 +1503,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; @@ -1357,11 +1514,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 @@ -1372,7 +1528,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; @@ -1385,20 +1540,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 @@ -1431,9 +1576,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; @@ -1606,9 +1752,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; @@ -1740,10 +1887,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 @@ -1792,11 +1945,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; @@ -1820,6 +1973,8 @@ drm_assign_planes(struct weston_output *output_base) pixman_region32_fini(&surface_overlap); } pixman_region32_fini(&overlap); + + output->state_pending = state; } /** @@ -3120,7 +3275,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; @@ -3147,6 +3302,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); } @@ -3156,7 +3315,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; } @@ -3169,6 +3328,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; } @@ -3222,6 +3383,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@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel