[PATCH weston v5 30/42] compositor-drm: Introduce drm_output_state structure
Daniel Stone
daniels at collabora.com
Wed Nov 16 14:25:22 UTC 2016
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) {
+ 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
More information about the wayland-devel
mailing list