Discussion:
[PATCH weston v12 01/40] compositor-drm: Introduce drm_output_state structure
Daniel Stone
2017-09-26 17:15:34 UTC
Permalink
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.

This merges the timing paths for scanout and plane-but-but-atomic-plane
content.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 354 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 300 insertions(+), 54 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5e87cb41..51aacd48 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -139,6 +139,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;
@@ -233,6 +246,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;
};

/**
@@ -326,6 +356,12 @@ struct drm_output {
* repaint is flushed. */
struct drm_fb *fb_pending;

+ /* The last state submitted to the kernel for this CRTC. */
+ struct drm_output_state *state_cur;
+ /* The previously-submitted state, where the hardware has not
+ * yet acknowledged completion of state_cur. */
+ struct drm_output_state *state_last;
+
struct drm_fb *dumb[2];
pixman_image_t *image[2];
int current_image;
@@ -600,6 +636,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)
{
@@ -900,11 +939,69 @@ 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 = zalloc(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);
+
+ /* Copy the whole structure, then individually modify the
+ * pending_state, as well as the list link into our pending
+ * state. */
+ *dst = *src;
+
+ 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);
}

/**
@@ -926,6 +1023,7 @@ drm_pending_state_alloc(struct drm_backend *backend)
return NULL;

ret->backend = backend;
+ wl_list_init(&ret->output_list);

return ret;
}
@@ -933,19 +1031,122 @@ drm_pending_state_alloc(struct drm_backend *backend)
/**
* Free a drm_pending_state structure
*
- * Frees a pending_state structure.
+ * Frees a pending_state structure, as well as any output_states connected
+ * to this pending state.
*
* @param pending_state Pending state structure to free
*/
static void
drm_pending_state_free(struct drm_pending_state *pending_state)
{
+ struct drm_output_state *output_state, *tmp;
+
if (!pending_state)
return;

+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link) {
+ drm_output_state_free(output_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;
+
+ /* Stop the pageflip timer instead of rearming it here */
+ if (output->pageflip_timer)
+ wl_event_source_timer_update(output->pageflip_timer, 0);
+
+ drm_output_state_free(output->state_last);
+ output->state_last = NULL;
+
+ if (output->destroy_pending) {
+ drm_output_destroy(&output->base);
+ return;
+ } else if (output->disable_pending) {
+ weston_output_disable(&output->base);
+ output->disable_pending = 0;
+ return;
+ }
+
+ 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);
+}
+
+/**
+ * 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);
+ state->pending_state = NULL;
+
+ 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)
@@ -977,9 +1178,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;
@@ -1046,8 +1248,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;
@@ -1073,8 +1276,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;

@@ -1100,8 +1305,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;
@@ -1112,9 +1318,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;
@@ -1176,6 +1382,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 = NULL;
struct drm_output *output = to_drm_output(output_base);
struct drm_backend *backend =
to_drm_backend(output->base.compositor);
@@ -1184,7 +1392,18 @@ 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);

@@ -1198,9 +1417,9 @@ drm_output_repaint(struct weston_output *output_base,
output->cursor_plane.y = INT32_MIN;
}

- 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->state_invalid || !output->fb_current ||
@@ -1211,7 +1430,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);

@@ -1222,7 +1441,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;
@@ -1290,12 +1509,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;
}
@@ -1304,6 +1524,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 = NULL;
+ struct drm_output_state *state;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
uint32_t fb_id;
@@ -1364,10 +1586,16 @@ 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_pending_state_free(pending_state);
goto finish_frame;
}

@@ -1378,9 +1606,15 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
output->fb_last = drm_fb_ref(output->fb_current);
output->page_flip_pending = 1;

+ drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+ drm_pending_state_free(pending_state);
+
return;

finish_frame:
+ if (pending_state)
+ drm_pending_state_free(pending_state);
+
/* if we cannot page-flip, immediately finish frame */
weston_output_finish_frame(output_base, NULL,
WP_PRESENTATION_FEEDBACK_INVALID);
@@ -1403,7 +1637,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;

@@ -1415,15 +1648,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) {
- /* Stop the pageflip timer instead of rearming it here */
- if (output->pageflip_timer)
- wl_event_source_timer_update(output->pageflip_timer, 0);
+ if (output->page_flip_pending || output->vblank_pending)
+ return;

- ts.tv_sec = sec;
- ts.tv_nsec = usec * 1000;
- weston_output_finish_frame(&output->base, &ts, flags);
- }
+ drm_output_update_complete(output, flags, sec, usec);
}

static void
@@ -1434,7 +1662,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;
@@ -1447,30 +1674,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) {
- /* Stop the pageflip timer instead of rearming it here */
- if (output->pageflip_timer)
- wl_event_source_timer_update(output->pageflip_timer, 0);
-
- 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)
@@ -1488,13 +1703,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;
@@ -1546,9 +1771,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;
@@ -1732,9 +1958,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;
@@ -1865,11 +2092,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
@@ -1918,11 +2152,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;

@@ -3244,7 +3478,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;
@@ -3279,6 +3513,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);
}

@@ -3288,7 +3525,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;
}
@@ -3296,12 +3533,19 @@ drm_output_disable(struct weston_output *base)
if (output->base.enabled)
drm_output_deinit(&output->base);

+ assert(!output->fb_last);
+ assert(!output->fb_current);
+ assert(!output->fb_pending);
+
output->disable_pending = 0;

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 = drm_output_state_alloc(output, NULL);
+
return 0;
}

@@ -3373,6 +3617,8 @@ create_output_for_connector(struct drm_backend *b,
find_and_parse_output_edid(b, output, props);
drmModeFreeObjectProperties(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.14.1
Daniel Stone
2017-09-26 17:15:35 UTC
Permalink
Track dynamic plane state (CRTC, FB, position) in separate structures,
rather than as part of the plane. This will make it easier to handle
state management later, and much more closely tracks what the kernel
does with atomic modesets.

The fb_last pointer previously used in drm_plane now becomes part of
output->state_last, and is not directly visible from the plane itself.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 342 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 277 insertions(+), 65 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 51aacd48..b28e602f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -263,6 +263,31 @@ struct drm_output_state {
struct drm_pending_state *pending_state;
struct drm_output *output;
struct wl_list link;
+ struct wl_list plane_list;
+};
+
+/**
+ * Plane state holds the dynamic state for a plane: where it is positioned,
+ * and which buffer it is currently displaying.
+ *
+ * The plane state is owned by an output state, except when setting an initial
+ * state. See drm_output_state for notes on state object lifetime.
+ */
+struct drm_plane_state {
+ struct drm_plane *plane;
+ struct drm_output *output;
+ struct drm_output_state *output_state;
+
+ struct drm_fb *fb;
+
+ int32_t src_x, src_y;
+ uint32_t src_w, src_h;
+ int32_t dest_x, dest_y;
+ uint32_t dest_w, dest_h;
+
+ bool complete;
+
+ struct wl_list link; /* drm_output_state::plane_list */
};

/**
@@ -281,11 +306,8 @@ struct drm_output_state {
* are referred to as 'sprites'.
*/
struct drm_plane {
- struct wl_list link;
-
struct weston_plane base;

- struct drm_output *output;
struct drm_backend *backend;

enum wdrm_plane_type type;
@@ -296,19 +318,10 @@ struct drm_plane {

struct drm_property_info props[WDRM_PLANE__COUNT];

- /* The last framebuffer submitted to the kernel for this plane. */
- struct drm_fb *fb_current;
- /* The previously-submitted framebuffer, where the hardware has not
- * yet acknowledged display of fb_current. */
- struct drm_fb *fb_last;
- /* Framebuffer we are going to submit to the kernel when the current
- * repaint is flushed. */
- struct drm_fb *fb_pending;
+ /* The last state submitted to the kernel for this plane. */
+ struct drm_plane_state *state_cur;

- int32_t src_x, src_y;
- uint32_t src_w, src_h;
- uint32_t dest_x, dest_y;
- uint32_t dest_w, dest_h;
+ struct wl_list link;

uint32_t formats[];
};
@@ -939,6 +952,137 @@ drm_fb_unref(struct drm_fb *fb)
}
}

+/**
+ * Allocate a new, empty, plane state.
+ */
+static struct drm_plane_state *
+drm_plane_state_alloc(struct drm_output_state *state_output,
+ struct drm_plane *plane)
+{
+ struct drm_plane_state *state = zalloc(sizeof(*state));
+
+ assert(state);
+ state->output_state = state_output;
+ state->plane = plane;
+
+ /* Here we only add the plane state to the desired link, and not
+ * set the member. Having an output pointer set means that the
+ * plane will be displayed on the output; this won't be the case
+ * when we go to disable a plane. In this case, it must be part of
+ * the commit (and thus the output state), but the member must be
+ * NULL, as it will not be on any output when the state takes
+ * effect.
+ */
+ if (state_output)
+ wl_list_insert(&state_output->plane_list, &state->link);
+ else
+ wl_list_init(&state->link);
+
+ return state;
+}
+
+/**
+ * Free an existing plane state. As a special case, the state will not
+ * normally be freed if it is the current state; see drm_plane_set_state.
+ */
+static void
+drm_plane_state_free(struct drm_plane_state *state, bool force)
+{
+ if (!state)
+ return;
+
+ wl_list_remove(&state->link);
+ wl_list_init(&state->link);
+ state->output_state = NULL;
+
+ if (force || state != state->plane->state_cur) {
+ drm_fb_unref(state->fb);
+ free(state);
+ }
+}
+
+/**
+ * Duplicate an existing plane state into a new plane state, storing it within
+ * the given output state. If the output state already contains a plane state
+ * for the drm_plane referenced by 'src', that plane state is freed first.
+ */
+static struct drm_plane_state *
+drm_plane_state_duplicate(struct drm_output_state *state_output,
+ struct drm_plane_state *src)
+{
+ struct drm_plane_state *dst = malloc(sizeof(*dst));
+ struct drm_plane_state *old, *tmp;
+
+ assert(src);
+ assert(dst);
+ *dst = *src;
+ wl_list_init(&dst->link);
+
+ wl_list_for_each_safe(old, tmp, &state_output->plane_list, link) {
+ if (old->plane == dst->plane)
+ drm_plane_state_free(old, false);
+ }
+
+ wl_list_insert(&state_output->plane_list, &dst->link);
+ if (src->fb)
+ dst->fb = drm_fb_ref(src->fb);
+ dst->output_state = state_output;
+ dst->complete = false;
+
+ return dst;
+}
+
+/**
+ * Remove a plane state from an output state; if the plane was previously
+ * enabled, then replace it with a disabling state. This ensures that the
+ * output state was untouched from it was before the plane state was
+ * modified by the caller of this function.
+ *
+ * This is required as drm_output_state_get_plane may either allocate a
+ * new plane state, in which case this function will just perform a matching
+ * drm_plane_state_free, or it may instead repurpose an existing disabling
+ * state (if the plane was previously active), in which case this function
+ * will reset it.
+ */
+static void
+drm_plane_state_put_back(struct drm_plane_state *state)
+{
+ struct drm_output_state *state_output;
+ struct drm_plane *plane;
+
+ if (!state)
+ return;
+
+ state_output = state->output_state;
+ plane = state->plane;
+ drm_plane_state_free(state, false);
+
+ /* Plane was previously disabled; no need to keep this temporary
+ * state around. */
+ if (!plane->state_cur->fb)
+ return;
+
+ (void) drm_plane_state_alloc(state_output, plane);
+}
+
+/**
+ * Return a plane state from a drm_output_state, either existing or
+ * freshly allocated.
+ */
+static struct drm_plane_state *
+drm_output_state_get_plane(struct drm_output_state *state_output,
+ struct drm_plane *plane)
+{
+ struct drm_plane_state *ps;
+
+ wl_list_for_each(ps, &state_output->plane_list, link) {
+ if (ps->plane == plane)
+ return ps;
+ }
+
+ return drm_plane_state_alloc(state_output, plane);
+}
+
/**
* Allocate a new, empty drm_output_state. This should not generally be used
* in the repaint cycle; see drm_output_state_duplicate.
@@ -957,6 +1101,8 @@ drm_output_state_alloc(struct drm_output *output,
else
wl_list_init(&state->link);

+ wl_list_init(&state->plane_list);
+
return state;
}

@@ -974,6 +1120,7 @@ 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));
+ struct drm_plane_state *ps;

assert(dst);

@@ -988,6 +1135,20 @@ drm_output_state_duplicate(struct drm_output_state *src,
else
wl_list_init(&dst->link);

+ wl_list_init(&dst->plane_list);
+
+ wl_list_for_each(ps, &src->plane_list, link) {
+ /* Don't carry planes which are now disabled; these should be
+ * free for other outputs to reuse. */
+ if (!ps->output)
+ continue;
+
+ if (plane_mode == DRM_OUTPUT_STATE_CLEAR_PLANES)
+ (void) drm_plane_state_alloc(dst, ps->plane);
+ else
+ (void) drm_plane_state_duplicate(dst, ps);
+ }
+
return dst;
}

@@ -997,10 +1158,16 @@ drm_output_state_duplicate(struct drm_output_state *src,
static void
drm_output_state_free(struct drm_output_state *state)
{
+ struct drm_plane_state *ps, *next;
+
if (!state)
return;

+ wl_list_for_each_safe(ps, next, &state->plane_list, link)
+ drm_plane_state_free(ps, false);
+
wl_list_remove(&state->link);
+
free(state);
}

@@ -1085,12 +1252,16 @@ static void
drm_output_update_complete(struct drm_output *output, uint32_t flags,
unsigned int sec, unsigned int usec)
{
+ struct drm_plane_state *ps;
struct timespec ts;

/* Stop the pageflip timer instead of rearming it here */
if (output->pageflip_timer)
wl_event_source_timer_update(output->pageflip_timer, 0);

+ wl_list_for_each(ps, &output->state_cur->plane_list, link)
+ ps->complete = true;
+
drm_output_state_free(output->state_last);
output->state_last = NULL;

@@ -1124,6 +1295,7 @@ drm_output_assign_state(struct drm_output_state *state,
enum drm_output_state_update_mode mode)
{
struct drm_output *output = state->output;
+ struct drm_plane_state *plane_state;

assert(!output->state_last);

@@ -1137,6 +1309,24 @@ drm_output_assign_state(struct drm_output_state *state,
state->pending_state = NULL;

output->state_cur = state;
+
+ /* Replace state_cur on each affected plane with the new state, being
+ * careful to dispose of orphaned (but only orphaned) previous state.
+ * If the previous state is not orphaned (still has an output_state
+ * attached), it will be disposed of by freeing the output_state. */
+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ struct drm_plane *plane = plane_state->plane;
+
+ if (plane->state_cur && !plane->state_cur->output_state)
+ drm_plane_state_free(plane->state_cur, true);
+ plane->state_cur = plane_state;
+
+ if (mode != DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+ continue;
+
+ if (plane->type == WDRM_PLANE_TYPE_OVERLAY)
+ output->vblank_pending++;
+ }
}


@@ -1387,6 +1577,7 @@ drm_output_repaint(struct weston_output *output_base,
struct drm_output *output = to_drm_output(output_base);
struct drm_backend *backend =
to_drm_backend(output->base.compositor);
+ struct drm_plane_state *ps;
struct drm_plane *p;
struct drm_mode *mode;
int ret = 0;
@@ -1460,29 +1651,33 @@ drm_output_repaint(struct weston_output *output_base,
/*
* Now, update all the sprite surfaces
*/
- wl_list_for_each(p, &backend->plane_list, link) {
+ wl_list_for_each(ps, &state->plane_list, link) {
uint32_t flags = 0, fb_id = 0;
drmVBlank vbl = {
.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
.request.sequence = 1,
};

+ p = ps->plane;
if (p->type != WDRM_PLANE_TYPE_OVERLAY)
continue;

- if ((!p->fb_current && !p->fb_pending) ||
- !drm_plane_crtc_supported(output, p))
- continue;
+ assert(p->state_cur->complete);
+ assert(!!p->state_cur->output == !!p->state_cur->fb);
+ assert(!p->state_cur->output || p->state_cur->output == output);
+ assert(!ps->complete);
+ assert(!ps->output || ps->output == output);
+ assert(!!ps->output == !!ps->fb);

- if (p->fb_pending && !backend->sprites_hidden)
- fb_id = p->fb_pending->fb_id;
+ if (ps->fb && !backend->sprites_hidden)
+ fb_id = ps->fb->fb_id;

ret = drmModeSetPlane(backend->drm.fd, p->plane_id,
output->crtc_id, fb_id, flags,
- p->dest_x, p->dest_y,
- p->dest_w, p->dest_h,
- p->src_x, p->src_y,
- p->src_w, p->src_h);
+ ps->dest_x, ps->dest_y,
+ ps->dest_w, ps->dest_h,
+ ps->src_x, ps->src_y,
+ ps->src_w, ps->src_h);
if (ret)
weston_log("setplane failed: %d: %s\n",
ret, strerror(errno));
@@ -1493,18 +1688,12 @@ drm_output_repaint(struct weston_output *output_base,
* Queue a vblank signal so we know when the surface
* becomes active on the display or has been replaced.
*/
- vbl.request.signal = (unsigned long) p;
+ vbl.request.signal = (unsigned long) ps;
ret = drmWaitVBlank(backend->drm.fd, &vbl);
if (ret) {
weston_log("vblank event request failed: %d: %s\n",
ret, strerror(errno));
}
-
- p->output = output;
- p->fb_last = p->fb_current;
- p->fb_current = p->fb_pending;
- p->fb_pending = NULL;
- output->vblank_pending++;
}

return 0;
@@ -1526,6 +1715,7 @@ 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 = NULL;
struct drm_output_state *state;
+ struct drm_plane_state *plane_state;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
uint32_t fb_id;
@@ -1606,6 +1796,17 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
output->fb_last = drm_fb_ref(output->fb_current);
output->page_flip_pending = 1;

+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY)
+ continue;
+
+ vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
+ vbl.request.type |= drm_waitvblank_pipe(output);
+ vbl.request.sequence = 1;
+ vbl.request.signal = (unsigned long) plane_state;
+ drmWaitVBlank(backend->drm.fd, &vbl);
+ }
+
drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
drm_pending_state_free(pending_state);

@@ -1635,8 +1836,9 @@ static void
vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
void *data)
{
- struct drm_plane *s = (struct drm_plane *)data;
- struct drm_output *output = s->output;
+ struct drm_plane_state *ps = (struct drm_plane_state *) data;
+ struct drm_output_state *os = ps->output_state;
+ struct drm_output *output = os->output;
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;

@@ -1644,9 +1846,7 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
output->vblank_pending--;
assert(output->vblank_pending >= 0);

- assert(s->fb_last || s->fb_current);
- drm_fb_unref(s->fb_last);
- s->fb_last = NULL;
+ assert(ps->fb);

if (output->page_flip_pending || output->vblank_pending)
return;
@@ -1780,8 +1980,8 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_resource *buffer_resource;
struct drm_plane *p;
+ struct drm_plane_state *state = NULL;
struct linux_dmabuf_buffer *dmabuf;
- int found = 0;
struct gbm_bo *bo;
pixman_region32_t dest_rect, src_rect;
pixman_box32_t *box, tbox;
@@ -1822,14 +2022,22 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
if (!drm_plane_crtc_supported(output, p))
continue;

- if (!p->fb_pending) {
- found = 1;
- break;
+ if (!p->state_cur->complete)
+ continue;
+ if (p->state_cur->output && p->state_cur->output != output)
+ continue;
+
+ state = drm_output_state_get_plane(output_state, p);
+ if (state->fb) {
+ state = NULL;
+ continue;
}
+
+ break;
}

/* No sprites available */
- if (!found)
+ if (!state)
return NULL;

if ((dmabuf = linux_dmabuf_buffer_get(buffer_resource))) {
@@ -1866,28 +2074,26 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
GBM_BO_USE_SCANOUT);
#else
- return NULL;
+ goto err;
#endif
} else {
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer_resource, GBM_BO_USE_SCANOUT);
}
if (!bo)
- return NULL;
+ goto err;

format = drm_output_check_plane_format(p, ev, bo);
- if (format == 0) {
- gbm_bo_destroy(bo);
- return NULL;
- }
+ if (format == 0)
+ goto err;

- p->fb_pending = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!p->fb_pending) {
- gbm_bo_destroy(bo);
- return NULL;
- }
+ state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
+ if (!state->fb)
+ goto err;

- drm_fb_set_buffer(p->fb_pending, ev->surface->buffer_ref.buffer);
+ drm_fb_set_buffer(state->fb, ev->surface->buffer_ref.buffer);
+
+ state->output = output;

box = pixman_region32_extents(&ev->transform.boundingbox);
p->base.x = box->x1;
@@ -1908,10 +2114,10 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
output->base.transform,
output->base.current_scale,
*box);
- p->dest_x = tbox.x1;
- p->dest_y = tbox.y1;
- p->dest_w = tbox.x2 - tbox.x1;
- p->dest_h = tbox.y2 - tbox.y1;
+ state->dest_x = tbox.x1;
+ state->dest_y = tbox.y1;
+ state->dest_w = tbox.x2 - tbox.x1;
+ state->dest_h = tbox.y2 - tbox.y1;
pixman_region32_fini(&dest_rect);

pixman_region32_init(&src_rect);
@@ -1948,13 +2154,19 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
viewport->buffer.scale,
tbox);

- p->src_x = tbox.x1 << 8;
- p->src_y = tbox.y1 << 8;
- p->src_w = (tbox.x2 - tbox.x1) << 8;
- p->src_h = (tbox.y2 - tbox.y1) << 8;
+ state->src_x = tbox.x1 << 8;
+ state->src_y = tbox.y1 << 8;
+ state->src_w = (tbox.x2 - tbox.x1) << 8;
+ state->src_h = (tbox.y2 - tbox.y1) << 8;
pixman_region32_fini(&src_rect);

return &p->base;
+
+err:
+ drm_plane_state_put_back(state);
+ if (bo)
+ gbm_bo_destroy(bo);
+ return NULL;
}

static struct weston_plane *
@@ -2489,6 +2701,8 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
plane->possible_crtcs = kplane->possible_crtcs;
plane->plane_id = kplane->plane_id;
plane->count_formats = kplane->count_formats;
+ plane->state_cur = drm_plane_state_alloc(NULL, plane);
+ plane->state_cur->complete = true;
memcpy(plane->formats, kplane->formats,
kplane->count_formats * sizeof(kplane->formats[0]));

@@ -2526,10 +2740,8 @@ drm_plane_destroy(struct drm_plane *plane)
{
drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- assert(!plane->fb_last);
- assert(!plane->fb_pending);
+ drm_plane_state_free(plane->state_cur, true);
drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
- drm_fb_unref(plane->fb_current);
weston_plane_release(&plane->base);
wl_list_remove(&plane->link);
free(plane);
--
2.14.1
Pekka Paalanen
2017-09-28 14:07:54 UTC
Permalink
On Tue, 26 Sep 2017 18:15:35 +0100
Post by Daniel Stone
Track dynamic plane state (CRTC, FB, position) in separate structures,
rather than as part of the plane. This will make it easier to handle
state management later, and much more closely tracks what the kernel
does with atomic modesets.
The fb_last pointer previously used in drm_plane now becomes part of
output->state_last, and is not directly visible from the plane itself.
---
libweston/compositor-drm.c | 342 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 277 insertions(+), 65 deletions(-)
+/**
+ * Duplicate an existing plane state into a new plane state, storing it within
+ * the given output state. If the output state already contains a plane state
+ * for the drm_plane referenced by 'src', that plane state is freed first.
+ */
+static struct drm_plane_state *
+drm_plane_state_duplicate(struct drm_output_state *state_output,
+ struct drm_plane_state *src)
+{
+ struct drm_plane_state *dst = malloc(sizeof(*dst));
+ struct drm_plane_state *old, *tmp;
+
+ assert(src);
+ assert(dst);
+ *dst = *src;
+ wl_list_init(&dst->link);
+
+ wl_list_for_each_safe(old, tmp, &state_output->plane_list, link) {
+ if (old->plane == dst->plane)
+ drm_plane_state_free(old, false);
+ }
Hi,

should this not protect against src == old?

That is, duplicating a plane state that is already in the output state
as well?

Or, should there be assert(src != old);?
Post by Daniel Stone
+
+ wl_list_insert(&state_output->plane_list, &dst->link);
Or move the below to above the loop?
Post by Daniel Stone
+ if (src->fb)
+ dst->fb = drm_fb_ref(src->fb);
+ dst->output_state = state_output;
+ dst->complete = false;
+
+ return dst;
+}
Otherwise I would be happy to give a R-b.


Thanks,
pq
Daniel Stone
2017-09-26 17:15:37 UTC
Permalink
Change the type of cursor_plane from a weston_plane (base tracking
structure) to a drm_plane (wrapper containing additional DRM-specific
details), and make it a dynamically-allocated pointer.

Using the standard drm_plane allows us to reuse code which already deals
with drm_planes, e.g. a common cleanup function.

This patch introduces a 'special plane' helper, creating a drm_plane
either from a real KMS plane when using universal planes, or a fake plane
otherwise. Without universal planes, the cursor and primary planes are
hidden from us; this helper allows us to pretend otherwise.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 423 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 322 insertions(+), 101 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 0827e953..33555d00 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -350,7 +350,7 @@ struct drm_output {
int disable_pending;

struct drm_fb *gbm_cursor_fb[2];
- struct weston_plane cursor_plane;
+ struct drm_plane *cursor_plane;
struct weston_view *cursor_view;
int current_cursor;

@@ -644,7 +644,7 @@ drm_property_info_free(struct drm_property_info *info, int num_props)
}

static void
-drm_output_set_cursor(struct drm_output *output);
+drm_output_set_cursor(struct drm_output_state *output_state);

static void
drm_output_update_msc(struct drm_output *output, unsigned int seq);
@@ -1066,12 +1066,11 @@ drm_plane_state_put_back(struct drm_plane_state *state)
}

/**
- * Return a plane state from a drm_output_state, either existing or
- * freshly allocated.
+ * Return a plane state from a drm_output_state.
*/
static struct drm_plane_state *
-drm_output_state_get_plane(struct drm_output_state *state_output,
- struct drm_plane *plane)
+drm_output_state_get_existing_plane(struct drm_output_state *state_output,
+ struct drm_plane *plane)
{
struct drm_plane_state *ps;

@@ -1080,6 +1079,23 @@ drm_output_state_get_plane(struct drm_output_state *state_output,
return ps;
}

+ return NULL;
+}
+
+/**
+ * Return a plane state from a drm_output_state, either existing or
+ * freshly allocated.
+ */
+static struct drm_plane_state *
+drm_output_state_get_plane(struct drm_output_state *state_output,
+ struct drm_plane *plane)
+{
+ struct drm_plane_state *ps;
+
+ ps = drm_output_state_get_existing_plane(state_output, plane);
+ if (ps)
+ return ps;
+
return drm_plane_state_alloc(state_output, plane);
}

@@ -1604,8 +1620,8 @@ drm_output_repaint(struct weston_output *output_base,
*/
if (output->base.disable_planes) {
output->cursor_view = NULL;
- output->cursor_plane.x = INT32_MIN;
- output->cursor_plane.y = INT32_MIN;
+ output->cursor_plane->base.x = INT32_MIN;
+ output->cursor_plane->base.y = INT32_MIN;
}

drm_output_render(state, damage);
@@ -1646,7 +1662,7 @@ drm_output_repaint(struct weston_output *output_base,
wl_event_source_timer_update(output->pageflip_timer,
backend->pageflip_timeout);

- drm_output_set_cursor(output);
+ drm_output_set_cursor(state);

/*
* Now, update all the sprite surfaces
@@ -2169,20 +2185,66 @@ err:
return NULL;
}

+/**
+ * Update the image for the current cursor surface
+ *
+ * @param b DRM backend structure
+ * @param bo GBM buffer object to write into
+ * @param ev View to use for cursor image
+ */
+static void
+cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo,
+ struct weston_view *ev)
+{
+ struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ uint32_t buf[b->cursor_width * b->cursor_height];
+ int32_t stride;
+ uint8_t *s;
+ int i;
+
+ assert(buffer && buffer->shm_buffer);
+ assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
+ assert(ev->surface->width <= b->cursor_width);
+ assert(ev->surface->height <= b->cursor_height);
+
+ memset(buf, 0, sizeof buf);
+ stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
+ s = wl_shm_buffer_get_data(buffer->shm_buffer);
+
+ wl_shm_buffer_begin_access(buffer->shm_buffer);
+ for (i = 0; i < ev->surface->height; i++)
+ memcpy(buf + i * b->cursor_width,
+ s + i * stride,
+ ev->surface->width * 4);
+ wl_shm_buffer_end_access(buffer->shm_buffer);
+
+ if (gbm_bo_write(bo, buf, sizeof buf) < 0)
+ weston_log("failed update cursor: %m\n");
+}
+
static struct weston_plane *
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 drm_plane *plane = output->cursor_plane;
+ struct drm_plane_state *plane_state;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_shm_buffer *shmbuf;
+ bool needs_update = false;
float x, y;

+ if (!plane)
+ return NULL;
+
if (b->cursors_are_broken)
return NULL;

- if (output->cursor_view)
+ if (!plane->state_cur->complete)
+ return NULL;
+
+ if (plane->state_cur->output && plane->state_cur->output != output)
return NULL;

/* Don't import buffers which span multiple outputs. */
@@ -2215,89 +2277,101 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
ev->surface->height > b->cursor_height)
return NULL;

- output->cursor_view = ev;
- weston_view_to_global_float(ev, 0, 0, &x, &y);
- output->cursor_plane.x = x;
- output->cursor_plane.y = y;
+ plane_state =
+ drm_output_state_get_plane(output_state, output->cursor_plane);

- return &output->cursor_plane;
-}
-
-/**
- * Update the image for the current cursor surface
- *
- * @param b DRM backend structure
- * @param bo GBM buffer object to write into
- * @param ev View to use for cursor image
- */
-static void
-cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo,
- struct weston_view *ev)
-{
- struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
- uint32_t buf[b->cursor_width * b->cursor_height];
- int32_t stride;
- uint8_t *s;
- int i;
-
- assert(buffer && buffer->shm_buffer);
- assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
- assert(ev->surface->width <= b->cursor_width);
- assert(ev->surface->height <= b->cursor_height);
-
- memset(buf, 0, sizeof buf);
- stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
- s = wl_shm_buffer_get_data(buffer->shm_buffer);
+ if (plane_state && plane_state->fb)
+ return NULL;

- wl_shm_buffer_begin_access(buffer->shm_buffer);
- for (i = 0; i < ev->surface->height; i++)
- memcpy(buf + i * b->cursor_width,
- s + i * stride,
- ev->surface->width * 4);
- wl_shm_buffer_end_access(buffer->shm_buffer);
+ /* Since we're setting plane state up front, we need to work out
+ * whether or not we need to upload a new cursor. We can't use the
+ * plane damage, since the planes haven't actually been calculated
+ * yet: instead try to figure it out directly. KMS cursor planes are
+ * pretty unique here, in that they lie partway between a Weston plane
+ * (direct scanout) and a renderer. */
+ if (ev != output->cursor_view ||
+ pixman_region32_not_empty(&ev->surface->damage)) {
+ output->current_cursor++;
+ output->current_cursor =
+ output->current_cursor %
+ ARRAY_LENGTH(output->gbm_cursor_fb);
+ needs_update = true;
+ }

- if (gbm_bo_write(bo, buf, sizeof buf) < 0)
- weston_log("failed update cursor: %m\n");
+ output->cursor_view = ev;
+ weston_view_to_global_float(ev, 0, 0, &x, &y);
+ plane->base.x = x;
+ plane->base.y = y;
+
+ plane_state->fb =
+ drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
+ plane_state->output = output;
+ plane_state->src_x = 0;
+ plane_state->src_y = 0;
+ plane_state->src_w = b->cursor_width << 16;
+ plane_state->src_h = b->cursor_height << 16;
+ plane_state->dest_x = (x - output->base.x) * output->base.current_scale;
+ plane_state->dest_y = (y - output->base.y) * output->base.current_scale;
+ plane_state->dest_w = b->cursor_width;
+ plane_state->dest_h = b->cursor_height;
+
+ if (needs_update)
+ cursor_bo_update(b, plane_state->fb->bo, ev);
+
+ return &plane->base;
}

static void
-drm_output_set_cursor(struct drm_output *output)
+drm_output_set_cursor(struct drm_output_state *output_state)
{
- struct weston_view *ev = output->cursor_view;
+ struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct drm_plane *plane = output->cursor_plane;
+ struct drm_plane_state *state;
EGLint handle;
struct gbm_bo *bo;
- float x, y;

- if (ev == NULL) {
+ if (!plane)
+ return;
+
+ state = drm_output_state_get_existing_plane(output_state, plane);
+ if (!state)
+ return;
+
+ if (!state->fb) {
+ pixman_region32_fini(&plane->base.damage);
+ pixman_region32_init(&plane->base.damage);
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
return;
}

- if (pixman_region32_not_empty(&output->cursor_plane.damage)) {
- pixman_region32_fini(&output->cursor_plane.damage);
- pixman_region32_init(&output->cursor_plane.damage);
- output->current_cursor ^= 1;
- bo = output->gbm_cursor_fb[output->current_cursor]->bo;
+ assert(state->fb == output->gbm_cursor_fb[output->current_cursor]);
+ assert(!plane->state_cur->output || plane->state_cur->output == output);

- cursor_bo_update(b, bo, ev);
+ if (plane->state_cur->fb != state->fb) {
+ bo = state->fb->bo;
handle = gbm_bo_get_handle(bo).s32;
if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle,
- b->cursor_width, b->cursor_height)) {
+ b->cursor_width, b->cursor_height)) {
weston_log("failed to set cursor: %m\n");
- b->cursors_are_broken = 1;
+ goto err;
}
}

- x = (output->cursor_plane.x - output->base.x) *
- output->base.current_scale;
- y = (output->cursor_plane.y - output->base.y) *
- output->base.current_scale;
+ pixman_region32_fini(&plane->base.damage);
+ pixman_region32_init(&plane->base.damage);

- if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
+ if (drmModeMoveCursor(b->drm.fd, output->crtc_id,
+ state->dest_x, state->dest_y)) {
weston_log("failed to move cursor: %m\n");
- b->cursors_are_broken = 1;
+ goto err;
}
+
+ return;
+
+err:
+ b->cursors_are_broken = 1;
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
}

static void
@@ -2307,6 +2381,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
struct drm_pending_state *pending_state = repaint_data;
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state;
+ struct drm_plane_state *plane_state;
struct weston_view *ev, *next;
pixman_region32_t overlap, surface_overlap;
struct weston_plane *primary, *next_plane;
@@ -2332,10 +2407,6 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
pixman_region32_init(&overlap);
primary = &output_base->compositor->primary_plane;

- output->cursor_view = NULL;
- output->cursor_plane.x = INT32_MIN;
- output->cursor_plane.y = INT32_MIN;
-
wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) {
struct weston_surface *es = ev->surface;

@@ -2379,7 +2450,8 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
&ev->transform.boundingbox);

if (next_plane == primary ||
- next_plane == &output->cursor_plane) {
+ (output->cursor_plane &&
+ next_plane == &output->cursor_plane->base)) {
/* cursor plane involves a copy */
ev->psf_flags = 0;
} else {
@@ -2392,6 +2464,19 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
pixman_region32_fini(&surface_overlap);
}
pixman_region32_fini(&overlap);
+
+ /* We rely on output->cursor_view being both an accurate reflection of
+ * the cursor plane's state, but also being maintained across repaints
+ * to avoid unnecessary damage uploads, per the comment in
+ * drm_output_prepare_cursor_view. In the event that we go from having
+ * a cursor view to not having a cursor view, we need to clear it. */
+ if (output->cursor_view) {
+ plane_state =
+ drm_output_state_get_existing_plane(state,
+ output->cursor_plane);
+ if (!plane_state || !plane_state->fb)
+ output->cursor_view = NULL;
+ }
}

/**
@@ -2659,19 +2744,30 @@ init_pixman(struct drm_backend *b)
* Creates one drm_plane structure for a hardware plane, and initialises its
* properties and formats.
*
+ * In the absence of universal plane support, where KMS does not explicitly
+ * expose the primary and cursor planes to userspace, this may also create
+ * an 'internal' plane for internal management.
+ *
* This function does not add the plane to the list of usable planes in Weston
* itself; the caller is responsible for this.
*
* Call drm_plane_destroy to clean up the plane.
*
+ * @sa drm_output_find_special_plane
* @param b DRM compositor backend
- * @param kplane DRM plane to create
+ * @param kplane DRM plane to create, or NULL if creating internal plane
+ * @param output Output to create internal plane for, or NULL
+ * @param type Type to use when creating internal plane, or invalid
+ * @param format Format to use for internal planes, or 0
*/
static struct drm_plane *
-drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
+drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
+ struct drm_output *output, enum wdrm_plane_type type,
+ uint32_t format)
{
struct drm_plane *plane;
drmModeObjectProperties *props;
+ int num_formats = (kplane) ? kplane->count_formats : 1;

static struct drm_property_enum_info plane_type_enums[] = {
[WDRM_PLANE_TYPE_PRIMARY] = {
@@ -2692,36 +2788,61 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
},
};

- plane = zalloc(sizeof(*plane) + ((sizeof(uint32_t)) *
- kplane->count_formats));
+ plane = zalloc(sizeof(*plane) +
+ (sizeof(uint32_t) * num_formats));
if (!plane) {
weston_log("%s: out of memory\n", __func__);
return NULL;
}

plane->backend = b;
- plane->possible_crtcs = kplane->possible_crtcs;
- plane->plane_id = kplane->plane_id;
- plane->count_formats = kplane->count_formats;
plane->state_cur = drm_plane_state_alloc(NULL, plane);
plane->state_cur->complete = true;
- memcpy(plane->formats, kplane->formats,
- kplane->count_formats * sizeof(kplane->formats[0]));

- props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id,
- DRM_MODE_OBJECT_PLANE);
- if (!props) {
- weston_log("couldn't get plane properties\n");
+ if (kplane) {
+ plane->possible_crtcs = kplane->possible_crtcs;
+ plane->plane_id = kplane->plane_id;
+ plane->count_formats = kplane->count_formats;
+ memcpy(plane->formats, kplane->formats,
+ kplane->count_formats * sizeof(kplane->formats[0]));
+
+ props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id,
+ DRM_MODE_OBJECT_PLANE);
+ if (!props) {
+ weston_log("couldn't get plane properties\n");
+ free(plane);
+ return NULL;
+ }
+ drm_property_info_populate(b, plane_props, plane->props,
+ WDRM_PLANE__COUNT, props);
+ plane->type =
+ drm_property_get_value(&plane->props[WDRM_PLANE_TYPE],
+ props,
+ WDRM_PLANE_TYPE__COUNT);
+ drmModeFreeObjectProperties(props);
+ }
+ else {
+ plane->possible_crtcs = (1 << output->pipe);
+ plane->plane_id = 0;
+ plane->count_formats = 1;
+ plane->formats[0] = format;
+ plane->type = type;
+ }
+
+ if (plane->type == WDRM_PLANE_TYPE__COUNT) {
free(plane);
return NULL;
}
- drm_property_info_populate(b, plane_props, plane->props,
- WDRM_PLANE__COUNT, props);
- plane->type =
- drm_property_get_value(&plane->props[WDRM_PLANE_TYPE],
- props,
- WDRM_PLANE_TYPE_OVERLAY);
- drmModeFreeObjectProperties(props);
+
+ /* With universal planes, everything is a DRM plane; without
+ * universal planes, the only DRM planes are overlay planes. */
+ if (b->universal_planes) {
+ assert(kplane);
+ } else {
+ assert((kplane && plane->type == WDRM_PLANE_TYPE_OVERLAY) ||
+ (!kplane && plane->type != WDRM_PLANE_TYPE_OVERLAY &&
+ output));
+ }

weston_plane_init(&plane->base, b->compositor, 0, 0);
wl_list_insert(&b->plane_list, &plane->link);
@@ -2729,6 +2850,88 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
return plane;
}

+/**
+ * Find, or create, a special-purpose plane
+ *
+ * Primary and cursor planes are a special case, in that before universal
+ * planes, they are driven by non-plane API calls. Without universal plane
+ * support, the only way to configure a primary plane is via drmModeSetCrtc,
+ * and the only way to configure a cursor plane is drmModeSetCursor2.
+ *
+ * Although they may actually be regular planes in the hardware, without
+ * universal plane support, these planes are not actually exposed to
+ * userspace in the regular plane list.
+ *
+ * However, for ease of internal tracking, we want to manage all planes
+ * through the same drm_plane structures. Therefore, when we are running
+ * without universal plane support, we create fake drm_plane structures
+ * to track these planes.
+ *
+ * @param b DRM backend
+ * @param output Output to use for plane
+ * @param type Type of plane
+ */
+static struct drm_plane *
+drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
+ enum wdrm_plane_type type)
+{
+ struct drm_plane *plane;
+
+ if (!b->universal_planes) {
+ uint32_t format;
+
+ switch (type) {
+ case WDRM_PLANE_TYPE_CURSOR:
+ format = GBM_FORMAT_ARGB8888;
+ break;
+ case WDRM_PLANE_TYPE_PRIMARY:
+ format = output->gbm_format;
+ break;
+ default:
+ assert(!"invalid type in drm_output_find_special_plane");
+ break;
+ }
+
+ return drm_plane_create(b, NULL, output, type, format);
+ }
+
+ wl_list_for_each(plane, &b->plane_list, link) {
+ struct drm_output *tmp;
+ bool found_elsewhere = false;
+
+ if (plane->type != type)
+ continue;
+ if (!drm_plane_crtc_supported(output, plane))
+ continue;
+
+ /* On some platforms, primary/cursor planes can roam
+ * between different CRTCs, so make sure we don't claim the
+ * same plane for two outputs. */
+ wl_list_for_each(tmp, &b->compositor->pending_output_list,
+ base.link) {
+ if (tmp->cursor_plane == plane) {
+ found_elsewhere = true;
+ break;
+ }
+ }
+ wl_list_for_each(tmp, &b->compositor->output_list,
+ base.link) {
+ if (tmp->cursor_plane == plane) {
+ found_elsewhere = true;
+ break;
+ }
+ }
+
+ if (found_elsewhere)
+ continue;
+
+ plane->possible_crtcs = (1 << output->pipe);
+ return plane;
+ }
+
+ return NULL;
+}
+
/**
* Destroy one DRM plane
*
@@ -2778,7 +2981,8 @@ create_sprites(struct drm_backend *b)
if (!kplane)
continue;

- drm_plane = drm_plane_create(b, kplane);
+ drm_plane = drm_plane_create(b, kplane, NULL,
+ WDRM_PLANE_TYPE__COUNT, 0);
drmModeFreePlane(kplane);
if (!drm_plane)
continue;
@@ -3044,6 +3248,10 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
{
unsigned int i;

+ /* No point creating cursors if we don't have a plane for them. */
+ if (!output->cursor_plane)
+ return 0;
+
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
struct gbm_bo *bo;

@@ -3631,11 +3839,15 @@ drm_output_enable(struct weston_output *base)
output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
output->base.connection_internal = true;

- weston_plane_init(&output->cursor_plane, b->compositor,
- INT32_MIN, INT32_MIN);
weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);

- weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL);
+ if (output->cursor_plane)
+ weston_compositor_stack_plane(b->compositor,
+ &output->cursor_plane->base,
+ NULL);
+ else
+ b->cursors_are_broken = 1;
+
weston_compositor_stack_plane(b->compositor, &output->scanout_plane,
&b->compositor->primary_plane);

@@ -3678,10 +3890,11 @@ drm_output_deinit(struct weston_output *base)
drm_output_fini_egl(output);

weston_plane_release(&output->scanout_plane);
- weston_plane_release(&output->cursor_plane);

- /* Turn off hardware cursor */
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ if (output->cursor_plane) {
+ /* Turn off hardware cursor */
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ }
}

static void
@@ -3845,6 +4058,12 @@ create_output_for_connector(struct drm_backend *b,
}
}

+ /* Failing to find a cursor plane is not fatal, as we'll fall back
+ * to software cursor. */
+ output->cursor_plane =
+ drm_output_find_special_plane(b, output,
+ WDRM_PLANE_TYPE_CURSOR);
+
weston_compositor_add_pending_output(&output->base, b->compositor);

return 0;
@@ -4083,7 +4302,9 @@ session_notify(struct wl_listener *listener, void *data)

wl_list_for_each(output, &compositor->output_list, base.link) {
output->base.repaint_needed = false;
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ if (output->cursor_plane)
+ drmModeSetCursor(b->drm.fd, output->crtc_id,
+ 0, 0, 0);
}

output = container_of(compositor->output_list.next,
--
2.14.1
Pekka Paalanen
2017-09-28 11:55:00 UTC
Permalink
On Tue, 26 Sep 2017 18:15:37 +0100
Post by Daniel Stone
Change the type of cursor_plane from a weston_plane (base tracking
structure) to a drm_plane (wrapper containing additional DRM-specific
details), and make it a dynamically-allocated pointer.
Using the standard drm_plane allows us to reuse code which already deals
with drm_planes, e.g. a common cleanup function.
This patch introduces a 'special plane' helper, creating a drm_plane
either from a real KMS plane when using universal planes, or a fake plane
otherwise. Without universal planes, the cursor and primary planes are
hidden from us; this helper allows us to pretend otherwise.
---
libweston/compositor-drm.c | 423 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 322 insertions(+), 101 deletions(-)
Hi,

test scenario: start with two outputs plugged in. Unplug one output.
Re-plug the output.

With this patch, the re-plug eventually causes
compositor_accumulate_damage() to enter an endless loop. It looks like
weston_compositor::plane_list could be corrupted.

Using WESTON_DISABLE_UNIVERSAL_PLANES=1, the problem does not appear.


I was originally bisecting a different failure triggered by the re-plug:
weston: /home/pq/git/weston/libweston/compositor-drm.c:2357: drm_output_start_repaint_loop:
Assertion `scanout_plane->state_cur->output == output' failed.

That happens at patch "compositor-drm: Atomic modesetting support".
Somewhere between that and this patch the behaviour changes from
endless loop to an assert fail.


Thanks,
pq
Pekka Paalanen
2017-09-29 13:46:39 UTC
Permalink
On Tue, 26 Sep 2017 18:15:37 +0100
Post by Daniel Stone
Change the type of cursor_plane from a weston_plane (base tracking
structure) to a drm_plane (wrapper containing additional DRM-specific
details), and make it a dynamically-allocated pointer.
Using the standard drm_plane allows us to reuse code which already deals
with drm_planes, e.g. a common cleanup function.
This patch introduces a 'special plane' helper, creating a drm_plane
either from a real KMS plane when using universal planes, or a fake plane
otherwise. Without universal planes, the cursor and primary planes are
hidden from us; this helper allows us to pretend otherwise.
---
libweston/compositor-drm.c | 423 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 322 insertions(+), 101 deletions(-)
Hi Daniel,

this patch looks mostly good, but there are lifetime issues.
Post by Daniel Stone
static struct weston_plane *
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 drm_plane *plane = output->cursor_plane;
+ struct drm_plane_state *plane_state;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_shm_buffer *shmbuf;
+ bool needs_update = false;
float x, y;
+ if (!plane)
+ return NULL;
+
if (b->cursors_are_broken)
return NULL;
- if (output->cursor_view)
+ if (!plane->state_cur->complete)
+ return NULL;
+
+ if (plane->state_cur->output && plane->state_cur->output != output)
return NULL;
Just a thought: would it make sense to wrap the two above to something
like

bool
drm_plane_is_available(struct drm_plane *plane, struct drm_output *output)
{
if (!plane->state_cur->complete)
return false;

if (plane->state_cur->output && plane->state_cur->output != output)
return false;

return drm_plane_crtc_supported(output, plane);
}

That would make a nice place to explain what the conditions mean:

!plane->state_cur->complete
The plane is needed for something else already programmed and
not yet hit the screen.

state_cur->output && state_cur->output != output
Plane is in use through a different CRTC, so cannot take it
into use here and now, would need a disabled cycle first.
Also prevents plane stealing that might otherwise happen
between outputs updating at different times.

drm_plane_crtc_supported()
The CRTC and the plane can actually work together
hardware-wise, at all.

Is that correct?

The cursor path doesn't really need the drm_plane_crtc_supported()
check because that is implied by the drm_output::cursor_plane
assignment, but it doesn't hurt. The overlay path needs it.
Post by Daniel Stone
/* Don't import buffers which span multiple outputs. */
@@ -2215,89 +2277,101 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
ev->surface->height > b->cursor_height)
return NULL;
- output->cursor_view = ev;
- weston_view_to_global_float(ev, 0, 0, &x, &y);
- output->cursor_plane.x = x;
- output->cursor_plane.y = y;
+ plane_state =
+ drm_output_state_get_plane(output_state, output->cursor_plane);
- return &output->cursor_plane;
-}
-
-/**
- * Update the image for the current cursor surface
- *
- */
-static void
-cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo,
- struct weston_view *ev)
-{
- struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
- uint32_t buf[b->cursor_width * b->cursor_height];
- int32_t stride;
- uint8_t *s;
- int i;
-
- assert(buffer && buffer->shm_buffer);
- assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
- assert(ev->surface->width <= b->cursor_width);
- assert(ev->surface->height <= b->cursor_height);
-
- memset(buf, 0, sizeof buf);
- stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
- s = wl_shm_buffer_get_data(buffer->shm_buffer);
+ if (plane_state && plane_state->fb)
+ return NULL;
I think plane_state cannot be NULL, so it should be not checked here.
It's not checked in the below code either.

plane_state->fb check prevents the same cursor plane for being used
multiple times on this repaint cycle, correct?

Checks on plane->state_cur are checking what has been submitted to the
kernel which is a different thing.
Post by Daniel Stone
- wl_shm_buffer_begin_access(buffer->shm_buffer);
- for (i = 0; i < ev->surface->height; i++)
- memcpy(buf + i * b->cursor_width,
- s + i * stride,
- ev->surface->width * 4);
- wl_shm_buffer_end_access(buffer->shm_buffer);
+ /* Since we're setting plane state up front, we need to work out
+ * whether or not we need to upload a new cursor. We can't use the
+ * plane damage, since the planes haven't actually been calculated
+ * yet: instead try to figure it out directly. KMS cursor planes are
+ * pretty unique here, in that they lie partway between a Weston plane
+ * (direct scanout) and a renderer. */
+ if (ev != output->cursor_view ||
+ pixman_region32_not_empty(&ev->surface->damage)) {
+ output->current_cursor++;
+ output->current_cursor =
+ output->current_cursor %
+ ARRAY_LENGTH(output->gbm_cursor_fb);
+ needs_update = true;
+ }
- if (gbm_bo_write(bo, buf, sizeof buf) < 0)
- weston_log("failed update cursor: %m\n");
+ output->cursor_view = ev;
+ weston_view_to_global_float(ev, 0, 0, &x, &y);
+ plane->base.x = x;
+ plane->base.y = y;
+
+ plane_state->fb =
+ drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
+ plane_state->output = output;
+ plane_state->src_x = 0;
+ plane_state->src_y = 0;
+ plane_state->src_w = b->cursor_width << 16;
+ plane_state->src_h = b->cursor_height << 16;
+ plane_state->dest_x = (x - output->base.x) * output->base.current_scale;
+ plane_state->dest_y = (y - output->base.y) * output->base.current_scale;
+ plane_state->dest_w = b->cursor_width;
+ plane_state->dest_h = b->cursor_height;
+
+ if (needs_update)
+ cursor_bo_update(b, plane_state->fb->bo, ev);
+
+ return &plane->base;
}
@@ -2659,19 +2744,30 @@ init_pixman(struct drm_backend *b)
* Creates one drm_plane structure for a hardware plane, and initialises its
* properties and formats.
*
+ * In the absence of universal plane support, where KMS does not explicitly
+ * expose the primary and cursor planes to userspace, this may also create
+ * an 'internal' plane for internal management.
When doing this, should you not also modify drm_plane_destroy() to do
the right thing for the 'internal' planes?

No need to drm_property_info_free() and should not drmModeSetPlane().
Post by Daniel Stone
+ *
* This function does not add the plane to the list of usable planes in Weston
* itself; the caller is responsible for this.
*
* Call drm_plane_destroy to clean up the plane.
*
*/
static struct drm_plane *
-drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
+drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
+ struct drm_output *output, enum wdrm_plane_type type,
+ uint32_t format)
{
struct drm_plane *plane;
drmModeObjectProperties *props;
+ int num_formats = (kplane) ? kplane->count_formats : 1;
static struct drm_property_enum_info plane_type_enums[] = {
[WDRM_PLANE_TYPE_PRIMARY] = {
@@ -2692,36 +2788,61 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
},
};
- plane = zalloc(sizeof(*plane) + ((sizeof(uint32_t)) *
- kplane->count_formats));
+ plane = zalloc(sizeof(*plane) +
+ (sizeof(uint32_t) * num_formats));
if (!plane) {
weston_log("%s: out of memory\n", __func__);
return NULL;
}
plane->backend = b;
- plane->possible_crtcs = kplane->possible_crtcs;
- plane->plane_id = kplane->plane_id;
- plane->count_formats = kplane->count_formats;
plane->state_cur = drm_plane_state_alloc(NULL, plane);
plane->state_cur->complete = true;
- memcpy(plane->formats, kplane->formats,
- kplane->count_formats * sizeof(kplane->formats[0]));
- props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id,
- DRM_MODE_OBJECT_PLANE);
- if (!props) {
- weston_log("couldn't get plane properties\n");
+ if (kplane) {
+ plane->possible_crtcs = kplane->possible_crtcs;
+ plane->plane_id = kplane->plane_id;
+ plane->count_formats = kplane->count_formats;
+ memcpy(plane->formats, kplane->formats,
+ kplane->count_formats * sizeof(kplane->formats[0]));
+
+ props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id,
+ DRM_MODE_OBJECT_PLANE);
+ if (!props) {
+ weston_log("couldn't get plane properties\n");
+ free(plane);
+ return NULL;
+ }
+ drm_property_info_populate(b, plane_props, plane->props,
+ WDRM_PLANE__COUNT, props);
+ plane->type =
+ drm_property_get_value(&plane->props[WDRM_PLANE_TYPE],
+ props,
+ WDRM_PLANE_TYPE__COUNT);
+ drmModeFreeObjectProperties(props);
+ }
+ else {
+ plane->possible_crtcs = (1 << output->pipe);
+ plane->plane_id = 0;
+ plane->count_formats = 1;
+ plane->formats[0] = format;
+ plane->type = type;
+ }
+
+ if (plane->type == WDRM_PLANE_TYPE__COUNT) {
This seems to leak plane->props and plane->state_cur.
Post by Daniel Stone
free(plane);
return NULL;
}
- drm_property_info_populate(b, plane_props, plane->props,
- WDRM_PLANE__COUNT, props);
- plane->type =
- drm_property_get_value(&plane->props[WDRM_PLANE_TYPE],
- props,
- WDRM_PLANE_TYPE_OVERLAY);
- drmModeFreeObjectProperties(props);
+
+ /* With universal planes, everything is a DRM plane; without
+ * universal planes, the only DRM planes are overlay planes. */
+ if (b->universal_planes) {
+ assert(kplane);
+ } else {
+ assert((kplane && plane->type == WDRM_PLANE_TYPE_OVERLAY) ||
+ (!kplane && plane->type != WDRM_PLANE_TYPE_OVERLAY &&
+ output));
+ }
weston_plane_init(&plane->base, b->compositor, 0, 0);
wl_list_insert(&b->plane_list, &plane->link);
@@ -2729,6 +2850,88 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
return plane;
}
+/**
+ * Find, or create, a special-purpose plane
+ *
+ * Primary and cursor planes are a special case, in that before universal
+ * planes, they are driven by non-plane API calls. Without universal plane
+ * support, the only way to configure a primary plane is via drmModeSetCrtc,
+ * and the only way to configure a cursor plane is drmModeSetCursor2.
+ *
+ * Although they may actually be regular planes in the hardware, without
+ * universal plane support, these planes are not actually exposed to
+ * userspace in the regular plane list.
+ *
+ * However, for ease of internal tracking, we want to manage all planes
+ * through the same drm_plane structures. Therefore, when we are running
+ * without universal plane support, we create fake drm_plane structures
+ * to track these planes.
+ *
+ */
+static struct drm_plane *
+drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
+ enum wdrm_plane_type type)
+{
+ struct drm_plane *plane;
+
+ if (!b->universal_planes) {
+ uint32_t format;
+
+ switch (type) {
+ format = GBM_FORMAT_ARGB8888;
+ break;
+ format = output->gbm_format;
There were discussions in IRC that output->gbm_format is not set at
this time yet, so we can't use it here.

I would propose to just hardcode the list of formats that
parse_gbm_format() supports. I presume the hardcoding here is because
we don't get it from the kernel, so it's no better or worse than before.
Post by Daniel Stone
+ break;
+ assert(!"invalid type in drm_output_find_special_plane");
+ break;
+ }
+
+ return drm_plane_create(b, NULL, output, type, format);
+ }
+
+ wl_list_for_each(plane, &b->plane_list, link) {
+ struct drm_output *tmp;
+ bool found_elsewhere = false;
+
+ if (plane->type != type)
+ continue;
+ if (!drm_plane_crtc_supported(output, plane))
+ continue;
+
+ /* On some platforms, primary/cursor planes can roam
+ * between different CRTCs, so make sure we don't claim the
+ * same plane for two outputs. */
+ wl_list_for_each(tmp, &b->compositor->pending_output_list,
+ base.link) {
+ if (tmp->cursor_plane == plane) {
+ found_elsewhere = true;
+ break;
+ }
+ }
+ wl_list_for_each(tmp, &b->compositor->output_list,
+ base.link) {
+ if (tmp->cursor_plane == plane) {
+ found_elsewhere = true;
+ break;
+ }
+ }
+
+ if (found_elsewhere)
+ continue;
+
+ plane->possible_crtcs = (1 << output->pipe);
+ return plane;
+ }
+
+ return NULL;
+}
+
/**
* Destroy one DRM plane
*
@@ -2778,7 +2981,8 @@ create_sprites(struct drm_backend *b)
if (!kplane)
continue;
- drm_plane = drm_plane_create(b, kplane);
+ drm_plane = drm_plane_create(b, kplane, NULL,
+ WDRM_PLANE_TYPE__COUNT, 0);
drmModeFreePlane(kplane);
if (!drm_plane)
continue;
@@ -3044,6 +3248,10 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
{
unsigned int i;
+ /* No point creating cursors if we don't have a plane for them. */
+ if (!output->cursor_plane)
+ return 0;
+
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
struct gbm_bo *bo;
@@ -3631,11 +3839,15 @@ drm_output_enable(struct weston_output *base)
output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
output->base.connection_internal = true;
- weston_plane_init(&output->cursor_plane, b->compositor,
- INT32_MIN, INT32_MIN);
weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);
- weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL);
+ if (output->cursor_plane)
+ weston_compositor_stack_plane(b->compositor,
+ &output->cursor_plane->base,
+ NULL);
+ else
+ b->cursors_are_broken = 1;
+
weston_compositor_stack_plane(b->compositor, &output->scanout_plane,
&b->compositor->primary_plane);
@@ -3678,10 +3890,11 @@ drm_output_deinit(struct weston_output *base)
drm_output_fini_egl(output);
weston_plane_release(&output->scanout_plane);
- weston_plane_release(&output->cursor_plane);
This weston_plane_release() call is removed here.

To remain symmetric with drm_output_enable(), I think you should add

wl_list_remove(&output->cursor_plane->base.link);
wl_list_init(&output->cursor_plane->base.link);

here. It is theoretically possible to disable and then enable the same
output.

This is necessary also because the drm_plane for the cursor is not
destroyed when the respective drm_output is destroyed. The drm_plane
remains in the drm_backend::plane_list. When the same connector becomes
connected again and a new drm_output is created,
drm_output_find_special_plane() will find it again, and
drm_output_enable() will call
weston_compositor_stack_plane(cursor_plane) again, leading to list
corruption.

There is a catch. On shutdown, destroy_sprites() will destroy the
drm_planes, leaving drm_output::cursor_plane dangling. Then we destroy
the outputs, and the list removal hits a use-after-free.

Should destroy_sprites() be called after weston_compositor_shutdown()?
Post by Daniel Stone
- /* Turn off hardware cursor */
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ if (output->cursor_plane) {
+ /* Turn off hardware cursor */
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ }
}
static void
@@ -3845,6 +4058,12 @@ create_output_for_connector(struct drm_backend *b,
}
}
+ /* Failing to find a cursor plane is not fatal, as we'll fall back
+ * to software cursor. */
+ output->cursor_plane =
+ drm_output_find_special_plane(b, output,
+ WDRM_PLANE_TYPE_CURSOR);
+
weston_compositor_add_pending_output(&output->base, b->compositor);
return 0;
@@ -4083,7 +4302,9 @@ session_notify(struct wl_listener *listener, void *data)
wl_list_for_each(output, &compositor->output_list, base.link) {
output->base.repaint_needed = false;
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ if (output->cursor_plane)
+ drmModeSetCursor(b->drm.fd, output->crtc_id,
+ 0, 0, 0);
}
output = container_of(compositor->output_list.next,
Thanks,
pq
Daniel Stone
2017-09-26 17:15:36 UTC
Permalink
Add a test environment variable to allow disabling universal planes.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b28e602f..0827e953 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2551,8 +2551,10 @@ init_kms_caps(struct drm_backend *b)
else
b->cursor_height = 64;

- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
- b->universal_planes = (ret == 0);
+ if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ b->universal_planes = (ret == 0);
+ }
weston_log("DRM: %s universal planes\n",
b->universal_planes ? "supports" : "does not support");
--
2.14.1
Pekka Paalanen
2017-09-29 09:06:13 UTC
Permalink
On Tue, 26 Sep 2017 18:15:36 +0100
Post by Daniel Stone
Add a test environment variable to allow disabling universal planes.
---
libweston/compositor-drm.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b28e602f..0827e953 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2551,8 +2551,10 @@ init_kms_caps(struct drm_backend *b)
else
b->cursor_height = 64;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
- b->universal_planes = (ret == 0);
+ if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ b->universal_planes = (ret == 0);
+ }
weston_log("DRM: %s universal planes\n",
b->universal_planes ? "supports" : "does not support");
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>

IMO we can push this to master, until we develop a better way to
control debugging features.


Thanks,
pq
Emil Velikov
2017-09-29 13:21:02 UTC
Permalink
Post by Daniel Stone
Add a test environment variable to allow disabling universal planes.
---
libweston/compositor-drm.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b28e602f..0827e953 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2551,8 +2551,10 @@ init_kms_caps(struct drm_backend *b)
else
b->cursor_height = 64;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
- b->universal_planes = (ret == 0);
+ if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ b->universal_planes = (ret == 0);
+ }
Just a general idea, please don't bother with it for now:

Normally it's great if one can have clear description of the format
env. variables expect.
Quick grep shows WAYLAND_DEBUG and this instance.

For bool sounding variables, one would expect FOO=0/1 to work.

-Emil
Pekka Paalanen
2017-10-02 09:29:20 UTC
Permalink
On Fri, 29 Sep 2017 14:21:02 +0100
Post by Emil Velikov
Post by Daniel Stone
Add a test environment variable to allow disabling universal planes.
---
libweston/compositor-drm.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b28e602f..0827e953 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2551,8 +2551,10 @@ init_kms_caps(struct drm_backend *b)
else
b->cursor_height = 64;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
- b->universal_planes = (ret == 0);
+ if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ b->universal_planes = (ret == 0);
+ }
Normally it's great if one can have clear description of the format
env. variables expect.
Quick grep shows WAYLAND_DEBUG and this instance.
For bool sounding variables, one would expect FOO=0/1 to work.
Hi Emil,

yes. I think we will replace all these ad hoc weston debug switches
eventually with something like
LIBWESTON_DEBUG=disable-universal-planes,disable-foofoo that is a
single env var accepting a list of debug switches. We need to also
think whether we want it to be a libweston or Weston thing. If it was a
Weston thing, then libweston would have ABI to set them. I haven't
thought it through yet.


Thanks,
pq
Daniel Stone
2017-10-05 09:22:13 UTC
Permalink
Hi Emil,
Post by Pekka Paalanen
On Fri, 29 Sep 2017 14:21:02 +0100
Post by Emil Velikov
Normally it's great if one can have clear description of the format
env. variables expect.
Quick grep shows WAYLAND_DEBUG and this instance.
For bool sounding variables, one would expect FOO=0/1 to work.
yes. I think we will replace all these ad hoc weston debug switches
eventually with something like
LIBWESTON_DEBUG=disable-universal-planes,disable-foofoo that is a
single env var accepting a list of debug switches. We need to also
think whether we want it to be a libweston or Weston thing. If it was a
Weston thing, then libweston would have ABI to set them. I haven't
thought it through yet.
As Pekka says, it's definitely not for the long term: it's just
temporary for debugging, and we'll definitely figure out something
better in the long run.

Cheers,
Daniel
Daniel Stone
2017-09-26 17:15:39 UTC
Permalink
If we don't have any damage for the primary plane, then don't force a
repaint; simply reuse the old buffer we already have.

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-drm.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 23ac2dec..f732d20d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1534,6 +1534,7 @@ 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_plane_state *scanout_state;
+ struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_backend *b = to_drm_backend(c);
struct drm_fb *fb;

@@ -1544,10 +1545,20 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
if (scanout_state->fb)
return;

- if (b->use_pixman)
+ if (!pixman_region32_not_empty(damage) &&
+ scanout_plane->state_cur->fb &&
+ (scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE ||
+ scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB) &&
+ scanout_plane->state_cur->fb->width ==
+ output->base.current_mode->width &&
+ scanout_plane->state_cur->fb->height ==
+ output->base.current_mode->height) {
+ fb = drm_fb_ref(scanout_plane->state_cur->fb);
+ } else if (b->use_pixman) {
fb = drm_output_render_pixman(state, damage);
- else
+ } else {
fb = drm_output_render_gl(state, damage);
+ }

if (!fb) {
drm_plane_state_put_back(scanout_state);
--
2.14.1
Daniel Stone
2017-09-26 17:15:41 UTC
Permalink
If we have an unused CRTC or connector, explicitly disable it during the
end of the repaint cycle, or when we get VT-switched back in.

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-drm.c | 60 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 48 insertions(+), 12 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d2ed8343..530b0fd5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -185,6 +185,7 @@ struct drm_backend {

void *repaint_data;

+ bool state_invalid;
struct wl_array unused_connectors;
struct wl_array unused_crtcs;

@@ -345,8 +346,6 @@ struct drm_output {
enum dpms_enum dpms;
struct backlight *backlight;

- bool state_invalid;
-
int vblank_pending;
int page_flip_pending;
int destroy_pending;
@@ -384,6 +383,26 @@ static struct gl_renderer_interface *gl_renderer;

static const char default_seat[] = "seat0";

+static void
+wl_array_remove_uint32(struct wl_array *array, uint32_t elm)
+{
+ uint32_t *pos, *end;
+
+ end = (uint32_t *) ((char *) array->data + array->size);
+
+ wl_array_for_each(pos, array) {
+ if (*pos != elm)
+ continue;
+
+ array->size -= sizeof(*pos);
+ if (pos + 1 == end)
+ break;
+
+ memmove(pos, pos + 1, (char *) end - (char *) (pos + 1));
+ break;
+ }
+}
+
static inline struct drm_output *
to_drm_output(struct weston_output *base)
{
@@ -1694,7 +1713,7 @@ drm_output_repaint(struct weston_output *output_base,
assert(scanout_state->dest_h == scanout_state->src_h >> 16);

mode = container_of(output->base.current_mode, struct drm_mode, base);
- if (output->state_invalid || !scanout_plane->state_cur->fb ||
+ if (backend->state_invalid || !scanout_plane->state_cur->fb ||
scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
@@ -1706,9 +1725,7 @@ drm_output_repaint(struct weston_output *output_base,
goto err;
}
output_base->set_dpms(output_base, WESTON_DPMS_ON);
-
- output->state_invalid = false;
- }
+ }

if (drmModePageFlip(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
@@ -1815,7 +1832,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
/* Need to smash all state in from scratch; current timings might not
* be what we want, page flip might not work, etc.
*/
- if (output->state_invalid)
+ if (backend->state_invalid)
goto finish_frame;

assert(scanout_plane->state_cur->output == output);
@@ -1984,6 +2001,18 @@ 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;
+ uint32_t *unused;
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs we aren't using. This also disables
+ * all connectors on these CRTCs, so we don't need to do that
+ * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
+ NULL);
+ }

wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
link) {
@@ -1991,6 +2020,8 @@ drm_repaint_flush(struct weston_compositor *compositor, void *repaint_data)
DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
}

+ b->state_invalid = false;
+
drm_pending_state_free(pending_state);
b->repaint_data = NULL;
}
@@ -3914,7 +3945,8 @@ drm_output_enable(struct weston_output *base)
output->connector->count_modes == 0 ?
", built-in" : "");

- output->state_invalid = true;
+ wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id);
+ wl_array_remove_uint32(&b->unused_connectors, output->connector_id);

return 0;

@@ -3993,6 +4025,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);
+ uint32_t *unused;

if (output->page_flip_pending || output->vblank_pending) {
output->disable_pending = 1;
@@ -4011,6 +4044,11 @@ drm_output_disable(struct weston_output *base)
drm_output_state_free(output->state_cur);
output->state_cur = drm_output_state_alloc(output, NULL);

+ unused = wl_array_add(&b->unused_connectors, sizeof(*unused));
+ *unused = output->connector_id;
+ unused = wl_array_add(&b->unused_crtcs, sizeof(*unused));
+ *unused = output->crtc_id;
+
return 0;
}

@@ -4377,10 +4415,7 @@ session_notify(struct wl_listener *listener, void *data)
weston_log("activating session\n");
weston_compositor_wake(compositor);
weston_compositor_damage_all(compositor);
-
- wl_list_for_each(output, &compositor->output_list, base.link)
- output->state_invalid = true;
-
+ b->state_invalid = true;
udev_input_enable(&b->input);
} else {
weston_log("deactivating session\n");
@@ -4768,6 +4803,7 @@ drm_backend_create(struct weston_compositor *compositor,
if (b == NULL)
return NULL;

+ b->state_invalid = true;
b->drm.fd = -1;
wl_array_init(&b->unused_crtcs);
wl_array_init(&b->unused_connectors);
--
2.14.1
Daniel Stone
2017-09-26 17:15:40 UTC
Permalink
Rather than a more piecemeal approach at backend creation, explicitly
track connectors and CRTCs we do not intend to use, so we can ensure
they are disabled where appropriate.

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>
---
libweston/compositor-drm.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index f732d20d..d2ed8343 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -185,6 +185,9 @@ struct drm_backend {

void *repaint_data;

+ struct wl_array unused_connectors;
+ struct wl_array unused_crtcs;
+
int cursors_are_broken;

bool universal_planes;
@@ -4011,6 +4014,47 @@ drm_output_disable(struct weston_output *base)
return 0;
}

+/**
+ * Update the list of unused connectors and CRTCs
+ *
+ * This keeps the unused_connectors and unused_crtcs arrays up to date.
+ *
+ * @param b Weston backend structure
+ * @param resources DRM resources for this device
+ */
+static void
+drm_backend_update_unused_outputs(struct drm_backend *b, drmModeRes *resources)
+{
+ int i;
+
+ wl_array_release(&b->unused_connectors);
+ wl_array_init(&b->unused_connectors);
+
+ for (i = 0; i < resources->count_connectors; i++) {
+ uint32_t *connector_id;
+
+ if (drm_output_find_by_connector(b, resources->connectors[i]))
+ continue;
+
+ connector_id = wl_array_add(&b->unused_connectors,
+ sizeof(*connector_id));
+ *connector_id = resources->connectors[i];
+ }
+
+ wl_array_release(&b->unused_crtcs);
+ wl_array_init(&b->unused_crtcs);
+
+ for (i = 0; i < resources->count_crtcs; i++) {
+ uint32_t *crtc_id;
+
+ if (drm_output_find_by_crtc(b, resources->crtcs[i]))
+ continue;
+
+ crtc_id = wl_array_add(&b->unused_crtcs, sizeof(*crtc_id));
+ *crtc_id = resources->crtcs[i];
+ }
+}
+
/**
* Create a Weston output structure
*
@@ -4155,6 +4199,8 @@ create_outputs(struct drm_backend *b, struct udev_device *drm_device)
}
}

+ drm_backend_update_unused_outputs(b, resources);
+
if (wl_list_empty(&b->compositor->output_list) &&
wl_list_empty(&b->compositor->pending_output_list))
weston_log("No currently active connector found.\n");
@@ -4246,6 +4292,8 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device)
drm_output_destroy(&output->base);
}

+ drm_backend_update_unused_outputs(b, resources);
+
free(connected);
drmModeFreeResources(resources);
}
@@ -4310,6 +4358,9 @@ drm_destroy(struct weston_compositor *ec)

weston_launcher_destroy(ec->launcher);

+ wl_array_release(&b->unused_crtcs);
+ wl_array_release(&b->unused_connectors);
+
close(b->drm.fd);
free(b);
}
@@ -4718,6 +4769,8 @@ drm_backend_create(struct weston_compositor *compositor,
return NULL;

b->drm.fd = -1;
+ wl_array_init(&b->unused_crtcs);
+ wl_array_init(&b->unused_connectors);

/*
* KMS support for hardware planes cannot properly synchronize
--
2.14.1
Daniel Stone
2017-09-26 17:15:46 UTC
Permalink
For atomic modesetting support, the mode is identified by a blob
property ID, rather than being passed inline. Add a blob_id member to
drm_mode to handle this, including refactoring mode destruction into a
helper function.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 664c12ae..2a72456d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -227,6 +227,7 @@ struct drm_backend {
struct drm_mode {
struct weston_mode base;
drmModeModeInfo mode_info;
+ uint32_t blob_id;
};

enum drm_fb_type {
@@ -3261,6 +3262,7 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)

mode->base.refresh = refresh;
mode->mode_info = *info;
+ mode->blob_id = 0;

if (info->type & DRM_MODE_TYPE_PREFERRED)
mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
@@ -3270,6 +3272,18 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
return mode;
}

+/**
+ * Destroys a mode, and removes it from the list.
+ */
+static void
+drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode)
+{
+ if (mode->blob_id)
+ drmModeDestroyPropertyBlob(backend->drm.fd, mode->blob_id);
+ wl_list_remove(&mode->base.link);
+ free(mode);
+}
+
static int
drm_subpixel_to_wayland(int drm_value)
{
@@ -4145,6 +4159,7 @@ static void
drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
+ struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_mode *drm_mode, *next;

if (output->page_flip_pending || output->vblank_pending) {
@@ -4157,10 +4172,8 @@ drm_output_destroy(struct weston_output *base)
drm_output_deinit(&output->base);

wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
- base.link) {
- wl_list_remove(&drm_mode->base.link);
- free(drm_mode);
- }
+ base.link)
+ drm_output_destroy_mode(b, drm_mode);

if (output->pageflip_timer)
wl_event_source_remove(output->pageflip_timer);
--
2.14.1
Pekka Paalanen
2017-10-04 09:09:34 UTC
Permalink
On Tue, 26 Sep 2017 18:15:46 +0100
Post by Daniel Stone
For atomic modesetting support, the mode is identified by a blob
property ID, rather than being passed inline. Add a blob_id member to
drm_mode to handle this, including refactoring mode destruction into a
helper function.
---
libweston/compositor-drm.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Daniel Stone
2017-09-26 17:15:45 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Set the atomic client cap, where it exists, and use this to discover the
plane/CRTC/connector properties we require for atomic modesetting.

Signed-off-by: Daniel Stone <***@collabora.com>
Co-authored-by: Pekka Paalanen <***@collabora.co.uk>
---
configure.ac | 3 +++
libweston/compositor-drm.c | 57 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 0ea7b703..398db801 100644
--- a/configure.ac
+++ b/configure.ac
@@ -200,6 +200,9 @@ AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes)
if test x$enable_drm_compositor = xyes; then
AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor])
PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm])
+ PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.62],
+ [AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
+ [AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
[AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])],
[AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])])
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 428205b8..664c12ae 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -88,6 +88,16 @@
*/
enum wdrm_plane_property {
WDRM_PLANE_TYPE = 0,
+ WDRM_PLANE_SRC_X,
+ WDRM_PLANE_SRC_Y,
+ WDRM_PLANE_SRC_W,
+ WDRM_PLANE_SRC_H,
+ WDRM_PLANE_CRTC_X,
+ WDRM_PLANE_CRTC_Y,
+ WDRM_PLANE_CRTC_W,
+ WDRM_PLANE_CRTC_H,
+ WDRM_PLANE_FB_ID,
+ WDRM_PLANE_CRTC_ID,
WDRM_PLANE__COUNT
};

@@ -107,6 +117,7 @@ enum wdrm_plane_type {
enum wdrm_connector_property {
WDRM_CONNECTOR_EDID = 0,
WDRM_CONNECTOR_DPMS,
+ WDRM_CONNECTOR_CRTC_ID,
WDRM_CONNECTOR__COUNT
};

@@ -139,6 +150,15 @@ struct drm_property_info {
struct drm_property_enum_info *enum_values; /**< array of enum values */
};

+/**
+ * List of properties attached to DRM CRTCs
+ */
+enum wdrm_crtc_property {
+ WDRM_CRTC_MODE_ID = 0,
+ WDRM_CRTC_ACTIVE,
+ WDRM_CRTC__COUNT
+};
+
/**
* Mode for drm_output_state_duplicate.
*/
@@ -192,6 +212,7 @@ struct drm_backend {
int cursors_are_broken;

bool universal_planes;
+ bool atomic_modeset;

int use_pixman;

@@ -342,6 +363,8 @@ struct drm_output {

/* Holds the properties for the connector */
struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT];
+ /* Holds the properties for the CRTC */
+ struct drm_property_info props_crtc[WDRM_CRTC__COUNT];

enum dpms_enum dpms;
struct backlight *backlight;
@@ -2816,6 +2839,13 @@ init_kms_caps(struct drm_backend *b)
weston_log("DRM: %s universal planes\n",
b->universal_planes ? "supports" : "does not support");

+#ifdef HAVE_DRM_ATOMIC
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ b->atomic_modeset = (ret == 0);
+#endif
+ weston_log("DRM: %s atomic modesetting\n",
+ b->atomic_modeset ? "supports" : "does not support");
+
return 0;
}

@@ -2959,6 +2989,16 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
.enum_values = plane_type_enums,
.num_enum_values = WDRM_PLANE_TYPE__COUNT,
},
+ [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
+ [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
+ [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
+ [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
+ [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
+ [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
+ [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
+ [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
+ [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
+ [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
};

plane = zalloc(sizeof(*plane) +
@@ -3141,7 +3181,6 @@ create_sprites(struct drm_backend *b)
drmModePlane *kplane;
struct drm_plane *drm_plane;
uint32_t i;
-
kplane_res = drmModeGetPlaneResources(b->drm.fd);
if (!kplane_res) {
weston_log("failed to get plane resources: %s\n",
@@ -4129,6 +4168,7 @@ drm_output_destroy(struct weston_output *base)
weston_output_destroy(&output->base);

drm_property_info_free(output->props_conn, WDRM_CONNECTOR__COUNT);
+ drm_property_info_free(output->props_crtc, WDRM_CRTC__COUNT);

drmModeFreeConnector(output->connector);

@@ -4246,6 +4286,11 @@ create_output_for_connector(struct drm_backend *b,
static const struct drm_property_info connector_props[] = {
[WDRM_CONNECTOR_EDID] = { .name = "EDID" },
[WDRM_CONNECTOR_DPMS] = { .name = "DPMS" },
+ [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
+ };
+ static const struct drm_property_info crtc_props[] = {
+ [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", },
+ [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", },
};

i = find_crtc_for_connector(b, resources, connector);
@@ -4290,6 +4335,16 @@ create_output_for_connector(struct drm_backend *b,
find_and_parse_output_edid(b, output, props);
drmModeFreeObjectProperties(props);

+ props = drmModeObjectGetProperties(b->drm.fd, output->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!props) {
+ weston_log("failed to get CRTC properties\n");
+ goto err;
+ }
+ drm_property_info_populate(b, crtc_props, output->props_crtc,
+ WDRM_CRTC__COUNT, props);
+ drmModeFreeObjectProperties(props);
+
output->state_cur = drm_output_state_alloc(output, NULL);

weston_output_init(&output->base, b->compositor);
--
2.14.1
Pekka Paalanen
2017-10-04 09:05:03 UTC
Permalink
On Tue, 26 Sep 2017 18:15:45 +0100
Post by Daniel Stone
Set the atomic client cap, where it exists, and use this to discover the
plane/CRTC/connector properties we require for atomic modesetting.
---
configure.ac | 3 +++
libweston/compositor-drm.c | 57 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 59 insertions(+), 1 deletion(-)
@@ -2816,6 +2839,13 @@ init_kms_caps(struct drm_backend *b)
weston_log("DRM: %s universal planes\n",
b->universal_planes ? "supports" : "does not support");
+#ifdef HAVE_DRM_ATOMIC
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ b->atomic_modeset = (ret == 0);
Hi,

as I saw this being discussed in IRC, maybe this should be conditional
on universal planes, because enabling atomic will also enable universal
planes, and that will break the debug code disabling universal planes.
Post by Daniel Stone
+#endif
+ weston_log("DRM: %s atomic modesetting\n",
+ b->atomic_modeset ? "supports" : "does not support");
+
return 0;
}
@@ -2959,6 +2989,16 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
.enum_values = plane_type_enums,
.num_enum_values = WDRM_PLANE_TYPE__COUNT,
},
+ [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
+ [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
+ [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
+ [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
+ [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
+ [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
+ [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
+ [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
+ [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
+ [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
};
plane = zalloc(sizeof(*plane) +
@@ -3141,7 +3181,6 @@ create_sprites(struct drm_backend *b)
drmModePlane *kplane;
struct drm_plane *drm_plane;
uint32_t i;
-
Stray change.
Post by Daniel Stone
kplane_res = drmModeGetPlaneResources(b->drm.fd);
if (!kplane_res) {
weston_log("failed to get plane resources: %s\n",
Those two fixed gives this:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Daniel Stone
2017-09-26 17:15:38 UTC
Permalink
Use a real drm_plane to back the scanout plane, displacing
output->fb_{last,cur,pending} to their plane-tracked equivalents.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 167 +++++++++++++++++++++++++++------------------
1 file changed, 101 insertions(+), 66 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 33555d00..23ac2dec 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -357,17 +357,8 @@ struct drm_output {
struct gbm_surface *gbm_surface;
uint32_t gbm_format;

- /* Plane for a fullscreen direct scanout view */
- struct weston_plane scanout_plane;
-
- /* The last framebuffer submitted to the kernel for this CRTC. */
- struct drm_fb *fb_current;
- /* The previously-submitted framebuffer, where the hardware has not
- * yet acknowledged display of fb_current. */
- struct drm_fb *fb_last;
- /* Framebuffer we are going to submit to the kernel when the current
- * repaint is flushed. */
- struct drm_fb *fb_pending;
+ /* Plane being displayed directly on the CRTC */
+ struct drm_plane *scanout_plane;

/* The last state submitted to the kernel for this CRTC. */
struct drm_output_state *state_cur;
@@ -1342,6 +1333,8 @@ drm_output_assign_state(struct drm_output_state *state,

if (plane->type == WDRM_PLANE_TYPE_OVERLAY)
output->vblank_pending++;
+ else if (plane->type == WDRM_PLANE_TYPE_PRIMARY)
+ output->page_flip_pending = 1;
}
}

@@ -1389,6 +1382,8 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
{
struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct drm_plane *scanout_plane = output->scanout_plane;
+ struct drm_plane_state *state;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct gbm_bo *bo;
@@ -1429,6 +1424,15 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
if (ev->alpha != 1.0f)
return NULL;

+ state = drm_output_state_get_plane(output_state, scanout_plane);
+ if (state->fb) {
+ /* If there is already a framebuffer on the scanout plane,
+ * a client view has already been placed on the scanout
+ * view. In that case, do not free or put back the state,
+ * but just leave it in place and quietly exit. */
+ return NULL;
+ }
+
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);

@@ -1438,19 +1442,33 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,

format = drm_output_check_scanout_format(output, ev->surface, bo);
if (format == 0) {
+ drm_plane_state_put_back(state);
gbm_bo_destroy(bo);
return NULL;
}

- output->fb_pending = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!output->fb_pending) {
+ state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
+ if (!state->fb) {
+ drm_plane_state_put_back(state);
gbm_bo_destroy(bo);
return NULL;
}

- drm_fb_set_buffer(output->fb_pending, buffer);
+ drm_fb_set_buffer(state->fb, buffer);
+
+ state->output = output;
+
+ state->src_x = 0;
+ state->src_y = 0;
+ state->src_w = state->fb->width << 16;
+ state->src_h = state->fb->height << 16;

- return &output->scanout_plane;
+ state->dest_x = 0;
+ state->dest_y = 0;
+ state->dest_w = output->base.current_mode->width;
+ state->dest_h = output->base.current_mode->height;
+
+ return &scanout_plane->base;
}

static struct drm_fb *
@@ -1515,12 +1533,15 @@ 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_plane_state *scanout_state;
struct drm_backend *b = to_drm_backend(c);
struct drm_fb *fb;

/* If we already have a client buffer promoted to scanout, then we don't
* want to render. */
- if (output->fb_pending)
+ scanout_state = drm_output_state_get_plane(state,
+ output->scanout_plane);
+ if (scanout_state->fb)
return;

if (b->use_pixman)
@@ -1528,9 +1549,24 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
else
fb = drm_output_render_gl(state, damage);

- if (!fb)
+ if (!fb) {
+ drm_plane_state_put_back(scanout_state);
return;
- output->fb_pending = fb;
+ }
+
+ scanout_state->fb = fb;
+ scanout_state->output = output;
+
+ scanout_state->src_x = 0;
+ scanout_state->src_y = 0;
+ scanout_state->src_w = output->base.current_mode->width << 16;
+ scanout_state->src_h = output->base.current_mode->height << 16;
+
+ scanout_state->dest_x = 0;
+ scanout_state->dest_y = 0;
+ scanout_state->dest_w = scanout_state->src_w >> 16;
+ scanout_state->dest_h = scanout_state->src_h >> 16;
+

pixman_region32_subtract(&c->primary_plane.damage,
&c->primary_plane.damage, damage);
@@ -1593,6 +1629,8 @@ drm_output_repaint(struct weston_output *output_base,
struct drm_output *output = to_drm_output(output_base);
struct drm_backend *backend =
to_drm_backend(output->base.compositor);
+ struct drm_plane *scanout_plane = output->scanout_plane;
+ struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
struct drm_mode *mode;
@@ -1612,7 +1650,6 @@ drm_output_repaint(struct weston_output *output_base,
pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES);

- assert(!output->fb_last);

/* If disable_planes is set then assign_planes() wasn't
* called for this render, so we could still have a stale
@@ -1625,14 +1662,29 @@ drm_output_repaint(struct weston_output *output_base,
}

drm_output_render(state, damage);
- if (!output->fb_pending)
+ scanout_state = drm_output_state_get_plane(state, scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
goto err;

+ /* The legacy SetCrtc API doesn't allow us to do scaling, and the
+ * legacy PageFlip API doesn't allow us to do clipping either. */
+ assert(scanout_state->src_x == 0);
+ assert(scanout_state->src_y == 0);
+ assert(scanout_state->src_w ==
+ (unsigned) (output->base.current_mode->width << 16));
+ assert(scanout_state->src_h ==
+ (unsigned) (output->base.current_mode->height << 16));
+ assert(scanout_state->dest_x == 0);
+ assert(scanout_state->dest_y == 0);
+ assert(scanout_state->dest_w == scanout_state->src_w >> 16);
+ assert(scanout_state->dest_h == scanout_state->src_h >> 16);
+
mode = container_of(output->base.current_mode, struct drm_mode, base);
- if (output->state_invalid || !output->fb_current ||
- output->fb_current->stride != output->fb_pending->stride) {
+ if (output->state_invalid || !scanout_plane->state_cur->fb ||
+ scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- output->fb_pending->fb_id, 0, 0,
+ scanout_state->fb->fb_id,
+ 0, 0,
&output->connector_id, 1,
&mode->mode_info);
if (ret) {
@@ -1645,18 +1697,13 @@ drm_output_repaint(struct weston_output *output_base,
}

if (drmModePageFlip(backend->drm.fd, output->crtc_id,
- output->fb_pending->fb_id,
+ scanout_state->fb->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
weston_log("queueing pageflip failed: %m\n");
goto err;
}

- output->fb_last = output->fb_current;
- output->fb_current = output->fb_pending;
- output->fb_pending = NULL;
-
assert(!output->page_flip_pending);
- output->page_flip_pending = 1;

if (output->pageflip_timer)
wl_event_source_timer_update(output->pageflip_timer,
@@ -1716,10 +1763,7 @@ drm_output_repaint(struct weston_output *output_base,

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;
@@ -1732,6 +1776,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
struct drm_pending_state *pending_state = NULL;
struct drm_output_state *state;
struct drm_plane_state *plane_state;
+ struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
uint32_t fb_id;
@@ -1748,7 +1793,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
if (output->disable_pending || output->destroy_pending)
return;

- if (!output->fb_current) {
+ if (!output->scanout_plane->state_cur->fb) {
/* We can't page flip if there's no mode set */
goto finish_frame;
}
@@ -1759,6 +1804,8 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
if (output->state_invalid)
goto finish_frame;

+ assert(scanout_plane->state_cur->output == output);
+
/* Try to get current msc and timestamp via instant query */
vbl.request.type |= drm_waitvblank_pipe(output);
ret = drmWaitVBlank(backend->drm.fd, &vbl);
@@ -1788,10 +1835,9 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
/* Immediate query didn't provide valid timestamp.
* Use pageflip fallback.
*/
- fb_id = output->fb_current->fb_id;
+ fb_id = scanout_plane->state_cur->fb->fb_id;

assert(!output->page_flip_pending);
- assert(!output->fb_last);
assert(!output->state_last);

pending_state = drm_pending_state_alloc(backend);
@@ -1809,9 +1855,6 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
wl_event_source_timer_update(output->pageflip_timer,
backend->pageflip_timeout);

- output->fb_last = drm_fb_ref(output->fb_current);
- output->page_flip_pending = 1;
-
wl_list_for_each(plane_state, &state->plane_list, link) {
if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY)
continue;
@@ -1887,9 +1930,6 @@ page_flip_handler(int fd, unsigned int frame,
assert(output->page_flip_pending);
output->page_flip_pending = 0;

- drm_fb_unref(output->fb_last);
- output->fb_last = NULL;
-
if (output->vblank_pending)
return;

@@ -2565,10 +2605,6 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
* sledgehammer modeswitch first, and only later showing new
* content.
*/
- drm_fb_unref(output->fb_current);
- assert(!output->fb_last);
- assert(!output->fb_pending);
- output->fb_last = output->fb_current = NULL;

if (b->use_pixman) {
drm_output_fini_pixman(output);
@@ -2909,14 +2945,16 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
* same plane for two outputs. */
wl_list_for_each(tmp, &b->compositor->pending_output_list,
base.link) {
- if (tmp->cursor_plane == plane) {
+ if (tmp->cursor_plane == plane ||
+ tmp->scanout_plane == plane) {
found_elsewhere = true;
break;
}
}
wl_list_for_each(tmp, &b->compositor->output_list,
base.link) {
- if (tmp->cursor_plane == plane) {
+ if (tmp->cursor_plane == plane ||
+ tmp->scanout_plane == plane) {
found_elsewhere = true;
break;
}
@@ -3839,8 +3877,6 @@ drm_output_enable(struct weston_output *base)
output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
output->base.connection_internal = true;

- weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);
-
if (output->cursor_plane)
weston_compositor_stack_plane(b->compositor,
&output->cursor_plane->base,
@@ -3848,7 +3884,8 @@ drm_output_enable(struct weston_output *base)
else
b->cursors_are_broken = 1;

- weston_compositor_stack_plane(b->compositor, &output->scanout_plane,
+ weston_compositor_stack_plane(b->compositor,
+ &output->scanout_plane->base,
&b->compositor->primary_plane);

weston_log("Output %s, (connector %d, crtc %d)\n",
@@ -3877,20 +3914,11 @@ drm_output_deinit(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);

- /* output->fb_last and output->fb_pending must not be set here;
- * destroy_pending/disable_pending exist to guarantee exactly this. */
- assert(!output->fb_last);
- assert(!output->fb_pending);
- drm_fb_unref(output->fb_current);
- output->fb_current = NULL;
-
if (b->use_pixman)
drm_output_fini_pixman(output);
else
drm_output_fini_egl(output);

- weston_plane_release(&output->scanout_plane);
-
if (output->cursor_plane) {
/* Turn off hardware cursor */
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
@@ -3960,10 +3988,6 @@ drm_output_disable(struct weston_output *base)
if (output->base.enabled)
drm_output_deinit(&output->base);

- assert(!output->fb_last);
- assert(!output->fb_current);
- assert(!output->fb_pending);
-
output->disable_pending = 0;

weston_log("Disabling output %s\n", output->base.name);
@@ -4058,6 +4082,16 @@ create_output_for_connector(struct drm_backend *b,
}
}

+ output->scanout_plane =
+ drm_output_find_special_plane(b, output,
+ WDRM_PLANE_TYPE_PRIMARY);
+ if (!output->scanout_plane) {
+ weston_log("Failed to find primary plane for output %s\n",
+ output->base.name);
+ drm_output_destroy(&output->base);
+ return -1;
+ }
+
/* Failing to find a cursor plane is not fatal, as we'll fall back
* to software cursor. */
output->cursor_plane =
@@ -4512,7 +4546,8 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
if (!output->recorder)
return;

- ret = drmPrimeHandleToFD(b->drm.fd, output->fb_current->handle,
+ ret = drmPrimeHandleToFD(b->drm.fd,
+ output->scanout_plane->state_cur->fb->handle,
DRM_CLOEXEC, &fd);
if (ret) {
weston_log("[libva recorder] "
@@ -4521,7 +4556,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
}

ret = vaapi_recorder_frame(output->recorder, fd,
- output->fb_current->stride);
+ output->scanout_plane->state_cur->fb->stride);
if (ret < 0) {
weston_log("[libva recorder] aborted: %m\n");
recorder_destroy(output);
--
2.14.1
Pekka Paalanen
2017-10-02 14:09:39 UTC
Permalink
On Tue, 26 Sep 2017 18:15:38 +0100
Post by Daniel Stone
Use a real drm_plane to back the scanout plane, displacing
output->fb_{last,cur,pending} to their plane-tracked equivalents.
---
libweston/compositor-drm.c | 167 +++++++++++++++++++++++++++------------------
1 file changed, 101 insertions(+), 66 deletions(-)
Hi Daniel,

my comments from last time are mostly fixed.
Post by Daniel Stone
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 33555d00..23ac2dec 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2565,10 +2605,6 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
* sledgehammer modeswitch first, and only later showing new
* content.
*/
- drm_fb_unref(output->fb_current);
- assert(!output->fb_last);
- assert(!output->fb_pending);
- output->fb_last = output->fb_current = NULL;
I know this path is all kinds of broken, but how about at least setting

output->state_invalid = true;

here to guarantee we hit the SetCrtc path the repaint?
Post by Daniel Stone
if (b->use_pixman) {
drm_output_fini_pixman(output);
@@ -2909,14 +2945,16 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
* same plane for two outputs. */
wl_list_for_each(tmp, &b->compositor->pending_output_list,
base.link) {
- if (tmp->cursor_plane == plane) {
+ if (tmp->cursor_plane == plane ||
+ tmp->scanout_plane == plane) {
found_elsewhere = true;
break;
}
}
wl_list_for_each(tmp, &b->compositor->output_list,
base.link) {
- if (tmp->cursor_plane == plane) {
+ if (tmp->cursor_plane == plane ||
+ tmp->scanout_plane == plane) {
found_elsewhere = true;
break;
}
@@ -3839,8 +3877,6 @@ drm_output_enable(struct weston_output *base)
output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
output->base.connection_internal = true;
- weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);
-
if (output->cursor_plane)
weston_compositor_stack_plane(b->compositor,
&output->cursor_plane->base,
@@ -3848,7 +3884,8 @@ drm_output_enable(struct weston_output *base)
else
b->cursors_are_broken = 1;
- weston_compositor_stack_plane(b->compositor, &output->scanout_plane,
+ weston_compositor_stack_plane(b->compositor,
+ &output->scanout_plane->base,
&b->compositor->primary_plane);
weston_log("Output %s, (connector %d, crtc %d)\n",
@@ -3877,20 +3914,11 @@ drm_output_deinit(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- /* output->fb_last and output->fb_pending must not be set here;
- * destroy_pending/disable_pending exist to guarantee exactly this. */
- assert(!output->fb_last);
- assert(!output->fb_pending);
- drm_fb_unref(output->fb_current);
- output->fb_current = NULL;
-
if (b->use_pixman)
drm_output_fini_pixman(output);
else
drm_output_fini_egl(output);
- weston_plane_release(&output->scanout_plane);
-
I would assume this has the same issues as the cursor plane patch does.
Deinit should undo weston_compositor_stack_plane().

It probably has the same shutdown problem as well: destroy_sprites()
destroying the plane, then output destruction hitting use-after-free.
Post by Daniel Stone
if (output->cursor_plane) {
/* Turn off hardware cursor */
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
@@ -3960,10 +3988,6 @@ drm_output_disable(struct weston_output *base)
if (output->base.enabled)
drm_output_deinit(&output->base);
- assert(!output->fb_last);
- assert(!output->fb_current);
- assert(!output->fb_pending);
-
output->disable_pending = 0;
weston_log("Disabling output %s\n", output->base.name);
@@ -4058,6 +4082,16 @@ create_output_for_connector(struct drm_backend *b,
}
}
+ output->scanout_plane =
+ drm_output_find_special_plane(b, output,
+ WDRM_PLANE_TYPE_PRIMARY);
+ if (!output->scanout_plane) {
+ weston_log("Failed to find primary plane for output %s\n",
+ output->base.name);
+ drm_output_destroy(&output->base);
+ return -1;
+ }
+
/* Failing to find a cursor plane is not fatal, as we'll fall back
* to software cursor. */
output->cursor_plane =
Other than those, seems fine to me.

I also re-read your scene-graph planes vs. KMS planes comment from last
time, and yeah, agreed. It's a little funny that primary_plane (by
libweston core) is used with renderer damage tracking even when
scanout_plane is used for scanning it out. Maybe when I or someone gets
to fixing the renderer-based clone mode it will get cleaned up.


Thanks,
pq
Daniel Stone
2017-09-26 17:15:44 UTC
Permalink
When leaving Weston, don't attempt to restore the previous CRTC
settings. The framebuffer may well have disappeared, and in every
likelihood, whoever gets the KMS device afterwards will be repainting
anyway.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 26 ++++++++------------------
1 file changed, 8 insertions(+), 18 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 1781c7df..428205b8 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -338,7 +338,6 @@ struct drm_output {
uint32_t crtc_id; /* object ID to pass to DRM functions */
int pipe; /* index of CRTC in resource array / bitmasks */
uint32_t connector_id;
- drmModeCrtcPtr original_crtc;
struct drm_edid edid;

/* Holds the properties for the connector */
@@ -1653,8 +1652,6 @@ drm_output_set_gamma(struct weston_output *output_base,
/* check */
if (output_base->gamma_size != size)
return;
- if (!output->original_crtc)
- return;

rc = drmModeCrtcSetGamma(backend->drm.fd,
output->crtc_id,
@@ -1779,7 +1776,7 @@ drm_output_apply_state(struct drm_output_state *state)
weston_log("set mode failed: %m\n");
goto err;
}
- }
+ }

if (drmModePageFlip(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
@@ -4049,10 +4046,7 @@ drm_output_enable(struct weston_output *base)
output->base.assign_planes = drm_assign_planes;
output->base.set_dpms = drm_set_dpms;
output->base.switch_mode = drm_output_switch_mode;
-
- output->base.gamma_size = output->original_crtc->gamma_size;
output->base.set_gamma = drm_output_set_gamma;
-
output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel);

if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
@@ -4112,9 +4106,7 @@ static void
drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
- struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_mode *drm_mode, *next;
- drmModeCrtcPtr origcrtc = output->original_crtc;

if (output->page_flip_pending || output->vblank_pending) {
output->destroy_pending = 1;
@@ -4131,14 +4123,6 @@ drm_output_destroy(struct weston_output *base)
free(drm_mode);
}

- if (origcrtc) {
- /* Restore original CRTC state */
- drmModeSetCrtc(b->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
- origcrtc->x, origcrtc->y,
- &output->connector_id, 1, &origcrtc->mode);
- drmModeFreeCrtc(origcrtc);
- }
-
if (output->pageflip_timer)
wl_event_source_remove(output->pageflip_timer);

@@ -4256,6 +4240,7 @@ create_output_for_connector(struct drm_backend *b,
struct drm_output *output;
drmModeObjectPropertiesPtr props;
struct drm_mode *drm_mode;
+ drmModeCrtcPtr origcrtc;
int i;

static const struct drm_property_info connector_props[] = {
@@ -4281,12 +4266,15 @@ create_output_for_connector(struct drm_backend *b,
output->backlight = backlight_init(drm_device,
connector->connector_type);

- output->original_crtc = drmModeGetCrtc(b->drm.fd, output->crtc_id);
+ origcrtc = drmModeGetCrtc(b->drm.fd, output->crtc_id);
+ if (origcrtc == NULL)
+ goto err;

output->base.enable = drm_output_enable;
output->base.destroy = drm_output_destroy;
output->base.disable = drm_output_disable;
output->base.name = make_connector_name(connector);
+ output->base.gamma_size = origcrtc->gamma_size;

output->destroy_pending = 0;
output->disable_pending = 0;
@@ -4306,6 +4294,8 @@ create_output_for_connector(struct drm_backend *b,

weston_output_init(&output->base, b->compositor);

+ drmModeFreeCrtc(origcrtc);
+
wl_list_init(&output->base.mode_list);

for (i = 0; i < output->connector->count_modes; i++) {
--
2.14.1
Pekka Paalanen
2017-10-04 08:54:53 UTC
Permalink
On Tue, 26 Sep 2017 18:15:44 +0100
Post by Daniel Stone
When leaving Weston, don't attempt to restore the previous CRTC
settings. The framebuffer may well have disappeared, and in every
likelihood, whoever gets the KMS device afterwards will be repainting
anyway.
---
libweston/compositor-drm.c | 26 ++++++++------------------
1 file changed, 8 insertions(+), 18 deletions(-)
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Random thoughts:

I suspect in the long run we will need to create a new struct for all
CRTCs to hold their properties, whether they have an output or not.

Hm, it's interesting... to set gamma, one needs to know the gamma table
size, for that one needs to have a CRTC allocated for the output, but
to allocate the CRTC one needs to atomic commit TEST_ONLY. Yeah, that's
for another era. Also dynamically switching the CRTC from under an
enabled output to accommodate enabling other outputs. All for when
atomic and head-based have long landed upstream already.


Thanks,
pq
Daniel Stone
2017-09-26 17:15:51 UTC
Permalink
Pull this into a helper function, so we can use it everywhere.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 157 +++++++++++++++++++++++++--------------------
1 file changed, 88 insertions(+), 69 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3e606413..2c4b8ba5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1155,6 +1155,92 @@ drm_plane_state_put_back(struct drm_plane_state *state)
(void) drm_plane_state_alloc(state_output, plane);
}

+/**
+ * Given a weston_view, fill the drm_plane_state's co-ordinates to display on
+ * a given plane.
+ */
+static void
+drm_plane_state_coords_for_view(struct drm_plane_state *state,
+ struct weston_view *ev)
+{
+ struct drm_output *output = state->output;
+ struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+ pixman_region32_t dest_rect, src_rect;
+ pixman_box32_t *box, tbox;
+ wl_fixed_t sx1, sy1, sx2, sy2;
+
+ /* Update the base weston_plane co-ordinates. */
+ box = pixman_region32_extents(&ev->transform.boundingbox);
+ state->plane->base.x = box->x1;
+ state->plane->base.y = box->y1;
+
+ /* First calculate the destination co-ordinates by taking the
+ * area of the view which is visible on this output, performing any
+ * transforms to account for output rotation and scale as necessary. */
+ pixman_region32_init(&dest_rect);
+ pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
+ &output->base.region);
+ pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
+ box = pixman_region32_extents(&dest_rect);
+ tbox = weston_transformed_rect(output->base.width,
+ output->base.height,
+ output->base.transform,
+ output->base.current_scale,
+ *box);
+ state->dest_x = tbox.x1;
+ state->dest_y = tbox.y1;
+ state->dest_w = tbox.x2 - tbox.x1;
+ state->dest_h = tbox.y2 - tbox.y1;
+ pixman_region32_fini(&dest_rect);
+
+ /* Now calculate the source rectangle, by finding the points in the
+ * view and working backwards to source co-ordinates. */
+ pixman_region32_init(&src_rect);
+ pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
+ &output->base.region);
+ box = pixman_region32_extents(&src_rect);
+
+ /* Accounting for any transformations made to this particular surface
+ * view, find the source rectangle to use. */
+ weston_view_from_global_fixed(ev,
+ wl_fixed_from_int(box->x1),
+ wl_fixed_from_int(box->y1),
+ &sx1, &sy1);
+ weston_view_from_global_fixed(ev,
+ wl_fixed_from_int(box->x2),
+ wl_fixed_from_int(box->y2),
+ &sx2, &sy2);
+
+ /* XXX: How is this possible ... ? */
+ if (sx1 < 0)
+ sx1 = 0;
+ if (sy1 < 0)
+ sy1 = 0;
+ if (sx2 > wl_fixed_from_int(ev->surface->width))
+ sx2 = wl_fixed_from_int(ev->surface->width);
+ if (sy2 > wl_fixed_from_int(ev->surface->height))
+ sy2 = wl_fixed_from_int(ev->surface->height);
+
+ tbox.x1 = sx1;
+ tbox.y1 = sy1;
+ tbox.x2 = sx2;
+ tbox.y2 = sy2;
+
+ /* Apply viewport transforms in reverse, to get the source co-ordinates
+ * in buffer space. */
+ tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
+ wl_fixed_from_int(ev->surface->height),
+ viewport->buffer.transform,
+ viewport->buffer.scale,
+ tbox);
+
+ state->src_x = tbox.x1 << 8;
+ state->src_y = tbox.y1 << 8;
+ state->src_w = (tbox.x2 - tbox.x1) << 8;
+ state->src_h = (tbox.y2 - tbox.y1) << 8;
+ pixman_region32_fini(&src_rect);
+}
+
/**
* Return a plane state from a drm_output_state.
*/
@@ -2552,16 +2638,13 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
{
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;
+ struct drm_backend *b = to_drm_backend(ec);
struct wl_resource *buffer_resource;
struct drm_plane *p;
struct drm_plane_state *state = NULL;
struct linux_dmabuf_buffer *dmabuf;
struct gbm_bo *bo;
- pixman_region32_t dest_rect, src_rect;
- pixman_box32_t *box, tbox;
- wl_fixed_t sx1, sy1, sx2, sy2;
unsigned int i;

if (b->sprites_are_broken)
@@ -2674,71 +2757,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
drm_fb_set_buffer(state->fb, ev->surface->buffer_ref.buffer);

state->output = output;
-
- box = pixman_region32_extents(&ev->transform.boundingbox);
- p->base.x = box->x1;
- p->base.y = box->y1;
-
- /*
- * Calculate the source & dest rects properly based on actual
- * position (note the caller has called weston_view_update_transform()
- * for us already).
- */
- pixman_region32_init(&dest_rect);
- pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
- &output->base.region);
- pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
- box = pixman_region32_extents(&dest_rect);
- tbox = weston_transformed_rect(output->base.width,
- output->base.height,
- output->base.transform,
- output->base.current_scale,
- *box);
- state->dest_x = tbox.x1;
- state->dest_y = tbox.y1;
- state->dest_w = tbox.x2 - tbox.x1;
- state->dest_h = tbox.y2 - tbox.y1;
- pixman_region32_fini(&dest_rect);
-
- pixman_region32_init(&src_rect);
- pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
- &output->base.region);
- box = pixman_region32_extents(&src_rect);
-
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x1),
- wl_fixed_from_int(box->y1),
- &sx1, &sy1);
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x2),
- wl_fixed_from_int(box->y2),
- &sx2, &sy2);
-
- if (sx1 < 0)
- sx1 = 0;
- if (sy1 < 0)
- sy1 = 0;
- if (sx2 > wl_fixed_from_int(ev->surface->width))
- sx2 = wl_fixed_from_int(ev->surface->width);
- if (sy2 > wl_fixed_from_int(ev->surface->height))
- sy2 = wl_fixed_from_int(ev->surface->height);
-
- tbox.x1 = sx1;
- tbox.y1 = sy1;
- tbox.x2 = sx2;
- tbox.y2 = sy2;
-
- tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
- wl_fixed_from_int(ev->surface->height),
- viewport->buffer.transform,
- viewport->buffer.scale,
- tbox);
-
- state->src_x = tbox.x1 << 8;
- state->src_y = tbox.y1 << 8;
- state->src_w = (tbox.x2 - tbox.x1) << 8;
- state->src_h = (tbox.y2 - tbox.y1) << 8;
- pixman_region32_fini(&src_rect);
+ drm_plane_state_coords_for_view(state, ev);

return &p->base;
--
2.14.1
Daniel Stone
2017-09-26 17:15:50 UTC
Permalink
Rather than a hardcoded ARGB8888 -> XRGB8888 translation inside a
GBM-specific helper, just determine whether or not the view is opaque,
and use the generic helpers to implement the format translation.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 111 +++++++++++++++++----------------------------
1 file changed, 41 insertions(+), 70 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8cd69f1f..3e606413 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -926,7 +926,7 @@ drm_fb_ref(struct drm_fb *fb)

static struct drm_fb *
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
- uint32_t format, enum drm_fb_type type)
+ bool is_opaque, enum drm_fb_type type)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
@@ -949,16 +949,19 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
fb->height = gbm_bo_get_height(bo);
fb->stride = gbm_bo_get_stride(bo);
fb->handle = gbm_bo_get_handle(bo).u32;
- fb->format = pixel_format_get_info(format);
+ fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
fb->size = fb->stride * fb->height;
fb->fd = backend->drm.fd;

if (!fb->format) {
weston_log("couldn't look up format 0x%lx\n",
- (unsigned long) format);
+ (unsigned long) gbm_bo_get_format(bo));
goto err_free;
}

+ if (is_opaque)
+ fb->format = pixel_format_get_opaque_substitute(fb->format);
+
if (backend->min_width > fb->width ||
fb->width > backend->max_width ||
backend->min_height > fb->height ||
@@ -969,13 +972,14 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,

ret = -1;

- if (format && !backend->no_addfb2) {
+ if (!backend->no_addfb2) {
handles[0] = fb->handle;
pitches[0] = fb->stride;
offsets[0] = 0;

ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
- format, handles, pitches, offsets,
+ fb->format->format,
+ handles, pitches, offsets,
&fb->fb_id, 0);
if (ret) {
weston_log("addfb2 failed: %m\n");
@@ -1480,34 +1484,26 @@ drm_view_transform_supported(struct weston_view *ev)
(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)
+static bool
+drm_view_is_opaque(struct weston_view *ev)
{
- uint32_t format;
pixman_region32_t r;
+ bool ret = false;

- format = gbm_bo_get_format(bo);
+ /* We can scanout an ARGB buffer if the surface's
+ * opaque region covers the whole output, but we have
+ * to use XRGB as the KMS format code. */
+ pixman_region32_init_rect(&r, 0, 0,
+ ev->surface->width,
+ ev->surface->height);
+ pixman_region32_subtract(&r, &r, &ev->surface->opaque);

- if (format == GBM_FORMAT_ARGB8888) {
- /* We can scanout an ARGB buffer if the surface's
- * opaque region covers the whole output, but we have
- * to use XRGB as the KMS format code. */
- pixman_region32_init_rect(&r, 0, 0,
- output->base.width,
- output->base.height);
- pixman_region32_subtract(&r, &r, &es->opaque);
+ if (!pixman_region32_not_empty(&r))
+ ret = true;

- if (!pixman_region32_not_empty(&r))
- format = GBM_FORMAT_XRGB8888;
-
- pixman_region32_fini(&r);
- }
+ pixman_region32_fini(&r);

- if (output->gbm_format == format)
- return format;
-
- return 0;
+ return ret;
}

static struct weston_plane *
@@ -1521,7 +1517,6 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct gbm_bo *bo;
- uint32_t format;

/* Don't import buffers which span multiple outputs. */
if (ev->output_mask != (1u << output->base.id))
@@ -1574,17 +1569,19 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
if (!bo)
return NULL;

- format = drm_output_check_scanout_format(output, ev->surface, bo);
- if (format == 0) {
+ state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
+ BUFFER_CLIENT);
+ if (!state->fb) {
drm_plane_state_put_back(state);
gbm_bo_destroy(bo);
return NULL;
}

- state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!state->fb) {
+ /* Can't change formats with just a pageflip */
+ if (state->fb->format->format != output->gbm_format) {
+ /* No need to destroy the GBM BO here, as it's now owned
+ * by the FB. */
drm_plane_state_put_back(state);
- gbm_bo_destroy(bo);
return NULL;
}

@@ -1622,7 +1619,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
return NULL;
}

- ret = drm_fb_get_from_bo(bo, b, output->gbm_format, BUFFER_GBM_SURFACE);
+ ret = drm_fb_get_from_bo(bo, b, false, BUFFER_GBM_SURFACE);
if (!ret) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
@@ -2549,35 +2546,6 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
}
#endif

-static uint32_t
-drm_output_check_plane_format(struct drm_plane *p,
- struct weston_view *ev, struct gbm_bo *bo)
-{
- uint32_t i, format;
-
- format = gbm_bo_get_format(bo);
-
- if (format == GBM_FORMAT_ARGB8888) {
- pixman_region32_t r;
-
- pixman_region32_init_rect(&r, 0, 0,
- ev->surface->width,
- ev->surface->height);
- pixman_region32_subtract(&r, &r, &ev->surface->opaque);
-
- if (!pixman_region32_not_empty(&r))
- format = GBM_FORMAT_XRGB8888;
-
- pixman_region32_fini(&r);
- }
-
- for (i = 0; i < p->count_formats; i++)
- if (p->formats[i] == format)
- return format;
-
- return 0;
-}
-
static struct weston_plane *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_view *ev)
@@ -2593,8 +2561,8 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct gbm_bo *bo;
pixman_region32_t dest_rect, src_rect;
pixman_box32_t *box, tbox;
- uint32_t format;
wl_fixed_t sx1, sy1, sx2, sy2;
+ unsigned int i;

if (b->sprites_are_broken)
return NULL;
@@ -2691,12 +2659,16 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
if (!bo)
goto err;

- format = drm_output_check_plane_format(p, ev, bo);
- if (format == 0)
+ state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
+ BUFFER_CLIENT);
+ if (!state->fb)
goto err;

- state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!state->fb)
+ /* Check whether the format is supported */
+ for (i = 0; i < p->count_formats; i++)
+ if (p->formats[i] == state->fb->format->format)
+ break;
+ if (i == p->count_formats)
goto err;

drm_fb_set_buffer(state->fb, ev->surface->buffer_ref.buffer);
@@ -3907,8 +3879,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
goto err;

output->gbm_cursor_fb[i] =
- drm_fb_get_from_bo(bo, b, GBM_FORMAT_ARGB8888,
- BUFFER_CURSOR);
+ drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR);
if (!output->gbm_cursor_fb[i]) {
gbm_bo_destroy(bo);
goto err;
--
2.14.1
Daniel Stone
2017-09-26 17:16:06 UTC
Permalink
When trying to assign planes, keep track of the areas which are
already occluded, and ignore views which are completely occluded. This
allows us to build a state using planes only, when there are occluded
views which cannot go into a plane behind views which can.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 35 +++++++++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7bfdef32..25d0342b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2976,7 +2976,7 @@ drm_output_propose_state(struct weston_output *output_base,
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state;
struct weston_view *ev;
- pixman_region32_t surface_overlap, renderer_region;
+ pixman_region32_t surface_overlap, renderer_region, occluded_region;
struct weston_plane *primary = &output_base->compositor->primary_plane;

assert(!output->state_last);
@@ -2998,9 +2998,12 @@ drm_output_propose_state(struct weston_output *output_base,
* as we do for flipping full screen surfaces.
*/
pixman_region32_init(&renderer_region);
+ pixman_region32_init(&occluded_region);

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct weston_plane *next_plane = NULL;
+ pixman_region32_t clipped_view;
+ bool occluded = false;

/* If this view doesn't touch our output at all, there's no
* reason to do anything with it. */
@@ -3012,18 +3015,35 @@ drm_output_propose_state(struct weston_output *output_base,
if (ev->output_mask != (1u << output->base.id))
next_plane = primary;

+ /* Ignore views we know to be totally occluded. */
+ pixman_region32_init(&clipped_view);
+ pixman_region32_intersect(&clipped_view,
+ &ev->transform.boundingbox,
+ &output->base.region);
+
+ pixman_region32_init(&surface_overlap);
+ pixman_region32_subtract(&surface_overlap, &clipped_view,
+ &occluded_region);
+ occluded = !pixman_region32_not_empty(&surface_overlap);
+ if (occluded) {
+ pixman_region32_fini(&surface_overlap);
+ pixman_region32_fini(&clipped_view);
+ continue;
+ }
+
/* Since we process views from top to bottom, we know that if
* the view intersects the calculated renderer region, it must
* be part of, or occluded by, it, and cannot go on a plane. */
- pixman_region32_init(&surface_overlap);
pixman_region32_intersect(&surface_overlap, &renderer_region,
- &ev->transform.boundingbox);
+ &clipped_view);
if (pixman_region32_not_empty(&surface_overlap))
next_plane = primary;
pixman_region32_fini(&surface_overlap);

if (next_plane == NULL)
next_plane = drm_output_prepare_cursor_view(state, ev);
+ if (next_plane == NULL && !drm_view_is_opaque(ev))
+ next_plane = primary;
if (next_plane == NULL)
next_plane = drm_output_prepare_scanout_view(state, ev);
if (next_plane == NULL)
@@ -3034,9 +3054,16 @@ drm_output_propose_state(struct weston_output *output_base,
if (next_plane == primary)
pixman_region32_union(&renderer_region,
&renderer_region,
- &ev->transform.boundingbox);
+ &clipped_view);
+ else if (output->cursor_plane &&
+ next_plane != &output->cursor_plane->base)
+ pixman_region32_union(&occluded_region,
+ &occluded_region,
+ &clipped_view);
+ pixman_region32_fini(&clipped_view);
}
pixman_region32_fini(&renderer_region);
+ pixman_region32_fini(&occluded_region);

return state;
}
--
2.14.1
Daniel Stone
2017-09-26 17:16:01 UTC
Permalink
Use the new drmModeAddFB2WithModifiers interface to import buffers with
modifiers.

Signed-off-by: Daniel Stone <***@collabora.com>
---
configure.ac | 3 +++
libweston/compositor-drm.c | 28 ++++++++++++++++++++++++++--
2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3996c80c..4c016999 100644
--- a/configure.ac
+++ b/configure.ac
@@ -203,6 +203,9 @@ if test x$enable_drm_compositor = xyes; then
PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
[AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
[AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
+ PKG_CHECK_MODULES(DRM_COMPOSITOR_MODIFIERS, [libdrm >= 2.4.83],
+ [AC_DEFINE([HAVE_DRM_ADDFB2_MODIFIERS], 1, [libdrm supports modifiers])],
+ [AC_MSG_WARN([libdrm does not support AddFB2 with modifiers])])
fi


diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 65934a01..72c9e5a2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -289,6 +289,7 @@ struct drm_fb {
uint32_t strides[4];
uint32_t offsets[4];
const struct pixel_format_info *format;
+ uint64_t modifier;
int width, height;
int fd;
struct weston_buffer_reference buffer_ref;
@@ -824,7 +825,28 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
static int
drm_fb_addfb(struct drm_fb *fb)
{
- int ret;
+ int ret = -EINVAL;
+#ifdef HAVE_DRM_ADDFB2_MODIFIERS
+ uint64_t mods[4] = { };
+ int i;
+#endif
+
+ /* If we have a modifier set, we must only use the WithModifiers
+ * entrypoint; we cannot import it through legacy ioctls. */
+ if (fb->modifier != DRM_FORMAT_MOD_INVALID) {
+ /* KMS demands that if a modifier is set, it must be the same
+ * for all planes. */
+#ifdef HAVE_DRM_ADDFB2_MODIFIERS
+ for (i = 0; fb->handles[i]; i++)
+ mods[i] = fb->modifier;
+ ret = drmModeAddFB2WithModifiers(fb->fd, fb->width, fb->height,
+ fb->format->format,
+ fb->handles, fb->strides,
+ fb->offsets, mods, &fb->fb_id,
+ DRM_MODE_FB_MODIFIERS);
+#endif
+ return ret;
+ }

ret = drmModeAddFB2(fb->fd, fb->width, fb->height, fb->format->format,
fb->handles, fb->strides, fb->offsets, &fb->fb_id,
@@ -886,6 +908,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
goto err_fb;

fb->type = BUFFER_PIXMAN_DUMB;
+ fb->modifier = DRM_FORMAT_MOD_INVALID;
fb->handles[0] = create_arg.handle;
fb->strides[0] = create_arg.pitch;
fb->size = create_arg.size;
@@ -1002,7 +1025,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides));
memcpy(fb->offsets, dmabuf->attributes.offset, sizeof(fb->offsets));
fb->format = pixel_format_get_info(dmabuf->attributes.format);
- //fb->modifier = dmabuf->attributes.modifier;
+ fb->modifier = dmabuf->attributes.modifier[0];
fb->size = 0;
fb->fd = backend->drm.fd;

@@ -1065,6 +1088,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
fb->strides[0] = gbm_bo_get_stride(bo);
fb->handles[0] = gbm_bo_get_handle(bo).u32;
fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
+ fb->modifier = DRM_FORMAT_MOD_INVALID;
fb->size = fb->strides[0] * fb->height;
fb->fd = backend->drm.fd;
--
2.14.1
Daniel Stone
2017-09-26 17:16:09 UTC
Permalink
The atomic API can allow us to test state before we apply it, to see if
it will be valid. Add support for this, which we will later use in
assign_planes to ensure our plane configuration is valid.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 180 +++++++++++++++++++++++++++++++++++----------
1 file changed, 141 insertions(+), 39 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7a720c34..e678fa7d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1619,6 +1619,7 @@ drm_pending_state_get_output(struct drm_pending_state *pending_state,
}

static int drm_pending_state_apply(struct drm_pending_state *state);
+static int drm_pending_state_test(struct drm_pending_state *state);

/**
* Mark a drm_output_state (the output's last state) as complete. This handles
@@ -1728,12 +1729,15 @@ enum drm_output_propose_state_mode {

static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
- struct weston_view *ev)
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode)
{
struct drm_output *output = output_state->output;
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
+ struct drm_plane_state *state_old = NULL;
struct drm_fb *fb;
+ int ret;

fb = drm_fb_get_from_view(output_state, ev);
if (!fb)
@@ -1746,7 +1750,16 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
}

state = drm_output_state_get_plane(output_state, scanout_plane);
- if (state->fb) {
+
+ /* Check if we've already placed a buffer on this plane; if there's a
+ * buffer there but it comes from GBM, then it's the result of
+ * drm_output_propose_state placing it here for testing purposes. */
+ if (state->fb &&
+ (state->fb->type == BUFFER_GBM_SURFACE ||
+ state->fb->type == BUFFER_PIXMAN_DUMB)) {
+ state_old = calloc(1, sizeof(*state_old));
+ memcpy(state_old, state, sizeof(*state_old));
+ } else if (state->fb) {
drm_fb_unref(fb);
return NULL;
}
@@ -1765,10 +1778,21 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
state->dest_h != (unsigned) output->base.current_mode->height)
goto err;

+ if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
+ return state;
+
+ ret = drm_pending_state_test(output_state->pending_state);
+ if (ret != 0)
+ goto err;
+
return state;

err:
- drm_plane_state_put_back(state);
+ drm_plane_state_free(state, false);
+ if (state_old) {
+ wl_list_insert(&output_state->plane_list, &state_old->link);
+ state_old->output_state = output_state;
+ }
return NULL;
}

@@ -1843,7 +1867,9 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
* want to render. */
scanout_state = drm_output_state_get_plane(state,
output->scanout_plane);
- if (scanout_state->fb)
+ if (scanout_state->fb &&
+ scanout_state->fb->type != BUFFER_GBM_SURFACE &&
+ scanout_state->fb->type != BUFFER_PIXMAN_DUMB)
return;

if (!pixman_region32_not_empty(damage) &&
@@ -1866,6 +1892,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
return;
}

+ drm_fb_unref(scanout_state->fb);
scanout_state->fb = fb;
scanout_state->output = output;

@@ -2245,8 +2272,14 @@ err:
return -1;
}

+enum drm_pending_state_mode {
+ DRM_PENDING_STATE_TEST,
+ DRM_PENDING_STATE_APPLY,
+};
+
static int
-drm_pending_state_apply_atomic(struct drm_pending_state *pending_state)
+drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
+ enum drm_pending_state_mode mode)
{
struct drm_backend *b = pending_state->backend;
struct drm_output_state *output_state, *tmp;
@@ -2386,8 +2419,15 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state)
goto out;
}

- flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ if (mode == DRM_PENDING_STATE_TEST)
+ flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+ else
+ flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
+
+ if (mode == DRM_PENDING_STATE_TEST)
+ return ret;
+
if (ret != 0) {
weston_log("atomic: couldn't commit new state: %m\n");
goto out;
@@ -2409,6 +2449,22 @@ out:
}
#endif

+static int
+drm_pending_state_test(struct drm_pending_state *pending_state)
+{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = pending_state->backend;
+
+ if (b->atomic_modeset)
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_PENDING_STATE_TEST);
+#endif
+
+ /* We have no way to test state before application on the legacy
+ * modesetting API, so just claim it succeeded. */
+ return 0;
+}
+
static int
drm_pending_state_apply(struct drm_pending_state *pending_state)
{
@@ -2418,7 +2474,8 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)

#ifdef HAVE_DRM_ATOMIC
if (b->atomic_modeset)
- return drm_pending_state_apply_atomic(pending_state);
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_PENDING_STATE_APPLY);
#endif

if (b->state_invalid) {
@@ -2720,7 +2777,8 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,

static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
- struct weston_view *ev)
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode)
{
struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
@@ -2729,9 +2787,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct drm_plane_state *state = NULL;
struct drm_fb *fb;
unsigned int i;
-
- if (b->sprites_are_broken)
- return NULL;
+ int ret;

fb = drm_fb_get_from_view(output_state, ev);
if (!fb)
@@ -2763,29 +2819,37 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
continue;
}

- break;
- }
+ state->ev = ev;
+ state->output = output;
+ drm_plane_state_coords_for_view(state, ev);
+ if (state->src_w != state->dest_w << 16 ||
+ state->src_h != state->dest_h << 16) {
+ drm_plane_state_put_back(state);
+ continue;
+ }

- /* No sprites available */
- if (!state) {
- drm_fb_unref(fb);
- return NULL;
- }
+ /* We hold one reference for the lifetime of this function;
+ * from calling drm_fb_get_from_view, to the out label where
+ * we unconditionally drop the reference. So, we take another
+ * reference here to live within the state. */
+ state->fb = drm_fb_ref(fb);

- state->fb = fb;
- state->ev = ev;
- state->output = output;
+ /* In planes-only mode, we don't have an incremental state to
+ * test against, so we just hope it'll work. */
+ if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
+ goto out;

- drm_plane_state_coords_for_view(state, ev);
- if (state->src_w != state->dest_w << 16 ||
- state->src_h != state->dest_h << 16)
- goto err;
+ ret = drm_pending_state_test(output_state->pending_state);
+ if (ret == 0)
+ goto out;

- return state;
+ drm_plane_state_put_back(state);
+ state = NULL;
+ }

-err:
- drm_plane_state_put_back(state);
- return NULL;
+out:
+ drm_fb_unref(fb);
+ return state;
}

/**
@@ -2985,12 +3049,42 @@ drm_output_propose_state(struct weston_output *output_base,
struct weston_view *ev;
pixman_region32_t surface_overlap, renderer_region, occluded_region;
bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
- bool planes_ok = !b->sprites_are_broken;
+ bool planes_ok = false;
+ bool duplicate_renderer = false;
+ int ret;
+
+ /* We can only propose planes if we're in plane-only mode (i.e. just
+ * get as much as possible into planes and test at the end), or mixed
+ * mode, where we have our previous renderer buffer (and it's broadly
+ * compatible) to test with. If we don't have these things, then we
+ * don't know whether or not the planes will work, so we conservatively
+ * fall back to just using the renderer. */
+ if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
+ if (b->sprites_are_broken)
+ return NULL;
+ planes_ok = true;
+ } else if (output->scanout_plane->state_cur->fb) {
+ struct drm_fb *scanout_fb =
+ output->scanout_plane->state_cur->fb;
+ if ((scanout_fb->type == BUFFER_GBM_SURFACE ||
+ scanout_fb->type == BUFFER_PIXMAN_DUMB) &&
+ scanout_fb->width == output_base->current_mode->width &&
+ scanout_fb->height == output_base->current_mode->height) {
+ planes_ok = true;
+ duplicate_renderer = true;
+ }
+ }

assert(!output->state_last);
state = drm_output_state_duplicate(output->state_cur,
pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES);
+ if (!planes_ok)
+ return state;
+
+ if (duplicate_renderer)
+ drm_plane_state_duplicate(state,
+ output->scanout_plane->state_cur);

/*
* Find a surface for each sprite in the output using some heuristics:
@@ -3024,6 +3118,9 @@ drm_output_propose_state(struct weston_output *output_base,
if (ev->output_mask != (1u << output->base.id))
force_renderer = true;

+ if (!ev->surface->buffer_ref.buffer)
+ force_renderer = true;
+
/* Ignore views we know to be totally occluded. */
pixman_region32_init(&clipped_view);
pixman_region32_intersect(&clipped_view,
@@ -3059,17 +3156,13 @@ drm_output_propose_state(struct weston_output *output_base,
* cursors_are_broken flag. */
if (!force_renderer && !b->cursors_are_broken)
ps = drm_output_prepare_cursor_view(state, ev);
-
if (!planes_ok || !drm_view_is_opaque(ev))
force_renderer = true;

- if (!drm_view_is_opaque(ev))
- force_renderer = true;
-
if (!force_renderer && !ps)
- ps = drm_output_prepare_scanout_view(state, ev);
+ ps = drm_output_prepare_scanout_view(state, ev, mode);
if (!force_renderer && !ps)
- ps = drm_output_prepare_overlay_view(state, ev);
+ ps = drm_output_prepare_overlay_view(state, ev, mode);

if (ps) {
pixman_region32_union(&occluded_region,
@@ -3077,8 +3170,6 @@ drm_output_propose_state(struct weston_output *output_base,
&clipped_view);
pixman_region32_fini(&clipped_view);
continue;
- }
-
if (!renderer_ok) {
pixman_region32_fini(&clipped_view);
goto err;
@@ -3092,6 +3183,11 @@ drm_output_propose_state(struct weston_output *output_base,
pixman_region32_fini(&renderer_region);
pixman_region32_fini(&occluded_region);

+ /* Check to see if this state will actually work. */
+ ret = drm_pending_state_test(state->pending_state);
+ if (ret != 0)
+ goto err;
+
return state;

err:
@@ -3112,8 +3208,14 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
struct weston_view *ev;
struct weston_plane *primary = &output_base->compositor->primary_plane;

+
state = drm_output_propose_state(output_base, pending_state,
- DRM_OUTPUT_PROPOSE_STATE_MIXED);
+ DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+ if (!state)
+ state = drm_output_propose_state(output_base, pending_state,
+ DRM_OUTPUT_PROPOSE_STATE_MIXED);
+
+ assert(state);

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct drm_plane *target_plane = NULL;
--
2.14.1
Michael Tretter
2017-11-14 11:21:33 UTC
Permalink
Hi Daniel,
Post by Daniel Stone
The atomic API can allow us to test state before we apply it, to see if
it will be valid. Add support for this, which we will later use in
assign_planes to ensure our plane configuration is valid.
---
libweston/compositor-drm.c | 180 +++++++++++++++++++++++++++++++++++----------
1 file changed, 141 insertions(+), 39 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7a720c34..e678fa7d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1619,6 +1619,7 @@ drm_pending_state_get_output(struct drm_pending_state *pending_state,
}
static int drm_pending_state_apply(struct drm_pending_state *state);
+static int drm_pending_state_test(struct drm_pending_state *state);
/**
* Mark a drm_output_state (the output's last state) as complete. This handles
@@ -1728,12 +1729,15 @@ enum drm_output_propose_state_mode {
static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
- struct weston_view *ev)
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode)
{
struct drm_output *output = output_state->output;
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
+ struct drm_plane_state *state_old = NULL;
struct drm_fb *fb;
+ int ret;
fb = drm_fb_get_from_view(output_state, ev);
if (!fb)
@@ -1746,7 +1750,16 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
}
state = drm_output_state_get_plane(output_state, scanout_plane);
- if (state->fb) {
+
+ /* Check if we've already placed a buffer on this plane; if there's a
+ * buffer there but it comes from GBM, then it's the result of
+ * drm_output_propose_state placing it here for testing purposes. */
+ if (state->fb &&
+ (state->fb->type == BUFFER_GBM_SURFACE ||
+ state->fb->type == BUFFER_PIXMAN_DUMB)) {
+ state_old = calloc(1, sizeof(*state_old));
+ memcpy(state_old, state, sizeof(*state_old));
+ } else if (state->fb) {
drm_fb_unref(fb);
return NULL;
}
@@ -1765,10 +1778,21 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
state->dest_h != (unsigned) output->base.current_mode->height)
goto err;
+ if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
+ return state;
+
+ ret = drm_pending_state_test(output_state->pending_state);
+ if (ret != 0)
+ goto err;
+
return state;
- drm_plane_state_put_back(state);
+ drm_plane_state_free(state, false);
+ if (state_old) {
+ wl_list_insert(&output_state->plane_list, &state_old->link);
+ state_old->output_state = output_state;
+ }
return NULL;
}
@@ -1843,7 +1867,9 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
* want to render. */
scanout_state = drm_output_state_get_plane(state,
output->scanout_plane);
- if (scanout_state->fb)
+ if (scanout_state->fb &&
+ scanout_state->fb->type != BUFFER_GBM_SURFACE &&
+ scanout_state->fb->type != BUFFER_PIXMAN_DUMB)
return;
if (!pixman_region32_not_empty(damage) &&
@@ -1866,6 +1892,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
return;
}
+ drm_fb_unref(scanout_state->fb);
scanout_state->fb = fb;
scanout_state->output = output;
return -1;
}
+enum drm_pending_state_mode {
+ DRM_PENDING_STATE_TEST,
+ DRM_PENDING_STATE_APPLY,
+};
+
static int
-drm_pending_state_apply_atomic(struct drm_pending_state *pending_state)
+drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
+ enum drm_pending_state_mode mode)
{
struct drm_backend *b = pending_state->backend;
struct drm_output_state *output_state, *tmp;
@@ -2386,8 +2419,15 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state)
goto out;
}
- flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ if (mode == DRM_PENDING_STATE_TEST)
+ flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+ else
+ flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
+
+ if (mode == DRM_PENDING_STATE_TEST)
+ return ret;
Leaks req and should drmModeAtomicFree(req) before returning or goto out.

Michael
Post by Daniel Stone
+
if (ret != 0) {
weston_log("atomic: couldn't commit new state: %m\n");
goto out;
}
#endif
+static int
+drm_pending_state_test(struct drm_pending_state *pending_state)
+{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = pending_state->backend;
+
+ if (b->atomic_modeset)
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_PENDING_STATE_TEST);
+#endif
+
+ /* We have no way to test state before application on the legacy
+ * modesetting API, so just claim it succeeded. */
+ return 0;
+}
+
static int
drm_pending_state_apply(struct drm_pending_state *pending_state)
{
@@ -2418,7 +2474,8 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
#ifdef HAVE_DRM_ATOMIC
if (b->atomic_modeset)
- return drm_pending_state_apply_atomic(pending_state);
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_PENDING_STATE_APPLY);
#endif
if (b->state_invalid) {
@@ -2720,7 +2777,8 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
- struct weston_view *ev)
+ struct weston_view *ev,
+ enum drm_output_propose_state_mode mode)
{
struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
@@ -2729,9 +2787,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct drm_plane_state *state = NULL;
struct drm_fb *fb;
unsigned int i;
-
- if (b->sprites_are_broken)
- return NULL;
+ int ret;
fb = drm_fb_get_from_view(output_state, ev);
if (!fb)
@@ -2763,29 +2819,37 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
continue;
}
- break;
- }
+ state->ev = ev;
+ state->output = output;
+ drm_plane_state_coords_for_view(state, ev);
+ if (state->src_w != state->dest_w << 16 ||
+ state->src_h != state->dest_h << 16) {
+ drm_plane_state_put_back(state);
+ continue;
+ }
- /* No sprites available */
- if (!state) {
- drm_fb_unref(fb);
- return NULL;
- }
+ /* We hold one reference for the lifetime of this function;
+ * from calling drm_fb_get_from_view, to the out label where
+ * we unconditionally drop the reference. So, we take another
+ * reference here to live within the state. */
+ state->fb = drm_fb_ref(fb);
- state->fb = fb;
- state->ev = ev;
- state->output = output;
+ /* In planes-only mode, we don't have an incremental state to
+ * test against, so we just hope it'll work. */
+ if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
+ goto out;
- drm_plane_state_coords_for_view(state, ev);
- if (state->src_w != state->dest_w << 16 ||
- state->src_h != state->dest_h << 16)
- goto err;
+ ret = drm_pending_state_test(output_state->pending_state);
+ if (ret == 0)
+ goto out;
- return state;
+ drm_plane_state_put_back(state);
+ state = NULL;
+ }
- drm_plane_state_put_back(state);
- return NULL;
+ drm_fb_unref(fb);
+ return state;
}
/**
@@ -2985,12 +3049,42 @@ drm_output_propose_state(struct weston_output *output_base,
struct weston_view *ev;
pixman_region32_t surface_overlap, renderer_region, occluded_region;
bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
- bool planes_ok = !b->sprites_are_broken;
+ bool planes_ok = false;
+ bool duplicate_renderer = false;
+ int ret;
+
+ /* We can only propose planes if we're in plane-only mode (i.e. just
+ * get as much as possible into planes and test at the end), or mixed
+ * mode, where we have our previous renderer buffer (and it's broadly
+ * compatible) to test with. If we don't have these things, then we
+ * don't know whether or not the planes will work, so we conservatively
+ * fall back to just using the renderer. */
+ if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
+ if (b->sprites_are_broken)
+ return NULL;
+ planes_ok = true;
+ } else if (output->scanout_plane->state_cur->fb) {
+ struct drm_fb *scanout_fb =
+ output->scanout_plane->state_cur->fb;
+ if ((scanout_fb->type == BUFFER_GBM_SURFACE ||
+ scanout_fb->type == BUFFER_PIXMAN_DUMB) &&
+ scanout_fb->width == output_base->current_mode->width &&
+ scanout_fb->height == output_base->current_mode->height) {
+ planes_ok = true;
+ duplicate_renderer = true;
+ }
+ }
assert(!output->state_last);
state = drm_output_state_duplicate(output->state_cur,
pending_state,
DRM_OUTPUT_STATE_CLEAR_PLANES);
+ if (!planes_ok)
+ return state;
+
+ if (duplicate_renderer)
+ drm_plane_state_duplicate(state,
+ output->scanout_plane->state_cur);
/*
@@ -3024,6 +3118,9 @@ drm_output_propose_state(struct weston_output *output_base,
if (ev->output_mask != (1u << output->base.id))
force_renderer = true;
+ if (!ev->surface->buffer_ref.buffer)
+ force_renderer = true;
+
/* Ignore views we know to be totally occluded. */
pixman_region32_init(&clipped_view);
pixman_region32_intersect(&clipped_view,
@@ -3059,17 +3156,13 @@ drm_output_propose_state(struct weston_output *output_base,
* cursors_are_broken flag. */
if (!force_renderer && !b->cursors_are_broken)
ps = drm_output_prepare_cursor_view(state, ev);
-
if (!planes_ok || !drm_view_is_opaque(ev))
force_renderer = true;
- if (!drm_view_is_opaque(ev))
- force_renderer = true;
-
if (!force_renderer && !ps)
- ps = drm_output_prepare_scanout_view(state, ev);
+ ps = drm_output_prepare_scanout_view(state, ev, mode);
if (!force_renderer && !ps)
- ps = drm_output_prepare_overlay_view(state, ev);
+ ps = drm_output_prepare_overlay_view(state, ev, mode);
if (ps) {
pixman_region32_union(&occluded_region,
@@ -3077,8 +3170,6 @@ drm_output_propose_state(struct weston_output *output_base,
&clipped_view);
pixman_region32_fini(&clipped_view);
continue;
- }
-
if (!renderer_ok) {
pixman_region32_fini(&clipped_view);
goto err;
@@ -3092,6 +3183,11 @@ drm_output_propose_state(struct weston_output *output_base,
pixman_region32_fini(&renderer_region);
pixman_region32_fini(&occluded_region);
+ /* Check to see if this state will actually work. */
+ ret = drm_pending_state_test(state->pending_state);
+ if (ret != 0)
+ goto err;
+
return state;
@@ -3112,8 +3208,14 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
struct weston_view *ev;
struct weston_plane *primary = &output_base->compositor->primary_plane;
+
state = drm_output_propose_state(output_base, pending_state,
- DRM_OUTPUT_PROPOSE_STATE_MIXED);
+ DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+ if (!state)
+ state = drm_output_propose_state(output_base, pending_state,
+ DRM_OUTPUT_PROPOSE_STATE_MIXED);
+
+ assert(state);
wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct drm_plane *target_plane = NULL;
Daniel Stone
2017-09-26 17:16:10 UTC
Permalink
Since we now incrementally test atomic state as we build it, we can
loosen restrictions on what we can do with planes, and let the kernel
tell us whether or not it's OK.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e678fa7d..05082af1 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1733,6 +1733,7 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
enum drm_output_propose_state_mode mode)
{
struct drm_output *output = output_state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
struct drm_plane_state *state_old = NULL;
@@ -1744,7 +1745,7 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
return NULL;

/* Can't change formats with just a pageflip */
- if (fb->format->format != output->gbm_format) {
+ if (!b->atomic_modeset && fb->format->format != output->gbm_format) {
drm_fb_unref(fb);
return NULL;
}
@@ -1769,15 +1770,18 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
state->output = output;
drm_plane_state_coords_for_view(state, ev);

- /* The legacy API does not let us perform cropping or scaling. */
- if (state->src_x != 0 || state->src_y != 0 ||
- state->src_w != state->dest_w << 16 ||
- state->src_h != state->dest_h << 16 ||
- state->dest_x != 0 || state->dest_y != 0 ||
+ if (state->dest_x != 0 || state->dest_y != 0 ||
state->dest_w != (unsigned) output->base.current_mode->width ||
state->dest_h != (unsigned) output->base.current_mode->height)
goto err;

+ /* The legacy API does not let us perform cropping or scaling. */
+ if (!b->atomic_modeset &&
+ (state->src_x != 0 || state->src_y != 0 ||
+ state->src_w != state->dest_w << 16 ||
+ state->src_h != state->dest_h << 16))
+ goto err;
+
if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
return state;

@@ -2822,8 +2826,9 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
state->ev = ev;
state->output = output;
drm_plane_state_coords_for_view(state, ev);
- if (state->src_w != state->dest_w << 16 ||
- state->src_h != state->dest_h << 16) {
+ if (!b->atomic_modeset &&
+ (state->src_w != state->dest_w << 16 ||
+ state->src_h != state->dest_h << 16)) {
drm_plane_state_put_back(state);
continue;
}
@@ -3170,6 +3175,7 @@ drm_output_propose_state(struct weston_output *output_base,
&clipped_view);
pixman_region32_fini(&clipped_view);
continue;
+ }
if (!renderer_ok) {
pixman_region32_fini(&clipped_view);
goto err;
--
2.14.1
Daniel Stone
2017-09-26 17:15:58 UTC
Permalink
If AddFB2 ever fails for any reason, we fall back to legacy AddFB, which
doesn't support the same swathe of formats, or multi-planar formats, or
modifiers.

This can happen with arbitrary client buffers, condemning us to the
fallback forever more. Remove this, at the cost of an unnecessary ioctl
for users on old kernels without AddFB2; unfortunately, we cannot detect
the complete absence of the ioctl, as the return here is -EINVAL rather
than -ENOTTY.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 44e47881..e03f8f7e 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -238,7 +238,6 @@ struct drm_backend {
*/
int min_width, max_width;
int min_height, max_height;
- int no_addfb2;

struct wl_list plane_list;
int sprites_are_broken;
@@ -828,6 +827,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
struct drm_mode_map_dumb map_arg;
+ uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };

fb = zalloc(sizeof *fb);
if (!fb)
@@ -867,23 +867,12 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,

ret = -1;

- if (!b->no_addfb2) {
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
-
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(b->drm.fd, width, height,
- fb->format->format,
- handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret) {
- weston_log("addfb2 failed: %m\n");
- b->no_addfb2 = 1;
- }
- }
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;

+ ret = drmModeAddFB2(b->drm.fd, width, height, fb->format->format,
+ handles, pitches, offsets, &fb->fb_id, 0);
if (ret) {
ret = drmModeAddFB(b->drm.fd, width, height,
fb->format->depth, fb->format->bpp,
@@ -970,24 +959,13 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
goto err_free;
}

- ret = -1;
-
- if (!backend->no_addfb2) {
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
- fb->format->format,
- handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret) {
- weston_log("addfb2 failed: %m\n");
- backend->no_addfb2 = 1;
- backend->sprites_are_broken = 1;
- }
- }
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;

+ ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
+ fb->format->format, handles, pitches, offsets,
+ &fb->fb_id, 0);
if (ret && fb->format->depth && fb->format->bpp)
ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
fb->format->depth, fb->format->bpp,
--
2.14.1
Daniel Stone
2017-09-26 17:16:03 UTC
Permalink
Make it a bit more clear what the purpose of the variable is.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3b22faf1..093e75b1 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2981,7 +2981,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
struct drm_output_state *state;
struct drm_plane_state *plane_state;
struct weston_view *ev;
- pixman_region32_t overlap, surface_overlap;
+ pixman_region32_t surface_overlap, renderer_region;
struct weston_plane *primary, *next_plane;

assert(!output->state_last);
@@ -3002,7 +3002,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
* the client buffer can be used directly for the sprite surface
* as we do for flipping full screen surfaces.
*/
- pixman_region32_init(&overlap);
+ pixman_region32_init(&renderer_region);
primary = &output_base->compositor->primary_plane;

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
@@ -3026,7 +3026,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
es->keep_buffer = false;

pixman_region32_init(&surface_overlap);
- pixman_region32_intersect(&surface_overlap, &overlap,
+ pixman_region32_intersect(&surface_overlap, &renderer_region,
&ev->transform.boundingbox);

next_plane = NULL;
@@ -3044,7 +3044,8 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
weston_view_move_to_plane(ev, next_plane);

if (next_plane == primary)
- pixman_region32_union(&overlap, &overlap,
+ pixman_region32_union(&renderer_region,
+ &renderer_region,
&ev->transform.boundingbox);

if (next_plane == primary ||
@@ -3061,7 +3062,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)

pixman_region32_fini(&surface_overlap);
}
- pixman_region32_fini(&overlap);
+ pixman_region32_fini(&renderer_region);

/* We rely on output->cursor_view being both an accurate reflection of
* the cursor plane's state, but also being maintained across repaints
--
2.14.1
Daniel Stone
2017-09-26 17:15:52 UTC
Permalink
In our new and improved helper to determine the src/dest values for a
buffer on a given plane, make sure we account for all buffer
transformations, including viewport clipping.

Rather than badly open-coding it ourselves, just use the helper which
does exactly this.

Signed-off-by: Daniel Stone <***@collabora.com>
Reported-by: Tiago Gomes <***@codethink.co.uk>
---
libweston/compositor-drm.c | 55 ++++++++++------------------------------------
1 file changed, 12 insertions(+), 43 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2c4b8ba5..e835d3b7 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1164,10 +1164,9 @@ drm_plane_state_coords_for_view(struct drm_plane_state *state,
struct weston_view *ev)
{
struct drm_output *output = state->output;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
pixman_region32_t dest_rect, src_rect;
pixman_box32_t *box, tbox;
- wl_fixed_t sx1, sy1, sx2, sy2;
+ float sxf1, syf1, sxf2, syf2;

/* Update the base weston_plane co-ordinates. */
box = pixman_region32_extents(&ev->transform.boundingbox);
@@ -1193,52 +1192,22 @@ drm_plane_state_coords_for_view(struct drm_plane_state *state,
state->dest_h = tbox.y2 - tbox.y1;
pixman_region32_fini(&dest_rect);

- /* Now calculate the source rectangle, by finding the points in the
- * view and working backwards to source co-ordinates. */
+ /* Now calculate the source rectangle, by finding the extents of the
+ * view, and working backwards to source co-ordinates. */
pixman_region32_init(&src_rect);
pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
&output->base.region);
box = pixman_region32_extents(&src_rect);
-
- /* Accounting for any transformations made to this particular surface
- * view, find the source rectangle to use. */
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x1),
- wl_fixed_from_int(box->y1),
- &sx1, &sy1);
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x2),
- wl_fixed_from_int(box->y2),
- &sx2, &sy2);
-
- /* XXX: How is this possible ... ? */
- if (sx1 < 0)
- sx1 = 0;
- if (sy1 < 0)
- sy1 = 0;
- if (sx2 > wl_fixed_from_int(ev->surface->width))
- sx2 = wl_fixed_from_int(ev->surface->width);
- if (sy2 > wl_fixed_from_int(ev->surface->height))
- sy2 = wl_fixed_from_int(ev->surface->height);
-
- tbox.x1 = sx1;
- tbox.y1 = sy1;
- tbox.x2 = sx2;
- tbox.y2 = sy2;
-
- /* Apply viewport transforms in reverse, to get the source co-ordinates
- * in buffer space. */
- tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
- wl_fixed_from_int(ev->surface->height),
- viewport->buffer.transform,
- viewport->buffer.scale,
- tbox);
-
- state->src_x = tbox.x1 << 8;
- state->src_y = tbox.y1 << 8;
- state->src_w = (tbox.x2 - tbox.x1) << 8;
- state->src_h = (tbox.y2 - tbox.y1) << 8;
+ weston_view_from_global_float(ev, box->x1, box->y1, &sxf1, &syf1);
+ weston_surface_to_buffer_float(ev->surface, sxf1, syf1, &sxf1, &syf1);
+ weston_view_from_global_float(ev, box->x2, box->y2, &sxf2, &syf2);
+ weston_surface_to_buffer_float(ev->surface, sxf2, syf2, &sxf2, &syf2);
pixman_region32_fini(&src_rect);
+
+ state->src_x = wl_fixed_from_double(sxf1) << 8;
+ state->src_y = wl_fixed_from_double(syf1) << 8;
+ state->src_w = wl_fixed_from_double(sxf2 - sxf1) << 8;
+ state->src_h = wl_fixed_from_double(syf2 - syf1) << 8;
}

/**
--
2.14.1
Daniel Stone
2017-09-26 17:16:13 UTC
Permalink
Use the extended GBM allocator interface to support modifiers and
multi-planar BOs.

Depends on bwidawsk's 'modifiers' tree.

Signed-off-by: Daniel Stone <***@collabora.com>
---
configure.ac | 3 +++
libweston/compositor-drm.c | 57 ++++++++++++++++++++++++++++++++++++++--------
2 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 4c016999..0a83efe0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -206,6 +206,9 @@ if test x$enable_drm_compositor = xyes; then
PKG_CHECK_MODULES(DRM_COMPOSITOR_MODIFIERS, [libdrm >= 2.4.83],
[AC_DEFINE([HAVE_DRM_ADDFB2_MODIFIERS], 1, [libdrm supports modifiers])],
[AC_MSG_WARN([libdrm does not support AddFB2 with modifiers])])
+ PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM_MODIFIERS, [gbm >= 17.1.0],
+ [AC_DEFINE([HAVE_GBM_MODIFIERS], 1, [GBM supports modifiers])],
+ [AC_MSG_WARN([GBM does not support modifiers])])
fi


diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 811adbbd..8b0b3146 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1091,6 +1091,9 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
bool is_opaque, enum drm_fb_type type)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
+#ifdef HAVE_GBM_MODIFIERS
+ int i;
+#endif

if (fb) {
assert(fb->type == type);
@@ -1104,15 +1107,25 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
fb->type = type;
fb->refcnt = 1;
fb->bo = bo;
+ fb->fd = backend->drm.fd;

fb->width = gbm_bo_get_width(bo);
fb->height = gbm_bo_get_height(bo);
+ fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
+ fb->size = 0;
+
+#ifdef HAVE_GBM_MODIFIERS
+ fb->modifier = gbm_bo_get_modifier(bo);
+ for (i = 0; i < gbm_bo_get_plane_count(bo); i++) {
+ fb->strides[i] = gbm_bo_get_stride_for_plane(bo, i);
+ fb->handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32;
+ fb->offsets[i] = gbm_bo_get_offset(bo, i);
+ }
+#else
fb->strides[0] = gbm_bo_get_stride(bo);
fb->handles[0] = gbm_bo_get_handle(bo).u32;
- fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
fb->modifier = DRM_FORMAT_MOD_INVALID;
- fb->size = fb->strides[0] * fb->height;
- fb->fd = backend->drm.fd;
+#endif

if (!fb->format) {
weston_log("couldn't look up format 0x%lx\n",
@@ -4274,13 +4287,39 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
fallback_format_for(output->gbm_format),
};
int n_formats = 1;
+ struct weston_mode *mode = output->base.current_mode;
+ struct drm_plane *plane = output->scanout_plane;
+ unsigned int i;
+
+ for (i = 0; i < plane->count_formats; i++) {
+ if (plane->formats[i].format == output->gbm_format)
+ break;
+ }
+
+ if (i == plane->count_formats) {
+ /* XXX: better error message */
+ weston_log("can't use format for output\n");
+ return -1;
+ }
+
+#ifdef HAVE_GBM_MODIFIERS
+ if (plane->formats[i].count_modifiers > 0) {
+ output->gbm_surface =
+ gbm_surface_create_with_modifiers(b->gbm,
+ mode->width,
+ mode->height,
+ format[0],
+ plane->formats[i].modifiers,
+ plane->formats[i].count_modifiers);
+ } else
+#endif
+ {
+ output->gbm_surface =
+ gbm_surface_create(b->gbm, mode->width, mode->height,
+ format[0],
+ GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
+ }

- output->gbm_surface = gbm_surface_create(b->gbm,
- output->base.current_mode->width,
- output->base.current_mode->height,
- format[0],
- GBM_BO_USE_SCANOUT |
- GBM_BO_USE_RENDERING);
if (!output->gbm_surface) {
weston_log("failed to create gbm surface\n");
return -1;
--
2.14.1
Daniel Stone
2017-09-26 17:16:00 UTC
Permalink
Add support for the GBM_BO_IMPORT_FD_MODIFIER path, which allows us to
import multi-plane dmabufs, as well as format modifiers.

Signed-off-by: Daniel Stone <***@collabora.com>
---
configure.ac | 3 -
libweston/compositor-drm.c | 181 +++++++++++++++++++++++++++++++++------------
2 files changed, 133 insertions(+), 51 deletions(-)

diff --git a/configure.ac b/configure.ac
index f09d0e04..3996c80c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -203,9 +203,6 @@ if test x$enable_drm_compositor = xyes; then
PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
[AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
[AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
- PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
- [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])],
- [AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])])
fi


diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7557ef55..65934a01 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -273,6 +273,7 @@ struct drm_mode {
enum drm_fb_type {
BUFFER_INVALID = 0, /**< never used */
BUFFER_CLIENT, /**< directly sourced from client */
+ BUFFER_DMABUF, /**< imported from linux_dmabuf client */
BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
BUFFER_GBM_SURFACE, /**< internal EGL rendering */
BUFFER_CURSOR, /**< internal cursor buffer */
@@ -928,6 +929,118 @@ drm_fb_ref(struct drm_fb *fb)
return fb;
}

+static void
+drm_fb_destroy_dmabuf(struct drm_fb *fb)
+{
+ /* We deliberately do not close the GEM handles here; GBM manages
+ * their lifetime through the BO. */
+ gbm_bo_destroy(fb->bo);
+ drm_fb_destroy(fb);
+}
+
+static struct drm_fb *
+drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
+ struct drm_backend *backend, bool is_opaque)
+{
+ struct drm_fb *fb;
+ struct gbm_import_fd_data import_legacy = {
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .format = dmabuf->attributes.format,
+ .stride = dmabuf->attributes.stride[0],
+ .fd = dmabuf->attributes.fd[0],
+ };
+ struct gbm_import_fd_modifier_data import_mod = {
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .format = dmabuf->attributes.format,
+ .num_fds = dmabuf->attributes.n_planes,
+ .modifier = dmabuf->attributes.modifier[0],
+ };
+ int i;
+
+ /* XXX: TODO:
+ *
+ * Currently the buffer is rejected if any dmabuf attribute
+ * flag is set. This keeps us from passing an inverted /
+ * interlaced / bottom-first buffer (or any other type that may
+ * be added in the future) through to an overlay. Ultimately,
+ * these types of buffers should be handled through buffer
+ * transforms and not as spot-checks requiring specific
+ * knowledge. */
+ if (dmabuf->attributes.flags)
+ return NULL;
+
+ fb = zalloc(sizeof *fb);
+ if (fb == NULL)
+ return NULL;
+
+ fb->refcnt = 1;
+ fb->type = BUFFER_DMABUF;
+
+ memcpy(import_mod.fds, dmabuf->attributes.fd, sizeof(import_mod.fds));
+ memcpy(import_mod.strides, dmabuf->attributes.stride,
+ sizeof(import_mod.fds));
+ memcpy(import_mod.offsets, dmabuf->attributes.offset,
+ sizeof(import_mod.fds));
+
+ if (dmabuf->attributes.modifier[0] != DRM_FORMAT_MOD_INVALID) {
+ fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER,
+ &import_mod,
+ GBM_BO_USE_SCANOUT);
+ } else {
+ fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD,
+ &import_legacy,
+ GBM_BO_USE_SCANOUT);
+ }
+
+ if (!fb->bo)
+ goto err_free;
+
+ fb->width = dmabuf->attributes.width;
+ fb->height = dmabuf->attributes.height;
+ memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides));
+ memcpy(fb->offsets, dmabuf->attributes.offset, sizeof(fb->offsets));
+ fb->format = pixel_format_get_info(dmabuf->attributes.format);
+ //fb->modifier = dmabuf->attributes.modifier;
+ fb->size = 0;
+ fb->fd = backend->drm.fd;
+
+ if (!fb->format) {
+ weston_log("couldn't look up format info for 0x%lx\n",
+ (unsigned long) fb->format);
+ goto err_free;
+ }
+
+ if (is_opaque)
+ fb->format = pixel_format_get_opaque_substitute(fb->format);
+
+ if (backend->min_width > fb->width ||
+ fb->width > backend->max_width ||
+ backend->min_height > fb->height ||
+ fb->height > backend->max_height) {
+ weston_log("bo geometry out of bounds\n");
+ goto err_free;
+ }
+
+ for (i = 0; i < dmabuf->attributes.n_planes; i++) {
+ fb->handles[i] = gbm_bo_get_handle_for_plane(fb->bo, i).u32;
+ if (!fb->handles[i])
+ goto err_free;
+ }
+
+ if (drm_fb_addfb(fb) != 0) {
+ weston_log("failed to create kms fb: %m\n");
+ goto err_free;
+ }
+
+ return fb;
+
+err_free:
+ drm_fb_destroy_dmabuf(fb);
+ return NULL;
+}
+
static struct drm_fb *
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
bool is_opaque, enum drm_fb_type type)
@@ -990,7 +1103,7 @@ static void
drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
{
assert(fb->buffer_ref.buffer == NULL);
- assert(fb->type == BUFFER_CLIENT);
+ assert(fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF);
weston_buffer_reference(&fb->buffer_ref, buffer);
}

@@ -1015,6 +1128,9 @@ drm_fb_unref(struct drm_fb *fb)
case BUFFER_GBM_SURFACE:
gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
break;
+ case BUFFER_DMABUF:
+ drm_fb_destroy_dmabuf(fb);
+ break;
default:
assert(NULL);
break;
@@ -1217,9 +1333,9 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ bool is_opaque = drm_view_is_opaque(ev);
struct linux_dmabuf_buffer *dmabuf;
struct drm_fb *fb;
- struct gbm_bo *bo;

/* Don't import buffers which span multiple outputs. */
if (ev->output_mask != (1u << output->base.id))
@@ -1234,58 +1350,27 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
if (wl_shm_buffer_get(buffer->resource))
return NULL;

- if (!b->gbm)
- return NULL;
-
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
if (dmabuf) {
-#ifdef HAVE_GBM_FD_IMPORT
- /* XXX: TODO:
- *
- * Use AddFB2 directly, do not go via GBM.
- * Add support for multiplanar formats.
- * Both require refactoring in the DRM-backend to
- * support a mix of gbm_bos and drmfbs.
- */
- struct gbm_import_fd_data gbm_dmabuf = {
- .fd = dmabuf->attributes.fd[0],
- .width = dmabuf->attributes.width,
- .height = dmabuf->attributes.height,
- .stride = dmabuf->attributes.stride[0],
- .format = dmabuf->attributes.format
- };
-
- /* XXX: TODO:
- *
- * Currently the buffer is rejected if any dmabuf attribute
- * flag is set. This keeps us from passing an inverted /
- * interlaced / bottom-first buffer (or any other type that may
- * be added in the future) through to an overlay. Ultimately,
- * these types of buffers should be handled through buffer
- * transforms and not as spot-checks requiring specific
- * knowledge. */
- if (dmabuf->attributes.n_planes != 1 ||
- dmabuf->attributes.offset[0] != 0 ||
- dmabuf->attributes.flags)
- goto err;
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
- GBM_BO_USE_SCANOUT);
-#else
- return NULL;
-#endif
+ fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque);
+ if (!fb)
+ return NULL;
} else {
+ struct gbm_bo *bo;
+
+ if (!b->gbm)
+ return NULL;
+
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);
- }
-
- if (!bo)
- return NULL;
+ if (!bo)
+ return NULL;

- fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev), BUFFER_CLIENT);
- if (!fb) {
- gbm_bo_destroy(bo);
- return NULL;
+ fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
+ if (!fb) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
}

drm_fb_set_buffer(fb, buffer);
--
2.14.1
Philipp Zabel
2017-09-27 09:45:34 UTC
Permalink
Post by Daniel Stone
Add support for the GBM_BO_IMPORT_FD_MODIFIER path, which allows us to
import multi-plane dmabufs, as well as format modifiers.
---
 configure.ac               |   3 -
 libweston/compositor-drm.c | 181 +++++++++++++++++++++++++++++++++---
---------
 2 files changed, 133 insertions(+), 51 deletions(-)
diff --git a/configure.ac b/configure.ac
index f09d0e04..3996c80c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -203,9 +203,6 @@ if test x$enable_drm_compositor = xyes; then
   PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
      [AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports
atomic API])],
      [AC_MSG_WARN([libdrm does not support atomic
modesetting, will omit that capability])])
-  PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
-     [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports
dmabuf import])],
-     [AC_MSG_WARN([gbm does not support dmabuf import,
will omit that capability])])
Should this check for a newer gbm instead? With the modifiers, this
won't build against the gbm from Mesa 17.1.10 anymore.

regards
Philipp
Pekka Paalanen
2017-09-28 11:03:33 UTC
Permalink
On Wed, 27 Sep 2017 11:45:34 +0200
Post by Philipp Zabel
Post by Daniel Stone
Add support for the GBM_BO_IMPORT_FD_MODIFIER path, which allows us to
import multi-plane dmabufs, as well as format modifiers.
---
 configure.ac               |   3 -
 libweston/compositor-drm.c | 181 +++++++++++++++++++++++++++++++++---
---------
 2 files changed, 133 insertions(+), 51 deletions(-)
diff --git a/configure.ac b/configure.ac
index f09d0e04..3996c80c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -203,9 +203,6 @@ if test x$enable_drm_compositor = xyes; then
   PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
      [AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports
atomic API])],
      [AC_MSG_WARN([libdrm does not support atomic
modesetting, will omit that capability])])
-  PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
-     [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports
dmabuf import])],
-     [AC_MSG_WARN([gbm does not support dmabuf import,
will omit that capability])])
Should this check for a newer gbm instead? With the modifiers, this
won't build against the gbm from Mesa 17.1.10 anymore.
Yup, it certainly fails to build here:

CC libweston/drm_backend_la-compositor-drm.lo
/home/pq/git/weston/libweston/compositor-drm.c: In function ‘drm_fb_get_from_dmabuf’:
/home/pq/git/weston/libweston/compositor-drm.c:953:9: error: variable ‘import_mod’ has initializer but incomplete type
struct gbm_import_fd_modifier_data import_mod = {

/home/pq/git/weston/libweston/compositor-drm.c:988:40: error: ‘GBM_BO_IMPORT_FD_MODIFIER’ undeclared (first use in this function)
fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER,

and a ton more.


Thanks,
pq
Matt Hoosier
2017-11-03 16:29:09 UTC
Permalink
Post by Daniel Stone
Add support for the GBM_BO_IMPORT_FD_MODIFIER path, which allows us to
import multi-plane dmabufs, as well as format modifiers.
---
configure.ac | 3 -
libweston/compositor-drm.c | 181 +++++++++++++++++++++++++++++++++------------
2 files changed, 133 insertions(+), 51 deletions(-)
diff --git a/configure.ac b/configure.ac
index f09d0e04..3996c80c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -203,9 +203,6 @@ if test x$enable_drm_compositor = xyes; then
PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
[AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
[AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
- PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
- [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])],
- [AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])])
fi
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7557ef55..65934a01 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -273,6 +273,7 @@ struct drm_mode {
enum drm_fb_type {
BUFFER_INVALID = 0, /**< never used */
BUFFER_CLIENT, /**< directly sourced from client */
+ BUFFER_DMABUF, /**< imported from linux_dmabuf client */
BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
BUFFER_GBM_SURFACE, /**< internal EGL rendering */
BUFFER_CURSOR, /**< internal cursor buffer */
@@ -928,6 +929,118 @@ drm_fb_ref(struct drm_fb *fb)
return fb;
}
+static void
+drm_fb_destroy_dmabuf(struct drm_fb *fb)
+{
+ /* We deliberately do not close the GEM handles here; GBM manages
+ * their lifetime through the BO. */
+ gbm_bo_destroy(fb->bo);
+ drm_fb_destroy(fb);
+}
+
+static struct drm_fb *
+drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
+ struct drm_backend *backend, bool is_opaque)
+{
+ struct drm_fb *fb;
+ struct gbm_import_fd_data import_legacy = {
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .format = dmabuf->attributes.format,
+ .stride = dmabuf->attributes.stride[0],
+ .fd = dmabuf->attributes.fd[0],
+ };
+ struct gbm_import_fd_modifier_data import_mod = {
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .format = dmabuf->attributes.format,
+ .num_fds = dmabuf->attributes.n_planes,
+ .modifier = dmabuf->attributes.modifier[0],
+ };
+ int i;
+
+ *
+ * Currently the buffer is rejected if any dmabuf attribute
+ * flag is set. This keeps us from passing an inverted /
+ * interlaced / bottom-first buffer (or any other type that may
+ * be added in the future) through to an overlay. Ultimately,
+ * these types of buffers should be handled through buffer
+ * transforms and not as spot-checks requiring specific
+ * knowledge. */
+ if (dmabuf->attributes.flags)
+ return NULL;
+
+ fb = zalloc(sizeof *fb);
+ if (fb == NULL)
+ return NULL;
+
+ fb->refcnt = 1;
+ fb->type = BUFFER_DMABUF;
+
+ memcpy(import_mod.fds, dmabuf->attributes.fd, sizeof(import_mod.fds));
+ memcpy(import_mod.strides, dmabuf->attributes.stride,
+ sizeof(import_mod.fds));
+ memcpy(import_mod.offsets, dmabuf->attributes.offset,
+ sizeof(import_mod.fds));
+
+ if (dmabuf->attributes.modifier[0] != DRM_FORMAT_MOD_INVALID) {
+ fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER,
+ &import_mod,
+ GBM_BO_USE_SCANOUT);
+ } else {
+ fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD,
+ &import_legacy,
+ GBM_BO_USE_SCANOUT);
+ }
+
+ if (!fb->bo)
+ goto err_free;
+
+ fb->width = dmabuf->attributes.width;
+ fb->height = dmabuf->attributes.height;
+ memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides));
+ memcpy(fb->offsets, dmabuf->attributes.offset, sizeof(fb->offsets));
+ fb->format = pixel_format_get_info(dmabuf->attributes.format);
+ //fb->modifier = dmabuf->attributes.modifier;
+ fb->size = 0;
+ fb->fd = backend->drm.fd;
+
+ if (!fb->format) {
+ weston_log("couldn't look up format info for 0x%lx\n",
+ (unsigned long) fb->format);
+ goto err_free;
+ }
+
+ if (is_opaque)
+ fb->format = pixel_format_get_opaque_substitute(fb->format);
+
+ if (backend->min_width > fb->width ||
+ fb->width > backend->max_width ||
+ backend->min_height > fb->height ||
+ fb->height > backend->max_height) {
+ weston_log("bo geometry out of bounds\n");
+ goto err_free;
+ }
+
+ for (i = 0; i < dmabuf->attributes.n_planes; i++) {
+ fb->handles[i] = gbm_bo_get_handle_for_plane(fb->bo, i).u32;
+ if (!fb->handles[i])
+ goto err_free;
+ }
+
+ if (drm_fb_addfb(fb) != 0) {
+ weston_log("failed to create kms fb: %m\n");
+ goto err_free;
+ }
+
+ return fb;
+
+ drm_fb_destroy_dmabuf(fb);
+ return NULL;
+}
+
static struct drm_fb *
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
bool is_opaque, enum drm_fb_type type)
@@ -990,7 +1103,7 @@ static void
drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
{
assert(fb->buffer_ref.buffer == NULL);
- assert(fb->type == BUFFER_CLIENT);
+ assert(fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF);
weston_buffer_reference(&fb->buffer_ref, buffer);
}
@@ -1015,6 +1128,9 @@ drm_fb_unref(struct drm_fb *fb)
gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
break;
+ drm_fb_destroy_dmabuf(fb);
+ break;
assert(NULL);
break;
@@ -1217,9 +1333,9 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
struct drm_output *output = state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ bool is_opaque = drm_view_is_opaque(ev);
struct linux_dmabuf_buffer *dmabuf;
struct drm_fb *fb;
- struct gbm_bo *bo;
/* Don't import buffers which span multiple outputs. */
if (ev->output_mask != (1u << output->base.id))
@@ -1234,58 +1350,27 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
if (wl_shm_buffer_get(buffer->resource))
return NULL;
- if (!b->gbm)
- return NULL;
-
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
if (dmabuf) {
-#ifdef HAVE_GBM_FD_IMPORT
- *
- * Use AddFB2 directly, do not go via GBM.
- * Add support for multiplanar formats.
- * Both require refactoring in the DRM-backend to
- * support a mix of gbm_bos and drmfbs.
- */
- struct gbm_import_fd_data gbm_dmabuf = {
- .fd = dmabuf->attributes.fd[0],
- .width = dmabuf->attributes.width,
- .height = dmabuf->attributes.height,
- .stride = dmabuf->attributes.stride[0],
- .format = dmabuf->attributes.format
- };
-
- *
- * Currently the buffer is rejected if any dmabuf attribute
- * flag is set. This keeps us from passing an inverted /
- * interlaced / bottom-first buffer (or any other type that may
- * be added in the future) through to an overlay. Ultimately,
- * these types of buffers should be handled through buffer
- * transforms and not as spot-checks requiring specific
- * knowledge. */
- if (dmabuf->attributes.n_planes != 1 ||
- dmabuf->attributes.offset[0] != 0 ||
- dmabuf->attributes.flags)
- goto err;
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
- GBM_BO_USE_SCANOUT);
-#else
- return NULL;
-#endif
+ fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque);
+ if (!fb)
+ return NULL;
} else {
+ struct gbm_bo *bo;
+
+ if (!b->gbm)
+ return NULL;
+
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);
- }
There seems to be a small hole in the logic here. Views whose buffers
are from wl_drm (Mesa GLES clients) are falling through all this logic
and failing to import. linux_dmabuf_buffer_get() succeeds on them,
which causes control to go down the successful case in "if (dmabuf) {
... }". But then drm_fb_get_from_dmabuf() itself fails.

It appears that the import would still actually work if control went
down the gbm_bo_import(GBM_BO_IMPORT_WL_BUFFER) path instead, since
the Wayland-EGL builtin for importing bo's from Wayland buffers would
succeed.

I did the testing on v13 of your patch series, which has a slightly
different implementation of than your v12 edition picture heref. But
the problem seems the same.
Post by Daniel Stone
-
- if (!bo)
- return NULL;
+ if (!bo)
+ return NULL;
- fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev), BUFFER_CLIENT);
- if (!fb) {
- gbm_bo_destroy(bo);
- return NULL;
+ fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
+ if (!fb) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
}
drm_fb_set_buffer(fb, buffer);
--
2.14.1
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Daniel Stone
2017-09-26 17:16:07 UTC
Permalink
Add support for multiple modes: toggling whether or not the renderer
and/or planes are acceptable. This will be used to implement a smarter
plane-placement heuristic when we have support for testing output
states.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 39 +++++++++++++++++++++++++++++++--------
1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 25d0342b..011b412a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1721,6 +1721,11 @@ drm_output_assign_state(struct drm_output_state *state,
}
}

+enum drm_output_propose_state_mode {
+ DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
+ DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY, /**< only assign to planes */
+};
+
static struct weston_plane *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev)
@@ -2971,13 +2976,17 @@ err:

static struct drm_output_state *
drm_output_propose_state(struct weston_output *output_base,
- struct drm_pending_state *pending_state)
+ struct drm_pending_state *pending_state,
+ enum drm_output_propose_state_mode mode)
{
struct drm_output *output = to_drm_output(output_base);
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
struct drm_output_state *state;
struct weston_view *ev;
pixman_region32_t surface_overlap, renderer_region, occluded_region;
struct weston_plane *primary = &output_base->compositor->primary_plane;
+ bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+ bool planes_ok = !b->sprites_are_broken;

assert(!output->state_last);
state = drm_output_state_duplicate(output->state_cur,
@@ -3040,32 +3049,45 @@ drm_output_propose_state(struct weston_output *output_base,
next_plane = primary;
pixman_region32_fini(&surface_overlap);

- if (next_plane == NULL)
+ /* The cursor plane is 'special' in the sense that we can still
+ * place it in the legacy API, and we gate that with a separate
+ * cursors_are_broken flag. */
+ if (next_plane == NULL && !b->cursors_are_broken)
next_plane = drm_output_prepare_cursor_view(state, ev);
if (next_plane == NULL && !drm_view_is_opaque(ev))
next_plane = primary;
+ if (!planes_ok)
+ next_plane = primary;
if (next_plane == NULL)
next_plane = drm_output_prepare_scanout_view(state, ev);
if (next_plane == NULL)
next_plane = drm_output_prepare_overlay_view(state, ev);
- if (next_plane == NULL)
- next_plane = primary;

- if (next_plane == primary)
+ if (!next_plane || next_plane == primary) {
+ if (!renderer_ok)
+ goto err;
+
pixman_region32_union(&renderer_region,
&renderer_region,
&clipped_view);
- else if (output->cursor_plane &&
- next_plane != &output->cursor_plane->base)
+ } else if (output->cursor_plane &&
+ next_plane != &output->cursor_plane->base) {
pixman_region32_union(&occluded_region,
&occluded_region,
&clipped_view);
+ }
pixman_region32_fini(&clipped_view);
}
pixman_region32_fini(&renderer_region);
pixman_region32_fini(&occluded_region);

return state;
+
+err:
+ pixman_region32_fini(&renderer_region);
+ pixman_region32_fini(&occluded_region);
+ drm_output_state_free(state);
+ return NULL;
}

static void
@@ -3079,7 +3101,8 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
struct weston_view *ev;
struct weston_plane *primary = &output_base->compositor->primary_plane;

- state = drm_output_propose_state(output_base, pending_state);
+ state = drm_output_propose_state(output_base, pending_state,
+ DRM_OUTPUT_PROPOSE_STATE_MIXED);

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct drm_plane *target_plane = NULL;
--
2.14.1
Daniel Stone
2017-09-26 17:16:05 UTC
Permalink
When we come to assign_planes, try very hard to ignore views which are
only visible on other outputs, rather than forcibly moving them to the
primary plane, which causes damage all round and unnecessary repaints.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8765491b..7bfdef32 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1363,10 +1363,6 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
struct linux_dmabuf_buffer *dmabuf;
struct drm_fb *fb;

- /* Don't import buffers which span multiple outputs. */
- if (ev->output_mask != (1u << output->base.id))
- return NULL;
-
if (ev->alpha != 1.0f)
return NULL;

@@ -2847,10 +2843,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
if (plane->state_cur->output && plane->state_cur->output != output)
return NULL;

- /* Don't import buffers which span multiple outputs. */
- if (ev->output_mask != (1u << output->base.id))
- return NULL;
-
/* We use GBM to import SHM buffers. */
if (b->gbm == NULL)
return NULL;
@@ -3010,13 +3002,22 @@ drm_output_propose_state(struct weston_output *output_base,
wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct weston_plane *next_plane = NULL;

+ /* If this view doesn't touch our output at all, there's no
+ * reason to do anything with it. */
+ if (!(ev->output_mask & (1u << output->base.id)))
+ continue;
+
+ /* We only assign planes to views which are exclusively present
+ * on our output. */
+ if (ev->output_mask != (1u << output->base.id))
+ next_plane = primary;
+
/* Since we process views from top to bottom, we know that if
* the view intersects the calculated renderer region, it must
* be part of, or occluded by, it, and cannot go on a plane. */
pixman_region32_init(&surface_overlap);
pixman_region32_intersect(&surface_overlap, &renderer_region,
&ev->transform.boundingbox);
-
if (pixman_region32_not_empty(&surface_overlap))
next_plane = primary;
pixman_region32_fini(&surface_overlap);
@@ -3056,6 +3057,11 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct drm_plane *target_plane = NULL;

+ /* If this view doesn't touch our output at all, there's no
+ * reason to do anything with it. */
+ if (!(ev->output_mask & (1u << output->base.id)))
+ continue;
+
/* Test whether this buffer can ever go into a plane:
* non-shm, or small enough to be a cursor.
*
--
2.14.1
Daniel Stone
2017-09-26 17:16:11 UTC
Permalink
Now that we can sensibly test proposed plane configurations with atomic,
sprites are not broken.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 05082af1..111cb9e3 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3472,6 +3472,17 @@ init_kms_caps(struct drm_backend *b)
weston_log("DRM: %s atomic modesetting\n",
b->atomic_modeset ? "supports" : "does not support");

+ /*
+ * KMS support for hardware planes cannot properly synchronize
+ * without nuclear page flip. Without nuclear/atomic, hw plane
+ * and cursor plane updates would either tear or cause extra
+ * waits for vblanks which means dropping the compositor framerate
+ * to a fraction. For cursors, it's not so bad, so they are
+ * enabled.
+ */
+ if (!b->atomic_modeset)
+ b->sprites_are_broken = 1;
+
return 0;
}

@@ -5594,17 +5605,6 @@ drm_backend_create(struct weston_compositor *compositor,
wl_array_init(&b->unused_crtcs);
wl_array_init(&b->unused_connectors);

- /*
- * KMS support for hardware planes cannot properly synchronize
- * without nuclear page flip. Without nuclear/atomic, hw plane
- * and cursor plane updates would either tear or cause extra
- * waits for vblanks which means dropping the compositor framerate
- * to a fraction. For cursors, it's not so bad, so they are
- * enabled.
- *
- * These can be enabled again when nuclear/atomic support lands.
- */
- b->sprites_are_broken = 1;
b->compositor = compositor;
b->use_pixman = config->use_pixman;
b->pageflip_timeout = config->pageflip_timeout;
--
2.14.1
Daniel Stone
2017-09-26 17:16:04 UTC
Permalink
Move drm_assign_planes into two functions: one which proposes a plane
configuration, and another which applies that state to the Weston
internal structures. This will be used to try multiple configurations
and see which is supported.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 109 ++++++++++++++++++++++++++++++---------------
1 file changed, 74 insertions(+), 35 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 093e75b1..8765491b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -352,6 +352,8 @@ struct drm_plane_state {

struct drm_fb *fb;

+ struct weston_view *ev; /**< maintained for drm_assign_planes only */
+
int32_t src_x, src_y;
uint32_t src_w, src_h;
int32_t dest_x, dest_y;
@@ -1749,6 +1751,7 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
}

state->fb = fb;
+ state->ev = ev;
state->output = output;
drm_plane_state_coords_for_view(state, ev);

@@ -2769,6 +2772,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
}

state->fb = fb;
+ state->ev = ev;
state->output = output;

drm_plane_state_coords_for_view(state, ev);
@@ -2905,6 +2909,7 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
}

output->cursor_view = ev;
+ plane_state->ev = ev;

plane_state->fb =
drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
@@ -2972,17 +2977,15 @@ err:
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
}

-static void
-drm_assign_planes(struct weston_output *output_base, void *repaint_data)
+static struct drm_output_state *
+drm_output_propose_state(struct weston_output *output_base,
+ struct drm_pending_state *pending_state)
{
- 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 drm_plane_state *plane_state;
struct weston_view *ev;
pixman_region32_t surface_overlap, renderer_region;
- struct weston_plane *primary, *next_plane;
+ struct weston_plane *primary = &output_base->compositor->primary_plane;

assert(!output->state_last);
state = drm_output_state_duplicate(output->state_cur,
@@ -3003,35 +3006,21 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
* as we do for flipping full screen surfaces.
*/
pixman_region32_init(&renderer_region);
- primary = &output_base->compositor->primary_plane;

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
- struct weston_surface *es = ev->surface;
-
- /* Test whether this buffer can ever go into a plane:
- * non-shm, or small enough to be a cursor.
- *
- * Also, keep a reference when using the pixman renderer.
- * That makes it possible to do a seamless switch to the GL
- * renderer and since the pixman renderer keeps a reference
- * to the buffer anyway, there is no side effects.
- */
- if (b->use_pixman ||
- (es->buffer_ref.buffer &&
- (!wl_shm_buffer_get(es->buffer_ref.buffer->resource) ||
- (ev->surface->width <= b->cursor_width &&
- ev->surface->height <= b->cursor_height))))
- es->keep_buffer = true;
- else
- es->keep_buffer = false;
+ struct weston_plane *next_plane = NULL;

+ /* Since we process views from top to bottom, we know that if
+ * the view intersects the calculated renderer region, it must
+ * be part of, or occluded by, it, and cannot go on a plane. */
pixman_region32_init(&surface_overlap);
pixman_region32_intersect(&surface_overlap, &renderer_region,
&ev->transform.boundingbox);

- next_plane = NULL;
if (pixman_region32_not_empty(&surface_overlap))
next_plane = primary;
+ pixman_region32_fini(&surface_overlap);
+
if (next_plane == NULL)
next_plane = drm_output_prepare_cursor_view(state, ev);
if (next_plane == NULL)
@@ -3041,17 +3030,70 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
if (next_plane == NULL)
next_plane = primary;

- weston_view_move_to_plane(ev, next_plane);
-
if (next_plane == primary)
pixman_region32_union(&renderer_region,
&renderer_region,
&ev->transform.boundingbox);
+ }
+ pixman_region32_fini(&renderer_region);
+
+ return state;
+}
+
+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 drm_plane_state *plane_state;
+ struct weston_view *ev;
+ struct weston_plane *primary = &output_base->compositor->primary_plane;
+
+ state = drm_output_propose_state(output_base, pending_state);

- if (next_plane == primary ||
- (output->cursor_plane &&
- next_plane == &output->cursor_plane->base)) {
- /* cursor plane involves a copy */
+ wl_list_for_each(ev, &output_base->compositor->view_list, link) {
+ struct drm_plane *target_plane = NULL;
+
+ /* Test whether this buffer can ever go into a plane:
+ * non-shm, or small enough to be a cursor.
+ *
+ * Also, keep a reference when using the pixman renderer.
+ * That makes it possible to do a seamless switch to the GL
+ * renderer and since the pixman renderer keeps a reference
+ * to the buffer anyway, there is no side effects.
+ */
+ if (b->use_pixman ||
+ (ev->surface->buffer_ref.buffer &&
+ (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
+ (ev->surface->width <= b->cursor_width &&
+ ev->surface->height <= b->cursor_height))))
+ ev->surface->keep_buffer = true;
+ else
+ ev->surface->keep_buffer = false;
+
+ /* This is a bit unpleasant, but lacking a temporary place to
+ * hang a plane off the view, we have to do a nested walk.
+ * Our first-order iteration has to be planes rather than
+ * views, because otherwise we won't reset views which were
+ * previously on planes to being on the primary plane. */
+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ if (plane_state->ev == ev) {
+ plane_state->ev = NULL;
+ target_plane = plane_state->plane;
+ break;
+ }
+ }
+
+ if (target_plane)
+ weston_view_move_to_plane(ev, &target_plane->base);
+ else
+ weston_view_move_to_plane(ev, primary);
+
+ if (!target_plane ||
+ target_plane->type == WDRM_PLANE_TYPE_CURSOR) {
+ /* cursor plane & renderer involve a copy */
ev->psf_flags = 0;
} else {
/* All other planes are a direct scanout of a
@@ -3059,10 +3101,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
*/
ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
}
-
- pixman_region32_fini(&surface_overlap);
}
- pixman_region32_fini(&renderer_region);

/* We rely on output->cursor_view being both an accurate reflection of
* the cursor plane's state, but also being maintained across repaints
--
2.14.1
Daniel Stone
2017-09-26 17:16:08 UTC
Permalink
Return a pointer to the plane state, rather than indirecting via a
weston_plane.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 73 ++++++++++++++++++++++++++--------------------
1 file changed, 42 insertions(+), 31 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 011b412a..7a720c34 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1726,7 +1726,7 @@ enum drm_output_propose_state_mode {
DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY, /**< only assign to planes */
};

-static struct weston_plane *
+static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
@@ -1765,7 +1765,7 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
state->dest_h != (unsigned) output->base.current_mode->height)
goto err;

- return &scanout_plane->base;
+ return state;

err:
drm_plane_state_put_back(state);
@@ -1939,8 +1939,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
&output->props_conn[WDRM_CONNECTOR_DPMS];
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
- struct drm_plane *p;
struct drm_mode *mode;
+ struct drm_plane *p;
struct timespec now;
int ret = 0;

@@ -2718,7 +2718,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
}
#endif

-static struct weston_plane *
+static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
@@ -2781,7 +2781,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
state->src_h != state->dest_h << 16)
goto err;

- return &p->base;
+ return state;

err:
drm_plane_state_put_back(state);
@@ -2825,7 +2825,7 @@ cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo,
weston_log("failed update cursor: %m\n");
}

-static struct weston_plane *
+static struct drm_plane_state *
drm_output_prepare_cursor_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
@@ -2914,7 +2914,7 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
if (needs_update)
cursor_bo_update(b, plane_state->fb->bo, ev);

- return &plane->base;
+ return plane_state;

err:
drm_plane_state_put_back(plane_state);
@@ -2984,7 +2984,6 @@ drm_output_propose_state(struct weston_output *output_base,
struct drm_output_state *state;
struct weston_view *ev;
pixman_region32_t surface_overlap, renderer_region, occluded_region;
- struct weston_plane *primary = &output_base->compositor->primary_plane;
bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
bool planes_ok = !b->sprites_are_broken;

@@ -3010,7 +3009,8 @@ drm_output_propose_state(struct weston_output *output_base,
pixman_region32_init(&occluded_region);

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
- struct weston_plane *next_plane = NULL;
+ struct drm_plane_state *ps = NULL;
+ bool force_renderer = false;
pixman_region32_t clipped_view;
bool occluded = false;

@@ -3022,7 +3022,7 @@ drm_output_propose_state(struct weston_output *output_base,
/* We only assign planes to views which are exclusively present
* on our output. */
if (ev->output_mask != (1u << output->base.id))
- next_plane = primary;
+ force_renderer = true;

/* Ignore views we know to be totally occluded. */
pixman_region32_init(&clipped_view);
@@ -3046,36 +3046,47 @@ drm_output_propose_state(struct weston_output *output_base,
pixman_region32_intersect(&surface_overlap, &renderer_region,
&clipped_view);
if (pixman_region32_not_empty(&surface_overlap))
- next_plane = primary;
+ force_renderer = true;
pixman_region32_fini(&surface_overlap);

+ if (force_renderer && !renderer_ok) {
+ pixman_region32_fini(&clipped_view);
+ goto err;
+ }
+
/* The cursor plane is 'special' in the sense that we can still
* place it in the legacy API, and we gate that with a separate
* cursors_are_broken flag. */
- if (next_plane == NULL && !b->cursors_are_broken)
- next_plane = drm_output_prepare_cursor_view(state, ev);
- if (next_plane == NULL && !drm_view_is_opaque(ev))
- next_plane = primary;
- if (!planes_ok)
- next_plane = primary;
- if (next_plane == NULL)
- next_plane = drm_output_prepare_scanout_view(state, ev);
- if (next_plane == NULL)
- next_plane = drm_output_prepare_overlay_view(state, ev);
-
- if (!next_plane || next_plane == primary) {
- if (!renderer_ok)
- goto err;
-
- pixman_region32_union(&renderer_region,
- &renderer_region,
- &clipped_view);
- } else if (output->cursor_plane &&
- next_plane != &output->cursor_plane->base) {
+ if (!force_renderer && !b->cursors_are_broken)
+ ps = drm_output_prepare_cursor_view(state, ev);
+
+ if (!planes_ok || !drm_view_is_opaque(ev))
+ force_renderer = true;
+
+ if (!drm_view_is_opaque(ev))
+ force_renderer = true;
+
+ if (!force_renderer && !ps)
+ ps = drm_output_prepare_scanout_view(state, ev);
+ if (!force_renderer && !ps)
+ ps = drm_output_prepare_overlay_view(state, ev);
+
+ if (ps) {
pixman_region32_union(&occluded_region,
&occluded_region,
&clipped_view);
+ pixman_region32_fini(&clipped_view);
+ continue;
+ }
+
+ if (!renderer_ok) {
+ pixman_region32_fini(&clipped_view);
+ goto err;
}
+
+ pixman_region32_union(&renderer_region,
+ &renderer_region,
+ &clipped_view);
pixman_region32_fini(&clipped_view);
}
pixman_region32_fini(&renderer_region);
--
2.14.1
Daniel Stone
2017-09-26 17:15:55 UTC
Permalink
Use the new helper to populate the cursor state as well, with some
special-case handling to account for how we always upload a full-size
BO.

Signed-off-by: Daniel Stone <***@collabora.com>
Reported-by: Derek Foreman <***@osg.samsung.com>
---
libweston/compositor-drm.c | 48 +++++++++++++++++++++++-----------------------
1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8244d4d8..5413ff3f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2759,10 +2759,8 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane *plane = output->cursor_plane;
struct drm_plane_state *plane_state;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_shm_buffer *shmbuf;
bool needs_update = false;
- float x, y;

if (!plane)
return NULL;
@@ -2792,16 +2790,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)
return NULL;

- if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
- return NULL;
- if (ev->transform.enabled &&
- (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE))
- return NULL;
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
- if (ev->geometry.scissor_enabled)
- return NULL;
-
if (ev->surface->width > b->cursor_width ||
ev->surface->height > b->cursor_height)
return NULL;
@@ -2812,6 +2800,26 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
if (plane_state && plane_state->fb)
return NULL;

+ /* We can't scale with the legacy API, and we don't try to account for
+ * simple cropping/translation in cursor_bo_update. */
+ plane_state->output = output;
+ drm_plane_state_coords_for_view(plane_state, ev);
+ if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
+ plane_state->src_w > (unsigned) b->cursor_width << 16 ||
+ plane_state->src_h > (unsigned) b->cursor_height << 16 ||
+ plane_state->src_w != plane_state->dest_w << 16 ||
+ plane_state->src_h != plane_state->dest_h << 16)
+ goto err;
+
+ /* The cursor API is somewhat special: in cursor_bo_update(), we upload
+ * a buffer which is always cursor_width x cursor_height, even if the
+ * surface we want to promote is actually smaller than this. Manually
+ * mangle the plane state to deal with this. */
+ plane_state->src_w = b->cursor_width << 16;
+ plane_state->src_h = b->cursor_height << 16;
+ plane_state->dest_w = b->cursor_width;
+ plane_state->dest_h = b->cursor_height;
+
/* Since we're setting plane state up front, we need to work out
* whether or not we need to upload a new cursor. We can't use the
* plane damage, since the planes haven't actually been calculated
@@ -2828,26 +2836,18 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
}

output->cursor_view = ev;
- weston_view_to_global_float(ev, 0, 0, &x, &y);
- plane->base.x = x;
- plane->base.y = y;

plane_state->fb =
drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
- plane_state->output = output;
- plane_state->src_x = 0;
- plane_state->src_y = 0;
- plane_state->src_w = b->cursor_width << 16;
- plane_state->src_h = b->cursor_height << 16;
- plane_state->dest_x = (x - output->base.x) * output->base.current_scale;
- plane_state->dest_y = (y - output->base.y) * output->base.current_scale;
- plane_state->dest_w = b->cursor_width;
- plane_state->dest_h = b->cursor_height;

if (needs_update)
cursor_bo_update(b, plane_state->fb->bo, ev);

return &plane->base;
+
+err:
+ drm_plane_state_put_back(plane_state);
+ return NULL;
}

static void
--
2.14.1
Daniel Stone
2017-09-26 17:15:49 UTC
Permalink
Add a test environment variable to allow disabling atomic modesetting.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d04ba12f..8cd69f1f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3240,11 +3240,13 @@ init_kms_caps(struct drm_backend *b)
b->universal_planes ? "supports" : "does not support");

#ifdef HAVE_DRM_ATOMIC
- ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
- if (ret != 0)
- cap = 0;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
- b->atomic_modeset = ((ret == 0) && (cap == 1));
+ if (!getenv("DRM_DISABLE_ATOMIC")) {
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
+ if (ret != 0)
+ cap = 0;
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ b->atomic_modeset = ((ret == 0) && (cap == 1));
+ {
#endif
weston_log("DRM: %s atomic modesetting\n",
b->atomic_modeset ? "supports" : "does not support");
--
2.14.1
Philipp Zabel
2017-09-27 09:45:24 UTC
Permalink
Hi Daniel,
Post by Daniel Stone
Add a test environment variable to allow disabling atomic modesetting.
---
 libweston/compositor-drm.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d04ba12f..8cd69f1f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3240,11 +3240,13 @@ init_kms_caps(struct drm_backend *b)
     b->universal_planes ? "supports" : "does not support");
 
 #ifdef HAVE_DRM_ATOMIC
- ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
- if (ret != 0)
- cap = 0;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
- b->atomic_modeset = ((ret == 0) && (cap == 1));
+ if (!getenv("DRM_DISABLE_ATOMIC")) {
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
+ if (ret != 0)
+ cap = 0;
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ b->atomic_modeset = ((ret == 0) && (cap == 1));
+ {
^-- A typo here.
Post by Daniel Stone
 #endif
  weston_log("DRM: %s atomic modesetting\n",
     b->atomic_modeset ? "supports" : "does not support");
regards
Philipp
Pekka Paalanen
2017-10-04 09:56:55 UTC
Permalink
On Tue, 26 Sep 2017 18:15:49 +0100
Post by Daniel Stone
Add a test environment variable to allow disabling atomic modesetting.
---
libweston/compositor-drm.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d04ba12f..8cd69f1f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3240,11 +3240,13 @@ init_kms_caps(struct drm_backend *b)
b->universal_planes ? "supports" : "does not support");
#ifdef HAVE_DRM_ATOMIC
- ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
- if (ret != 0)
- cap = 0;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
- b->atomic_modeset = ((ret == 0) && (cap == 1));
+ if (!getenv("DRM_DISABLE_ATOMIC")) {
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
+ if (ret != 0)
+ cap = 0;
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ b->atomic_modeset = ((ret == 0) && (cap == 1));
+ {
#endif
weston_log("DRM: %s atomic modesetting\n",
b->atomic_modeset ? "supports" : "does not support");
Hi,

I think this patch should come before the "compositor-drm: Atomic
modesetting support" patch if not even squashed into "compositor-drm:
Discover atomic properties", so that if someone is bisecting an issue
with DISABLE_ATOMIC, there is no commit where it suddenly doesn't work.


Thanks,
pq
Daniel Stone
2017-09-26 17:15:54 UTC
Permalink
Use the shiny new helper we have for working through scanout as well.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 66 ++++++++++++++++------------------------------
1 file changed, 23 insertions(+), 43 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 07f0b105..8244d4d8 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1532,13 +1532,6 @@ drm_output_assign_state(struct drm_output_state *state,
}
}

-static int
-drm_view_transform_supported(struct weston_view *ev)
-{
- return !ev->transform.enabled ||
- (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
-}
-
static bool
drm_view_is_opaque(struct weston_view *ev)
{
@@ -1570,7 +1563,6 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct gbm_bo *bo;

/* Don't import buffers which span multiple outputs. */
@@ -1586,27 +1578,24 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
if (wl_shm_buffer_get(buffer->resource))
return NULL;

- /* Make sure our view is exactly compatible with the output. */
- if (ev->geometry.x != output->base.x ||
- ev->geometry.y != output->base.y)
- return NULL;
- if (buffer->width != output->base.current_mode->width ||
- buffer->height != output->base.current_mode->height)
- return NULL;
+ state = drm_output_state_get_plane(output_state, scanout_plane);
+ if (state->fb)
+ goto err;

- if (ev->transform.enabled)
- return NULL;
- if (ev->geometry.scissor_enabled)
- return NULL;
- if (viewport->buffer.transform != output->base.transform)
- return NULL;
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
- if (!drm_view_transform_supported(ev))
- return NULL;
+ state->output = output;
+ drm_plane_state_coords_for_view(state, ev);
+
+ /* The legacy API does not let us perform cropping or scaling. */
+ if (state->src_x != 0 || state->src_y != 0 ||
+ state->src_w != state->dest_w << 16 ||
+ state->src_h != state->dest_h << 16 ||
+ state->dest_x != 0 || state->dest_y != 0 ||
+ state->dest_w != (unsigned) output->base.current_mode->width ||
+ state->dest_h != (unsigned) output->base.current_mode->height)
+ goto err;

if (ev->alpha != 1.0f)
- return NULL;
+ goto err;

state = drm_output_state_get_plane(output_state, scanout_plane);
if (state->fb) {
@@ -1622,39 +1611,30 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,

/* Unable to use the buffer for scanout */
if (!bo)
- return NULL;
+ goto err;

state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
BUFFER_CLIENT);
if (!state->fb) {
- drm_plane_state_put_back(state);
+ /* We need to explicitly destroy the BO. */
gbm_bo_destroy(bo);
- return NULL;
+ goto err;
}

/* Can't change formats with just a pageflip */
if (state->fb->format->format != output->gbm_format) {
/* No need to destroy the GBM BO here, as it's now owned
* by the FB. */
- drm_plane_state_put_back(state);
- return NULL;
+ goto err;
}

drm_fb_set_buffer(state->fb, buffer);

- state->output = output;
-
- state->src_x = 0;
- state->src_y = 0;
- state->src_w = state->fb->width << 16;
- state->src_h = state->fb->height << 16;
-
- state->dest_x = 0;
- state->dest_y = 0;
- state->dest_w = output->base.current_mode->width;
- state->dest_h = output->base.current_mode->height;
-
return &scanout_plane->base;
+
+err:
+ drm_plane_state_put_back(state);
+ return NULL;
}

static struct drm_fb *
--
2.14.1
Daniel Stone
2017-09-26 17:15:42 UTC
Permalink
Split repaint into two stages, as implied by the grouped-repaint
interface: drm_output_repaint generates the repaint state only, and
drm_repaint_flush applies it.

This also moves DPMS into output state. Previously, the usual way to
DPMS off was that repaint would be called and apply its state, followed
by set_dpms being called afterwards to push the DPMS state separately.
As this happens before the repaint_flush hook, with no change to DPMS we
would set DPMS off, then immediately re-enable the output by posting the
repaint. Not ideal.

Moving DPMS application at the same time complicates this patch, but I
couldn't find a way to split it; if we keep set_dpms before begin_flush
then we break DPMS off, or if we try to move DPMS to output state before
using the repaint flush, we get stuck as the repaint hook generates an
asynchronous state update, followed immediately by set_dpms generating a
synchronous state update.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 313 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 240 insertions(+), 73 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 530b0fd5..6c1c6881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -267,6 +267,7 @@ struct drm_output_state {
struct drm_pending_state *pending_state;
struct drm_output *output;
struct wl_list link;
+ enum dpms_enum dpms;
struct wl_list plane_list;
};

@@ -350,6 +351,7 @@ struct drm_output {
int page_flip_pending;
int destroy_pending;
int disable_pending;
+ int dpms_off_pending;

struct drm_fb *gbm_cursor_fb[2];
struct drm_plane *cursor_plane;
@@ -1124,6 +1126,7 @@ drm_output_state_alloc(struct drm_output *output,

assert(state);
state->output = output;
+ state->dpms = WESTON_DPMS_OFF;
state->pending_state = pending_state;
if (pending_state)
wl_list_insert(&pending_state->output_list, &state->link);
@@ -1200,6 +1203,30 @@ drm_output_state_free(struct drm_output_state *state)
free(state);
}

+/**
+ * Get output state to disable output
+ *
+ * Returns a pointer to an output_state object which can be used to disable
+ * an output (e.g. DPMS off).
+ *
+ * @param pending_state The pending state object owning this update
+ * @param output The output to disable
+ * @returns A drm_output_state to disable the output
+ */
+static struct drm_output_state *
+drm_output_get_disable_state(struct drm_pending_state *pending_state,
+ struct drm_output *output)
+{
+ struct drm_output_state *output_state;
+
+ output_state = drm_output_state_duplicate(output->state_cur,
+ pending_state,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+ output_state->dpms = WESTON_DPMS_OFF;
+
+ return output_state;
+}
+
/**
* Allocate a new drm_pending_state
*
@@ -1272,6 +1299,8 @@ drm_pending_state_get_output(struct drm_pending_state *pending_state,
return NULL;
}

+static int drm_pending_state_apply(struct drm_pending_state *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
@@ -1281,6 +1310,7 @@ static void
drm_output_update_complete(struct drm_output *output, uint32_t flags,
unsigned int sec, unsigned int usec)
{
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane_state *ps;
struct timespec ts;

@@ -1300,6 +1330,13 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
} else if (output->disable_pending) {
weston_output_disable(&output->base);
output->disable_pending = 0;
+ output->dpms_off_pending = 0;
+ return;
+ } else if (output->dpms_off_pending) {
+ struct drm_pending_state *pending = drm_pending_state_alloc(b);
+ drm_output_get_disable_state(pending, output);
+ drm_pending_state_apply(pending);
+ output->dpms_off_pending = 0;
return;
}

@@ -1360,7 +1397,6 @@ drm_output_assign_state(struct drm_output_state *state,
}
}

-
static int
drm_view_transform_supported(struct weston_view *ev)
{
@@ -1653,37 +1689,20 @@ drm_waitvblank_pipe(struct drm_output *output)
}

static int
-drm_output_repaint(struct weston_output *output_base,
- pixman_region32_t *damage,
- void *repaint_data)
+drm_output_apply_state(struct drm_output_state *state)
{
- struct drm_pending_state *pending_state = repaint_data;
- struct drm_output_state *state = NULL;
- struct drm_output *output = to_drm_output(output_base);
- struct drm_backend *backend =
- to_drm_backend(output->base.compositor);
+ struct drm_output *output = state->output;
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
+ struct drm_property_info *dpms_prop =
+ &output->props_conn[WDRM_CONNECTOR_DPMS];
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
struct drm_mode *mode;
+ struct timespec now;
int ret = 0;

- if (output->disable_pending || output->destroy_pending)
- 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);
-
-
/* If disable_planes is set then assign_planes() wasn't
* called for this render, so we could still have a stale
* cursor plane set up.
@@ -1694,10 +1713,46 @@ drm_output_repaint(struct weston_output *output_base,
output->cursor_plane->base.y = INT32_MIN;
}

- drm_output_render(state, damage);
- scanout_state = drm_output_state_get_plane(state, scanout_plane);
- if (!scanout_state || !scanout_state->fb)
- goto err;
+ if (state->dpms != WESTON_DPMS_ON) {
+ wl_list_for_each(ps, &state->plane_list, link) {
+ p = ps->plane;
+ assert(ps->fb == NULL);
+ assert(ps->output == NULL);
+
+ if (p->type != WDRM_PLANE_TYPE_OVERLAY)
+ continue;
+
+
+ ret = drmModeSetPlane(backend->drm.fd, p->plane_id,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (ret)
+ weston_log("drmModeSetPlane failed disable: %m\n");
+ }
+
+ if (output->cursor_plane) {
+ ret = drmModeSetCursor(backend->drm.fd, output->crtc_id,
+ 0, 0, 0);
+ if (ret)
+ weston_log("drmModeSetCursor failed disable: %m\n");
+ }
+
+ ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
+ &output->connector_id, 0, NULL);
+ if (ret)
+ weston_log("drmModeSetCrtc failed disabling: %m\n");
+
+ drm_output_assign_state(state,
+ DRM_OUTPUT_STATE_UPDATE_SYNCHRONOUS);
+ weston_compositor_read_presentation_clock(output->base.compositor, &now);
+ weston_output_finish_frame(&output->base,
+ &now,
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION);
+
+ return 0;
+ }
+
+ scanout_state =
+ drm_output_state_get_existing_plane(state, scanout_plane);

/* The legacy SetCrtc API doesn't allow us to do scaling, and the
* legacy PageFlip API doesn't allow us to do clipping either. */
@@ -1724,7 +1779,6 @@ drm_output_repaint(struct weston_output *output_base,
weston_log("set mode failed: %m\n");
goto err;
}
- output_base->set_dpms(output_base, WESTON_DPMS_ON);
}

if (drmModePageFlip(backend->drm.fd, output->crtc_id,
@@ -1790,13 +1844,101 @@ drm_output_repaint(struct weston_output *output_base,
}
}

+ if (dpms_prop->prop_id && state->dpms != output->state_cur->dpms) {
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+ output->connector_id,
+ dpms_prop->prop_id,
+ state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for %s\n",
+ output->base.name);
+ }
+ }
+
+ drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+
return 0;

err:
output->cursor_view = NULL;
-
drm_output_state_free(state);
+ return -1;
+}
+
+static int
+drm_pending_state_apply(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ uint32_t *unused;
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs we aren't using. This also disables
+ * all connectors on these CRTCs, so we don't need to do that
+ * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
+ NULL);
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link) {
+ struct drm_output *output = output_state->output;
+ int ret;
+
+ ret = drm_output_apply_state(output_state);
+ if (ret != 0) {
+ weston_log("Couldn't apply state for output %s\n",
+ output->base.name);
+ }
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));

+ drm_pending_state_free(pending_state);
+
+ return 0;
+}
+
+static int
+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 *output = to_drm_output(output_base);
+ struct drm_output_state *state;
+ struct drm_plane_state *scanout_state;
+
+ if (output->disable_pending || output->destroy_pending)
+ 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);
+ state->dpms = WESTON_DPMS_ON;
+
+ drm_output_render(state, damage);
+ scanout_state = drm_output_state_get_plane(state,
+ output->scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return 0;
+
+err:
+ drm_output_state_free(state);
return -1;
}

@@ -1922,6 +2064,9 @@ drm_output_update_msc(struct drm_output *output, unsigned int seq)
output->base.msc = (msc_hi << 32) + seq;
}

+static void
+drm_output_destroy(struct weston_output *base);
+
static void
vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
void *data)
@@ -1944,9 +2089,6 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
drm_output_update_complete(output, flags, sec, usec);
}

-static void
-drm_output_destroy(struct weston_output *base);
-
static void
page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void *data)
@@ -2000,29 +2142,8 @@ 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;
- uint32_t *unused;

- if (b->state_invalid) {
- /* If we need to reset all our state (e.g. because we've
- * just started, or just been VT-switched in), explicitly
- * disable all the CRTCs we aren't using. This also disables
- * all connectors on these CRTCs, so we don't need to do that
- * separately with the pre-atomic API. */
- wl_array_for_each(unused, &b->unused_crtcs)
- drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
- NULL);
- }
-
- wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
- link) {
- drm_output_assign_state(output_state,
- DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
- }
-
- b->state_invalid = false;
-
- drm_pending_state_free(pending_state);
+ drm_pending_state_apply(pending_state);
b->repaint_data = NULL;
}

@@ -3026,8 +3147,6 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
static void
drm_plane_destroy(struct drm_plane *plane)
{
- drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0);
drm_plane_state_free(plane->state_cur, true);
drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
weston_plane_release(&plane->base);
@@ -3197,28 +3316,72 @@ drm_set_backlight(struct weston_output *output_base, uint32_t value)
backlight_set_brightness(output->backlight, new_brightness);
}

+/**
+ * Power output on or off
+ *
+ * The DPMS/power level of an output is used to switch it on or off. This
+ * is DRM's hook for doing so, which can called either as part of repaint,
+ * or independently of the repaint loop.
+ *
+ * If we are called as part of repaint, we simply set the relevant bit in
+ * state and return.
+ */
static void
drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
{
struct drm_output *output = to_drm_output(output_base);
- struct weston_compositor *ec = output_base->compositor;
- struct drm_backend *b = to_drm_backend(ec);
- struct drm_property_info *prop =
- &output->props_conn[WDRM_CONNECTOR_DPMS];
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+ struct drm_pending_state *pending_state = b->repaint_data;
+ struct drm_output_state *state;
int ret;

- if (!prop->prop_id)
+ /* If we're being called during the repaint loop, then this is
+ * simple: discard any previously-generated state, and create a new
+ * state where we disable everything. When we come to flush, this
+ * will be applied. */
+ if (pending_state) {
+ /* The repaint loop already sets DPMS on; we don't need to
+ * explicitly set it on here, as it will already happen
+ * whilst applying the repaint state. */
+ if (level == WESTON_DPMS_ON)
+ return;
+
+ state = drm_pending_state_get_output(pending_state, output);
+ if (state)
+ drm_output_state_free(state);
+ state = drm_output_get_disable_state(pending_state, output);
+ state->dpms = level;
+ return;
+ }
+
+ if (output->state_cur->dpms == level)
return;

- ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id,
- prop->prop_id, level);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
+ /* As we throw everything away when disabling, just send us back through
+ * a repaint cycle. */
+ if (level == WESTON_DPMS_ON) {
+ if (output->dpms_off_pending)
+ output->dpms_off_pending = 0;
+ weston_output_schedule_repaint(output_base);
return;
}

- output->dpms = level;
+ if (output->state_cur->dpms == WESTON_DPMS_OFF)
+ return;
+
+ /* If we've already got a request in the pipeline, then we need to
+ * park our DPMS request until that request has quiesced. */
+ if (output->state_last) {
+ output->dpms_off_pending = 1;
+ return;
+ }
+
+ pending_state = drm_pending_state_alloc(b);
+ drm_output_state_duplicate(output->state_cur, pending_state,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+ ret = drm_pending_state_apply(pending_state);
+ if (ret != 0)
+ weston_log("drm_set_dpms: couldn't disable output?\n");
}

static const char * const connector_type_names[] = {
@@ -4025,7 +4188,9 @@ drm_output_disable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
+ struct drm_pending_state *pending_state;
uint32_t *unused;
+ int ret;

if (output->page_flip_pending || output->vblank_pending) {
output->disable_pending = 1;
@@ -4038,11 +4203,13 @@ drm_output_disable(struct weston_output *base)
output->disable_pending = 0;

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 = drm_output_state_alloc(output, NULL);
+ pending_state = drm_pending_state_alloc(b);
+ drm_output_get_disable_state(pending_state, output);
+ ret = drm_pending_state_apply(pending_state);
+ if (ret) {
+ weston_log("Couldn't disable output output %s\n",
+ output->base.name);
+ }

unused = wl_array_add(&b->unused_connectors, sizeof(*unused));
*unused = output->connector_id;
--
2.14.1
Philipp Zabel
2017-09-27 10:51:57 UTC
Permalink
Hi Daniel,
Post by Daniel Stone
Split repaint into two stages, as implied by the grouped-repaint
interface: drm_output_repaint generates the repaint state only, and
drm_repaint_flush applies it.
This also moves DPMS into output state. Previously, the usual way to
DPMS off was that repaint would be called and apply its state, followed
by set_dpms being called afterwards to push the DPMS state separately.
As this happens before the repaint_flush hook, with no change to DPMS we
would set DPMS off, then immediately re-enable the output by posting the
repaint. Not ideal.
Moving DPMS application at the same time complicates this patch, but I
couldn't find a way to split it; if we keep set_dpms before begin_flush
then we break DPMS off, or if we try to move DPMS to output state before
using the repaint flush, we get stuck as the repaint hook generates an
asynchronous state update, followed immediately by set_dpms generating a
synchronous state update.
---
 libweston/compositor-drm.c | 313 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 240 insertions(+), 73 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 530b0fd5..6c1c6881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
[...]
Post by Daniel Stone
@@ -1360,7 +1397,6 @@ drm_output_assign_state(struct drm_output_state *state,
  }
 }
 
-
 static int
 drm_view_transform_supported(struct weston_view *ev)
 {
@@ -1653,37 +1689,20 @@ drm_waitvblank_pipe(struct drm_output *output)
 }
 
 static int
-drm_output_repaint(struct weston_output *output_base,
-    pixman_region32_t *damage,
-    void *repaint_data)
+drm_output_apply_state(struct drm_output_state *state)
 {
- struct drm_pending_state *pending_state = repaint_data;
- struct drm_output_state *state = NULL;
Here state was initialized ...
Post by Daniel Stone
- struct drm_output *output = to_drm_output(output_base);
- struct drm_backend *backend =
- to_drm_backend(output->base.compositor);
+ struct drm_output *output = state->output;
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
  struct drm_plane *scanout_plane = output->scanout_plane;
+ struct drm_property_info *dpms_prop =
+ &output->props_conn[WDRM_CONNECTOR_DPMS];
  struct drm_plane_state *scanout_state;
  struct drm_plane_state *ps;
  struct drm_plane *p;
  struct drm_mode *mode;
+ struct timespec now;
  int ret = 0;
 
- if (output->disable_pending || output->destroy_pending)
- goto err;
... so this may return ...
Post by Daniel Stone
-
- 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);
-
-
  /* If disable_planes is set then assign_planes() wasn't
   * called for this render, so we could still have a stale
   * cursor plane set up.
[...]
Post by Daniel Stone
@@ -1790,13 +1844,101 @@ drm_output_repaint(struct weston_output *output_base,
  }
  }
 
+ if (dpms_prop->prop_id && state->dpms != output->state_cur->dpms) {
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+   output->connector_id,
+   dpms_prop->prop_id,
+   state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for %s\n",
+    output->base.name);
+ }
+ }
+
+ drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+
  return 0;
 
  output->cursor_view = NULL;
-
  drm_output_state_free(state);
... without causing issues here.
Post by Daniel Stone
+ return -1;
+}
+
+static int
+drm_pending_state_apply(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ uint32_t *unused;
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because we've
+  * just started, or just been VT-switched in), explicitly
+  * disable all the CRTCs we aren't using. This also disables
+  * all connectors on these CRTCs, so we don't need to do that
+  * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
+        NULL);
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+       link) {
+ struct drm_output *output = output_state->output;
+ int ret;
+
+ ret = drm_output_apply_state(output_state);
+ if (ret != 0) {
+ weston_log("Couldn't apply state for output %s\n",
+    output->base.name);
+ }
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
 
+ drm_pending_state_free(pending_state);
+
+ return 0;
+}
+
+static int
+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 *output = to_drm_output(output_base);
+ struct drm_output_state *state;
Here state is not initialized anymore ...
Post by Daniel Stone
+ struct drm_plane_state *scanout_state;
+
+ if (output->disable_pending || output->destroy_pending)
+ goto err;
... so this error path ...
Post by Daniel Stone
+
+ 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);
+ state->dpms = WESTON_DPMS_ON;
+
+ drm_output_render(state, damage);
+ scanout_state = drm_output_state_get_plane(state,
+    output->scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return 0;
+
+ drm_output_state_free(state);
... will cause drm_output_state_free to be called with uninitialized state.
Post by Daniel Stone
  return -1;
 }
regards
Philipp
Sergi Granell
2017-09-28 14:53:22 UTC
Permalink
Post by Daniel Stone
Split repaint into two stages, as implied by the grouped-repaint
interface: drm_output_repaint generates the repaint state only, and
drm_repaint_flush applies it.
This also moves DPMS into output state. Previously, the usual way to
DPMS off was that repaint would be called and apply its state, followed
by set_dpms being called afterwards to push the DPMS state separately.
As this happens before the repaint_flush hook, with no change to DPMS we
would set DPMS off, then immediately re-enable the output by posting the
repaint. Not ideal.
Moving DPMS application at the same time complicates this patch, but I
couldn't find a way to split it; if we keep set_dpms before begin_flush
then we break DPMS off, or if we try to move DPMS to output state before
using the repaint flush, we get stuck as the repaint hook generates an
asynchronous state update, followed immediately by set_dpms generating a
synchronous state update.
---
libweston/compositor-drm.c | 313 ++++++++++++++++++++++++++++++++++-----
------
1 file changed, 240 insertions(+), 73 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 530b0fd5..6c1c6881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
[snip]
Post by Daniel Stone
@@ -1790,13 +1844,101 @@ drm_output_repaint(struct weston_output *output_base,
}
}
+ if (dpms_prop->prop_id && state->dpms != output->state_cur-
Post by Daniel Stone
dpms) {
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+ output->connector_id,
+ dpms_prop->prop_id,
+ state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for
%s\n",
+ output->base.name);
+ }
+ }
+
+ drm_output_assign_state(state,
DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+
return 0;
output->cursor_view = NULL;
-
drm_output_state_free(state);
+ return -1;
+}
+
+static int
+drm_pending_state_apply(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ uint32_t *unused;
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in),
explicitly
+ * disable all the CRTCs we aren't using. This also
disables
+ * all connectors on these CRTCs, so we don't need to do
that
+ * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0,
NULL, 0,
+ NULL);
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state-
Post by Daniel Stone
output_list,
+ link) {
+ struct drm_output *output = output_state->output;
+ int ret;
+
+ ret = drm_output_apply_state(output_state);
+ if (ret != 0) {
+ weston_log("Couldn't apply state for output
%s\n",
+ output->base.name);
+ }
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+ drm_pending_state_free(pending_state);
+
+ return 0;
+}
+
+static int
+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 *output = to_drm_output(output_base);
+ struct drm_output_state *state;
+ struct drm_plane_state *scanout_state;
+
+ if (output->disable_pending || output->destroy_pending)
+ goto err;
If this goes to 'err', 'drm_output_state_free' will be called
with an uninitialized 'state'.
Post by Daniel Stone
+
+ 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_CLEA
R_PLANES);
+ state->dpms = WESTON_DPMS_ON;
+
+ drm_output_render(state, damage);
+ scanout_state = drm_output_state_get_plane(state,
+ output-
Post by Daniel Stone
scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return 0;
+
+ drm_output_state_free(state);
return -1;
}
[snip]
Pekka Paalanen
2017-10-03 11:18:17 UTC
Permalink
On Tue, 26 Sep 2017 18:15:42 +0100
Post by Daniel Stone
Split repaint into two stages, as implied by the grouped-repaint
interface: drm_output_repaint generates the repaint state only, and
drm_repaint_flush applies it.
This also moves DPMS into output state. Previously, the usual way to
DPMS off was that repaint would be called and apply its state, followed
by set_dpms being called afterwards to push the DPMS state separately.
As this happens before the repaint_flush hook, with no change to DPMS we
would set DPMS off, then immediately re-enable the output by posting the
repaint. Not ideal.
Moving DPMS application at the same time complicates this patch, but I
couldn't find a way to split it; if we keep set_dpms before begin_flush
then we break DPMS off, or if we try to move DPMS to output state before
using the repaint flush, we get stuck as the repaint hook generates an
asynchronous state update, followed immediately by set_dpms generating a
synchronous state update.
Hi Daniel,

yes, it's messy, but probably not worth the effort to redesign how DPMS
state gets applied as a prerequisite. You did fine with the logic
anyway, I couldn't spot a mistake.
Post by Daniel Stone
---
libweston/compositor-drm.c | 313 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 240 insertions(+), 73 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 530b0fd5..6c1c6881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
+static int
+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 *output = to_drm_output(output_base);
+ struct drm_output_state *state;
+ struct drm_plane_state *scanout_state;
+
+ if (output->disable_pending || output->destroy_pending)
+ 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);
+ state->dpms = WESTON_DPMS_ON;
+
+ drm_output_render(state, damage);
+ scanout_state = drm_output_state_get_plane(state,
+ output->scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return 0;
+
+ drm_output_state_free(state);
Others already pointed out the use of uninitialized 'state'. I'll check
that and raise with a
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>
given a trivial fix.
Post by Daniel Stone
return -1;
}
@@ -3026,8 +3147,6 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
static void
drm_plane_destroy(struct drm_plane *plane)
{
- drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0);
This was a slightly surprising piece in this patch. Is the rationale
here that Weston should not reset CRTC state on exit? Shouldn't that
wait for the "compositor-drm: Don't restore original CRTC mode" patch?

Feels like this hunk doesn't belong in this patch.

I suppose this might leave an overlay up, but since we have overlays
disabled by default, it shouldn't cause issues.
Post by Daniel Stone
drm_plane_state_free(plane->state_cur, true);
drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
weston_plane_release(&plane->base);
Thanks,
pq
Michael Tretter
2017-11-10 13:53:56 UTC
Permalink
Hi Daniel,
Post by Daniel Stone
Split repaint into two stages, as implied by the grouped-repaint
interface: drm_output_repaint generates the repaint state only, and
drm_repaint_flush applies it.
This also moves DPMS into output state. Previously, the usual way to
DPMS off was that repaint would be called and apply its state,
followed by set_dpms being called afterwards to push the DPMS state
separately. As this happens before the repaint_flush hook, with no
change to DPMS we would set DPMS off, then immediately re-enable the
output by posting the repaint. Not ideal.
Moving DPMS application at the same time complicates this patch, but I
couldn't find a way to split it; if we keep set_dpms before
begin_flush then we break DPMS off, or if we try to move DPMS to
output state before using the repaint flush, we get stuck as the
repaint hook generates an asynchronous state update, followed
immediately by set_dpms generating a synchronous state update.
---
libweston/compositor-drm.c | 313
++++++++++++++++++++++++++++++++++----------- 1 file changed, 240
insertions(+), 73 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 530b0fd5..6c1c6881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -267,6 +267,7 @@ struct drm_output_state {
struct drm_pending_state *pending_state;
struct drm_output *output;
struct wl_list link;
+ enum dpms_enum dpms;
struct wl_list plane_list;
};
@@ -350,6 +351,7 @@ struct drm_output {
int page_flip_pending;
int destroy_pending;
int disable_pending;
+ int dpms_off_pending;
struct drm_fb *gbm_cursor_fb[2];
struct drm_plane *cursor_plane;
@@ -1124,6 +1126,7 @@ drm_output_state_alloc(struct drm_output
*output,
assert(state);
state->output = output;
+ state->dpms = WESTON_DPMS_OFF;
state->pending_state = pending_state;
if (pending_state)
wl_list_insert(&pending_state->output_list,
drm_output_state *state) free(state);
}
+/**
+ * Get output state to disable output
+ *
+ * Returns a pointer to an output_state object which can be used to disable
+ * an output (e.g. DPMS off).
+ *
+ */
+static struct drm_output_state *
+drm_output_get_disable_state(struct drm_pending_state *pending_state,
+ struct drm_output *output)
+{
+ struct drm_output_state *output_state;
+
+ output_state = drm_output_state_duplicate(output->state_cur,
+ pending_state,
+
DRM_OUTPUT_STATE_CLEAR_PLANES);
+ output_state->dpms = WESTON_DPMS_OFF;
+
+ return output_state;
+}
+
/**
* Allocate a new drm_pending_state
*
@@ -1272,6 +1299,8 @@ drm_pending_state_get_output(struct
drm_pending_state *pending_state, return NULL;
}
+static int drm_pending_state_apply(struct drm_pending_state *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,
drm_output_update_complete(struct drm_output *output, uint32_t flags,
unsigned int sec, unsigned int usec)
{
+ struct drm_backend *b =
to_drm_backend(output->base.compositor); struct drm_plane_state *ps;
struct timespec ts;
@@ -1300,6 +1330,13 @@ drm_output_update_complete(struct drm_output
*output, uint32_t flags, } else if (output->disable_pending) {
weston_output_disable(&output->base);
output->disable_pending = 0;
+ output->dpms_off_pending = 0;
+ return;
+ } else if (output->dpms_off_pending) {
+ struct drm_pending_state *pending =
drm_pending_state_alloc(b);
+ drm_output_get_disable_state(pending, output);
+ drm_pending_state_apply(pending);
+ output->dpms_off_pending = 0;
return;
}
@@ -1360,7 +1397,6 @@ drm_output_assign_state(struct drm_output_state
*state, }
}
-
static int
drm_view_transform_supported(struct weston_view *ev)
{
@@ -1653,37 +1689,20 @@ drm_waitvblank_pipe(struct drm_output *output)
}
static int
-drm_output_repaint(struct weston_output *output_base,
- pixman_region32_t *damage,
- void *repaint_data)
+drm_output_apply_state(struct drm_output_state *state)
{
- struct drm_pending_state *pending_state = repaint_data;
- struct drm_output_state *state = NULL;
- struct drm_output *output = to_drm_output(output_base);
- struct drm_backend *backend =
- to_drm_backend(output->base.compositor);
+ struct drm_output *output = state->output;
+ struct drm_backend *backend =
to_drm_backend(output->base.compositor); struct drm_plane
*scanout_plane = output->scanout_plane;
+ struct drm_property_info *dpms_prop =
+ &output->props_conn[WDRM_CONNECTOR_DPMS];
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
struct drm_mode *mode;
+ struct timespec now;
int ret = 0;
- if (output->disable_pending || output->destroy_pending)
- 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); -
-
/* If disable_planes is set then assign_planes() wasn't
* called for this render, so we could still have a stale
* cursor plane set up.
@@ -1694,10 +1713,46 @@ drm_output_repaint(struct weston_output
*output_base, output->cursor_plane->base.y = INT32_MIN;
}
- drm_output_render(state, damage);
- scanout_state = drm_output_state_get_plane(state,
scanout_plane);
- if (!scanout_state || !scanout_state->fb)
- goto err;
+ if (state->dpms != WESTON_DPMS_ON) {
+ wl_list_for_each(ps, &state->plane_list, link) {
+ p = ps->plane;
+ assert(ps->fb == NULL);
+ assert(ps->output == NULL);
+
+ if (p->type != WDRM_PLANE_TYPE_OVERLAY)
+ continue;
+
+
+ ret = drmModeSetPlane(backend->drm.fd,
p->plane_id,
+ 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0);
+ if (ret)
+ weston_log("drmModeSetPlane failed
disable: %m\n");
+ }
+
+ if (output->cursor_plane) {
+ ret = drmModeSetCursor(backend->drm.fd,
output->crtc_id,
+ 0, 0, 0);
+ if (ret)
+ weston_log("drmModeSetCursor failed
disable: %m\n");
+ }
+
+ ret = drmModeSetCrtc(backend->drm.fd,
output->crtc_id, 0, 0, 0,
+ &output->connector_id, 0, NULL);
+ if (ret)
%m\n"); +
+ drm_output_assign_state(state,
+
DRM_OUTPUT_STATE_UPDATE_SYNCHRONOUS);
+
weston_compositor_read_presentation_clock(output->base.compositor,
&now);
+ weston_output_finish_frame(&output->base,
+ &now,
+
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION); +
+ return 0;
+ }
+
+ scanout_state =
+ drm_output_state_get_existing_plane(state,
scanout_plane);
/* The legacy SetCrtc API doesn't allow us to do scaling,
and the
* legacy PageFlip API doesn't allow us to do clipping
weston_output *output_base, weston_log("set mode failed: %m\n");
goto err;
}
- output_base->set_dpms(output_base, WESTON_DPMS_ON);
}
if (drmModePageFlip(backend->drm.fd, output->crtc_id,
@@ -1790,13 +1844,101 @@ drm_output_repaint(struct weston_output
*output_base, }
}
+ if (dpms_prop->prop_id && state->dpms !=
output->state_cur->dpms) {
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+
output->connector_id,
+ dpms_prop->prop_id,
+ state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set
for %s\n",
+ output->base.name);
+ }
+ }
+
+ drm_output_assign_state(state,
DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS); +
return 0;
output->cursor_view = NULL;
-
drm_output_state_free(state);
+ return -1;
+}
+
+static int
+drm_pending_state_apply(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ uint32_t *unused;
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because
we've
+ * just started, or just been VT-switched in),
explicitly
+ * disable all the CRTCs we aren't using. This also
disables
+ * all connectors on these CRTCs, so we don't need
to do that
+ * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0,
NULL, 0,
+ NULL);
+ }
+
+ wl_list_for_each_safe(output_state, tmp,
&pending_state->output_list,
+ link) {
+ struct drm_output *output = output_state->output;
+ int ret;
+
+ ret = drm_output_apply_state(output_state);
+ if (ret != 0) {
+ weston_log("Couldn't apply state for output
%s\n",
+ output->base.name);
+ }
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+ drm_pending_state_free(pending_state);
+
+ return 0;
+}
+
+static int
+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 *output = to_drm_output(output_base);
+ struct drm_output_state *state;
+ struct drm_plane_state *scanout_state;
+
+ if (output->disable_pending || output->destroy_pending)
+ 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);
+ state->dpms = WESTON_DPMS_ON;
+
+ drm_output_render(state, damage);
+ scanout_state = drm_output_state_get_plane(state,
+
output->scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return 0;
+
+ drm_output_state_free(state);
return -1;
}
@@ -1922,6 +2064,9 @@ drm_output_update_msc(struct drm_output
*output, unsigned int seq) output->base.msc = (msc_hi << 32) + seq;
}
+static void
+drm_output_destroy(struct weston_output *base);
+
static void
vblank_handler(int fd, unsigned int frame, unsigned int sec,
unsigned int usec, void *data)
@@ -1944,9 +2089,6 @@ vblank_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec,
drm_output_update_complete(output, flags, sec, usec); }
-static void
-drm_output_destroy(struct weston_output *base);
-
static void
page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void *data)
@@ -2000,29 +2142,8 @@ 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;
- uint32_t *unused;
- if (b->state_invalid) {
- /* If we need to reset all our state (e.g. because
we've
- * just started, or just been VT-switched in),
explicitly
- * disable all the CRTCs we aren't using. This also
disables
- * all connectors on these CRTCs, so we don't need
to do that
- * separately with the pre-atomic API. */
- wl_array_for_each(unused, &b->unused_crtcs)
- drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0,
NULL, 0,
- NULL);
- }
-
- wl_list_for_each_safe(output_state, tmp,
&pending_state->output_list,
- link) {
- drm_output_assign_state(output_state,
-
DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
- }
-
- b->state_invalid = false;
-
- drm_pending_state_free(pending_state);
+ drm_pending_state_apply(pending_state);
b->repaint_data = NULL;
}
@@ -3026,8 +3147,6 @@ drm_output_find_special_plane(struct
drm_backend *b, struct drm_output *output, static void
drm_plane_destroy(struct drm_plane *plane)
{
- drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, 0,
0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0);
drm_plane_state_free(plane->state_cur, true);
drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
weston_plane_release(&plane->base);
@@ -3197,28 +3316,72 @@ drm_set_backlight(struct weston_output
*output_base, uint32_t value)
backlight_set_brightness(output->backlight, new_brightness); }
+/**
+ * Power output on or off
+ *
+ * The DPMS/power level of an output is used to switch it on or off. This
+ * is DRM's hook for doing so, which can called either as part of repaint,
+ * or independently of the repaint loop.
+ *
+ * If we are called as part of repaint, we simply set the relevant bit in
+ * state and return.
+ */
static void
drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
{
struct drm_output *output = to_drm_output(output_base);
- struct weston_compositor *ec = output_base->compositor;
- struct drm_backend *b = to_drm_backend(ec);
- struct drm_property_info *prop =
- &output->props_conn[WDRM_CONNECTOR_DPMS];
+ struct drm_backend *b =
to_drm_backend(output_base->compositor);
+ struct drm_pending_state *pending_state = b->repaint_data;
+ struct drm_output_state *state;
int ret;
- if (!prop->prop_id)
+ /* If we're being called during the repaint loop, then this
is
+ * simple: discard any previously-generated state, and
create a new
+ * state where we disable everything. When we come to flush,
this
+ * will be applied. */
+ if (pending_state) {
+ /* The repaint loop already sets DPMS on; we don't
need to
+ * explicitly set it on here, as it will already
happen
+ * whilst applying the repaint state. */
+ if (level == WESTON_DPMS_ON)
+ return;
+
+ state = drm_pending_state_get_output(pending_state,
output);
+ if (state)
+ drm_output_state_free(state);
+ state = drm_output_get_disable_state(pending_state,
output);
+ state->dpms = level;
+ return;
+ }
+
+ if (output->state_cur->dpms == level)
return;
- ret = drmModeConnectorSetProperty(b->drm.fd,
output->connector_id,
- prop->prop_id, level);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
+ /* As we throw everything away when disabling, just send us
back through
+ * a repaint cycle. */
+ if (level == WESTON_DPMS_ON) {
+ if (output->dpms_off_pending)
+ output->dpms_off_pending = 0;
+ weston_output_schedule_repaint(output_base);
return;
}
- output->dpms = level;
+ if (output->state_cur->dpms == WESTON_DPMS_OFF)
+ return;
+
+ /* If we've already got a request in the pipeline, then we
need to
+ * park our DPMS request until that request has quiesced. */
+ if (output->state_last) {
+ output->dpms_off_pending = 1;
+ return;
+ }
+
+ pending_state = drm_pending_state_alloc(b);
+ drm_output_state_duplicate(output->state_cur, pending_state,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+ ret = drm_pending_state_apply(pending_state);
The page flip from this call violates the assertion repaint_state ==
REPAINT_AWAITING_COMPLETION in weston_output_finish_frame(). The
FADE_OUT animation seems to mask this case, but I can trigger it by
directly calling weston_compositor_sleep().

As a workaround, I check for the repaint_status in
drm_output_update_complete() and skip the call to
weston_output_frame_finish(), but I don't really like, because it
exposes the compositor status to compositor-drm.

Michael
Post by Daniel Stone
+ if (ret != 0)
+ weston_log("drm_set_dpms: couldn't disable
output?\n"); }
static const char * const connector_type_names[] = {
@@ -4025,7 +4188,9 @@ drm_output_disable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
+ struct drm_pending_state *pending_state;
uint32_t *unused;
+ int ret;
if (output->page_flip_pending || output->vblank_pending) {
output->disable_pending = 1;
@@ -4038,11 +4203,13 @@ drm_output_disable(struct weston_output *base)
output->disable_pending = 0;
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 = drm_output_state_alloc(output, NULL);
+ pending_state = drm_pending_state_alloc(b);
+ drm_output_get_disable_state(pending_state, output);
+ ret = drm_pending_state_apply(pending_state);
+ if (ret) {
+ weston_log("Couldn't disable output output %s\n",
+ output->base.name);
+ }
unused = wl_array_add(&b->unused_connectors,
sizeof(*unused)); *unused = output->connector_id;
Pekka Paalanen
2018-01-17 13:27:43 UTC
Permalink
On Fri, 10 Nov 2017 14:53:56 +0100
Post by Philipp Zabel
Hi Daniel,
Post by Daniel Stone
Split repaint into two stages, as implied by the grouped-repaint
interface: drm_output_repaint generates the repaint state only, and
drm_repaint_flush applies it.
This also moves DPMS into output state. Previously, the usual way to
DPMS off was that repaint would be called and apply its state,
followed by set_dpms being called afterwards to push the DPMS state
separately. As this happens before the repaint_flush hook, with no
change to DPMS we would set DPMS off, then immediately re-enable the
output by posting the repaint. Not ideal.
Moving DPMS application at the same time complicates this patch, but I
couldn't find a way to split it; if we keep set_dpms before
begin_flush then we break DPMS off, or if we try to move DPMS to
output state before using the repaint flush, we get stuck as the
repaint hook generates an asynchronous state update, followed
immediately by set_dpms generating a synchronous state update.
---
libweston/compositor-drm.c | 313
++++++++++++++++++++++++++++++++++----------- 1 file changed, 240
insertions(+), 73 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 530b0fd5..6c1c6881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3197,28 +3316,72 @@ drm_set_backlight(struct weston_output
*output_base, uint32_t value)
backlight_set_brightness(output->backlight, new_brightness); }
+/**
+ * Power output on or off
+ *
+ * The DPMS/power level of an output is used to switch it on or off. This
+ * is DRM's hook for doing so, which can called either as part of repaint,
+ * or independently of the repaint loop.
+ *
+ * If we are called as part of repaint, we simply set the relevant bit in
+ * state and return.
+ */
static void
drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
{
struct drm_output *output = to_drm_output(output_base);
- struct weston_compositor *ec = output_base->compositor;
- struct drm_backend *b = to_drm_backend(ec);
- struct drm_property_info *prop =
- &output->props_conn[WDRM_CONNECTOR_DPMS];
+ struct drm_backend *b =
to_drm_backend(output_base->compositor);
+ struct drm_pending_state *pending_state = b->repaint_data;
+ struct drm_output_state *state;
int ret;
- if (!prop->prop_id)
+ /* If we're being called during the repaint loop, then this is
+ * simple: discard any previously-generated state, and
create a new
+ * state where we disable everything. When we come to flush, this
+ * will be applied. */
+ if (pending_state) {
+ /* The repaint loop already sets DPMS on; we don't
need to
+ * explicitly set it on here, as it will already
happen
+ * whilst applying the repaint state. */
+ if (level == WESTON_DPMS_ON)
+ return;
+
+ state = drm_pending_state_get_output(pending_state,
output);
+ if (state)
+ drm_output_state_free(state);
+ state = drm_output_get_disable_state(pending_state,
output);
+ state->dpms = level;
+ return;
+ }
+
+ if (output->state_cur->dpms == level)
return;
- ret = drmModeConnectorSetProperty(b->drm.fd,
output->connector_id,
- prop->prop_id, level);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
+ /* As we throw everything away when disabling, just send us back through
+ * a repaint cycle. */
+ if (level == WESTON_DPMS_ON) {
+ if (output->dpms_off_pending)
+ output->dpms_off_pending = 0;
+ weston_output_schedule_repaint(output_base);
return;
}
- output->dpms = level;
+ if (output->state_cur->dpms == WESTON_DPMS_OFF)
+ return;
+
+ /* If we've already got a request in the pipeline, then we
need to
+ * park our DPMS request until that request has quiesced. */
+ if (output->state_last) {
+ output->dpms_off_pending = 1;
+ return;
+ }
+
+ pending_state = drm_pending_state_alloc(b);
+ drm_output_state_duplicate(output->state_cur, pending_state,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+ ret = drm_pending_state_apply(pending_state);
The page flip from this call violates the assertion repaint_state ==
REPAINT_AWAITING_COMPLETION in weston_output_finish_frame(). The
FADE_OUT animation seems to mask this case, but I can trigger it by
directly calling weston_compositor_sleep().
Hi,

when reviewing v14, I have been going through old review comments, and
this one made me think hard. Turning an output DPMS off should not
generate a pageflip event, and since a fresh pending_state is being
allocated and applied here, there should be no other cause for a
pagelip event either.

Finally I realized, the above code is missing
output_state->dpms = WESTON_DPMS_OFF;

So it's not even turning off the output, it's just committing a normal
state with no framebuffers... changed?
Post by Philipp Zabel
As a workaround, I check for the repaint_status in
drm_output_update_complete() and skip the call to
weston_output_frame_finish(), but I don't really like, because it
exposes the compositor status to compositor-drm.
v14 has a similar workaround, and I wonder if that is unnecessary when
the turning off is properly synchronous.


Thanks,
pq

Daniel Stone
2017-09-26 17:15:57 UTC
Permalink
Use the same codepath, which has the added advantage of being able to
import dmabufs.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 62 +++++++++-------------------------------------
1 file changed, 12 insertions(+), 50 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index de80506a..44e47881 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1640,29 +1640,27 @@ 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 drm_plane *scanout_plane = output->scanout_plane;
struct drm_plane_state *state;
- struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
- struct gbm_bo *bo;
-
- /* Don't import buffers which span multiple outputs. */
- if (ev->output_mask != (1u << output->base.id))
- return NULL;
+ struct drm_fb *fb;

- /* We use GBM to import buffers. */
- if (b->gbm == NULL)
+ fb = drm_fb_get_from_view(output_state, ev);
+ if (!fb)
return NULL;

- if (buffer == NULL)
- return NULL;
- if (wl_shm_buffer_get(buffer->resource))
+ /* Can't change formats with just a pageflip */
+ if (fb->format->format != output->gbm_format) {
+ drm_fb_unref(fb);
return NULL;
+ }

state = drm_output_state_get_plane(output_state, scanout_plane);
- if (state->fb)
- goto err;
+ if (state->fb) {
+ drm_fb_unref(fb);
+ return NULL;
+ }

+ state->fb = fb;
state->output = output;
drm_plane_state_coords_for_view(state, ev);

@@ -1675,42 +1673,6 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
state->dest_h != (unsigned) output->base.current_mode->height)
goto err;

- if (ev->alpha != 1.0f)
- goto err;
-
- state = drm_output_state_get_plane(output_state, scanout_plane);
- if (state->fb) {
- /* If there is already a framebuffer on the scanout plane,
- * a client view has already been placed on the scanout
- * view. In that case, do not free or put back the state,
- * but just leave it in place and quietly exit. */
- return NULL;
- }
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
- buffer->resource, GBM_BO_USE_SCANOUT);
-
- /* Unable to use the buffer for scanout */
- if (!bo)
- goto err;
-
- state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
- BUFFER_CLIENT);
- if (!state->fb) {
- /* We need to explicitly destroy the BO. */
- gbm_bo_destroy(bo);
- goto err;
- }
-
- /* Can't change formats with just a pageflip */
- if (state->fb->format->format != output->gbm_format) {
- /* No need to destroy the GBM BO here, as it's now owned
- * by the FB. */
- goto err;
- }
-
- drm_fb_set_buffer(state->fb, buffer);
-
return &scanout_plane->base;

err:
--
2.14.1
Daniel Stone
2017-09-26 17:15:48 UTC
Permalink
Add support for using the atomic-modesetting API to apply output state.
Unlike previous series, this commit does not unflip sprites_are_broken,
until further work has been done with assign_planes to make it reliable.

Signed-off-by: Daniel Stone <***@collabora.com>
Co-authored-by: Pekka Paalanen <***@collabora.co.uk>
Co-authored-by: Louis-Francis Ratté-Boulianne <louis-francis.ratte-***@collabora.com>
Co-authored-by: Derek Foreman <***@collabora.co.uk>
---
configure.ac | 2 +-
libweston/compositor-drm.c | 507 ++++++++++++++++++++++++++++++++++++++-------
2 files changed, 434 insertions(+), 75 deletions(-)

diff --git a/configure.ac b/configure.ac
index 398db801..f09d0e04 100644
--- a/configure.ac
+++ b/configure.ac
@@ -200,7 +200,7 @@ AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes)
if test x$enable_drm_compositor = xyes; then
AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor])
PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm])
- PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.62],
+ PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
[AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
[AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3c75ec56..d04ba12f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -83,6 +83,35 @@
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif

+/**
+ * Represents the values of an enum-type KMS property
+ */
+struct drm_property_enum_info {
+ const char *name; /**< name as string (static, not freed) */
+ bool valid; /**< true if value is supported; ignore if false */
+ uint64_t value; /**< raw value */
+};
+
+/**
+ * Holds information on a DRM property, including its ID and the enum
+ * values it holds.
+ *
+ * DRM properties are allocated dynamically, and maintained as DRM objects
+ * within the normal object ID space; they thus do not have a stable ID
+ * to refer to. This includes enum values, which must be referred to by
+ * integer values, but these are not stable.
+ *
+ * drm_property_info allows a cache to be maintained where Weston can use
+ * enum values internally to refer to properties, with the mapping to DRM
+ * ID values being maintained internally.
+ */
+struct drm_property_info {
+ const char *name; /**< name as string (static, not freed) */
+ uint32_t prop_id; /**< KMS property object ID */
+ unsigned int num_enum_values; /**< number of enum values */
+ struct drm_property_enum_info *enum_values; /**< array of enum values */
+};
+
/**
* List of properties attached to DRM planes
*/
@@ -111,6 +140,36 @@ enum wdrm_plane_type {
WDRM_PLANE_TYPE__COUNT
};

+static struct drm_property_enum_info plane_type_enums[] = {
+ [WDRM_PLANE_TYPE_PRIMARY] = {
+ .name = "Primary",
+ },
+ [WDRM_PLANE_TYPE_OVERLAY] = {
+ .name = "Overlay",
+ },
+ [WDRM_PLANE_TYPE_CURSOR] = {
+ .name = "Cursor",
+ },
+};
+
+static const struct drm_property_info plane_props[] = {
+ [WDRM_PLANE_TYPE] = {
+ .name = "type",
+ .enum_values = plane_type_enums,
+ .num_enum_values = WDRM_PLANE_TYPE__COUNT,
+ },
+ [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
+ [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
+ [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
+ [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
+ [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
+ [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
+ [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
+ [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
+ [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
+ [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
+};
+
/**
* List of properties attached to a DRM connector
*/
@@ -121,33 +180,10 @@ enum wdrm_connector_property {
WDRM_CONNECTOR__COUNT
};

-/**
- * Represents the values of an enum-type KMS property
- */
-struct drm_property_enum_info {
- const char *name; /**< name as string (static, not freed) */
- bool valid; /**< true if value is supported; ignore if false */
- uint64_t value; /**< raw value */
-};
-
-/**
- * Holds information on a DRM property, including its ID and the enum
- * values it holds.
- *
- * DRM properties are allocated dynamically, and maintained as DRM objects
- * within the normal object ID space; they thus do not have a stable ID
- * to refer to. This includes enum values, which must be referred to by
- * integer values, but these are not stable.
- *
- * drm_property_info allows a cache to be maintained where Weston can use
- * enum values internally to refer to properties, with the mapping to DRM
- * ID values being maintained internally.
- */
-struct drm_property_info {
- const char *name; /**< name as string (static, not freed) */
- uint32_t prop_id; /**< KMS property object ID */
- unsigned int num_enum_values; /**< number of enum values */
- struct drm_property_enum_info *enum_values; /**< array of enum values */
+static const struct drm_property_info connector_props[] = {
+ [WDRM_CONNECTOR_EDID] = { .name = "EDID" },
+ [WDRM_CONNECTOR_DPMS] = { .name = "DPMS" },
+ [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
};

/**
@@ -159,6 +195,11 @@ enum wdrm_crtc_property {
WDRM_CRTC__COUNT
};

+static const struct drm_property_info crtc_props[] = {
+ [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", },
+ [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", },
+};
+
/**
* Mode for drm_output_state_duplicate.
*/
@@ -372,6 +413,7 @@ struct drm_output {

int vblank_pending;
int page_flip_pending;
+ int atomic_complete_pending;
int destroy_pending;
int disable_pending;
int dpms_off_pending;
@@ -1390,6 +1432,7 @@ drm_output_assign_state(struct drm_output_state *state,
enum drm_output_state_update_mode mode)
{
struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;

assert(!output->state_last);
@@ -1405,6 +1448,9 @@ drm_output_assign_state(struct drm_output_state *state,

output->state_cur = state;

+ if (b->atomic_modeset && mode == DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+ output->atomic_complete_pending = 1;
+
/* Replace state_cur on each affected plane with the new state, being
* careful to dispose of orphaned (but only orphaned) previous state.
* If the previous state is not orphaned (still has an output_state
@@ -1416,7 +1462,8 @@ drm_output_assign_state(struct drm_output_state *state,
drm_plane_state_free(plane->state_cur, true);
plane->state_cur = plane_state;

- if (mode != DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+ if (mode != DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS ||
+ b->atomic_modeset)
continue;

if (plane->type == WDRM_PLANE_TYPE_OVERLAY)
@@ -1716,7 +1763,7 @@ drm_waitvblank_pipe(struct drm_output *output)
}

static int
-drm_output_apply_state(struct drm_output_state *state)
+drm_output_apply_state_legacy(struct drm_output_state *state)
{
struct drm_output *output = state->output;
struct drm_backend *backend = to_drm_backend(output->base.compositor);
@@ -1892,6 +1939,307 @@ err:
return -1;
}

+#ifdef HAVE_DRM_ATOMIC
+static int
+crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_crtc_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &output->props_crtc[prop];
+ int ret;
+
+ if (!info)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->crtc_id, info->prop_id,
+ val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+connector_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_connector_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &output->props_conn[prop];
+ int ret;
+
+ if (!info)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->connector_id,
+ info->prop_id, val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
+ enum wdrm_plane_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &plane->props[prop];
+ int ret;
+
+ if (!info)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
+ val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode)
+{
+ int ret;
+
+ if (mode->blob_id)
+ return 0;
+
+ ret = drmModeCreatePropertyBlob(backend->drm.fd,
+ &mode->mode_info,
+ sizeof(mode->mode_info),
+ &mode->blob_id);
+ if (ret != 0)
+ weston_log("failed to create mode property blob: %m\n");
+
+ return ret;
+}
+
+static int
+drm_output_apply_state_atomic(struct drm_output_state *state,
+ drmModeAtomicReq *req,
+ uint32_t *flags)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ struct drm_plane_state *plane_state;
+ struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
+ int ret = 0;
+
+ if (state->dpms != output->state_cur->dpms)
+ *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+
+ if (state->dpms == WESTON_DPMS_ON) {
+ ret = drm_mode_ensure_blob(backend, current_mode);
+ if (ret != 0)
+ goto err;
+
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
+ current_mode->blob_id);
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
+ ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
+ output->crtc_id);
+ } else {
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
+ ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
+ 0);
+ }
+
+ if (ret != 0) {
+ weston_log("couldn't set atomic CRTC/connector state\n");
+ goto err;
+ }
+
+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ struct drm_plane *plane = plane_state->plane;
+
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_ID,
+ plane_state->fb ? plane_state->fb->fb_id : 0);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID,
+ plane_state->fb ? output->crtc_id : 0);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_X,
+ plane_state->src_x);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_Y,
+ plane_state->src_y);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_W,
+ plane_state->src_w);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_H,
+ plane_state->src_h);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_X,
+ plane_state->dest_x);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_Y,
+ plane_state->dest_y);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_W,
+ plane_state->dest_w);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_H,
+ plane_state->dest_h);
+
+ if (ret != 0) {
+ weston_log("couldn't set plane state\n");
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ drm_output_state_free(state);
+ return -1;
+}
+
+static int
+drm_pending_state_apply_atomic(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+ uint32_t flags = 0;
+ int ret = 0;
+
+ if (!req)
+ return -1;
+
+ if (b->state_invalid) {
+ uint32_t *unused;
+ int err;
+
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs and connectors we aren't using. */
+ wl_array_for_each(unused, &b->unused_connectors) {
+ struct drm_property_info infos[WDRM_CONNECTOR__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+
+ memset(infos, 0, sizeof(infos));
+
+ props = drmModeObjectGetProperties(b->drm.fd,
+ *unused,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ ret = -1;
+ continue;
+ }
+
+ drm_property_info_populate(b, connector_props, infos,
+ WDRM_CONNECTOR__COUNT,
+ props);
+ drmModeFreeObjectProperties(props);
+
+ info = &infos[WDRM_CONNECTOR_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_CONNECTOR_DPMS];
+ if (info->prop_id > 0)
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id,
+ DRM_MODE_DPMS_OFF);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_CONNECTOR__COUNT);
+ }
+
+ wl_array_for_each(unused, &b->unused_crtcs) {
+ struct drm_property_info infos[WDRM_CRTC__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+ uint64_t active;
+
+ memset(infos, 0, sizeof(infos));
+
+ /* We can't emit a disable on a CRTC that's already
+ * off, as the kernel will refuse to generate an event
+ * for an off->off state and fail the commit.
+ */
+ props = drmModeObjectGetProperties(b->drm.fd,
+ *unused,
+ DRM_MODE_OBJECT_CRTC);
+ if (!props) {
+ ret = -1;
+ continue;
+ }
+
+ drm_property_info_populate(b, crtc_props, infos,
+ WDRM_CRTC__COUNT,
+ props);
+
+ info = &infos[WDRM_CRTC_ACTIVE];
+ active = drm_property_get_value(info, props, 0);
+ drmModeFreeObjectProperties(props);
+ if (active == 0) {
+ drm_property_info_free(infos, WDRM_CRTC__COUNT);
+ continue;
+ }
+
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_CRTC_MODE_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_CRTC__COUNT);
+ }
+
+#if 0
+ wl_array_for_each(unused, &b->unused_planes) {
+ struct drm_property_info infos[WDRM_PLANE__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+
+ memset(infos, 0, sizeof(infos));
+
+ drm_property_info_populate(b, props_plane, infos,
+ WDRM_PLANE__COUNT, props);
+ drmModeFreeObjectProperties(props);
+
+ info = &infos[WDRM_PLANE_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_PLANE_FB_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_PLANE__COUNT);
+ }
+#endif
+
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ }
+
+ wl_list_for_each(output_state, &pending_state->output_list, link)
+ ret |= drm_output_apply_state_atomic(output_state, req, &flags);
+
+ if (ret != 0) {
+ weston_log("atomic: couldn't compile atomic state\n");
+ goto out;
+ }
+
+ flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
+ if (ret != 0) {
+ weston_log("atomic: couldn't commit new state: %m\n");
+ goto out;
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link) {
+ drm_output_assign_state(output_state,
+ DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+
+out:
+ drmModeAtomicFree(req);
+ return ret;
+}
+#endif
+
static int
drm_pending_state_apply(struct drm_pending_state *pending_state)
{
@@ -1899,6 +2247,11 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
struct drm_output_state *output_state, *tmp;
uint32_t *unused;

+#ifdef HAVE_DRM_ATOMIC
+ if (b->atomic_modeset)
+ return drm_pending_state_apply_atomic(pending_state);
+#endif
+
if (b->state_invalid) {
/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
@@ -1915,7 +2268,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
struct drm_output *output = output_state->output;
int ret;

- ret = drm_output_apply_state(output_state);
+ ret = drm_output_apply_state_legacy(output_state);
if (ret != 0) {
weston_log("Couldn't apply state for output %s\n",
output->base.name);
@@ -2075,9 +2428,12 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
struct drm_plane_state *ps = (struct drm_plane_state *) data;
struct drm_output_state *os = ps->output_state;
struct drm_output *output = os->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;

+ assert(!b->atomic_modeset);
+
drm_output_update_msc(output, frame);
output->vblank_pending--;
assert(output->vblank_pending >= 0);
@@ -2095,12 +2451,14 @@ page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void *data)
{
struct drm_output *output = data;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;

drm_output_update_msc(output, frame);

+ assert(!b->atomic_modeset);
assert(output->page_flip_pending);
output->page_flip_pending = 0;

@@ -2164,6 +2522,33 @@ drm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data)
b->repaint_data = NULL;
}

+#ifdef HAVE_DRM_ATOMIC
+static void
+atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, unsigned int crtc_id, void *data)
+{
+ struct drm_backend *b = data;
+ struct drm_output *output = drm_output_find_by_crtc(b, crtc_id);
+ uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+ /* During the initial modeset, we can disable CRTCs which we don't
+ * actually handle during normal operation; this will give us events
+ * for unknown outputs. Ignore them. */
+ if (!output)
+ return;
+
+ drm_output_update_msc(output, frame);
+
+ assert(b->atomic_modeset);
+ assert(output->atomic_complete_pending);
+ output->atomic_complete_pending = 0;
+
+ drm_output_update_complete(output, flags, sec, usec);
+}
+#endif
+
static uint32_t
drm_output_check_plane_format(struct drm_plane *p,
struct weston_view *ev, struct gbm_bo *bo)
@@ -2795,11 +3180,19 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
static int
on_drm_input(int fd, uint32_t mask, void *data)
{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = data;
+#endif
drmEventContext evctx;

memset(&evctx, 0, sizeof evctx);
- evctx.version = 2;
- evctx.page_flip_handler = page_flip_handler;
+ evctx.version = 3;
+#ifdef HAVE_DRM_ATOMIC
+ if (b->atomic_modeset)
+ evctx.page_flip_handler2 = atomic_flip_handler;
+ else
+#endif
+ evctx.page_flip_handler = page_flip_handler;
evctx.vblank_handler = vblank_handler;
drmHandleEvent(fd, &evctx);

@@ -2847,8 +3240,11 @@ init_kms_caps(struct drm_backend *b)
b->universal_planes ? "supports" : "does not support");

#ifdef HAVE_DRM_ATOMIC
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
+ if (ret != 0)
+ cap = 0;
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
- b->atomic_modeset = (ret == 0);
+ b->atomic_modeset = ((ret == 0) && (cap == 1));
#endif
weston_log("DRM: %s atomic modesetting\n",
b->atomic_modeset ? "supports" : "does not support");
@@ -2979,35 +3375,6 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
drmModeObjectProperties *props;
int num_formats = (kplane) ? kplane->count_formats : 1;

- static struct drm_property_enum_info plane_type_enums[] = {
- [WDRM_PLANE_TYPE_PRIMARY] = {
- .name = "Primary",
- },
- [WDRM_PLANE_TYPE_OVERLAY] = {
- .name = "Overlay",
- },
- [WDRM_PLANE_TYPE_CURSOR] = {
- .name = "Cursor",
- },
- };
- static const struct drm_property_info plane_props[] = {
- [WDRM_PLANE_TYPE] = {
- .name = "type",
- .enum_values = plane_type_enums,
- .num_enum_values = WDRM_PLANE_TYPE__COUNT,
- },
- [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
- [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
- [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
- [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
- [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
- [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
- [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
- [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
- [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
- [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
- };
-
plane = zalloc(sizeof(*plane) +
(sizeof(uint32_t) * num_formats));
if (!plane) {
@@ -4168,7 +4535,8 @@ drm_output_destroy(struct weston_output *base)
struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_mode *drm_mode, *next;

- if (output->page_flip_pending || output->vblank_pending) {
+ if (output->page_flip_pending || output->vblank_pending ||
+ output->atomic_complete_pending) {
output->destroy_pending = 1;
weston_log("destroy output while page flip pending\n");
return;
@@ -4209,7 +4577,8 @@ drm_output_disable(struct weston_output *base)
uint32_t *unused;
int ret;

- if (output->page_flip_pending || output->vblank_pending) {
+ if (output->page_flip_pending || output->vblank_pending ||
+ output->atomic_complete_pending) {
output->disable_pending = 1;
return -1;
}
@@ -4302,16 +4671,6 @@ create_output_for_connector(struct drm_backend *b,
drmModeCrtcPtr origcrtc;
int i;

- static const struct drm_property_info connector_props[] = {
- [WDRM_CONNECTOR_EDID] = { .name = "EDID" },
- [WDRM_CONNECTOR_DPMS] = { .name = "DPMS" },
- [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
- };
- static const struct drm_property_info crtc_props[] = {
- [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", },
- [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", },
- };
-
i = find_crtc_for_connector(b, resources, connector);
if (i < 0) {
weston_log("No usable crtc/encoder pair for connector.\n");
--
2.14.1
Pekka Paalanen
2017-10-04 11:34:55 UTC
Permalink
On Tue, 26 Sep 2017 18:15:48 +0100
Post by Daniel Stone
Add support for using the atomic-modesetting API to apply output state.
Unlike previous series, this commit does not unflip sprites_are_broken,
until further work has been done with assign_planes to make it reliable.
---
configure.ac | 2 +-
libweston/compositor-drm.c | 507 ++++++++++++++++++++++++++++++++++++++-------
2 files changed, 434 insertions(+), 75 deletions(-)
Hi Daniel!
Post by Daniel Stone
+#ifdef HAVE_DRM_ATOMIC
+static int
+crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_crtc_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &output->props_crtc[prop];
+ int ret;
+
+ if (!info)
+ return -1;
Should not this and the two functions below check that the prop_id was
actually found?

What happens if one uses property ID 0?
Post by Daniel Stone
+
+ ret = drmModeAtomicAddProperty(req, output->crtc_id, info->prop_id,
+ val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+connector_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_connector_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &output->props_conn[prop];
+ int ret;
+
+ if (!info)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->connector_id,
+ info->prop_id, val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
+ enum wdrm_plane_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &plane->props[prop];
+ int ret;
+
+ if (!info)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
+ val);
+ return (ret <= 0) ? -1 : 0;
+}
+static int
+drm_pending_state_apply_atomic(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+ uint32_t flags = 0;
+ int ret = 0;
+
+ if (!req)
+ return -1;
+
+ if (b->state_invalid) {
+ uint32_t *unused;
+ int err;
+
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs and connectors we aren't using. */
+ wl_array_for_each(unused, &b->unused_connectors) {
+ struct drm_property_info infos[WDRM_CONNECTOR__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+
+ memset(infos, 0, sizeof(infos));
memset would be easier done with
struct drm_property_info infos[WDRM_CONNECTOR__COUNT] = {};
Post by Daniel Stone
+
+ props = drmModeObjectGetProperties(b->drm.fd,
+ *unused,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ ret = -1;
+ continue;
+ }
+
+ drm_property_info_populate(b, connector_props, infos,
+ WDRM_CONNECTOR__COUNT,
+ props);
+ drmModeFreeObjectProperties(props);
+
+ info = &infos[WDRM_CONNECTOR_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_CONNECTOR_DPMS];
+ if (info->prop_id > 0)
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id,
+ DRM_MODE_DPMS_OFF);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_CONNECTOR__COUNT);
+ }
+
+ wl_array_for_each(unused, &b->unused_crtcs) {
+ struct drm_property_info infos[WDRM_CRTC__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+ uint64_t active;
+
+ memset(infos, 0, sizeof(infos));
+
+ /* We can't emit a disable on a CRTC that's already
+ * off, as the kernel will refuse to generate an event
+ * for an off->off state and fail the commit.
+ */
+ props = drmModeObjectGetProperties(b->drm.fd,
+ *unused,
+ DRM_MODE_OBJECT_CRTC);
+ if (!props) {
+ ret = -1;
+ continue;
+ }
+
+ drm_property_info_populate(b, crtc_props, infos,
+ WDRM_CRTC__COUNT,
+ props);
+
+ info = &infos[WDRM_CRTC_ACTIVE];
+ active = drm_property_get_value(info, props, 0);
+ drmModeFreeObjectProperties(props);
+ if (active == 0) {
+ drm_property_info_free(infos, WDRM_CRTC__COUNT);
+ continue;
+ }
+
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_CRTC_MODE_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_CRTC__COUNT);
+ }
+
+#if 0
+ wl_array_for_each(unused, &b->unused_planes) {
I don't think we would ever need a unused_planes array since we create
a drm_plane for them all, right?

Should just go through the list of drm_planes.
Post by Daniel Stone
+ struct drm_property_info infos[WDRM_PLANE__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+
+ memset(infos, 0, sizeof(infos));
+
+ drm_property_info_populate(b, props_plane, infos,
+ WDRM_PLANE__COUNT, props);
+ drmModeFreeObjectProperties(props);
+
+ info = &infos[WDRM_PLANE_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_PLANE_FB_ID];
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_PLANE__COUNT);
+ }
+#endif
+
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ }
+
+ wl_list_for_each(output_state, &pending_state->output_list, link)
+ ret |= drm_output_apply_state_atomic(output_state, req, &flags);
+
+ if (ret != 0) {
+ weston_log("atomic: couldn't compile atomic state\n");
+ goto out;
+ }
+
+ flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
+ if (ret != 0) {
+ weston_log("atomic: couldn't commit new state: %m\n");
+ goto out;
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link) {
+ drm_output_assign_state(output_state,
+ DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+
+ drmModeAtomicFree(req);
Missing drm_pending_state_free()?
Post by Daniel Stone
+ return ret;
+}
+#endif
+
static int
drm_pending_state_apply(struct drm_pending_state *pending_state)
{
Otherwise looks good to me.


Thanks,
pq
Daniel Stone
2017-09-26 17:15:47 UTC
Permalink
Much like we already have to_drm_output and to_drm_backend.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2a72456d..3c75ec56 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -479,6 +479,12 @@ drm_output_pageflip_timer_create(struct drm_output *output)
return 0;
}

+static inline struct drm_mode *
+to_drm_mode(struct weston_mode *base)
+{
+ return container_of(base, struct drm_mode, base);
+}
+
/**
* Get the current value of a KMS property
*
@@ -1788,7 +1794,7 @@ drm_output_apply_state(struct drm_output_state *state)
assert(scanout_state->dest_w == scanout_state->src_w >> 16);
assert(scanout_state->dest_h == scanout_state->src_h >> 16);

- mode = container_of(output->base.current_mode, struct drm_mode, base);
+ mode = to_drm_mode(output->base.current_mode);
if (backend->state_invalid || !scanout_plane->state_cur->fb ||
scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
--
2.14.1
Pekka Paalanen
2017-10-04 09:27:48 UTC
Permalink
On Tue, 26 Sep 2017 18:15:47 +0100
Post by Daniel Stone
Much like we already have to_drm_output and to_drm_backend.
---
libweston/compositor-drm.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2a72456d..3c75ec56 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -479,6 +479,12 @@ drm_output_pageflip_timer_create(struct drm_output *output)
return 0;
}
+static inline struct drm_mode *
+to_drm_mode(struct weston_mode *base)
+{
+ return container_of(base, struct drm_mode, base);
+}
+
/**
* Get the current value of a KMS property
*
@@ -1788,7 +1794,7 @@ drm_output_apply_state(struct drm_output_state *state)
assert(scanout_state->dest_w == scanout_state->src_w >> 16);
assert(scanout_state->dest_h == scanout_state->src_h >> 16);
- mode = container_of(output->base.current_mode, struct drm_mode, base);
+ mode = to_drm_mode(output->base.current_mode);
if (backend->state_invalid || !scanout_plane->state_cur->fb ||
scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
Hi,

as mentioned in IRC, this misses the cast in choose_mode(). That fixed:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>

choose_mode() also has a sneaky secret cast in the wl_list_for_each()
statement. It does not do any harm in this particular case, but I have
tried to weed them out for drm_output casts because in the far future
we might have more than one type of outputs which makes the cast
without a type check dangerous. A type check will be easy to add in
to_drm_output().


Thanks,
pq
Daniel Stone
2017-09-26 17:15:56 UTC
Permalink
... in order to be able to use it from scanout as well.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 221 ++++++++++++++++++++++++---------------------
1 file changed, 119 insertions(+), 102 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5413ff3f..de80506a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1210,6 +1210,109 @@ drm_plane_state_coords_for_view(struct drm_plane_state *state,
state->src_h = wl_fixed_from_double(syf2 - syf1) << 8;
}

+static bool
+drm_view_is_opaque(struct weston_view *ev)
+{
+ pixman_region32_t r;
+ bool ret = false;
+
+ /* We can scanout an ARGB buffer if the surface's
+ * opaque region covers the whole output, but we have
+ * to use XRGB as the KMS format code. */
+ pixman_region32_init_rect(&r, 0, 0,
+ ev->surface->width,
+ ev->surface->height);
+ pixman_region32_subtract(&r, &r, &ev->surface->opaque);
+
+ if (!pixman_region32_not_empty(&r))
+ ret = true;
+
+ pixman_region32_fini(&r);
+
+ return ret;
+}
+
+static struct drm_fb *
+drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ struct linux_dmabuf_buffer *dmabuf;
+ struct drm_fb *fb;
+ struct gbm_bo *bo;
+
+ /* Don't import buffers which span multiple outputs. */
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
+ if (ev->alpha != 1.0f)
+ return NULL;
+
+ if (!buffer)
+ return NULL;
+
+ if (wl_shm_buffer_get(buffer->resource))
+ return NULL;
+
+ if (!b->gbm)
+ return NULL;
+
+ dmabuf = linux_dmabuf_buffer_get(buffer->resource);
+ if (dmabuf) {
+#ifdef HAVE_GBM_FD_IMPORT
+ /* XXX: TODO:
+ *
+ * Use AddFB2 directly, do not go via GBM.
+ * Add support for multiplanar formats.
+ * Both require refactoring in the DRM-backend to
+ * support a mix of gbm_bos and drmfbs.
+ */
+ struct gbm_import_fd_data gbm_dmabuf = {
+ .fd = dmabuf->attributes.fd[0],
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .stride = dmabuf->attributes.stride[0],
+ .format = dmabuf->attributes.format
+ };
+
+ /* XXX: TODO:
+ *
+ * Currently the buffer is rejected if any dmabuf attribute
+ * flag is set. This keeps us from passing an inverted /
+ * interlaced / bottom-first buffer (or any other type that may
+ * be added in the future) through to an overlay. Ultimately,
+ * these types of buffers should be handled through buffer
+ * transforms and not as spot-checks requiring specific
+ * knowledge. */
+ if (dmabuf->attributes.n_planes != 1 ||
+ dmabuf->attributes.offset[0] != 0 ||
+ dmabuf->attributes.flags)
+ goto err;
+
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
+ GBM_BO_USE_SCANOUT);
+#else
+ return NULL;
+#endif
+ } else {
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
+ buffer->resource, GBM_BO_USE_SCANOUT);
+ }
+
+ if (!bo)
+ return NULL;
+
+ fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev), BUFFER_CLIENT);
+ if (!fb) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ drm_fb_set_buffer(fb, buffer);
+ return fb;
+}
+
/**
* Return a plane state from a drm_output_state.
*/
@@ -1532,28 +1635,6 @@ drm_output_assign_state(struct drm_output_state *state,
}
}

-static bool
-drm_view_is_opaque(struct weston_view *ev)
-{
- pixman_region32_t r;
- bool ret = false;
-
- /* We can scanout an ARGB buffer if the surface's
- * opaque region covers the whole output, but we have
- * to use XRGB as the KMS format code. */
- pixman_region32_init_rect(&r, 0, 0,
- ev->surface->width,
- ev->surface->height);
- pixman_region32_subtract(&r, &r, &ev->surface->opaque);
-
- if (!pixman_region32_not_empty(&r))
- ret = true;
-
- pixman_region32_fini(&r);
-
- return ret;
-}
-
static struct weston_plane *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev)
@@ -2588,31 +2669,16 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
struct drm_backend *b = to_drm_backend(ec);
- struct wl_resource *buffer_resource;
struct drm_plane *p;
struct drm_plane_state *state = NULL;
- struct linux_dmabuf_buffer *dmabuf;
- struct gbm_bo *bo;
+ struct drm_fb *fb;
unsigned int i;

if (b->sprites_are_broken)
return NULL;

- /* Don't import buffers which span multiple outputs. */
- if (ev->output_mask != (1u << output->base.id))
- return NULL;
-
- /* We can only import GBM buffers. */
- if (b->gbm == NULL)
- return NULL;
-
- if (ev->surface->buffer_ref.buffer == NULL)
- return NULL;
- buffer_resource = ev->surface->buffer_ref.buffer->resource;
- if (wl_shm_buffer_get(buffer_resource))
- return NULL;
-
- if (ev->alpha != 1.0f)
+ fb = drm_fb_get_from_view(output_state, ev);
+ if (!fb)
return NULL;

wl_list_for_each(p, &b->plane_list, link) {
@@ -2627,6 +2693,14 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
if (p->state_cur->output && p->state_cur->output != output)
continue;

+ /* Check whether the format is supported */
+ for (i = 0; i < p->count_formats; i++) {
+ if (p->formats[i] == fb->format->format)
+ break;
+ }
+ if (i == p->count_formats)
+ continue;
+
state = drm_output_state_get_plane(output_state, p);
if (state->fb) {
state = NULL;
@@ -2637,76 +2711,19 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
}

/* No sprites available */
- if (!state)
+ if (!state) {
+ drm_fb_unref(fb);
return NULL;
+ }

+ state->fb = fb;
state->output = output;
+
drm_plane_state_coords_for_view(state, ev);
if (state->src_w != state->dest_w << 16 ||
state->src_h != state->dest_h << 16)
goto err;

- if ((dmabuf = linux_dmabuf_buffer_get(buffer_resource))) {
-#ifdef HAVE_GBM_FD_IMPORT
- /* XXX: TODO:
- *
- * Use AddFB2 directly, do not go via GBM.
- * Add support for multiplanar formats.
- * Both require refactoring in the DRM-backend to
- * support a mix of gbm_bos and drmfbs.
- */
- struct gbm_import_fd_data gbm_dmabuf = {
- .fd = dmabuf->attributes.fd[0],
- .width = dmabuf->attributes.width,
- .height = dmabuf->attributes.height,
- .stride = dmabuf->attributes.stride[0],
- .format = dmabuf->attributes.format
- };
-
- /* XXX: TODO:
- *
- * Currently the buffer is rejected if any dmabuf attribute
- * flag is set. This keeps us from passing an inverted /
- * interlaced / bottom-first buffer (or any other type that may
- * be added in the future) through to an overlay. Ultimately,
- * these types of buffers should be handled through buffer
- * transforms and not as spot-checks requiring specific
- * knowledge. */
- if (dmabuf->attributes.n_planes != 1 ||
- dmabuf->attributes.offset[0] != 0 ||
- dmabuf->attributes.flags)
- goto err;
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
- GBM_BO_USE_SCANOUT);
-#else
- goto err;
-#endif
- } else {
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
- buffer_resource, GBM_BO_USE_SCANOUT);
- }
- if (!bo)
- goto err;
-
- state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
- BUFFER_CLIENT);
- if (!state->fb) {
- /* Destroy the BO as we've allocated it, but it won't yet
- * be deallocated by the state. */
- gbm_bo_destroy(bo);
- goto err;
- }
-
- /* Check whether the format is supported */
- for (i = 0; i < p->count_formats; i++)
- if (p->formats[i] == state->fb->format->format)
- break;
- if (i == p->count_formats)
- goto err;
-
- drm_fb_set_buffer(state->fb, ev->surface->buffer_ref.buffer);
-
return &p->base;

err:
--
2.14.1
Pekka Paalanen
2017-09-28 11:12:14 UTC
Permalink
On Tue, 26 Sep 2017 18:15:56 +0100
Post by Daniel Stone
... in order to be able to use it from scanout as well.
---
libweston/compositor-drm.c | 221 ++++++++++++++++++++++++---------------------
1 file changed, 119 insertions(+), 102 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5413ff3f..de80506a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
+static struct drm_fb *
+drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ struct linux_dmabuf_buffer *dmabuf;
+ struct drm_fb *fb;
+ struct gbm_bo *bo;
+
+ /* Don't import buffers which span multiple outputs. */
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
+ if (ev->alpha != 1.0f)
+ return NULL;
+
+ if (!buffer)
+ return NULL;
+
+ if (wl_shm_buffer_get(buffer->resource))
+ return NULL;
+
+ if (!b->gbm)
+ return NULL;
+
+ dmabuf = linux_dmabuf_buffer_get(buffer->resource);
+ if (dmabuf) {
+#ifdef HAVE_GBM_FD_IMPORT
+ *
+ * Use AddFB2 directly, do not go via GBM.
+ * Add support for multiplanar formats.
+ * Both require refactoring in the DRM-backend to
+ * support a mix of gbm_bos and drmfbs.
+ */
+ struct gbm_import_fd_data gbm_dmabuf = {
+ .fd = dmabuf->attributes.fd[0],
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .stride = dmabuf->attributes.stride[0],
+ .format = dmabuf->attributes.format
+ };
+
+ *
+ * Currently the buffer is rejected if any dmabuf attribute
+ * flag is set. This keeps us from passing an inverted /
+ * interlaced / bottom-first buffer (or any other type that may
+ * be added in the future) through to an overlay. Ultimately,
+ * these types of buffers should be handled through buffer
+ * transforms and not as spot-checks requiring specific
+ * knowledge. */
+ if (dmabuf->attributes.n_planes != 1 ||
+ dmabuf->attributes.offset[0] != 0 ||
+ dmabuf->attributes.flags)
+ goto err;
Hi,

this breaks the build, as 'err' is no longer defined.
Post by Daniel Stone
+
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
+ GBM_BO_USE_SCANOUT);
+#else
+ return NULL;
+#endif
+ } else {
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
+ buffer->resource, GBM_BO_USE_SCANOUT);
+ }
+
+ if (!bo)
+ return NULL;
+
+ fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev), BUFFER_CLIENT);
+ if (!fb) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ drm_fb_set_buffer(fb, buffer);
+ return fb;
+}
+
Thanks,
pq
Daniel Stone
2017-09-26 17:16:02 UTC
Permalink
Nothing in this loop reorders views within the compositor's view_list.

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 72c9e5a2..3b22faf1 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2980,7 +2980,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state;
struct drm_plane_state *plane_state;
- struct weston_view *ev, *next;
+ struct weston_view *ev;
pixman_region32_t overlap, surface_overlap;
struct weston_plane *primary, *next_plane;

@@ -3005,7 +3005,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
pixman_region32_init(&overlap);
primary = &output_base->compositor->primary_plane;

- wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) {
+ wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct weston_surface *es = ev->surface;

/* Test whether this buffer can ever go into a plane:
--
2.14.1
Daniel Stone
2017-09-26 17:15:59 UTC
Permalink
We currently do the same thing in two places, and will soon have a
third.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 93 ++++++++++++++++++++++++----------------------
1 file changed, 48 insertions(+), 45 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e03f8f7e..7557ef55 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -283,7 +283,10 @@ struct drm_fb {

int refcnt;

- uint32_t fb_id, stride, handle, size;
+ uint32_t fb_id, size;
+ uint32_t handles[4];
+ uint32_t strides[4];
+ uint32_t offsets[4];
const struct pixel_format_info *format;
int width, height;
int fd;
@@ -801,7 +804,7 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
munmap(fb->map, fb->size);

memset(&destroy_arg, 0, sizeof(destroy_arg));
- destroy_arg.handle = fb->handle;
+ destroy_arg.handle = fb->handles[0];
drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);

drm_fb_destroy(fb);
@@ -817,6 +820,32 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
drm_fb_destroy(fb);
}

+static int
+drm_fb_addfb(struct drm_fb *fb)
+{
+ int ret;
+
+ ret = drmModeAddFB2(fb->fd, fb->width, fb->height, fb->format->format,
+ fb->handles, fb->strides, fb->offsets, &fb->fb_id,
+ 0);
+ if (ret == 0)
+ return 0;
+
+ /* Legacy AddFB can't always infer the format from depth/bpp alone, so
+ * check if our format is one of the lucky ones. */
+ if (!fb->format->depth || !fb->format->bpp)
+ return ret;
+
+ /* Cannot fall back to AddFB for multi-planar formats either. */
+ if (fb->handles[1] || fb->handles[2] || fb->handles[3])
+ return ret;
+
+ ret = drmModeAddFB(fb->fd, fb->width, fb->height,
+ fb->format->depth, fb->format->bpp,
+ fb->strides[0], fb->handles[0], &fb->fb_id);
+ return ret;
+}
+
static struct drm_fb *
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
uint32_t format)
@@ -827,12 +856,10 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
struct drm_mode_map_dumb map_arg;
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };

fb = zalloc(sizeof *fb);
if (!fb)
return NULL;
-
fb->refcnt = 1;

fb->format = pixel_format_get_info(format);
@@ -858,32 +885,20 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
goto err_fb;

fb->type = BUFFER_PIXMAN_DUMB;
- fb->handle = create_arg.handle;
- fb->stride = create_arg.pitch;
+ fb->handles[0] = create_arg.handle;
+ fb->strides[0] = create_arg.pitch;
fb->size = create_arg.size;
fb->width = width;
fb->height = height;
fb->fd = b->drm.fd;

- ret = -1;
-
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(b->drm.fd, width, height, fb->format->format,
- handles, pitches, offsets, &fb->fb_id, 0);
- if (ret) {
- ret = drmModeAddFB(b->drm.fd, width, height,
- fb->format->depth, fb->format->bpp,
- fb->stride, fb->handle, &fb->fb_id);
- }
-
- if (ret)
+ if (drm_fb_addfb(fb) != 0) {
+ weston_log("failed to create kms fb: %m\n");
goto err_bo;
+ }

memset(&map_arg, 0, sizeof map_arg);
- map_arg.handle = fb->handle;
+ map_arg.handle = fb->handles[0];
ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
if (ret)
goto err_add_fb;
@@ -918,8 +933,6 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
bool is_opaque, enum drm_fb_type type)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
- int ret;

if (fb) {
assert(fb->type == type);
@@ -936,10 +949,10 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,

fb->width = gbm_bo_get_width(bo);
fb->height = gbm_bo_get_height(bo);
- fb->stride = gbm_bo_get_stride(bo);
- fb->handle = gbm_bo_get_handle(bo).u32;
+ fb->strides[0] = gbm_bo_get_stride(bo);
+ fb->handles[0] = gbm_bo_get_handle(bo).u32;
fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
- fb->size = fb->stride * fb->height;
+ fb->size = fb->strides[0] * fb->height;
fb->fd = backend->drm.fd;

if (!fb->format) {
@@ -959,19 +972,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
goto err_free;
}

- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
- fb->format->format, handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret && fb->format->depth && fb->format->bpp)
- ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
- fb->format->depth, fb->format->bpp,
- fb->stride, fb->handle, &fb->fb_id);
-
- if (ret) {
+ if (drm_fb_addfb(fb) != 0) {
weston_log("failed to create kms fb: %m\n");
goto err_free;
}
@@ -1895,8 +1896,10 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
assert(scanout_state->dest_h == scanout_state->src_h >> 16);

mode = to_drm_mode(output->base.current_mode);
- if (backend->state_invalid || !scanout_plane->state_cur->fb ||
- scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
+ if (backend->state_invalid ||
+ !scanout_plane->state_cur->fb ||
+ scanout_plane->state_cur->fb->strides[0] !=
+ scanout_state->fb->strides[0]) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
0, 0,
@@ -3894,7 +3897,7 @@ drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
output->image[i] =
pixman_image_create_bits(pixman_format, w, h,
output->dumb[i]->map,
- output->dumb[i]->stride);
+ output->dumb[i]->strides[0]);
if (!output->image[i])
goto err;
}
@@ -5103,7 +5106,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
return;

ret = drmPrimeHandleToFD(b->drm.fd,
- output->scanout_plane->state_cur->fb->handle,
+ output->scanout_plane->state_cur->fb->handles[0],
DRM_CLOEXEC, &fd);
if (ret) {
weston_log("[libva recorder] "
@@ -5112,7 +5115,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
}

ret = vaapi_recorder_frame(output->recorder, fd,
- output->scanout_plane->state_cur->fb->stride);
+ output->scanout_plane->state_cur->fb->strides[0]);
if (ret < 0) {
weston_log("[libva recorder] aborted: %m\n");
recorder_destroy(output);
--
2.14.1
Prabhu S
2017-09-26 17:25:16 UTC
Permalink
Any plan to support for drmModeAddFB2WithModifiers and query preferred
FB modifier?
Post by Daniel Stone
We currently do the same thing in two places, and will soon have a
third.
---
libweston/compositor-drm.c | 93 ++++++++++++++++++++++++------
----------------
1 file changed, 48 insertions(+), 45 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e03f8f7e..7557ef55 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -283,7 +283,10 @@ struct drm_fb {
int refcnt;
- uint32_t fb_id, stride, handle, size;
+ uint32_t fb_id, size;
+ uint32_t handles[4];
+ uint32_t strides[4];
+ uint32_t offsets[4];
const struct pixel_format_info *format;
int width, height;
int fd;
@@ -801,7 +804,7 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
munmap(fb->map, fb->size);
memset(&destroy_arg, 0, sizeof(destroy_arg));
- destroy_arg.handle = fb->handle;
+ destroy_arg.handle = fb->handles[0];
drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
drm_fb_destroy(fb);
@@ -817,6 +820,32 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
drm_fb_destroy(fb);
}
+static int
+drm_fb_addfb(struct drm_fb *fb)
+{
+ int ret;
+
+ ret = drmModeAddFB2(fb->fd, fb->width, fb->height,
fb->format->format,
+ fb->handles, fb->strides, fb->offsets,
&fb->fb_id,
+ 0);
+ if (ret == 0)
+ return 0;
+
+ /* Legacy AddFB can't always infer the format from depth/bpp
alone, so
+ * check if our format is one of the lucky ones. */
+ if (!fb->format->depth || !fb->format->bpp)
+ return ret;
+
+ /* Cannot fall back to AddFB for multi-planar formats either. */
+ if (fb->handles[1] || fb->handles[2] || fb->handles[3])
+ return ret;
+
+ ret = drmModeAddFB(fb->fd, fb->width, fb->height,
+ fb->format->depth, fb->format->bpp,
+ fb->strides[0], fb->handles[0], &fb->fb_id);
+ return ret;
+}
+
static struct drm_fb *
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
uint32_t format)
@@ -827,12 +856,10 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
struct drm_mode_map_dumb map_arg;
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0
};
fb = zalloc(sizeof *fb);
if (!fb)
return NULL;
-
fb->refcnt = 1;
fb->format = pixel_format_get_info(format);
@@ -858,32 +885,20 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
goto err_fb;
fb->type = BUFFER_PIXMAN_DUMB;
- fb->handle = create_arg.handle;
- fb->stride = create_arg.pitch;
+ fb->handles[0] = create_arg.handle;
+ fb->strides[0] = create_arg.pitch;
fb->size = create_arg.size;
fb->width = width;
fb->height = height;
fb->fd = b->drm.fd;
- ret = -1;
-
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(b->drm.fd, width, height, fb->format->format,
- handles, pitches, offsets, &fb->fb_id, 0);
- if (ret) {
- ret = drmModeAddFB(b->drm.fd, width, height,
- fb->format->depth, fb->format->bpp,
- fb->stride, fb->handle, &fb->fb_id);
- }
-
- if (ret)
+ if (drm_fb_addfb(fb) != 0) {
+ weston_log("failed to create kms fb: %m\n");
goto err_bo;
+ }
memset(&map_arg, 0, sizeof map_arg);
- map_arg.handle = fb->handle;
+ map_arg.handle = fb->handles[0];
ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
if (ret)
goto err_add_fb;
@@ -918,8 +933,6 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
bool is_opaque, enum drm_fb_type type)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0
};
- int ret;
if (fb) {
assert(fb->type == type);
@@ -936,10 +949,10 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
fb->width = gbm_bo_get_width(bo);
fb->height = gbm_bo_get_height(bo);
- fb->stride = gbm_bo_get_stride(bo);
- fb->handle = gbm_bo_get_handle(bo).u32;
+ fb->strides[0] = gbm_bo_get_stride(bo);
+ fb->handles[0] = gbm_bo_get_handle(bo).u32;
fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
- fb->size = fb->stride * fb->height;
+ fb->size = fb->strides[0] * fb->height;
fb->fd = backend->drm.fd;
if (!fb->format) {
@@ -959,19 +972,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
goto err_free;
}
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
- fb->format->format, handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret && fb->format->depth && fb->format->bpp)
- ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
- fb->format->depth, fb->format->bpp,
- fb->stride, fb->handle, &fb->fb_id);
-
- if (ret) {
+ if (drm_fb_addfb(fb) != 0) {
weston_log("failed to create kms fb: %m\n");
goto err_free;
}
@@ -1895,8 +1896,10 @@ drm_output_apply_state_legacy(struct
drm_output_state *state)
assert(scanout_state->dest_h == scanout_state->src_h >> 16);
mode = to_drm_mode(output->base.current_mode);
- if (backend->state_invalid || !scanout_plane->state_cur->fb ||
- scanout_plane->state_cur->fb->stride !=
scanout_state->fb->stride) {
+ if (backend->state_invalid ||
+ !scanout_plane->state_cur->fb ||
+ scanout_plane->state_cur->fb->strides[0] !=
+ scanout_state->fb->strides[0]) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
0, 0,
@@ -3894,7 +3897,7 @@ drm_output_init_pixman(struct drm_output *output,
struct drm_backend *b)
output->image[i] =
pixman_image_create_bits(pixman_format, w, h,
output->dumb[i]->map,
- output->dumb[i]->stride);
+
output->dumb[i]->strides[0]);
if (!output->image[i])
goto err;
}
@@ -5103,7 +5106,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
return;
ret = drmPrimeHandleToFD(b->drm.fd,
- output->scanout_plane->state_
cur->fb->handle,
+ output->scanout_plane->state_
cur->fb->handles[0],
DRM_CLOEXEC, &fd);
if (ret) {
weston_log("[libva recorder] "
@@ -5112,7 +5115,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
}
ret = vaapi_recorder_frame(output->recorder, fd,
- output->scanout_plane->state_
cur->fb->stride);
+ output->scanout_plane->state_
cur->fb->strides[0]);
if (ret < 0) {
weston_log("[libva recorder] aborted: %m\n");
recorder_destroy(output);
--
2.14.1
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Daniel Stone
2017-09-26 17:15:43 UTC
Permalink
Rather than open-coding it ourselves, use the new apply_state helper in
drm_output_start_repaint.

Signed-off-by: Daniel Stone <***@collabora.com>
Reported-by: Fabien DESSENNE <***@st.com>
---
libweston/compositor-drm.c | 38 ++++++--------------------------------
1 file changed, 6 insertions(+), 32 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 6c1c6881..1781c7df 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1946,13 +1946,10 @@ 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 = NULL;
- struct drm_output_state *state;
- struct drm_plane_state *plane_state;
+ struct drm_pending_state *pending_state;
struct drm_plane *scanout_plane = output->scanout_plane;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
- uint32_t fb_id;
struct timespec ts, tnow;
struct timespec vbl2now;
int64_t refresh_nsec;
@@ -2008,46 +2005,23 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
/* Immediate query didn't provide valid timestamp.
* Use pageflip fallback.
*/
- fb_id = scanout_plane->state_cur->fb->fb_id;

assert(!output->page_flip_pending);
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);
+ 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_pending_state_free(pending_state);
+ ret = drm_pending_state_apply(pending_state);
+ if (ret != 0) {
+ weston_log("applying repaint-start state failed: %m\n");
goto finish_frame;
}

- if (output->pageflip_timer)
- wl_event_source_timer_update(output->pageflip_timer,
- backend->pageflip_timeout);
-
- wl_list_for_each(plane_state, &state->plane_list, link) {
- if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY)
- continue;
-
- vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
- vbl.request.type |= drm_waitvblank_pipe(output);
- vbl.request.sequence = 1;
- vbl.request.signal = (unsigned long) plane_state;
- drmWaitVBlank(backend->drm.fd, &vbl);
- }
-
- drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
- drm_pending_state_free(pending_state);
-
return;

finish_frame:
- if (pending_state)
- drm_pending_state_free(pending_state);
-
/* if we cannot page-flip, immediately finish frame */
weston_output_finish_frame(output_base, NULL,
WP_PRESENTATION_FEEDBACK_INVALID);
--
2.14.1
Pekka Paalanen
2017-10-03 11:52:48 UTC
Permalink
On Tue, 26 Sep 2017 18:15:43 +0100
Post by Daniel Stone
Rather than open-coding it ourselves, use the new apply_state helper in
drm_output_start_repaint.
---
libweston/compositor-drm.c | 38 ++++++--------------------------------
1 file changed, 6 insertions(+), 32 deletions(-)
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Daniel Stone
2017-09-26 17:15:53 UTC
Permalink
Now we have a more comprehensive overview of the transform we're going
to apply, use this to explicitly disallow scaling and rotation.

XXX: This does not actually disallow rotation for square surfaces.
We would have to build up a full buffer->view->output transform
matrix, and then analyse that.

Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e835d3b7..07f0b105 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2607,7 +2607,6 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
{
struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct drm_backend *b = to_drm_backend(ec);
struct wl_resource *buffer_resource;
struct drm_plane *p;
@@ -2633,13 +2632,6 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
if (wl_shm_buffer_get(buffer_resource))
return NULL;

- if (viewport->buffer.transform != output->base.transform)
- return NULL;
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
- if (!drm_view_transform_supported(ev))
- return NULL;
-
if (ev->alpha != 1.0f)
return NULL;

@@ -2668,6 +2660,12 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
if (!state)
return NULL;

+ state->output = output;
+ drm_plane_state_coords_for_view(state, ev);
+ if (state->src_w != state->dest_w << 16 ||
+ state->src_h != state->dest_h << 16)
+ goto err;
+
if ((dmabuf = linux_dmabuf_buffer_get(buffer_resource))) {
#ifdef HAVE_GBM_FD_IMPORT
/* XXX: TODO:
@@ -2697,7 +2695,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
if (dmabuf->attributes.n_planes != 1 ||
dmabuf->attributes.offset[0] != 0 ||
dmabuf->attributes.flags)
- return NULL;
+ goto err;

bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
GBM_BO_USE_SCANOUT);
@@ -2713,8 +2711,12 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,

state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
BUFFER_CLIENT);
- if (!state->fb)
+ if (!state->fb) {
+ /* Destroy the BO as we've allocated it, but it won't yet
+ * be deallocated by the state. */
+ gbm_bo_destroy(bo);
goto err;
+ }

/* Check whether the format is supported */
for (i = 0; i < p->count_formats; i++)
@@ -2725,15 +2727,10 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,

drm_fb_set_buffer(state->fb, ev->surface->buffer_ref.buffer);

- state->output = output;
- drm_plane_state_coords_for_view(state, ev);
-
return &p->base;

err:
drm_plane_state_put_back(state);
- if (bo)
- gbm_bo_destroy(bo);
return NULL;
}
--
2.14.1
Daniel Stone
2017-09-26 17:16:12 UTC
Permalink
From: Sergi Granell <***@gmail.com>

The per-plane IN_FORMATS property adds information about format
modifiers; we can use these to both feed GBM with the set of modifiers
we want to use for rendering, and also as an early-out test when we're
trying to see if a FB will go on a particular plane.

Signed-off-by: Sergi Granell <***@gmail.com>
Reviewed-by: Daniel Stone <***@collabora.com>
Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 126 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 118 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 111cb9e3..811adbbd 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -83,6 +83,20 @@
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif

+#ifdef HAVE_DRM_ATOMIC
+static inline uint32_t *
+formats_ptr(struct drm_format_modifier_blob *blob)
+{
+ return (uint32_t *)(((char *)blob) + blob->formats_offset);
+}
+
+static inline struct drm_format_modifier *
+modifiers_ptr(struct drm_format_modifier_blob *blob)
+{
+ return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset);
+}
+#endif
+
/**
* Represents the values of an enum-type KMS property
*/
@@ -127,6 +141,7 @@ enum wdrm_plane_property {
WDRM_PLANE_CRTC_H,
WDRM_PLANE_FB_ID,
WDRM_PLANE_CRTC_ID,
+ WDRM_PLANE_IN_FORMATS,
WDRM_PLANE__COUNT
};

@@ -168,6 +183,7 @@ static const struct drm_property_info plane_props[] = {
[WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
[WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
[WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
+ [WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" },
};

/**
@@ -397,7 +413,11 @@ struct drm_plane {

struct wl_list link;

- uint32_t formats[];
+ struct {
+ uint32_t format;
+ uint32_t count_modifiers;
+ uint64_t *modifiers;
+ } formats[];
};

struct drm_output {
@@ -2811,7 +2831,19 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,

/* Check whether the format is supported */
for (i = 0; i < p->count_formats; i++) {
- if (p->formats[i] == fb->format->format)
+ unsigned int j;
+
+ if (p->formats[i].format != fb->format->format)
+ continue;
+
+ if (fb->modifier == DRM_FORMAT_MOD_INVALID)
+ break;
+
+ for (j = 0; j < p->formats[i].count_modifiers; j++) {
+ if (p->formats[i].modifiers[j] == fb->modifier)
+ break;
+ }
+ if (j != p->formats[i].count_modifiers)
break;
}
if (i == p->count_formats)
@@ -3578,6 +3610,61 @@ init_pixman(struct drm_backend *b)
return pixman_renderer_init(b->compositor);
}

+/**
+ * Populates the formats array, and the modifiers of each format for a drm_plane.
+ */
+#ifdef HAVE_DRM_ATOMIC
+static bool
+populate_format_modifiers(struct drm_plane *plane, const drmModePlane *kplane,
+ uint32_t blob_id)
+{
+ unsigned i, j;
+ drmModePropertyBlobRes *blob;
+ struct drm_format_modifier_blob *fmt_mod_blob;
+ uint32_t *blob_formats;
+ struct drm_format_modifier *blob_modifiers;
+
+ blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
+ if (!blob)
+ return false;
+
+ fmt_mod_blob = blob->data;
+ blob_formats = formats_ptr(fmt_mod_blob);
+ blob_modifiers = modifiers_ptr(fmt_mod_blob);
+
+ assert(plane->count_formats == fmt_mod_blob->count_formats);
+
+ for (i = 0; i < fmt_mod_blob->count_formats; i++) {
+ uint32_t count_modifiers = 0;
+ uint64_t *modifiers = NULL;
+
+ for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
+ struct drm_format_modifier *mod = &blob_modifiers[j];
+
+ if ((i < mod->offset) || (i > mod->offset + 63))
+ continue;
+ if (!(mod->formats & (1 << (i - mod->offset))))
+ continue;
+
+ modifiers = realloc(modifiers, (count_modifiers + 1) * sizeof(modifiers[0]));
+ if (!modifiers) {
+ drmModeFreePropertyBlob(blob);
+ return false;
+ }
+ modifiers[count_modifiers++] = mod->modifier;
+ }
+
+ plane->formats[i].format = blob_formats[i];
+ plane->formats[i].modifiers = modifiers;
+ plane->formats[i].count_modifiers = count_modifiers;
+ }
+
+ drmModeFreePropertyBlob(blob);
+
+ return true;
+}
+#endif
+
/**
* Create a drm_plane for a hardware plane
*
@@ -3607,25 +3694,27 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
{
struct drm_plane *plane;
drmModeObjectProperties *props;
- int num_formats = (kplane) ? kplane->count_formats : 1;
+ uint32_t num_formats = (kplane) ? kplane->count_formats : 1;

plane = zalloc(sizeof(*plane) +
- (sizeof(uint32_t) * num_formats));
+ (sizeof(plane->formats[0]) * num_formats));
if (!plane) {
weston_log("%s: out of memory\n", __func__);
return NULL;
}

plane->backend = b;
+ plane->count_formats = num_formats;
plane->state_cur = drm_plane_state_alloc(NULL, plane);
plane->state_cur->complete = true;

if (kplane) {
+#ifdef HAVE_DRM_ATOMIC
+ uint32_t blob_id;
+#endif
+
plane->possible_crtcs = kplane->possible_crtcs;
plane->plane_id = kplane->plane_id;
- plane->count_formats = kplane->count_formats;
- memcpy(plane->formats, kplane->formats,
- kplane->count_formats * sizeof(kplane->formats[0]));

props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id,
DRM_MODE_OBJECT_PLANE);
@@ -3640,13 +3729,34 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
drm_property_get_value(&plane->props[WDRM_PLANE_TYPE],
props,
WDRM_PLANE_TYPE__COUNT);
+
+#ifdef HAVE_DRM_ATOMIC
+ blob_id = drm_property_get_value(&plane->props[WDRM_PLANE_IN_FORMATS],
+ props,
+ 0);
+ if (blob_id) {
+ if (!populate_format_modifiers(plane, kplane, blob_id)) {
+ weston_log("%s: out of memory\n", __func__);
+ drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
+ drmModeFreeObjectProperties(props);
+ free(plane);
+ return NULL;
+ }
+ } else
+#endif
+ {
+ uint32_t i;
+ for (i = 0; i < kplane->count_formats; i++)
+ plane->formats[i].format = kplane->formats[i];
+ }
+
drmModeFreeObjectProperties(props);
}
else {
plane->possible_crtcs = (1 << output->pipe);
plane->plane_id = 0;
plane->count_formats = 1;
- plane->formats[0] = format;
+ plane->formats[0].format = format;
plane->type = type;
}
--
2.14.1
Pekka Paalanen
2017-09-28 10:59:56 UTC
Permalink
On Tue, 26 Sep 2017 18:16:12 +0100
Post by Daniel Stone
The per-plane IN_FORMATS property adds information about format
modifiers; we can use these to both feed GBM with the set of modifiers
we want to use for rendering, and also as an early-out test when we're
trying to see if a FB will go on a particular plane.
---
libweston/compositor-drm.c | 126 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 118 insertions(+), 8 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 111cb9e3..811adbbd 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -83,6 +83,20 @@
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif
+#ifdef HAVE_DRM_ATOMIC
+static inline uint32_t *
+formats_ptr(struct drm_format_modifier_blob *blob)
+{
+ return (uint32_t *)(((char *)blob) + blob->formats_offset);
+}
+
+static inline struct drm_format_modifier *
+modifiers_ptr(struct drm_format_modifier_blob *blob)
+{
+ return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset);
+}
+#endif
Hi,

a quick build test with libdrm 2.4.80 produces this:

CC libweston/drm_backend_la-compositor-drm.lo
/home/pq/git/weston/libweston/compositor-drm.c:88:20: warning: ‘struct drm_format_modifier_blob’ declared inside parameter list
formats_ptr(struct drm_format_modifier_blob *blob)
^
/home/pq/git/weston/libweston/compositor-drm.c:88:20: warning: its scope is only this definition or declaration, which is probably not what you want
/home/pq/git/weston/libweston/compositor-drm.c: In function ‘formats_ptr’:
/home/pq/git/weston/libweston/compositor-drm.c:90:43: error: dereferencing pointer to incomplete type
return (uint32_t *)(((char *)blob) + blob->formats_offset);
^
and a ton more. I suppose HAVE_DRM_ATOMIC check is not quite enough?
Looks like it should require libdrm 2.4.83.


Thanks,
pq
Post by Daniel Stone
+
/**
* Represents the values of an enum-type KMS property
*/
@@ -127,6 +141,7 @@ enum wdrm_plane_property {
WDRM_PLANE_CRTC_H,
WDRM_PLANE_FB_ID,
WDRM_PLANE_CRTC_ID,
+ WDRM_PLANE_IN_FORMATS,
WDRM_PLANE__COUNT
};
@@ -168,6 +183,7 @@ static const struct drm_property_info plane_props[] = {
[WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
[WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
[WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
+ [WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" },
};
/**
@@ -397,7 +413,11 @@ struct drm_plane {
struct wl_list link;
- uint32_t formats[];
+ struct {
+ uint32_t format;
+ uint32_t count_modifiers;
+ uint64_t *modifiers;
+ } formats[];
};
struct drm_output {
@@ -2811,7 +2831,19 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
/* Check whether the format is supported */
for (i = 0; i < p->count_formats; i++) {
- if (p->formats[i] == fb->format->format)
+ unsigned int j;
+
+ if (p->formats[i].format != fb->format->format)
+ continue;
+
+ if (fb->modifier == DRM_FORMAT_MOD_INVALID)
+ break;
+
+ for (j = 0; j < p->formats[i].count_modifiers; j++) {
+ if (p->formats[i].modifiers[j] == fb->modifier)
+ break;
+ }
+ if (j != p->formats[i].count_modifiers)
break;
}
if (i == p->count_formats)
@@ -3578,6 +3610,61 @@ init_pixman(struct drm_backend *b)
return pixman_renderer_init(b->compositor);
}
+/**
+ * Populates the formats array, and the modifiers of each format for a drm_plane.
+ */
+#ifdef HAVE_DRM_ATOMIC
+static bool
+populate_format_modifiers(struct drm_plane *plane, const drmModePlane *kplane,
+ uint32_t blob_id)
+{
+ unsigned i, j;
+ drmModePropertyBlobRes *blob;
+ struct drm_format_modifier_blob *fmt_mod_blob;
+ uint32_t *blob_formats;
+ struct drm_format_modifier *blob_modifiers;
+
+ blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
+ if (!blob)
+ return false;
+
+ fmt_mod_blob = blob->data;
+ blob_formats = formats_ptr(fmt_mod_blob);
+ blob_modifiers = modifiers_ptr(fmt_mod_blob);
+
+ assert(plane->count_formats == fmt_mod_blob->count_formats);
+
+ for (i = 0; i < fmt_mod_blob->count_formats; i++) {
+ uint32_t count_modifiers = 0;
+ uint64_t *modifiers = NULL;
+
+ for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
+ struct drm_format_modifier *mod = &blob_modifiers[j];
+
+ if ((i < mod->offset) || (i > mod->offset + 63))
+ continue;
+ if (!(mod->formats & (1 << (i - mod->offset))))
+ continue;
+
+ modifiers = realloc(modifiers, (count_modifiers + 1) * sizeof(modifiers[0]));
+ if (!modifiers) {
+ drmModeFreePropertyBlob(blob);
+ return false;
+ }
+ modifiers[count_modifiers++] = mod->modifier;
+ }
+
+ plane->formats[i].format = blob_formats[i];
+ plane->formats[i].modifiers = modifiers;
+ plane->formats[i].count_modifiers = count_modifiers;
+ }
+
+ drmModeFreePropertyBlob(blob);
+
+ return true;
+}
+#endif
+
/**
* Create a drm_plane for a hardware plane
*
@@ -3607,25 +3694,27 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
{
struct drm_plane *plane;
drmModeObjectProperties *props;
- int num_formats = (kplane) ? kplane->count_formats : 1;
+ uint32_t num_formats = (kplane) ? kplane->count_formats : 1;
plane = zalloc(sizeof(*plane) +
- (sizeof(uint32_t) * num_formats));
+ (sizeof(plane->formats[0]) * num_formats));
if (!plane) {
weston_log("%s: out of memory\n", __func__);
return NULL;
}
plane->backend = b;
+ plane->count_formats = num_formats;
plane->state_cur = drm_plane_state_alloc(NULL, plane);
plane->state_cur->complete = true;
if (kplane) {
+#ifdef HAVE_DRM_ATOMIC
+ uint32_t blob_id;
+#endif
+
plane->possible_crtcs = kplane->possible_crtcs;
plane->plane_id = kplane->plane_id;
- plane->count_formats = kplane->count_formats;
- memcpy(plane->formats, kplane->formats,
- kplane->count_formats * sizeof(kplane->formats[0]));
props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id,
DRM_MODE_OBJECT_PLANE);
@@ -3640,13 +3729,34 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane,
drm_property_get_value(&plane->props[WDRM_PLANE_TYPE],
props,
WDRM_PLANE_TYPE__COUNT);
+
+#ifdef HAVE_DRM_ATOMIC
+ blob_id = drm_property_get_value(&plane->props[WDRM_PLANE_IN_FORMATS],
+ props,
+ 0);
+ if (blob_id) {
+ if (!populate_format_modifiers(plane, kplane, blob_id)) {
+ weston_log("%s: out of memory\n", __func__);
+ drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
+ drmModeFreeObjectProperties(props);
+ free(plane);
+ return NULL;
+ }
+ } else
+#endif
+ {
+ uint32_t i;
+ for (i = 0; i < kplane->count_formats; i++)
+ plane->formats[i].format = kplane->formats[i];
+ }
+
drmModeFreeObjectProperties(props);
}
else {
plane->possible_crtcs = (1 << output->pipe);
plane->plane_id = 0;
plane->count_formats = 1;
- plane->formats[0] = format;
+ plane->formats[0].format = format;
plane->type = type;
}
Matt Hoosier
2017-09-27 13:46:23 UTC
Permalink
Hi,
Here's a cleaned-up and fully-tested version of the atomic series.
Hi;

Just wondering: do you maintain these in a publicly fetchable repo
somewhere? I didn't turn up anything after some light searching --
there's git://people.freedesktop.org/~daniels/westonover on FDO's Git
server, but it looks disused for a few years now.

Cheers,
Matt
Daniel Stone
2017-09-27 14:18:30 UTC
Permalink
Hi Matt,
Post by Matt Hoosier
Here's a cleaned-up and fully-tested version of the atomic series.
Just wondering: do you maintain these in a publicly fetchable repo
somewhere? I didn't turn up anything after some light searching --
there's git://people.freedesktop.org/~daniels/westonover on FDO's Git
server, but it looks disused for a few years now.
Oops, I should've said in the cover letter:
git://git.collabora.com/git/user/daniels/weston # wip/2017-09/atomic-v12

Cheers,
Daniel
Pekka Paalanen
2017-09-28 10:32:34 UTC
Permalink
On Tue, 26 Sep 2017 18:15:34 +0100
Post by Daniel Stone
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.
This merges the timing paths for scanout and plane-but-but-atomic-plane
content.
---
libweston/compositor-drm.c | 354 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 300 insertions(+), 54 deletions(-)
Hi,

two minor nits below.
Post by Daniel Stone
@@ -1304,6 +1524,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 = NULL;
+ struct drm_output_state *state;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
uint32_t fb_id;
@@ -1364,10 +1586,16 @@ 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_pending_state_free(pending_state);
Should not call this, because the finish_frame path also calls
drm_pending_state_free().
Post by Daniel Stone
goto finish_frame;
}
@@ -1378,9 +1606,15 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
output->fb_last = drm_fb_ref(output->fb_current);
output->page_flip_pending = 1;
+ drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+ drm_pending_state_free(pending_state);
+
return;
+ if (pending_state)
+ drm_pending_state_free(pending_state);
No need to check, drm_pending_state_free(NULL) is fine.
Post by Daniel Stone
+
/* if we cannot page-flip, immediately finish frame */
weston_output_finish_frame(output_base, NULL,
WP_PRESENTATION_FEEDBACK_INVALID);
With those fixed:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Ucan, Emre (ADITG/ESB)
2017-10-12 09:20:58 UTC
Permalink
Hi Daniel,

I tested your branch " wip/2017-09/atomic-v12" on Intel Apollo Lake with:
- Linux Kernel v4.9
- Libdrm 2.4.83
- Mesa 13.0.6

In the end, I could not test the atomic modesetting with this setup. I got this warning "DRM: does not support atomic modesetting".
When I checked the source code, I saw that atomic modesetting depends on DRM_CAP_CRTC_IN_VBLANK_EVENT, which is introduced to the kernel with this commit "5db06a8a98f515f67446a69c57577c4c363ec65d" in 4.12 Linux kernel.

I also saw a regression. After desktop-shell sets the display black, weston crashed with this callstack:
Oct 04 02:25:39 gr-mrb-64 weston[1922]: weston: ../../../../../../../../weston/libweston/compositor.c:2474: weston_output_finish_frame: Assertion `output->repaint_status == REPAINT_AWAITING_COMPLETION' fa
iled.
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] caught signal: 6
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [0000000000405d2a] -- (/usr/bin/weston)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [0000000000405d97] -- (/usr/bin/weston)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [000000308aa330e0] -- (/lib64/libc.so.6)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [000000308aa3307f] gsignal (/lib64/libc.so.6)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [000000308aa344aa] abort (/lib64/libc.so.6)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [000000308aa2bed7] -- (/lib64/libc.so.6)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [000000308aa2bf82] -- (/lib64/libc.so.6)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [00007fe98acf909b] weston_output_finish_frame (/usr/lib64/libweston-3.so.0)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [00007fe98aaccaae] -- (/usr/lib64/libweston-3/drm-backend.so)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.784] [00007fe98aacd8e0] -- (/usr/lib64/libweston-3/drm-backend.so)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [00007fe98acf9241] -- (/usr/lib64/libweston-3.so.0)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [0000003093009849] -- (/usr/lib64/libwayland-server.so.0)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [0000003093009e02] wl_event_loop_dispatch (/usr/lib64/libwayland-server.so.0)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [00000030930087ba] wl_display_run (/usr/lib64/libwayland-server.so.0)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [0000000000405160] -- (/usr/bin/weston)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [000000308aa202c1] __libc_start_main (/lib64/libc.so.6)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: [02:25:39.785] [00000000004057aa] _start (/usr/bin/weston)

Best regards

Emre Ucan
Engineering Software Base (ADITG/ESB)

Tel. +49 5121 49 6937
-----Original Message-----
From: wayland-devel [mailto:wayland-devel-
Sent: Dienstag, 26. September 2017 19:20
Subject: [PATCH weston v12 00/40] Atomic modesetting
Hi,
Here's a cleaned-up and fully-tested version of the atomic series. I'm
fairly happy with it now. There are a few things I'd want to change, but
figured I'd send it all out now rather than waiting another however
* I'd rather track unused_{crtcs,connectors} by putting them all in
originally, and only removing them when an enabled output state
is committed from repaint_flush
* Adding unused_planes would also be smart (actually, I see it's
still #if 0'ed out in the atomic patch; for shame)
* The property handling for unused_* is not overly pretty, and I'd
happily take suggestions on how to fix it
* A temporary commit allows disabling universal-plane/atomic support
through an environment variable, allowing easier testing of those
paths
* Cursor/scanout planes are now discovered at plane creation, rather
than enable, time
* dmabuf import now uses GBM_BO_IMPORT_FD_MODIFIER, rather than
using
EGLImage import, to avoid handle-uniqueness issues
* drmModeGetPlane2 is now spelled IN_FORMATS
* Bugfixes for viewported/scaled client buffers (tested with a cropped
and scaled simple-egl)
Cheers,
Daniel
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Daniel Stone
2017-10-12 11:56:39 UTC
Permalink
Hi Emre,

On 12 October 2017 at 10:20, Ucan, Emre (ADITG/ESB)
Post by Ucan, Emre (ADITG/ESB)
- Linux Kernel v4.9
- Libdrm 2.4.83
- Mesa 13.0.6
Thanks for testing!
Post by Ucan, Emre (ADITG/ESB)
In the end, I could not test the atomic modesetting with this setup. I got this warning "DRM: does not support atomic modesetting".
When I checked the source code, I saw that atomic modesetting depends on DRM_CAP_CRTC_IN_VBLANK_EVENT, which is introduced to the kernel with this commit "5db06a8a98f515f67446a69c57577c4c363ec65d" in 4.12 Linux kernel.
Yep; it should be quite a simple backport.
Post by Ucan, Emre (ADITG/ESB)
Oct 04 02:25:39 gr-mrb-64 weston[1922]: weston: ../../../../../../../../weston/libweston/compositor.c:2474: weston_output_finish_frame: Assertion `output->repaint_status == REPAINT_AWAITING_COMPLETION' fa
iled.
This is already fixed. :) My current WIP branch and status is
available here: https://phabricator.freedesktop.org/T7595

There are still some issues with DPMS, but I think we have quite a
good workaround now.

Cheers,
Daniel
Ucan, Emre (ADITG/ESB)
2017-11-01 11:56:55 UTC
Permalink
Hi Daniel,

Is this the latest WIP branch to test " https://gitlab.collabora.com/daniels/weston/commits/wip/2017-10/atomic-v13" ?

In my opinion, it would easier to review/test your patches if you can separate them in multiple patch series.

For example, you can send at first up to "compositor-drm: Atomic modesetting support". Commit message states that it enables atomic API support for weston.
Other features like GBM_BO_IMPORT_FD_MODIFIER support are nice to have but not a hard requirement of atomic modesetting support.

What do you think ?

Best regards

Emre Ucan
Engineering Software Base (ADITG/ESB)

Tel. +49 5121 49 6937
-----Original Message-----
Sent: Donnerstag, 12. Oktober 2017 13:57
To: Ucan, Emre (ADITG/ESB)
Subject: Re: [PATCH weston v12 00/40] Atomic modesetting
Hi Emre,
On 12 October 2017 at 10:20, Ucan, Emre (ADITG/ESB)
Post by Ucan, Emre (ADITG/ESB)
- Linux Kernel v4.9
- Libdrm 2.4.83
- Mesa 13.0.6
Thanks for testing!
Post by Ucan, Emre (ADITG/ESB)
In the end, I could not test the atomic modesetting with this setup. I got
this warning "DRM: does not support atomic modesetting".
Post by Ucan, Emre (ADITG/ESB)
When I checked the source code, I saw that atomic modesetting depends
on DRM_CAP_CRTC_IN_VBLANK_EVENT, which is introduced to the kernel
with this commit "5db06a8a98f515f67446a69c57577c4c363ec65d" in 4.12 Linux
kernel.
Yep; it should be quite a simple backport.
Post by Ucan, Emre (ADITG/ESB)
I also saw a regression. After desktop-shell sets the display black, weston
weston_output_finish_frame: Assertion `output->repaint_status ==
REPAINT_AWAITING_COMPLETION' fa
Post by Ucan, Emre (ADITG/ESB)
iled.
This is already fixed. :) My current WIP branch and status is
available here: https://phabricator.freedesktop.org/T7595
There are still some issues with DPMS, but I think we have quite a
good workaround now.
Cheers,
Daniel
Daniel Stone
2017-11-01 14:13:52 UTC
Permalink
Hi Emre.

On 1 November 2017 at 11:56, Ucan, Emre (ADITG/ESB)
Post by Ucan, Emre (ADITG/ESB)
Is this the latest WIP branch to test " https://gitlab.collabora.com/daniels/weston/commits/wip/2017-10/atomic-v13" ?
Right you are.
Post by Ucan, Emre (ADITG/ESB)
In my opinion, it would easier to review/test your patches if you can separate them in multiple patch series.
For example, you can send at first up to "compositor-drm: Atomic modesetting support". Commit message states that it enables atomic API support for weston.
Other features like GBM_BO_IMPORT_FD_MODIFIER support are nice to have but not a hard requirement of atomic modesetting support.
What do you think ?
It's a reasonable idea, but in practice the two aren't completely
independent. The reason GBM_BO_IMPORT_FD_MODIFIER was tied up with
this is that it relies quite heavily on changes made to drm_fb which
have now been merged, but were previously part of the atomic series.
I've been considering pulling those out separately, but on the other
hand there are quite large conflicts doing so: before the 'helper'
commits, there are two separate GBM import paths for primary/scanout
and overlay planes, which only get unified inside the atomic series.

My current thinking is:
* everything up to 'atomic modesetting support' is qutie
self-contained, largely reviewed, and should hopefully be very very
close to landing by the time I can send out a new revision next week
(been busy with internal stuff & travel recently)
* once that's landed, everything up to 'Add modifiers to GBM dmabuf
import', and possibly including 'Support plane IN_FORMATS' + 'Support
modifiers with GBM' can be considered as one independent series
(though will need a non-trivial rebase) which should be quite easy to
review
* the rest of the code dealing with plane assignments (up to 'Enable
planes for atomic') can be considered another separate series; though
there are a couple of bugfixes in there, the rest is more complex and
difficult

I think it makes the most sense to work through like that in order. Of
course if you have any other ideas or priorities, I'd be really
interested to hear - anything which makes it easier to review is
obviously good! :)

Cheers,
Daniel
Ucan, Emre (ADITG/ESB)
2017-11-02 07:05:38 UTC
Permalink
Hi Daniel,

Your proposal is exactly what I thought, how it should be.
I am looking forward for next revision of patches.

Best regards

Emre Ucan
Engineering Software Base (ADITG/ESB)

Tel. +49 5121 49 6937
-----Original Message-----
Sent: Mittwoch, 1. November 2017 15:14
To: Ucan, Emre (ADITG/ESB)
Subject: Re: [PATCH weston v12 00/40] Atomic modesetting
Hi Emre.
On 1 November 2017 at 11:56, Ucan, Emre (ADITG/ESB)
Post by Ucan, Emre (ADITG/ESB)
Is this the latest WIP branch to test "
https://gitlab.collabora.com/daniels/weston/commits/wip/2017-10/atomic-
v13" ?
Right you are.
Post by Ucan, Emre (ADITG/ESB)
In my opinion, it would easier to review/test your patches if you can
separate them in multiple patch series.
Post by Ucan, Emre (ADITG/ESB)
For example, you can send at first up to "compositor-drm: Atomic
modesetting support". Commit message states that it enables atomic API
support for weston.
Post by Ucan, Emre (ADITG/ESB)
Other features like GBM_BO_IMPORT_FD_MODIFIER support are nice to
have but not a hard requirement of atomic modesetting support.
Post by Ucan, Emre (ADITG/ESB)
What do you think ?
It's a reasonable idea, but in practice the two aren't completely
independent. The reason GBM_BO_IMPORT_FD_MODIFIER was tied up with
this is that it relies quite heavily on changes made to drm_fb which
have now been merged, but were previously part of the atomic series.
I've been considering pulling those out separately, but on the other
hand there are quite large conflicts doing so: before the 'helper'
commits, there are two separate GBM import paths for primary/scanout
and overlay planes, which only get unified inside the atomic series.
* everything up to 'atomic modesetting support' is qutie
self-contained, largely reviewed, and should hopefully be very very
close to landing by the time I can send out a new revision next week
(been busy with internal stuff & travel recently)
* once that's landed, everything up to 'Add modifiers to GBM dmabuf
import', and possibly including 'Support plane IN_FORMATS' + 'Support
modifiers with GBM' can be considered as one independent series
(though will need a non-trivial rebase) which should be quite easy to
review
* the rest of the code dealing with plane assignments (up to 'Enable
planes for atomic') can be considered another separate series; though
there are a couple of bugfixes in there, the rest is more complex and
difficult
I think it makes the most sense to work through like that in order. Of
course if you have any other ideas or priorities, I'd be really
interested to hear - anything which makes it easier to review is
obviously good! :)
Cheers,
Daniel
Matt Hoosier
2017-11-02 19:13:42 UTC
Permalink
What sort of testing on this series would be most helpful? I figure that
you and pq have most of the code review covered, so the main contribution I
can make with the hardware available to me is to exercise this on
consumer-grade systems like Macbooks with Intel display controllers. Are
there are specific use-cases that will put the most stress on your new
stuff? I assume that doing some sub-fullscreen OpenGL windows will kick in
the overlay selections to test that out.
Post by Philipp Zabel
Hi Daniel,
Your proposal is exactly what I thought, how it should be.
I am looking forward for next revision of patches.
Best regards
Emre Ucan
Engineering Software Base (ADITG/ESB)
Tel. +49 5121 49 6937
-----Original Message-----
Sent: Mittwoch, 1. November 2017 15:14
To: Ucan, Emre (ADITG/ESB)
Subject: Re: [PATCH weston v12 00/40] Atomic modesetting
Hi Emre.
On 1 November 2017 at 11:56, Ucan, Emre (ADITG/ESB)
Post by Ucan, Emre (ADITG/ESB)
Is this the latest WIP branch to test "
https://gitlab.collabora.com/daniels/weston/commits/wip/2017-10/atomic-
v13" ?
Right you are.
Post by Ucan, Emre (ADITG/ESB)
In my opinion, it would easier to review/test your patches if you can
separate them in multiple patch series.
Post by Ucan, Emre (ADITG/ESB)
For example, you can send at first up to "compositor-drm: Atomic
modesetting support". Commit message states that it enables atomic API
support for weston.
Post by Ucan, Emre (ADITG/ESB)
Other features like GBM_BO_IMPORT_FD_MODIFIER support are nice to
have but not a hard requirement of atomic modesetting support.
Post by Ucan, Emre (ADITG/ESB)
What do you think ?
It's a reasonable idea, but in practice the two aren't completely
independent. The reason GBM_BO_IMPORT_FD_MODIFIER was tied up with
this is that it relies quite heavily on changes made to drm_fb which
have now been merged, but were previously part of the atomic series.
I've been considering pulling those out separately, but on the other
hand there are quite large conflicts doing so: before the 'helper'
commits, there are two separate GBM import paths for primary/scanout
and overlay planes, which only get unified inside the atomic series.
* everything up to 'atomic modesetting support' is qutie
self-contained, largely reviewed, and should hopefully be very very
close to landing by the time I can send out a new revision next week
(been busy with internal stuff & travel recently)
* once that's landed, everything up to 'Add modifiers to GBM dmabuf
import', and possibly including 'Support plane IN_FORMATS' + 'Support
modifiers with GBM' can be considered as one independent series
(though will need a non-trivial rebase) which should be quite easy to
review
* the rest of the code dealing with plane assignments (up to 'Enable
planes for atomic') can be considered another separate series; though
there are a couple of bugfixes in there, the rest is more complex and
difficult
I think it makes the most sense to work through like that in order. Of
course if you have any other ideas or priorities, I'd be really
interested to hear - anything which makes it easier to review is
obviously good! :)
Cheers,
Daniel
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
Pekka Paalanen
2017-11-03 07:46:17 UTC
Permalink
On Thu, 2 Nov 2017 14:13:42 -0500
Post by Matt Hoosier
What sort of testing on this series would be most helpful? I figure that
you and pq have most of the code review covered, so the main contribution I
can make with the hardware available to me is to exercise this on
consumer-grade systems like Macbooks with Intel display controllers. Are
there are specific use-cases that will put the most stress on your new
stuff? I assume that doing some sub-fullscreen OpenGL windows will kick in
the overlay selections to test that out.
Hi,

from my point of view, output hotplug is always a pain to test on DRM,
so I would appreciate help with that. This kind of cases:

- on a running compositor, add an output with hotplug
- on a running compositor, remove an output with hot-unplug
- hot-unplug all outputs, the compositor should remain running
- after hot-unplugging all outputs, plug them back in
- start the compositor without any outputs, then hotplug some
- hotplug/unplug tests with MST, so that we have DRM connectors
appearing and disappearing

At all times, weston-info should reflect the actual number of outputs,
0..N.

All the cases could be taken further by hotplugging and hot-unplugging
while VT-switched away. It is also possible that some of this is
already broken in upstream master, so if you encounter failures, it
would be good to know if it was already broken.

Those are my wishes, Daniel probably has others. Any testing reports
at all would be awesome.


Thanks,
pq
Matt Hoosier
2017-11-03 13:45:33 UTC
Permalink
Post by Pekka Paalanen
On Thu, 2 Nov 2017 14:13:42 -0500
Post by Matt Hoosier
What sort of testing on this series would be most helpful? I figure that
you and pq have most of the code review covered, so the main
contribution I
Post by Pekka Paalanen
Post by Matt Hoosier
can make with the hardware available to me is to exercise this on
consumer-grade systems like Macbooks with Intel display controllers. Are
there are specific use-cases that will put the most stress on your new
stuff? I assume that doing some sub-fullscreen OpenGL windows will kick in
the overlay selections to test that out.
Hi,
from my point of view, output hotplug is always a pain to test on DRM,
- on a running compositor, add an output with hotplug
- on a running compositor, remove an output with hot-unplug
- hot-unplug all outputs, the compositor should remain running
- after hot-unplugging all outputs, plug them back in
- start the compositor without any outputs, then hotplug some
- hotplug/unplug tests with MST, so that we have DRM connectors
appearing and disappearing
At all times, weston-info should reflect the actual number of outputs,
0..N.
All the cases could be taken further by hotplugging and hot-unplugging
while VT-switched away. It is also possible that some of this is
already broken in upstream master, so if you encounter failures, it
would be good to know if it was already broken.
Those are my wishes, Daniel probably has others. Any testing reports
at all would be awesome.
Thanks,
pq
Okay, I'll see how many of those cases I can manage to exercise.

At the moment, I already noticed what looks like some sort of failure to
handle wl_surface damage regions as submitted by weston-simple-egl. (Only
part of the triangle refreshes while the remainder is stuck at its initial
frame's contents.)

It doesn't look to me like this has anything to do with overlay usage. The
contents of /sys/kernel/debug/dri/0/i915_display_info show:

CRTC info
---------
CRTC 31: pipe: A, active=yes, (size=2560x1600), dither=no, bpp=24
fb: 72, pos: 0x0, size: 2560x1600
encoder 46: type: DDI A, connectors:
connector 47: type: eDP-1, status: connected, mode:
id 0:"2560x1600" freq 60 clock 268500 hdisp 2560 hss 2608
hse 2640 htot 2720 vdisp 1600 vss 1603 vse 1609 vtot 1646 type 0x48 flags
0x9
cursor visible? no, position (0, 0), size 0x0, addr 0x00000000,
active? no
No scalers available on this platform
--Plane id 25: type=PRI, crtc_pos= 0x 0, crtc_size=2560x1600,
src_pos=0.0000x0.0000, src_size=2560.0000x1600.0000, format=XR24
little-endian (0x34325258), rotation=0 (0x00000001)
--Plane id 27: type=OVL, crtc_pos= 0x 0, crtc_size= 0x 0,
src_pos=0.0000x0.0000, src_size=0.0000x0.0000, format=N/A, rotation=0
(0x00000001)
--Plane id 29: type=CUR, crtc_pos= 0x 0, crtc_size= 0x 0,
src_pos=0.0000x0.0000, src_size=0.0000x0.0000, format=N/A, rotation=0
(0x00000001)


So I think that everything is going through the primary plane.

(This behavior isn't seen in the last common point between master and
Daniel's wip/2017-10/atomic-v13 branch head.)

-Matt
Daniel Vetter
2017-11-22 09:04:00 UTC
Permalink
Post by Pekka Paalanen
On Thu, 2 Nov 2017 14:13:42 -0500
Post by Matt Hoosier
What sort of testing on this series would be most helpful? I figure that
you and pq have most of the code review covered, so the main contribution I
can make with the hardware available to me is to exercise this on
consumer-grade systems like Macbooks with Intel display controllers. Are
there are specific use-cases that will put the most stress on your new
stuff? I assume that doing some sub-fullscreen OpenGL windows will kick in
the overlay selections to test that out.
Hi,
from my point of view, output hotplug is always a pain to test on DRM,
- on a running compositor, add an output with hotplug
- on a running compositor, remove an output with hot-unplug
- hot-unplug all outputs, the compositor should remain running
- after hot-unplugging all outputs, plug them back in
- start the compositor without any outputs, then hotplug some
- hotplug/unplug tests with MST, so that we have DRM connectors
appearing and disappearing
At all times, weston-info should reflect the actual number of outputs,
0..N.
All the cases could be taken further by hotplugging and hot-unplugging
while VT-switched away. It is also possible that some of this is
already broken in upstream master, so if you encounter failures, it
would be good to know if it was already broken.
Since a while we have "vkms.ko" on our wishlist (as a gsoc or something)
on the kernel side, so that stuff like this could be tested in a fully
automated way. Especially once you get into DP MST connector hotplugging,
or the recent link_status stuff, the things a proper compositor is
supposed to do (and most don't) is quite long and tricky. Unfortunately no
one has bitten yet ...
-Daniel
Post by Pekka Paalanen
Those are my wishes, Daniel probably has others. Any testing reports
at all would be awesome.
Thanks,
pq
_______________________________________________
wayland-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/wayland-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Loading...