Discussion:
[PATCH weston v2 0/68] Atomic modesetting, planes, extended drm_fb
(too old to reply)
Daniel Stone
2016-12-09 19:58:01 UTC
Permalink
Hi all,
This is v2 of the atomic patchset, which incorporates quite a few
fixups on the original code, and extends it far enough to flip
sprites_are_broken off for atomic. \o/

Compared to earlier versions, drm_output_state is introduced much
earlier, and everything hangs off this. This made a lot of things much
more clean than they used to be. Most of the patches before this are
relatively untouched, but for bugfixes.

The pixel-format helpers patch is new, after I got frustrated at
having to write yet another table of formats with validation. I'm
fully intending to convert gl-renderer over to using this for its SHM
upload path, but on the other hand I didn't want to tie that together
with this series; it's big enough as it is. (Similarly, for
multi-output you'll need the 'Assign new views to the primary plane'
patch I sent out earlier, separately.)

On top of the code from v1, which is primarily concerned with how we
apply state, v2 attacks how we _generate_ the state in the first
place. The major difference is the whittling down of the
prepare_*_view() functions into common helpers, and then cleaving
assign_planes() in two. Doing the latter allows us to actually
reasonably do atomic TEST_ONLY.

First we try to construct a view made entirely out of planes and
nothing else; if this succeeds, we just use that and we don't need to
render anything. Failing that, we try to incrementally build a state,
trying one view at a time on one plane at a time, and seeing if that
changes anything. Doing this is what lets us flip sprites_are_broken,
because we can not only generate an optimal configuration, but make
sure it'll actually work when we do it.

Included in with this is a set of patches which let us import more
exotic buffers: primarily multi-planar, and with buffers. The earlier
work means that this is now generalised, and we can use them for
scanout, overlay, or anything really.

After this, a set of optimisation patches to ignore views which aren't
on other outputs and occluded views. This prevents us from triggering
spurious repaints on multi-head systems.

At the end of this, on my multihead setup, I was able to set up one
fullscreen (but scaled, due to HiDPI) and one non-fullscreen view
above it. With nothing else on screen, these were promoted to planes
(scanout and overlay, respectively), and no rendering was done. When
something was moved on top of them, they moved back to GPU composition
where necessary.

Any review or testing would be appreciated; I have no idea when 1.13
is supposed to be (in 11 days ...?), so I assume it won't land for
then, but I'd like to get this in as early as possible for the 1.14
cycle, so we can iron out as many of the bugs as possible.

Many thanks to Fabien DESSENNE for testing and fixes, Tomohito Esaki
for the dmabuf-without-GBM patch, Derek Foreman and others for things
like cursor clipping (already fixed, but the reports were nice). And
of course, I somehow forgot to mention in my original mail - to Pekka
Paalanen, Louis-Francis Ratté-Boulianne and Derek Foreman, who worked
on much earlier iterations of atomic support.

Cheers,
Daniel
Daniel Stone
2016-12-09 19:57:18 UTC
Permalink
Clarify the difference between crtc_id (DRM object) and pipe (index into
drmModeRes->crtcs array, possible_crtcs bitmask).

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Quentin Glidic <sardemff7+***@sardemff7.net>
Differential Revision: https://phabricator.freedesktop.org/D1405
---
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 a899213..268117d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -155,8 +155,8 @@ struct drm_output {
struct weston_output base;
drmModeConnector *connector;

- uint32_t crtc_id;
- int pipe;
+ 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;
--
2.9.3
Armin Krezović
2016-12-09 20:38:44 UTC
Permalink
Post by Daniel Stone
Clarify the difference between crtc_id (DRM object) and pipe (index into
drmModeRes->crtcs array, possible_crtcs bitmask).
Differential Revision: https://phabricator.freedesktop.org/D1405
Documenting structure members is always welcome.

Reviewed-by: Armin Krezović <***@gmail.com>

(this time it's me!)
Post by Daniel Stone
---
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 a899213..268117d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -155,8 +155,8 @@ struct drm_output {
struct weston_output base;
drmModeConnector *connector;
- uint32_t crtc_id;
- int pipe;
+ 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;
Daniel Stone
2016-12-09 19:57:19 UTC
Permalink
Even if we do have a framebuffer matching the mode, we immediately
schedule a repaint, meaning we either do work for no reason, or show
stale content before we bring up the new content.

Delete this and just let repaint deal with it.

Differential Revision: https://phabricator.freedesktop.org/D1481

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 268117d..7d1c01b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2824,38 +2824,6 @@ drm_destroy(struct weston_compositor *ec)
}

static void
-drm_backend_set_modes(struct drm_backend *backend)
-{
- struct drm_output *output;
- struct drm_mode *drm_mode;
- int ret;
-
- wl_list_for_each(output, &backend->compositor->output_list, base.link) {
- if (!output->current) {
- /* If something that would cause the output to
- * switch mode happened while in another vt, we
- * might not have a current drm_fb. In that case,
- * schedule a repaint and let drm_output_repaint
- * handle setting the mode. */
- weston_output_schedule_repaint(&output->base);
- continue;
- }
-
- drm_mode = (struct drm_mode *) output->base.current_mode;
- ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- output->current->fb_id, 0, 0,
- &output->connector_id, 1,
- &drm_mode->mode_info);
- if (ret < 0) {
- weston_log(
- "failed to set mode %dx%d for output at %d,%d: %m\n",
- drm_mode->base.width, drm_mode->base.height,
- output->base.x, output->base.y);
- }
- }
-}
-
-static void
session_notify(struct wl_listener *listener, void *data)
{
struct weston_compositor *compositor = data;
@@ -2866,7 +2834,6 @@ session_notify(struct wl_listener *listener, void *data)
if (compositor->session_active) {
weston_log("activating session\n");
compositor->state = b->prev_state;
- drm_backend_set_modes(b);
weston_compositor_damage_all(compositor);
udev_input_enable(&b->input);
} else {
--
2.9.3
Armin Krezović
2016-12-09 20:52:30 UTC
Permalink
Post by Daniel Stone
Even if we do have a framebuffer matching the mode, we immediately
schedule a repaint, meaning we either do work for no reason, or show
stale content before we bring up the new content.
Delete this and just let repaint deal with it.
Differential Revision: https://phabricator.freedesktop.org/D1481
Since drm_output_repaint() calls drmModeSetCrtc() when needed and
weston_compositor_damage_all() will schedule a repaint, this makes
sense.
Post by Daniel Stone
---
libweston/compositor-drm.c | 33 ---------------------------------
1 file changed, 33 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 268117d..7d1c01b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2824,38 +2824,6 @@ drm_destroy(struct weston_compositor *ec)
}
static void
-drm_backend_set_modes(struct drm_backend *backend)
-{
- struct drm_output *output;
- struct drm_mode *drm_mode;
- int ret;
-
- wl_list_for_each(output, &backend->compositor->output_list, base.link) {
- if (!output->current) {
- /* If something that would cause the output to
- * switch mode happened while in another vt, we
- * might not have a current drm_fb. In that case,
- * schedule a repaint and let drm_output_repaint
- * handle setting the mode. */
- weston_output_schedule_repaint(&output->base);
- continue;
- }
-
- drm_mode = (struct drm_mode *) output->base.current_mode;
- ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- output->current->fb_id, 0, 0,
- &output->connector_id, 1,
- &drm_mode->mode_info);
- if (ret < 0) {
- weston_log(
- "failed to set mode %dx%d for output at %d,%d: %m\n",
- drm_mode->base.width, drm_mode->base.height,
- output->base.x, output->base.y);
- }
- }
-}
-
-static void
session_notify(struct wl_listener *listener, void *data)
{
struct weston_compositor *compositor = data;
@@ -2866,7 +2834,6 @@ session_notify(struct wl_listener *listener, void *data)
if (compositor->session_active) {
weston_log("activating session\n");
compositor->state = b->prev_state;
- drm_backend_set_modes(b);
weston_compositor_damage_all(compositor);
udev_input_enable(&b->input);
} else {
Daniel Stone
2016-12-09 19:57:20 UTC
Permalink
This always changes the state to ACTIVE when we enter the session,
whereas the previous implementation preserved the state (i.e. if state
was SLEEPING on exit, it would be restored to SLEEPING, but also with a
repaint). This seems more helpful behaviour, however: if you enter a
session, it's probably in order to interact with it.

Differential Revision: https://phabricator.freedesktop.org/D1482

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Quentin Glidic <sardemff7+***@sardemff7.net>
---
libweston/compositor-drm.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7d1c01b..d577c05 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -116,8 +116,6 @@ struct drm_backend {

int use_pixman;

- uint32_t prev_state;
-
struct udev_input input;

int32_t cursor_width;
@@ -2833,14 +2831,13 @@ session_notify(struct wl_listener *listener, void *data)

if (compositor->session_active) {
weston_log("activating session\n");
- compositor->state = b->prev_state;
+ weston_compositor_wake(compositor);
weston_compositor_damage_all(compositor);
udev_input_enable(&b->input);
} else {
weston_log("deactivating session\n");
udev_input_disable(&b->input);

- b->prev_state = compositor->state;
weston_compositor_offscreen(compositor);

/* If we have a repaint scheduled (either from a
@@ -3197,8 +3194,6 @@ drm_backend_create(struct weston_compositor *compositor,
b->base.destroy = drm_destroy;
b->base.restore = drm_restore;

- b->prev_state = WESTON_COMPOSITOR_ACTIVE;
-
weston_setup_vt_switch_bindings(compositor);

wl_list_init(&b->sprite_list);
--
2.9.3
Daniel Stone
2016-12-09 19:57:23 UTC
Permalink
No functional change.

Differential Revision: https://phabricator.freedesktop.org/D1484

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2d5faa0..5fb45b4 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1322,9 +1322,6 @@ drm_assign_planes(struct weston_output *output_base)
pixman_region32_fini(&overlap);
}

-static void
-drm_output_fini_pixman(struct drm_output *output);
-
/**
* Find the closest-matching mode for a given target
*
@@ -1363,8 +1360,12 @@ choose_mode (struct drm_output *output, struct weston_mode *target_mode)

static int
drm_output_init_egl(struct drm_output *output, struct drm_backend *b);
+static void
+drm_output_fini_egl(struct drm_output *output);
static int
drm_output_init_pixman(struct drm_output *output, struct drm_backend *b);
+static void
+drm_output_fini_pixman(struct drm_output *output);

static int
drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
@@ -1414,9 +1415,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
return -1;
}
} else {
- gl_renderer->output_destroy(&output->base);
- gbm_surface_destroy(output->gbm_surface);
-
+ drm_output_fini_egl(output);
if (drm_output_init_egl(output, b) < 0) {
weston_log("failed to init output egl state with "
"new mode");
@@ -1853,6 +1852,13 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
return 0;
}

+static void
+drm_output_fini_egl(struct drm_output *output)
+{
+ gl_renderer->output_destroy(&output->base);
+ gbm_surface_destroy(output->gbm_surface);
+}
+
static int
drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
{
@@ -2423,12 +2429,10 @@ drm_output_deinit(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);

- if (b->use_pixman) {
+ if (b->use_pixman)
drm_output_fini_pixman(output);
- } else {
- gl_renderer->output_destroy(&output->base);
- gbm_surface_destroy(output->gbm_surface);
- }
+ else
+ drm_output_fini_egl(output);

weston_plane_release(&output->fb_plane);
weston_plane_release(&output->cursor_plane);
--
2.9.3
Armin Krezović
2016-12-09 20:55:41 UTC
Permalink
Post by Daniel Stone
No functional change.
Differential Revision: https://phabricator.freedesktop.org/D1484
Nice
Post by Daniel Stone
---
libweston/compositor-drm.c | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2d5faa0..5fb45b4 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1322,9 +1322,6 @@ drm_assign_planes(struct weston_output *output_base)
pixman_region32_fini(&overlap);
}
-static void
-drm_output_fini_pixman(struct drm_output *output);
-
/**
* Find the closest-matching mode for a given target
*
@@ -1363,8 +1360,12 @@ choose_mode (struct drm_output *output, struct weston_mode *target_mode)
static int
drm_output_init_egl(struct drm_output *output, struct drm_backend *b);
+static void
+drm_output_fini_egl(struct drm_output *output);
static int
drm_output_init_pixman(struct drm_output *output, struct drm_backend *b);
+static void
+drm_output_fini_pixman(struct drm_output *output);
static int
drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
@@ -1414,9 +1415,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
return -1;
}
} else {
- gl_renderer->output_destroy(&output->base);
- gbm_surface_destroy(output->gbm_surface);
-
+ drm_output_fini_egl(output);
if (drm_output_init_egl(output, b) < 0) {
weston_log("failed to init output egl state with "
"new mode");
@@ -1853,6 +1852,13 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
return 0;
}
+static void
+drm_output_fini_egl(struct drm_output *output)
+{
+ gl_renderer->output_destroy(&output->base);
+ gbm_surface_destroy(output->gbm_surface);
+}
+
static int
drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
{
@@ -2423,12 +2429,10 @@ drm_output_deinit(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- if (b->use_pixman) {
+ if (b->use_pixman)
drm_output_fini_pixman(output);
- } else {
- gl_renderer->output_destroy(&output->base);
- gbm_surface_destroy(output->gbm_surface);
- }
+ else
+ drm_output_fini_egl(output);
weston_plane_release(&output->fb_plane);
weston_plane_release(&output->cursor_plane);
Daniel Stone
2016-12-09 19:57:27 UTC
Permalink
Everyone else uses fb->fd rather than pulling the FD back out of GBM.
Use that in the destroy callback too.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1406
---
libweston/compositor-drm.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3276ed0..a9bde0c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -243,10 +243,9 @@ static void
drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
{
struct drm_fb *fb = data;
- struct gbm_device *gbm = gbm_bo_get_device(bo);

if (fb->fb_id)
- drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
+ drmModeRmFB(fb->fd, fb->fb_id);

weston_buffer_reference(&fb->buffer_ref, NULL);
--
2.9.3
Armin Krezović
2016-12-09 21:01:22 UTC
Permalink
Post by Daniel Stone
Everyone else uses fb->fd rather than pulling the FD back out of GBM.
Use that in the destroy callback too.
Makes sense.
Post by Daniel Stone
Differential Revision: https://phabricator.freedesktop.org/D1406
---
libweston/compositor-drm.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3276ed0..a9bde0c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -243,10 +243,9 @@ static void
drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
{
struct drm_fb *fb = data;
- struct gbm_device *gbm = gbm_bo_get_device(bo);
if (fb->fb_id)
- drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
+ drmModeRmFB(fb->fd, fb->fb_id);
weston_buffer_reference(&fb->buffer_ref, NULL);
Daniel Stone
2016-12-09 19:57:17 UTC
Permalink
Differential Revision: https://phabricator.freedesktop.org/D1512
---
libweston/meson.build | 1 +
1 file changed, 1 insertion(+)

diff --git a/libweston/meson.build b/libweston/meson.build
index d396c00..aee444a 100644
--- a/libweston/meson.build
+++ b/libweston/meson.build
@@ -19,6 +19,7 @@ srcs_libweston = [
'noop-renderer.c',
'pixman-renderer.c',
'linux-dmabuf.c',
+ 'pixel-formats.c',
'screenshooter.c',
'../shared/file-util.c',
'../shared/matrix.c',
--
2.9.3
Armin Krezović
2016-12-09 20:37:56 UTC
Permalink
Post by Daniel Stone
Differential Revision: https://phabricator.freedesktop.org/D1512
---
libweston/meson.build | 1 +
1 file changed, 1 insertion(+)
diff --git a/libweston/meson.build b/libweston/meson.build
index d396c00..aee444a 100644
--- a/libweston/meson.build
+++ b/libweston/meson.build
@@ -19,6 +19,7 @@ srcs_libweston = [
'noop-renderer.c',
'pixman-renderer.c',
'linux-dmabuf.c',
+ 'pixel-formats.c',
'screenshooter.c',
'../shared/file-util.c',
'../shared/matrix.c',
Since when is meson supported? I don't see it in git master.
Daniel Stone
2016-12-09 21:22:01 UTC
Permalink
Hi Armin,
Post by Armin Krezović
Post by Daniel Stone
diff --git a/libweston/meson.build b/libweston/meson.build
index d396c00..aee444a 100644
--- a/libweston/meson.build
+++ b/libweston/meson.build
@@ -19,6 +19,7 @@ srcs_libweston = [
'noop-renderer.c',
'pixman-renderer.c',
'linux-dmabuf.c',
+ 'pixel-formats.c',
'screenshooter.c',
'../shared/file-util.c',
'../shared/matrix.c',
Since when is meson supported? I don't see it in git master.
Oh, it's not; it's just there to make it easier for people who want to
use Meson to build. Like me. :) It's really useful for this branch,
since when rebasing I'll frequently do 'git rebase -i --exec
[make|ninja]' along the entire branch to make sure it builds the whole
way; it's easy to introduce warnings in intermediate patches when
doing that. With 68 patches, Meson does this in around 30sec for the
whole tree, against ~4min for autotools.

Safe to say you can ignore this if you're not using Meson; it'll be
squashed into either the actual pixel-formats patch or the Meson
patch, depending on what lands first, as introducing intermediate
build breakage is bad. I wanted to keep it separate from pixel-formats
though, so that can land separately if necessary.

Cheers,
Daniel
Daniel Stone
2016-12-09 19:57:28 UTC
Permalink
This makes it sign-compatible with weston_output->{width,height}.

Differential Revision: https://phabricator.freedesktop.org/D1486

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Quentin Glidic <sardemff7+***@sardemff7.net>
---
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 a9bde0c..a5052b9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -104,8 +104,8 @@ struct drm_backend {
* due to out of bounds dimensions, and then mistakenly set
* sprites_are_broken:
*/
- uint32_t min_width, max_width;
- uint32_t min_height, max_height;
+ int min_width, max_width;
+ int min_height, max_height;
int no_addfb2;

struct wl_list sprite_list;
@@ -253,7 +253,7 @@ drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
}

static struct drm_fb *
-drm_fb_create_dumb(struct drm_backend *b, unsigned width, unsigned height,
+drm_fb_create_dumb(struct drm_backend *b, int width, int height,
uint32_t format)
{
struct drm_fb *fb;
@@ -371,7 +371,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
struct drm_backend *backend, uint32_t format)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
- uint32_t width, height;
+ int width, height;
uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
int ret;

@@ -391,7 +391,8 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
fb->size = fb->stride * height;
fb->fd = backend->drm.fd;

- if (backend->min_width > width || width > backend->max_width ||
+ if (backend->min_width > width ||
+ width > backend->max_width ||
backend->min_height > height ||
height > backend->max_height) {
weston_log("bo geometry out of bounds\n");
--
2.9.3
Armin Krezović
2016-12-09 21:02:39 UTC
Permalink
Post by Daniel Stone
This makes it sign-compatible with weston_output->{width,height}.
Differential Revision: https://phabricator.freedesktop.org/D1486
Makes sense
Post by Daniel Stone
---
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 a9bde0c..a5052b9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -104,8 +104,8 @@ struct drm_backend {
* due to out of bounds dimensions, and then mistakenly set
*/
- uint32_t min_width, max_width;
- uint32_t min_height, max_height;
+ int min_width, max_width;
+ int min_height, max_height;
int no_addfb2;
struct wl_list sprite_list;
@@ -253,7 +253,7 @@ drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
}
static struct drm_fb *
-drm_fb_create_dumb(struct drm_backend *b, unsigned width, unsigned height,
+drm_fb_create_dumb(struct drm_backend *b, int width, int height,
uint32_t format)
{
struct drm_fb *fb;
@@ -371,7 +371,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
struct drm_backend *backend, uint32_t format)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
- uint32_t width, height;
+ int width, height;
uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
int ret;
@@ -391,7 +391,8 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
fb->size = fb->stride * height;
fb->fd = backend->drm.fd;
- if (backend->min_width > width || width > backend->max_width ||
+ if (backend->min_width > width ||
+ width > backend->max_width ||
backend->min_height > height ||
height > backend->max_height) {
weston_log("bo geometry out of bounds\n");
Ha, the patch description didn't mention coding style fix!

(Just kidding)
Daniel Stone
2016-12-09 19:57:31 UTC
Permalink
This uses the new pixel-format helpers, so we can also replace depth/bpp
with these.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1513
---
libweston/compositor-drm.c | 43 +++++++++++++++++++++++++++----------------
1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 217db32..af43a15 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -54,6 +54,7 @@
#include "gl-renderer.h"
#include "weston-egl-ext.h"
#include "pixman-renderer.h"
+#include "pixel-formats.h"
#include "libbacklight.h"
#include "libinput-seat.h"
#include "launcher-util.h"
@@ -140,6 +141,7 @@ struct drm_fb {
enum drm_fb_type type;

uint32_t fb_id, stride, handle, size;
+ const struct pixel_format_info *format;
int width, height;
int fd;
struct weston_buffer_reference buffer_ref;
@@ -267,7 +269,6 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
{
struct drm_fb *fb;
int ret;
- uint32_t bpp, depth;

struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
@@ -277,20 +278,20 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb)
return NULL;

- switch (format) {
- case GBM_FORMAT_XRGB8888:
- bpp = 32;
- depth = 24;
- break;
- case GBM_FORMAT_RGB565:
- bpp = depth = 16;
- break;
- default:
- return NULL;
+ fb->format = pixel_format_get_info(format);
+ if (!fb->format) {
+ weston_log("failed to look up format 0x%lx\n",
+ (unsigned long) format);
+ goto err_fb;
+ }
+
+ if (!fb->format->depth || !fb->format->bpp) {
+ weston_log("format 0x%lx is not compatible with dumb buffers\n",
+ (unsigned long) format);
}

memset(&create_arg, 0, sizeof create_arg);
- create_arg.bpp = bpp;
+ create_arg.bpp = fb->format->bpp;
create_arg.width = width;
create_arg.height = height;

@@ -316,7 +317,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
offsets[0] = 0;

ret = drmModeAddFB2(b->drm.fd, width, height,
- format, handles, pitches, offsets,
+ fb->format->format,
+ handles, pitches, offsets,
&fb->fb_id, 0);
if (ret) {
weston_log("addfb2 failed: %m\n");
@@ -325,7 +327,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
}

if (ret) {
- ret = drmModeAddFB(b->drm.fd, width, height, depth, bpp,
+ ret = drmModeAddFB(b->drm.fd, width, height,
+ fb->format->depth, fb->format->bpp,
fb->stride, fb->handle, &fb->fb_id);
}

@@ -402,9 +405,16 @@ 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->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);
+ goto err_free;
+ }
+
if (backend->min_width > fb->width ||
fb->width > backend->max_width ||
backend->min_height > fb->height ||
@@ -430,9 +440,10 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
}
}

- if (ret)
+ if (ret && fb->format->depth && fb->format->bpp)
ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
- 24, 32, fb->stride, fb->handle, &fb->fb_id);
+ fb->format->depth, fb->format->bpp,
+ fb->stride, fb->handle, &fb->fb_id);

if (ret) {
weston_log("failed to create kms fb: %m\n");
--
2.9.3
Armin Krezović
2016-12-09 21:07:02 UTC
Permalink
Post by Daniel Stone
This uses the new pixel-format helpers, so we can also replace depth/bpp
with these.
Differential Revision: https://phabricator.freedesktop.org/D1513
So, this is where code added by patch 1 is being used. I suggest
squashing it with this one (unless I missed an earlier patch that
also uses it).
Post by Daniel Stone
---
libweston/compositor-drm.c | 43 +++++++++++++++++++++++++++----------------
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 217db32..af43a15 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -54,6 +54,7 @@
#include "gl-renderer.h"
#include "weston-egl-ext.h"
#include "pixman-renderer.h"
+#include "pixel-formats.h"
#include "libbacklight.h"
#include "libinput-seat.h"
#include "launcher-util.h"
@@ -140,6 +141,7 @@ struct drm_fb {
enum drm_fb_type type;
uint32_t fb_id, stride, handle, size;
+ const struct pixel_format_info *format;
int width, height;
int fd;
struct weston_buffer_reference buffer_ref;
@@ -267,7 +269,6 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
{
struct drm_fb *fb;
int ret;
- uint32_t bpp, depth;
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
@@ -277,20 +278,20 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb)
return NULL;
- switch (format) {
- bpp = 32;
- depth = 24;
- break;
- bpp = depth = 16;
- break;
- return NULL;
+ fb->format = pixel_format_get_info(format);
+ if (!fb->format) {
+ weston_log("failed to look up format 0x%lx\n",
+ (unsigned long) format);
+ goto err_fb;
+ }
+
+ if (!fb->format->depth || !fb->format->bpp) {
+ weston_log("format 0x%lx is not compatible with dumb buffers\n",
+ (unsigned long) format);
}
memset(&create_arg, 0, sizeof create_arg);
- create_arg.bpp = bpp;
+ create_arg.bpp = fb->format->bpp;
create_arg.width = width;
create_arg.height = height;
@@ -316,7 +317,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
offsets[0] = 0;
ret = drmModeAddFB2(b->drm.fd, width, height,
- format, handles, pitches, offsets,
+ fb->format->format,
+ handles, pitches, offsets,
&fb->fb_id, 0);
if (ret) {
weston_log("addfb2 failed: %m\n");
@@ -325,7 +327,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
}
if (ret) {
- ret = drmModeAddFB(b->drm.fd, width, height, depth, bpp,
+ ret = drmModeAddFB(b->drm.fd, width, height,
+ fb->format->depth, fb->format->bpp,
fb->stride, fb->handle, &fb->fb_id);
}
@@ -402,9 +405,16 @@ 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->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);
+ goto err_free;
+ }
+
if (backend->min_width > fb->width ||
fb->width > backend->max_width ||
backend->min_height > fb->height ||
@@ -430,9 +440,10 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
}
}
- if (ret)
+ if (ret && fb->format->depth && fb->format->bpp)
ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
- 24, 32, fb->stride, fb->handle, &fb->fb_id);
+ fb->format->depth, fb->format->bpp,
+ fb->stride, fb->handle, &fb->fb_id);
if (ret) {
weston_log("failed to create kms fb: %m\n");
Pekka Paalanen
2017-02-21 13:46:25 UTC
Permalink
On Fri, 9 Dec 2016 19:57:31 +0000
Post by Daniel Stone
This uses the new pixel-format helpers, so we can also replace depth/bpp
with these.
Differential Revision: https://phabricator.freedesktop.org/D1513
---
libweston/compositor-drm.c | 43 +++++++++++++++++++++++++++----------------
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 217db32..af43a15 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -54,6 +54,7 @@
#include "gl-renderer.h"
#include "weston-egl-ext.h"
#include "pixman-renderer.h"
+#include "pixel-formats.h"
#include "libbacklight.h"
#include "libinput-seat.h"
#include "launcher-util.h"
@@ -140,6 +141,7 @@ struct drm_fb {
enum drm_fb_type type;
uint32_t fb_id, stride, handle, size;
+ const struct pixel_format_info *format;
int width, height;
int fd;
struct weston_buffer_reference buffer_ref;
@@ -267,7 +269,6 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
{
struct drm_fb *fb;
int ret;
- uint32_t bpp, depth;
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
@@ -277,20 +278,20 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb)
return NULL;
- switch (format) {
- bpp = 32;
- depth = 24;
- break;
- bpp = depth = 16;
- break;
- return NULL;
+ fb->format = pixel_format_get_info(format);
+ if (!fb->format) {
+ weston_log("failed to look up format 0x%lx\n",
+ (unsigned long) format);
+ goto err_fb;
+ }
+
+ if (!fb->format->depth || !fb->format->bpp) {
+ weston_log("format 0x%lx is not compatible with dumb buffers\n",
+ (unsigned long) format);
goto err_fb?

Otherwise:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Post by Daniel Stone
}
memset(&create_arg, 0, sizeof create_arg);
- create_arg.bpp = bpp;
+ create_arg.bpp = fb->format->bpp;
create_arg.width = width;
create_arg.height = height;
Daniel Stone
2016-12-09 19:57:38 UTC
Permalink
Call drm_output_render unconditionally, doing an early exit if we're
already rendering a client buffer on the primary plane, and asserting
for damage on the way out.

Differential Revision: https://phabricator.freedesktop.org/D1494

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8071737..08634cd 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -672,6 +672,11 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
struct weston_compositor *c = output->base.compositor;
struct drm_backend *b = to_drm_backend(c);

+ /* If we already have a client buffer promoted to scanout, then we don't
+ * want to render. */
+ if (output->fb_pending)
+ return;
+
if (b->use_pixman)
drm_output_render_pixman(output, damage);
else
@@ -742,8 +747,7 @@ drm_output_repaint(struct weston_output *output_base,
if (output->disable_pending || output->destroy_pending)
return -1;

- if (!output->fb_pending)
- drm_output_render(output, damage);
+ drm_output_render(output, damage);
if (!output->fb_pending)
return -1;
--
2.9.3
Daniel Stone
2016-12-09 19:57:24 UTC
Permalink
Try to harmonise the various plane-import paths a little bit, starting
with reshuffling and commenting the conditions to do so.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1413
---
libweston/compositor-drm.c | 79 ++++++++++++++++++++++++++++------------------
1 file changed, 48 insertions(+), 31 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5fb45b4..8cd9d5a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -502,18 +502,28 @@ drm_output_prepare_scanout_view(struct drm_output *output,
struct gbm_bo *bo;
uint32_t format;

- if (ev->geometry.x != output->base.x ||
- ev->geometry.y != output->base.y ||
- buffer == NULL || b->gbm == NULL ||
- buffer->width != output->base.current_mode->width ||
- buffer->height != output->base.current_mode->height ||
- output->base.transform != viewport->buffer.transform ||
- ev->transform.enabled)
+ /* We use GBM to import buffers. */
+ if (b->gbm == NULL)
+ return NULL;
+
+ if (buffer == NULL)
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 (ev->transform.enabled)
+ return NULL;
if (ev->geometry.scissor_enabled)
return NULL;

+ if (buffer->width != output->base.current_mode->width ||
+ buffer->height != output->base.current_mode->height)
+ return NULL;
+ if (viewport->buffer.transform != output->base.transform)
+ return NULL;
+
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);

@@ -950,34 +960,33 @@ drm_output_prepare_overlay_view(struct drm_output *output,
uint32_t format;
wl_fixed_t sx1, sy1, sx2, sy2;

- if (b->gbm == NULL)
- return NULL;
-
- if (viewport->buffer.transform != output->base.transform)
- return NULL;
-
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
-
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;

- if (ev->surface->buffer_ref.buffer == NULL)
+ /* We can only import GBM buffers. */
+ if (b->gbm == NULL)
return NULL;
- buffer_resource = ev->surface->buffer_ref.buffer->resource;

- if (ev->alpha != 1.0f)
+ 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 (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;
+
wl_list_for_each(s, &b->sprite_list, link) {
if (!drm_sprite_crtc_supported(output, s))
continue;
@@ -1114,23 +1123,20 @@ drm_output_prepare_cursor_view(struct drm_output *output,
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_shm_buffer *shmbuf;

- if (ev->transform.enabled &&
- (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE))
- return NULL;
- if (b->gbm == NULL)
- return NULL;
- if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
- return NULL;
- if (viewport->buffer.scale != output->base.current_scale)
+ if (b->cursors_are_broken)
return NULL;
+
if (output->cursor_view)
return NULL;
+
+ /* Don't import buffers which span multiple outputs. */
if (ev->output_mask != (1u << output->base.id))
return NULL;
- if (b->cursors_are_broken)
- return NULL;
- if (ev->geometry.scissor_enabled)
+
+ /* We use GBM to import SHM buffers. */
+ if (b->gbm == NULL)
return NULL;
+
if (ev->surface->buffer_ref.buffer == NULL)
return NULL;
shmbuf = wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
@@ -1138,6 +1144,17 @@ drm_output_prepare_cursor_view(struct drm_output *output,
return NULL;
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;
--
2.9.3
Armin Krezović
2016-12-09 20:58:30 UTC
Permalink
Post by Daniel Stone
Try to harmonise the various plane-import paths a little bit, starting
with reshuffling and commenting the conditions to do so.
This makes code more readable and understandable. So have a
Post by Daniel Stone
Differential Revision: https://phabricator.freedesktop.org/D1413
---
libweston/compositor-drm.c | 79 ++++++++++++++++++++++++++++------------------
1 file changed, 48 insertions(+), 31 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5fb45b4..8cd9d5a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -502,18 +502,28 @@ drm_output_prepare_scanout_view(struct drm_output *output,
struct gbm_bo *bo;
uint32_t format;
- if (ev->geometry.x != output->base.x ||
- ev->geometry.y != output->base.y ||
- buffer == NULL || b->gbm == NULL ||
- buffer->width != output->base.current_mode->width ||
- buffer->height != output->base.current_mode->height ||
- output->base.transform != viewport->buffer.transform ||
- ev->transform.enabled)
+ /* We use GBM to import buffers. */
+ if (b->gbm == NULL)
+ return NULL;
+
+ if (buffer == NULL)
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 (ev->transform.enabled)
+ return NULL;
if (ev->geometry.scissor_enabled)
return NULL;
+ if (buffer->width != output->base.current_mode->width ||
+ buffer->height != output->base.current_mode->height)
+ return NULL;
+ if (viewport->buffer.transform != output->base.transform)
+ return NULL;
+
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);
@@ -950,34 +960,33 @@ drm_output_prepare_overlay_view(struct drm_output *output,
uint32_t format;
wl_fixed_t sx1, sy1, sx2, sy2;
- if (b->gbm == NULL)
- return NULL;
-
- if (viewport->buffer.transform != output->base.transform)
- return NULL;
-
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
-
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;
- if (ev->surface->buffer_ref.buffer == NULL)
+ /* We can only import GBM buffers. */
+ if (b->gbm == NULL)
return NULL;
- buffer_resource = ev->surface->buffer_ref.buffer->resource;
- if (ev->alpha != 1.0f)
+ 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 (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;
+
wl_list_for_each(s, &b->sprite_list, link) {
if (!drm_sprite_crtc_supported(output, s))
continue;
@@ -1114,23 +1123,20 @@ drm_output_prepare_cursor_view(struct drm_output *output,
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_shm_buffer *shmbuf;
- if (ev->transform.enabled &&
- (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE))
- return NULL;
- if (b->gbm == NULL)
- return NULL;
- if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
- return NULL;
- if (viewport->buffer.scale != output->base.current_scale)
+ if (b->cursors_are_broken)
return NULL;
+
if (output->cursor_view)
return NULL;
+
+ /* Don't import buffers which span multiple outputs. */
if (ev->output_mask != (1u << output->base.id))
return NULL;
- if (b->cursors_are_broken)
- return NULL;
- if (ev->geometry.scissor_enabled)
+
+ /* We use GBM to import SHM buffers. */
+ if (b->gbm == NULL)
return NULL;
+
if (ev->surface->buffer_ref.buffer == NULL)
return NULL;
shmbuf = wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
@@ -1138,6 +1144,17 @@ drm_output_prepare_cursor_view(struct drm_output *output,
return NULL;
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;
Daniel Stone
2016-12-09 19:57:36 UTC
Permalink
Now that we have better types in drm_fb, use it for cursor buffers as
well.

Differential Revision: https://phabricator.freedesktop.org/D1493

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 950c265..557b97d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -135,6 +135,7 @@ enum drm_fb_type {
BUFFER_CLIENT, /**< directly sourced from client */
BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
BUFFER_GBM_SURFACE, /**< internal EGL rendering */
+ BUFFER_CURSOR, /**< internal cursor buffer */
};

struct drm_fb {
@@ -183,11 +184,12 @@ struct drm_output {
int disable_pending;

struct gbm_surface *gbm_surface;
- struct gbm_bo *gbm_cursor_bo[2];
+ struct drm_fb *gbm_cursor_fb[2];
struct weston_plane cursor_plane;
- struct weston_plane fb_plane;
struct weston_view *cursor_view;
int current_cursor;
+
+ struct weston_plane fb_plane;
struct drm_fb *current, *next;
struct backlight *backlight;

@@ -285,7 +287,8 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
{
struct drm_fb *fb = data;

- assert(fb->type == BUFFER_GBM_SURFACE || fb->type == BUFFER_CLIENT);
+ assert(fb->type == BUFFER_GBM_SURFACE || fb->type == BUFFER_CLIENT ||
+ fb->type == BUFFER_CURSOR);
drm_fb_destroy(fb);
}

@@ -493,6 +496,7 @@ drm_fb_unref(struct drm_fb *fb)
case BUFFER_PIXMAN_DUMB:
drm_fb_destroy_dumb(fb);
break;
+ case BUFFER_CURSOR:
case BUFFER_CLIENT:
gbm_bo_destroy(fb->bo);
break;
@@ -1281,7 +1285,7 @@ drm_output_set_cursor(struct drm_output *output)
pixman_region32_fini(&output->cursor_plane.damage);
pixman_region32_init(&output->cursor_plane.damage);
output->current_cursor ^= 1;
- bo = output->gbm_cursor_bo[output->current_cursor];
+ bo = output->gbm_cursor_fb[output->current_cursor]->bo;

cursor_bo_update(b, bo, ev);
handle = gbm_bo_get_handle(bo).s32;
@@ -1867,6 +1871,48 @@ find_crtc_for_connector(struct drm_backend *b,
return -1;
}

+static void drm_output_fini_cursor_egl(struct drm_output *output)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
+ drm_fb_unref(output->gbm_cursor_fb[i]);
+ output->gbm_cursor_fb[i] = NULL;
+ }
+}
+
+static int
+drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
+ struct gbm_bo *bo;
+
+ bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
+ GBM_FORMAT_ARGB8888,
+ GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+ if (!bo)
+ goto err;
+
+ output->gbm_cursor_fb[i] =
+ drm_fb_get_from_bo(bo, b, GBM_FORMAT_ARGB8888,
+ BUFFER_CURSOR);
+ if (!output->gbm_cursor_fb[i]) {
+ gbm_bo_destroy(bo);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ weston_log("cursor buffers unavailable, using gl cursors\n");
+ b->cursors_are_broken = 1;
+ drm_output_fini_cursor_egl(output);
+ return -1;
+}
+
/* Init output state that depends on gl or gbm */
static int
drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
@@ -1875,7 +1921,7 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
output->gbm_format,
fallback_format_for(output->gbm_format),
};
- int i, flags, n_formats = 1;
+ int n_formats = 1;

output->gbm_surface = gbm_surface_create(b->gbm,
output->base.current_mode->width,
@@ -1901,21 +1947,7 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
return -1;
}

- flags = GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE;
-
- for (i = 0; i < 2; i++) {
- if (output->gbm_cursor_bo[i])
- continue;
-
- output->gbm_cursor_bo[i] =
- gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
- GBM_FORMAT_ARGB8888, flags);
- }
-
- if (output->gbm_cursor_bo[0] == NULL || output->gbm_cursor_bo[1] == NULL) {
- weston_log("cursor buffers unavailable, using gl cursors\n");
- b->cursors_are_broken = 1;
- }
+ drm_output_init_cursor_egl(output, b);

return 0;
}
@@ -1925,6 +1957,7 @@ drm_output_fini_egl(struct drm_output *output)
{
gl_renderer->output_destroy(&output->base);
gbm_surface_destroy(output->gbm_surface);
+ drm_output_fini_cursor_egl(output);
}

static int
--
2.9.3
Daniel Stone
2016-12-09 19:57:26 UTC
Permalink
Make drm_output_set_cursor more deterministic, by calculating more state
and performing more plane manipulation, inside
drm_output_prepare_cursor_view.

Differential Revision: https://phabricator.freedesktop.org/D1485

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e22d792..3276ed0 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1135,6 +1135,7 @@ drm_output_prepare_cursor_view(struct drm_output *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;
+ float x, y;

if (b->cursors_are_broken)
return NULL;
@@ -1173,6 +1174,9 @@ drm_output_prepare_cursor_view(struct drm_output *output,
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;

return &output->cursor_plane;
}
@@ -1218,24 +1222,17 @@ static void
drm_output_set_cursor(struct drm_output *output)
{
struct weston_view *ev = output->cursor_view;
- struct weston_buffer *buffer;
struct drm_backend *b = to_drm_backend(output->base.compositor);
EGLint handle;
struct gbm_bo *bo;
float x, y;

- output->cursor_view = NULL;
if (ev == NULL) {
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
- output->cursor_plane.x = INT32_MIN;
- output->cursor_plane.y = INT32_MIN;
return;
}

- buffer = ev->surface->buffer_ref.buffer;
-
- if (buffer &&
- pixman_region32_not_empty(&output->cursor_plane.damage)) {
+ 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;
@@ -1250,22 +1247,14 @@ drm_output_set_cursor(struct drm_output *output)
}
}

- weston_view_to_global_float(ev, 0, 0, &x, &y);
-
- /* From global to output space, output transform is guaranteed to be
- * NORMAL by drm_output_prepare_cursor_view().
- */
- x = (x - output->base.x) * output->base.current_scale;
- y = (y - output->base.y) * output->base.current_scale;
+ x = (output->cursor_plane.x - output->base.x) *
+ output->base.current_scale;
+ y = (output->cursor_plane.y - output->base.y) *
+ output->base.current_scale;

- if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
- if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
- weston_log("failed to move cursor: %m\n");
- b->cursors_are_broken = 1;
- }
-
- output->cursor_plane.x = x;
- output->cursor_plane.y = y;
+ if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
+ weston_log("failed to move cursor: %m\n");
+ b->cursors_are_broken = 1;
}
}

@@ -1294,6 +1283,10 @@ drm_assign_planes(struct weston_output *output_base)
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;
--
2.9.3
Derek Foreman
2017-01-27 14:44:59 UTC
Permalink
Post by Daniel Stone
Make drm_output_set_cursor more deterministic, by calculating more state
and performing more plane manipulation, inside
drm_output_prepare_cursor_view.
Differential Revision: https://phabricator.freedesktop.org/D1485
Reviewed-by: Derek Foreman <***@osg.samsung.com>

(this also looks to me like it could be landed independently of the bulk
of the series.)
Post by Daniel Stone
---
libweston/compositor-drm.c | 39 ++++++++++++++++-----------------------
1 file changed, 16 insertions(+), 23 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e22d792..3276ed0 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1135,6 +1135,7 @@ drm_output_prepare_cursor_view(struct drm_output *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;
+ float x, y;
if (b->cursors_are_broken)
return NULL;
@@ -1173,6 +1174,9 @@ drm_output_prepare_cursor_view(struct drm_output *output,
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;
return &output->cursor_plane;
}
@@ -1218,24 +1222,17 @@ static void
drm_output_set_cursor(struct drm_output *output)
{
struct weston_view *ev = output->cursor_view;
- struct weston_buffer *buffer;
struct drm_backend *b = to_drm_backend(output->base.compositor);
EGLint handle;
struct gbm_bo *bo;
float x, y;
- output->cursor_view = NULL;
if (ev == NULL) {
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
- output->cursor_plane.x = INT32_MIN;
- output->cursor_plane.y = INT32_MIN;
return;
}
- buffer = ev->surface->buffer_ref.buffer;
-
- if (buffer &&
- pixman_region32_not_empty(&output->cursor_plane.damage)) {
+ 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;
@@ -1250,22 +1247,14 @@ drm_output_set_cursor(struct drm_output *output)
}
}
- weston_view_to_global_float(ev, 0, 0, &x, &y);
-
- /* From global to output space, output transform is guaranteed to be
- * NORMAL by drm_output_prepare_cursor_view().
- */
- x = (x - output->base.x) * output->base.current_scale;
- y = (y - output->base.y) * output->base.current_scale;
+ x = (output->cursor_plane.x - output->base.x) *
+ output->base.current_scale;
+ y = (output->cursor_plane.y - output->base.y) *
+ output->base.current_scale;
- if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
- if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
- weston_log("failed to move cursor: %m\n");
- b->cursors_are_broken = 1;
- }
-
- output->cursor_plane.x = x;
- output->cursor_plane.y = y;
+ if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
+ weston_log("failed to move cursor: %m\n");
+ b->cursors_are_broken = 1;
}
}
@@ -1294,6 +1283,10 @@ drm_assign_planes(struct weston_output *output_base)
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;
Daniel Stone
2016-12-09 19:57:25 UTC
Permalink
Don't import buffers which span multiple outputs, short-cut any attempt
to import SHM buffers, and ignore buffers with a global alpha set.

I'm not convinced all of these conditions entirely make sense, but this
at least makes them equally nonsensical.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1414
---
libweston/compositor-drm.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8cd9d5a..e22d792 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -462,6 +462,13 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
}
}

+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)
@@ -502,27 +509,40 @@ drm_output_prepare_scanout_view(struct drm_output *output,
struct gbm_bo *bo;
uint32_t format;

+ /* Don't import buffers which span multiple outputs. */
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
/* We use GBM to import buffers. */
if (b->gbm == NULL)
return NULL;

if (buffer == NULL)
return NULL;
+ 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;
+
if (ev->transform.enabled)
return NULL;
if (ev->geometry.scissor_enabled)
return NULL;
-
- if (buffer->width != output->base.current_mode->width ||
- buffer->height != output->base.current_mode->height)
- 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;

bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);
@@ -936,13 +956,6 @@ drm_output_check_sprite_format(struct drm_sprite *s,
return 0;
}

-static int
-drm_view_transform_supported(struct weston_view *ev)
-{
- return !ev->transform.enabled ||
- (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
-}
-
static struct weston_plane *
drm_output_prepare_overlay_view(struct drm_output *output,
struct weston_view *ev)
--
2.9.3
Derek Foreman
2017-01-27 14:25:34 UTC
Permalink
Post by Daniel Stone
Don't import buffers which span multiple outputs, short-cut any attempt
to import SHM buffers, and ignore buffers with a global alpha set.
I'm not convinced all of these conditions entirely make sense, but this
at least makes them equally nonsensical.
Differential Revision: https://phabricator.freedesktop.org/D1414
---
libweston/compositor-drm.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 8cd9d5a..e22d792 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -462,6 +462,13 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
}
}
+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,
Not sure this function name is really ideal anymore? Maybe it never
was, lots of stuff here I'm not sure one would all "format".
Post by Daniel Stone
struct weston_surface *es, struct gbm_bo *bo)
@@ -502,27 +509,40 @@ drm_output_prepare_scanout_view(struct drm_output *output,
struct gbm_bo *bo;
uint32_t format;
+ /* Don't import buffers which span multiple outputs. */
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
I suppose technically this is overly stringent, but I'd rather it be too
careful than broken.
Post by Daniel Stone
/* We use GBM to import buffers. */
if (b->gbm == NULL)
return NULL;
if (buffer == NULL)
return NULL;
+ 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;
+
Was there a reason to move this block?

I really don't have any concerns that would warrant another revision,
this looks good to me. (Also looks like it could be trivially landed
without dependency on any other patch in this series, with no functional
issues.)
Post by Daniel Stone
if (ev->transform.enabled)
return NULL;
if (ev->geometry.scissor_enabled)
return NULL;
-
- if (buffer->width != output->base.current_mode->width ||
- buffer->height != output->base.current_mode->height)
- 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;
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
buffer->resource, GBM_BO_USE_SCANOUT);
@@ -936,13 +956,6 @@ drm_output_check_sprite_format(struct drm_sprite *s,
return 0;
}
-static int
-drm_view_transform_supported(struct weston_view *ev)
-{
- return !ev->transform.enabled ||
- (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
-}
-
static struct weston_plane *
drm_output_prepare_overlay_view(struct drm_output *output,
struct weston_view *ev)
Daniel Stone
2017-01-27 14:30:20 UTC
Permalink
Hi,
Post by Daniel Stone
+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,
Not sure this function name is really ideal anymore? Maybe it never was,
lots of stuff here I'm not sure one would all "format".
Yeah, it's probably not the best, but I also didn't touch it here, so ... :)
Post by Daniel Stone
@@ -502,27 +509,40 @@ drm_output_prepare_scanout_view(struct drm_output *output,
struct gbm_bo *bo;
uint32_t format;
+ /* Don't import buffers which span multiple outputs. */
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
I suppose technically this is overly stringent, but I'd rather it be too
careful than broken.
Yeah, it should if anything just serve as an early-out. We can never
hoist multiple-output views to planes, because otherwise it falls out
of the scene graph for the other outputs. So we later build up to
using this unconditionally for plane import; these early patches are
just about making the scanout/overlay/cursor test paths as similar as
possible, to save any surprises further on down the line.
Post by Daniel Stone
/* 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;
+
Was there a reason to move this block?
Just for symmetry with the others; certainly no functional effect.
I really don't have any concerns that would warrant another revision, this
looks good to me. (Also looks like it could be trivially landed without
dependency on any other patch in this series, with no functional issues.)
Thanks! I'm going to go ahead and just do that I think, and then if
any of the cleanups apply to the atomic series, we could address them
there perhaps?

Cheers,
Daniel
Derek Foreman
2017-01-27 14:32:34 UTC
Permalink
Post by Daniel Stone
Hi,
Post by Daniel Stone
+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,
Not sure this function name is really ideal anymore? Maybe it never was,
lots of stuff here I'm not sure one would all "format".
Yeah, it's probably not the best, but I also didn't touch it here, so ... :)
Post by Daniel Stone
@@ -502,27 +509,40 @@ drm_output_prepare_scanout_view(struct drm_output *output,
struct gbm_bo *bo;
uint32_t format;
+ /* Don't import buffers which span multiple outputs. */
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
I suppose technically this is overly stringent, but I'd rather it be too
careful than broken.
Yeah, it should if anything just serve as an early-out. We can never
hoist multiple-output views to planes, because otherwise it falls out
of the scene graph for the other outputs. So we later build up to
using this unconditionally for plane import; these early patches are
just about making the scanout/overlay/cursor test paths as similar as
possible, to save any surprises further on down the line.
Post by Daniel Stone
/* 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;
+
Was there a reason to move this block?
Just for symmetry with the others; certainly no functional effect.
I really don't have any concerns that would warrant another revision, this
looks good to me. (Also looks like it could be trivially landed without
dependency on any other patch in this series, with no functional issues.)
Thanks! I'm going to go ahead and just do that I think, and then if
any of the cleanups apply to the atomic series, we could address them
there perhaps?
Agreed!
Post by Daniel Stone
Cheers,
Daniel
Daniel Stone
2016-12-09 19:57:32 UTC
Permalink
From: Tomohito Esaki <***@igel.co.jp>

The drm_fb destroy callback to mostly the same thing regardless of
whether the buffer is a dumb buffer or gbm buffer. This patch refactors
the common parts into a new function that can be called for both cases.

[daniels: Rebased on top of fb->fd changes, cosmetic changes.]

Differential Revision: https://phabricator.freedesktop.org/D1489

Signed-off-by: Tomohito Esaki <***@igel.co.jp>
Signed-off-by: Daniel Stone <***@collabora.com>
---
libweston/compositor-drm.c | 61 +++++++++++++++++++++++-----------------------
1 file changed, 30 insertions(+), 31 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index af43a15..7dbfc6b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -251,16 +251,39 @@ drm_sprite_crtc_supported(struct drm_output *output, struct drm_sprite *sprite)
}

static void
-drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+drm_fb_destroy(struct drm_fb *fb)
{
- struct drm_fb *fb = data;
+ drmModeRmFB(fb->fd, fb->fb_id);
+ weston_buffer_reference(&fb->buffer_ref, NULL);
+ free(fb);
+}
+
+static void
+drm_fb_destroy_dumb(struct drm_fb *fb)
+{
+ struct drm_mode_destroy_dumb destroy_arg;

- if (fb->fb_id)
- drmModeRmFB(fb->fd, fb->fb_id);
+ if (!fb)
+ return;

- weston_buffer_reference(&fb->buffer_ref, NULL);
+ assert(fb->type == BUFFER_PIXMAN_DUMB);
+
+ munmap(fb->map, fb->size);
+
+ memset(&destroy_arg, 0, sizeof(destroy_arg));
+ destroy_arg.handle = fb->handle;
+ drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+ drm_fb_destroy(fb);
+}
+
+static void
+drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
+{
+ struct drm_fb *fb = data;

- free(data);
+ assert(fb->type == BUFFER_GBM_SURFACE || fb->type == BUFFER_CLIENT);
+ drm_fb_destroy(fb);
}

static struct drm_fb *
@@ -359,30 +382,6 @@ err_fb:
return NULL;
}

-static void
-drm_fb_destroy_dumb(struct drm_fb *fb)
-{
- struct drm_mode_destroy_dumb destroy_arg;
-
- assert(fb->type == BUFFER_PIXMAN_DUMB);
-
- if (!fb->map)
- return;
-
- if (fb->fb_id)
- drmModeRmFB(fb->fd, fb->fb_id);
-
- weston_buffer_reference(&fb->buffer_ref, NULL);
-
- munmap(fb->map, fb->size);
-
- memset(&destroy_arg, 0, sizeof(destroy_arg));
- destroy_arg.handle = fb->handle;
- drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
-
- free(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)
@@ -450,7 +449,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
goto err_free;
}

- gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
+ gbm_bo_set_user_data(bo, fb, drm_fb_destroy_gbm);

return fb;
--
2.9.3
Pekka Paalanen
2017-02-21 13:58:58 UTC
Permalink
On Fri, 9 Dec 2016 19:57:32 +0000
Post by Daniel Stone
The drm_fb destroy callback to mostly the same thing regardless of
whether the buffer is a dumb buffer or gbm buffer. This patch refactors
the common parts into a new function that can be called for both cases.
[daniels: Rebased on top of fb->fd changes, cosmetic changes.]
Differential Revision: https://phabricator.freedesktop.org/D1489
---
libweston/compositor-drm.c | 61 +++++++++++++++++++++++-----------------------
1 file changed, 30 insertions(+), 31 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index af43a15..7dbfc6b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -251,16 +251,39 @@ drm_sprite_crtc_supported(struct drm_output *output, struct drm_sprite *sprite)
}
static void
-drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+drm_fb_destroy(struct drm_fb *fb)
{
- struct drm_fb *fb = data;
+ drmModeRmFB(fb->fd, fb->fb_id);
fb_id cannot be zero, even thought earlier there was a check. Ok.
Post by Daniel Stone
+ weston_buffer_reference(&fb->buffer_ref, NULL);
+ free(fb);
+}
+
+static void
+drm_fb_destroy_dumb(struct drm_fb *fb)
+{
+ struct drm_mode_destroy_dumb destroy_arg;
- if (fb->fb_id)
- drmModeRmFB(fb->fd, fb->fb_id);
+ if (!fb)
+ return;
Silent acceptance of NULL argument, really?
Post by Daniel Stone
- weston_buffer_reference(&fb->buffer_ref, NULL);
+ assert(fb->type == BUFFER_PIXMAN_DUMB);
+
fb->map cannot be NULL, even thought earlier there was a check. Ok.
Post by Daniel Stone
+ munmap(fb->map, fb->size);
+
+ memset(&destroy_arg, 0, sizeof(destroy_arg));
+ destroy_arg.handle = fb->handle;
+ drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+ drm_fb_destroy(fb);
+}
With that one nit of silent acceptance fixed:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Daniel Stone
2016-12-09 19:57:40 UTC
Permalink
Clean up some ambiguity around current/next: current could previously
have referred to a buffer which was being displayed, or the last buffer
being displayed whilst we waited for a configuration we'd requested to
take effect.

Introduce a new variable, fb_last, which exclusively holds the latter
case, thus leaving us with three unambiguous members: fb_pending is used
as a scratch space for a buffer we're about to post, fb_current holds
the buffer we last requested to display (whether active yet or not), and
fb_last is again scratch space for a buffer which is no longer being
displayed after a previous configuration reqeust.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1411
---
libweston/compositor-drm.c | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5909239..0d3ca35 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -190,7 +190,7 @@ struct drm_output {
int current_cursor;

struct weston_plane fb_plane;
- struct drm_fb *fb_current, *fb_pending;
+ struct drm_fb *fb_current, *fb_pending, *fb_last;
struct backlight *backlight;

struct drm_fb *dumb[2];
@@ -211,7 +211,7 @@ struct drm_sprite {

struct weston_plane plane;

- struct drm_fb *fb_current, *fb_pending;
+ struct drm_fb *fb_current, *fb_pending, *fb_last;
struct drm_output *output;
struct drm_backend *backend;

@@ -755,6 +755,8 @@ drm_output_repaint(struct weston_output *output_base,
if (output->disable_pending || output->destroy_pending)
return -1;

+ assert(!output->fb_last);
+
drm_output_render(output, damage);
if (!output->fb_pending)
return -1;
@@ -780,6 +782,10 @@ drm_output_repaint(struct weston_output *output_base,
goto err_pageflip;
}

+ output->fb_last = output->fb_current;
+ output->fb_current = output->fb_pending;
+ output->fb_pending = NULL;
+
output->page_flip_pending = 1;

drm_output_set_cursor(output);
@@ -794,6 +800,8 @@ drm_output_repaint(struct weston_output *output_base,
.request.sequence = 1,
};

+ /* XXX: Set output much earlier, so we don't attempt to place
+ * planes on entirely the wrong output. */
if ((!s->fb_current && !s->fb_pending) ||
!drm_sprite_crtc_supported(output, s))
continue;
@@ -825,6 +833,9 @@ drm_output_repaint(struct weston_output *output_base,
}

s->output = output;
+ s->fb_last = s->fb_current;
+ s->fb_current = s->fb_pending;
+ s->fb_pending = NULL;
output->vblank_pending = 1;
}

@@ -935,9 +946,9 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
drm_output_update_msc(output, frame);
output->vblank_pending = 0;

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

if (!output->page_flip_pending) {
ts.tv_sec = sec;
@@ -965,9 +976,8 @@ page_flip_handler(int fd, unsigned int frame,
* we just want to page flip to the current buffer to get an accurate
* timestamp */
if (output->page_flip_pending) {
- drm_fb_unref(output->fb_current);
- output->fb_current = output->fb_pending;
- output->fb_pending = NULL;
+ drm_fb_unref(output->fb_last);
+ output->fb_last = NULL;
}

output->page_flip_pending = 0;
@@ -1486,10 +1496,16 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
output->base.current_mode->flags =
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;

- /* reset rendering stuff. */
+ /* XXX: This drops our current buffer too early, before we've started
+ * displaying it. Ideally this should be much more atomic and
+ * integrated with a full repaint cycle, rather than doing a
+ * sledgehammer modeswitch first, and only later showing new
+ * content.
+ */
drm_fb_unref(output->fb_current);
- drm_fb_unref(output->fb_pending);
- output->fb_current = output->fb_pending = NULL;
+ assert(!output->fb_last);
+ assert(!output->fb_pending);
+ output->fb_last = output->fb_current = NULL;

if (b->use_pixman) {
drm_output_fini_pixman(output);
@@ -2705,6 +2721,7 @@ create_sprites(struct drm_backend *b)

sprite->possible_crtcs = plane->possible_crtcs;
sprite->plane_id = plane->plane_id;
+ sprite->fb_last = NULL;
sprite->fb_current = NULL;
sprite->fb_pending = NULL;
sprite->backend = b;
@@ -2736,8 +2753,9 @@ destroy_sprites(struct drm_backend *backend)
sprite->plane_id,
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
+ drm_fb_unref(sprite->fb_last);
drm_fb_unref(sprite->fb_current);
- drm_fb_unref(sprite->fb_pending);
+ assert(!sprite->fb_pending);
weston_plane_release(&sprite->plane);
free(sprite);
}
--
2.9.3
Daniel Stone
2016-12-09 19:57:43 UTC
Permalink
We make the differentiation where planes are an abstract framebuffer
with a position within a CRTC/output, and sprites are special cases of
planes that are neither the primary (base/framebuffer) nor cursor plane.

drm_sprite, OTOH, contains nothing that's actually specific to sprites,
and we end up duplicating a lot of code to deal with them, especially
when we come to use an entirely plane-based interface with atomic
modesetting.

Rename drm_sprite to drm_plane, to reflect that it's actually generic.

No functional changes.

Signed-off-by: Daniel Stone <***@collabora.com>
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>
Reviewed-by: Quentin Glidic <sardemff7+***@sardemff7.net>

Differential Revision: https://phabricator.freedesktop.org/D1408
---
libweston/compositor-drm.c | 187 ++++++++++++++++++++++++---------------------
1 file changed, 98 insertions(+), 89 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index bd6170a..1b123b5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -164,6 +164,42 @@ struct drm_edid {
char serial_number[13];
};

+/**
+ * A plane represents one buffer, positioned within a CRTC, and stacked
+ * relative to other planes on the same CRTC.
+ *
+ * Each CRTC has a 'primary plane', which use used to display the classic
+ * framebuffer contents, as accessed through the legacy drmModeSetCrtc
+ * call (which combines setting the CRTC's actual physical mode, and the
+ * properties of the primary plane).
+ *
+ * The cursor plane also has its own alternate legacy API.
+ *
+ * Other planes are used opportunistically to display content we do not
+ * wish to blit into the primary plane. These non-primary/cursor planes
+ * are referred to as 'sprites'.
+ */
+struct drm_plane {
+ struct wl_list link;
+
+ struct weston_plane base;
+
+ struct drm_fb *fb_current, *fb_pending, *fb_last;
+ struct drm_output *output;
+ struct drm_backend *backend;
+
+ uint32_t possible_crtcs;
+ uint32_t plane_id;
+ uint32_t count_formats;
+
+ int32_t src_x, src_y;
+ uint32_t src_w, src_h;
+ uint32_t dest_x, dest_y;
+ uint32_t dest_w, dest_h;
+
+ uint32_t formats[];
+};
+
struct drm_output {
struct weston_output base;
drmModeConnector *connector;
@@ -202,31 +238,6 @@ struct drm_output {
struct wl_listener recorder_frame_listener;
};

-/*
- * An output has a primary display plane plus zero or more sprites for
- * blending display contents.
- */
-struct drm_sprite {
- struct wl_list link;
-
- struct weston_plane plane;
-
- struct drm_fb *fb_current, *fb_pending, *fb_last;
- struct drm_output *output;
- struct drm_backend *backend;
-
- uint32_t possible_crtcs;
- uint32_t plane_id;
- uint32_t count_formats;
-
- int32_t src_x, src_y;
- uint32_t src_w, src_h;
- uint32_t dest_x, dest_y;
- uint32_t dest_w, dest_h;
-
- uint32_t formats[];
-};
-
static struct gl_renderer_interface *gl_renderer;

static const char default_seat[] = "seat0";
@@ -250,9 +261,9 @@ static void
drm_output_update_msc(struct drm_output *output, unsigned int seq);

static int
-drm_sprite_crtc_supported(struct drm_output *output, struct drm_sprite *sprite)
+drm_plane_crtc_supported(struct drm_output *output, struct drm_plane *plane)
{
- return !!(sprite->possible_crtcs & (1 << output->pipe));
+ return !!(plane->possible_crtcs & (1 << output->pipe));
}

static void
@@ -748,7 +759,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_sprite *s;
+ struct drm_plane *s;
struct drm_mode *mode;
int ret = 0;

@@ -801,10 +812,8 @@ drm_output_repaint(struct weston_output *output_base,
.request.sequence = 1,
};

- /* XXX: Set output much earlier, so we don't attempt to place
- * planes on entirely the wrong output. */
if ((!s->fb_current && !s->fb_pending) ||
- !drm_sprite_crtc_supported(output, s))
+ !drm_plane_crtc_supported(output, s))
continue;

if (s->fb_pending && !backend->sprites_hidden)
@@ -944,7 +953,7 @@ static void
vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
void *data)
{
- struct drm_sprite *s = (struct drm_sprite *)data;
+ 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 |
@@ -1003,7 +1012,7 @@ page_flip_handler(int fd, unsigned int frame,
}

static uint32_t
-drm_output_check_sprite_format(struct drm_sprite *s,
+drm_output_check_plane_format(struct drm_plane *p,
struct weston_view *ev, struct gbm_bo *bo)
{
uint32_t i, format;
@@ -1024,8 +1033,8 @@ drm_output_check_sprite_format(struct drm_sprite *s,
pixman_region32_fini(&r);
}

- for (i = 0; i < s->count_formats; i++)
- if (s->formats[i] == format)
+ for (i = 0; i < p->count_formats; i++)
+ if (p->formats[i] == format)
return format;

return 0;
@@ -1039,7 +1048,7 @@ drm_output_prepare_overlay_view(struct drm_output *output,
struct drm_backend *b = to_drm_backend(ec);
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_resource *buffer_resource;
- struct drm_sprite *s;
+ struct drm_plane *p;
struct linux_dmabuf_buffer *dmabuf;
int found = 0;
struct gbm_bo *bo;
@@ -1075,11 +1084,11 @@ drm_output_prepare_overlay_view(struct drm_output *output,
if (ev->alpha != 1.0f)
return NULL;

- wl_list_for_each(s, &b->sprite_list, link) {
- if (!drm_sprite_crtc_supported(output, s))
+ wl_list_for_each(p, &b->sprite_list, link) {
+ if (!drm_plane_crtc_supported(output, p))
continue;

- if (!s->fb_pending) {
+ if (!p->fb_pending) {
found = 1;
break;
}
@@ -1121,23 +1130,23 @@ drm_output_prepare_overlay_view(struct drm_output *output,
if (!bo)
return NULL;

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

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

- drm_fb_set_buffer(s->fb_pending, ev->surface->buffer_ref.buffer);
+ drm_fb_set_buffer(p->fb_pending, ev->surface->buffer_ref.buffer);

box = pixman_region32_extents(&ev->transform.boundingbox);
- s->plane.x = box->x1;
- s->plane.y = box->y1;
+ p->base.x = box->x1;
+ p->base.y = box->y1;

/*
* Calculate the source & dest rects properly based on actual
@@ -1154,10 +1163,10 @@ drm_output_prepare_overlay_view(struct drm_output *output,
output->base.transform,
output->base.current_scale,
*box);
- s->dest_x = tbox.x1;
- s->dest_y = tbox.y1;
- s->dest_w = tbox.x2 - tbox.x1;
- s->dest_h = tbox.y2 - tbox.y1;
+ p->dest_x = tbox.x1;
+ p->dest_y = tbox.y1;
+ p->dest_w = tbox.x2 - tbox.x1;
+ p->dest_h = tbox.y2 - tbox.y1;
pixman_region32_fini(&dest_rect);

pixman_region32_init(&src_rect);
@@ -1194,13 +1203,13 @@ drm_output_prepare_overlay_view(struct drm_output *output,
viewport->buffer.scale,
tbox);

- s->src_x = tbox.x1 << 8;
- s->src_y = tbox.y1 << 8;
- s->src_w = (tbox.x2 - tbox.x1) << 8;
- s->src_h = (tbox.y2 - tbox.y1) << 8;
+ 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;
pixman_region32_fini(&src_rect);

- return &s->plane;
+ return &p->base;
}

static struct weston_plane *
@@ -2697,71 +2706,71 @@ create_output_for_connector(struct drm_backend *b,
static void
create_sprites(struct drm_backend *b)
{
- struct drm_sprite *sprite;
- drmModePlaneRes *plane_res;
- drmModePlane *plane;
+ struct drm_plane *plane;
+ drmModePlaneRes *kplane_res;
+ drmModePlane *kplane;
uint32_t i;

- plane_res = drmModeGetPlaneResources(b->drm.fd);
- if (!plane_res) {
+ kplane_res = drmModeGetPlaneResources(b->drm.fd);
+ if (!kplane_res) {
weston_log("failed to get plane resources: %s\n",
strerror(errno));
return;
}

- for (i = 0; i < plane_res->count_planes; i++) {
- plane = drmModeGetPlane(b->drm.fd, plane_res->planes[i]);
- if (!plane)
+ for (i = 0; i < kplane_res->count_planes; i++) {
+ kplane = drmModeGetPlane(b->drm.fd, kplane_res->planes[i]);
+ if (!kplane)
continue;

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

- sprite->possible_crtcs = plane->possible_crtcs;
- sprite->plane_id = plane->plane_id;
- sprite->fb_last = NULL;
- sprite->fb_current = NULL;
- sprite->fb_pending = NULL;
- sprite->backend = b;
- sprite->count_formats = plane->count_formats;
- memcpy(sprite->formats, plane->formats,
- plane->count_formats * sizeof(plane->formats[0]));
- drmModeFreePlane(plane);
- weston_plane_init(&sprite->plane, b->compositor, 0, 0);
- weston_compositor_stack_plane(b->compositor, &sprite->plane,
+ plane->possible_crtcs = kplane->possible_crtcs;
+ plane->plane_id = kplane->plane_id;
+ plane->fb_last = NULL;
+ plane->fb_current = NULL;
+ plane->fb_pending = NULL;
+ plane->backend = b;
+ plane->count_formats = kplane->count_formats;
+ memcpy(plane->formats, kplane->formats,
+ kplane->count_formats * sizeof(kplane->formats[0]));
+ drmModeFreePlane(kplane);
+ weston_plane_init(&plane->base, b->compositor, 0, 0);
+ weston_compositor_stack_plane(b->compositor, &plane->base,
&b->compositor->primary_plane);

- wl_list_insert(&b->sprite_list, &sprite->link);
+ wl_list_insert(&b->sprite_list, &plane->link);
}

- drmModeFreePlaneResources(plane_res);
+ drmModeFreePlaneResources(kplane_res);
}

static void
destroy_sprites(struct drm_backend *backend)
{
- struct drm_sprite *sprite, *next;
+ struct drm_plane *plane, *next;
struct drm_output *output;

output = container_of(backend->compositor->output_list.next,
struct drm_output, base.link);

- wl_list_for_each_safe(sprite, next, &backend->sprite_list, link) {
+ wl_list_for_each_safe(plane, next, &backend->sprite_list, link) {
drmModeSetPlane(backend->drm.fd,
- sprite->plane_id,
+ plane->plane_id,
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- drm_fb_unref(sprite->fb_last);
- drm_fb_unref(sprite->fb_current);
- assert(!sprite->fb_pending);
- weston_plane_release(&sprite->plane);
- free(sprite);
+ drm_fb_unref(plane->fb_last);
+ drm_fb_unref(plane->fb_current);
+ assert(!plane->fb_pending);
+ weston_plane_release(&plane->base);
+ free(plane);
}
}

@@ -2953,7 +2962,7 @@ session_notify(struct wl_listener *listener, void *data)
{
struct weston_compositor *compositor = data;
struct drm_backend *b = to_drm_backend(compositor);
- struct drm_sprite *sprite;
+ struct drm_plane *sprite;
struct drm_output *output;

if (compositor->session_active) {
--
2.9.3
Daniel Stone
2016-12-09 19:57:33 UTC
Permalink
We only need it for the GBM surface the FB was originally created
against; a mismatch here is very bad indeed, so no reason to pass it in
explictly every time rather than store it.

Differential Revision: https://phabricator.freedesktop.org/D1490

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7dbfc6b..eb735b2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -148,6 +148,7 @@ struct drm_fb {

/* Used by gbm fbs */
struct gbm_bo *bo;
+ struct gbm_surface *gbm_surface;

/* Used by dumb fbs */
void *map;
@@ -466,7 +467,7 @@ drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
}

static void
-drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
+drm_fb_unref(struct drm_fb *fb)
{
if (!fb)
return;
@@ -479,7 +480,7 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
gbm_bo_destroy(fb->bo);
break;
case BUFFER_GBM_SURFACE:
- gbm_surface_release_buffer(output->gbm_surface, fb->bo);
+ gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
break;
default:
assert(NULL);
@@ -615,6 +616,7 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
gbm_surface_release_buffer(output->gbm_surface, bo);
return;
}
+ output->next->gbm_surface = output->gbm_surface;
}

static void
@@ -798,7 +800,7 @@ drm_output_repaint(struct weston_output *output_base,
err_pageflip:
output->cursor_view = NULL;
if (output->next) {
- drm_output_release_fb(output, output->next);
+ drm_fb_unref(output->next);
output->next = NULL;
}

@@ -900,7 +902,7 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
drm_output_update_msc(output, frame);
output->vblank_pending = 0;

- drm_output_release_fb(output, s->current);
+ drm_fb_unref(s->current);
s->current = s->next;
s->next = NULL;

@@ -930,7 +932,7 @@ page_flip_handler(int fd, unsigned int frame,
* we just want to page flip to the current buffer to get an accurate
* timestamp */
if (output->page_flip_pending) {
- drm_output_release_fb(output, output->current);
+ drm_fb_unref(output->current);
output->current = output->next;
output->next = NULL;
}
@@ -1452,8 +1454,8 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;

/* reset rendering stuff. */
- drm_output_release_fb(output, output->current);
- drm_output_release_fb(output, output->next);
+ drm_fb_unref(output->current);
+ drm_fb_unref(output->next);
output->current = output->next = NULL;

if (b->use_pixman) {
@@ -2672,8 +2674,8 @@ destroy_sprites(struct drm_backend *backend)
sprite->plane_id,
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- drm_output_release_fb(output, sprite->current);
- drm_output_release_fb(output, sprite->next);
+ drm_fb_unref(sprite->current);
+ drm_fb_unref(sprite->next);
weston_plane_release(&sprite->plane);
free(sprite);
}
--
2.9.3
Armin Krezović
2016-12-09 21:08:44 UTC
Permalink
Post by Daniel Stone
We only need it for the GBM surface the FB was originally created
against; a mismatch here is very bad indeed, so no reason to pass it in
explictly every time rather than store it.
Differential Revision: https://phabricator.freedesktop.org/D1490
Makes sense.
Post by Daniel Stone
---
libweston/compositor-drm.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7dbfc6b..eb735b2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -148,6 +148,7 @@ struct drm_fb {
/* Used by gbm fbs */
struct gbm_bo *bo;
+ struct gbm_surface *gbm_surface;
/* Used by dumb fbs */
void *map;
@@ -466,7 +467,7 @@ drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
}
static void
-drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
+drm_fb_unref(struct drm_fb *fb)
{
if (!fb)
return;
@@ -479,7 +480,7 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
gbm_bo_destroy(fb->bo);
break;
- gbm_surface_release_buffer(output->gbm_surface, fb->bo);
+ gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
break;
assert(NULL);
@@ -615,6 +616,7 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
gbm_surface_release_buffer(output->gbm_surface, bo);
return;
}
+ output->next->gbm_surface = output->gbm_surface;
}
static void
@@ -798,7 +800,7 @@ drm_output_repaint(struct weston_output *output_base,
output->cursor_view = NULL;
if (output->next) {
- drm_output_release_fb(output, output->next);
+ drm_fb_unref(output->next);
output->next = NULL;
}
@@ -900,7 +902,7 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
drm_output_update_msc(output, frame);
output->vblank_pending = 0;
- drm_output_release_fb(output, s->current);
+ drm_fb_unref(s->current);
s->current = s->next;
s->next = NULL;
@@ -930,7 +932,7 @@ page_flip_handler(int fd, unsigned int frame,
* we just want to page flip to the current buffer to get an accurate
* timestamp */
if (output->page_flip_pending) {
- drm_output_release_fb(output, output->current);
+ drm_fb_unref(output->current);
output->current = output->next;
output->next = NULL;
}
@@ -1452,8 +1454,8 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
/* reset rendering stuff. */
- drm_output_release_fb(output, output->current);
- drm_output_release_fb(output, output->next);
+ drm_fb_unref(output->current);
+ drm_fb_unref(output->next);
output->current = output->next = NULL;
if (b->use_pixman) {
@@ -2672,8 +2674,8 @@ destroy_sprites(struct drm_backend *backend)
sprite->plane_id,
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- drm_output_release_fb(output, sprite->current);
- drm_output_release_fb(output, sprite->next);
+ drm_fb_unref(sprite->current);
+ drm_fb_unref(sprite->next);
weston_plane_release(&sprite->plane);
free(sprite);
}
Pekka Paalanen
2017-02-21 14:43:23 UTC
Permalink
On Fri, 9 Dec 2016 19:57:33 +0000
Post by Daniel Stone
We only need it for the GBM surface the FB was originally created
against; a mismatch here is very bad indeed, so no reason to pass it in
explictly every time rather than store it.
Differential Revision: https://phabricator.freedesktop.org/D1490
---
libweston/compositor-drm.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7dbfc6b..eb735b2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -148,6 +148,7 @@ struct drm_fb {
/* Used by gbm fbs */
struct gbm_bo *bo;
+ struct gbm_surface *gbm_surface;
/* Used by dumb fbs */
void *map;
@@ -466,7 +467,7 @@ drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
}
static void
-drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
+drm_fb_unref(struct drm_fb *fb)
Hi,

first I was slightly surprised by the renaming, but reading the patch
thoroughly and seeing the following patch explained why it's nice to do
here. It would have saved a bit of effort if it was mentioned in the
commit message.

Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Daniel Stone
2016-12-09 19:57:41 UTC
Permalink
vblank_pending is currently a bool, which is reset on every vblank
requests (i.e. sprite pageflip). This can occur more than once per
frame, so turn it into a callback, so we only fire frame-done when we've
collected all the events.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1418
---
libweston/compositor-drm.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 0d3ca35..382c8e2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -836,7 +836,7 @@ drm_output_repaint(struct weston_output *output_base,
s->fb_last = s->fb_current;
s->fb_current = s->fb_pending;
s->fb_pending = NULL;
- output->vblank_pending = 1;
+ output->vblank_pending++;
}

return 0;
@@ -944,13 +944,14 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;

drm_output_update_msc(output, frame);
- output->vblank_pending = 0;
+ 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;

- if (!output->page_flip_pending) {
+ if (!output->page_flip_pending && !output->vblank_pending) {
ts.tv_sec = sec;
ts.tv_nsec = usec * 1000;
weston_output_finish_frame(&output->base, &ts, flags);
--
2.9.3
Daniel Stone
2016-12-09 19:57:29 UTC
Permalink
This will be used so we can later determine the compatibility of drm_fbs
without needing to introspect external state.

Differential Revision: https://phabricator.freedesktop.org/D1487

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a5052b9..4ef7343 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -131,6 +131,7 @@ struct drm_mode {

struct drm_fb {
uint32_t fb_id, stride, handle, size;
+ int width, height;
int fd;
int is_client_buffer;
struct weston_buffer_reference buffer_ref;
@@ -292,6 +293,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
fb->handle = create_arg.handle;
fb->stride = create_arg.pitch;
fb->size = create_arg.size;
+ fb->width = width;
+ fb->height = height;
fb->fd = b->drm.fd;

ret = -1;
@@ -371,7 +374,6 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
struct drm_backend *backend, uint32_t format)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
- int width, height;
uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
int ret;

@@ -384,17 +386,17 @@ drm_fb_get_from_bo(struct gbm_bo *bo,

fb->bo = bo;

- width = gbm_bo_get_width(bo);
- height = gbm_bo_get_height(bo);
+ 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->size = fb->stride * height;
+ fb->size = fb->stride * fb->height;
fb->fd = backend->drm.fd;

- if (backend->min_width > width ||
- width > backend->max_width ||
- backend->min_height > height ||
- height > backend->max_height) {
+ 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;
}
@@ -406,7 +408,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
pitches[0] = fb->stride;
offsets[0] = 0;

- ret = drmModeAddFB2(backend->drm.fd, width, height,
+ ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
format, handles, pitches, offsets,
&fb->fb_id, 0);
if (ret) {
@@ -417,8 +419,8 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
}

if (ret)
- ret = drmModeAddFB(backend->drm.fd, width, height, 24, 32,
- fb->stride, fb->handle, &fb->fb_id);
+ ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
+ 24, 32, fb->stride, fb->handle, &fb->fb_id);

if (ret) {
weston_log("failed to create kms fb: %m\n");
--
2.9.3
Armin Krezović
2016-12-09 21:04:16 UTC
Permalink
Post by Daniel Stone
This will be used so we can later determine the compatibility of drm_fbs
without needing to introspect external state.
Differential Revision: https://phabricator.freedesktop.org/D1487
Looks fine.
Post by Daniel Stone
---
libweston/compositor-drm.c | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a5052b9..4ef7343 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -131,6 +131,7 @@ struct drm_mode {
struct drm_fb {
uint32_t fb_id, stride, handle, size;
+ int width, height;
int fd;
int is_client_buffer;
struct weston_buffer_reference buffer_ref;
@@ -292,6 +293,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
fb->handle = create_arg.handle;
fb->stride = create_arg.pitch;
fb->size = create_arg.size;
+ fb->width = width;
+ fb->height = height;
fb->fd = b->drm.fd;
ret = -1;
@@ -371,7 +374,6 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
struct drm_backend *backend, uint32_t format)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
- int width, height;
uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
int ret;
@@ -384,17 +386,17 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
fb->bo = bo;
- width = gbm_bo_get_width(bo);
- height = gbm_bo_get_height(bo);
+ 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->size = fb->stride * height;
+ fb->size = fb->stride * fb->height;
fb->fd = backend->drm.fd;
- if (backend->min_width > width ||
- width > backend->max_width ||
- backend->min_height > height ||
- height > backend->max_height) {
+ 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;
}
@@ -406,7 +408,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
pitches[0] = fb->stride;
offsets[0] = 0;
- ret = drmModeAddFB2(backend->drm.fd, width, height,
+ ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
format, handles, pitches, offsets,
&fb->fb_id, 0);
if (ret) {
@@ -417,8 +419,8 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
}
if (ret)
- ret = drmModeAddFB(backend->drm.fd, width, height, 24, 32,
- fb->stride, fb->handle, &fb->fb_id);
+ ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
+ 24, 32, fb->stride, fb->handle, &fb->fb_id);
if (ret) {
weston_log("failed to create kms fb: %m\n");
Daniel Stone
2016-12-09 19:58:05 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>

Differential Revision: https://phabricator.freedesktop.org/D1519
---
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 202772b..1dc63c8 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2435,10 +2435,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;
@@ -2468,16 +2466,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;
@@ -2488,6 +2476,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
@@ -2504,26 +2512,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.9.3
Daniel Stone
2016-12-09 19:57:46 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

Add awareness of, rather than support for, universal planes. Activate
the client cap when we start if possible, and if this is activated,
studiously ignore non-overlay planes. For now.

[daniels: Rebased, split up, otherwise modified.]

Differential Revision: https://phabricator.freedesktop.org/D1495

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.co.uk>
---
libweston/compositor-drm.c | 406 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 406 insertions(+)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e4d6743..46fcf0a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -62,10 +62,18 @@
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf.h"

+#ifndef static_assert
+#define static_assert(cond, msg) assert((cond) && msg)
+#endif
+
#ifndef DRM_CAP_TIMESTAMP_MONOTONIC
#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
#endif

+#ifndef DRM_CLIENT_CAP_UNIVERSAL_PLANES
+#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
+#endif
+
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
@@ -78,6 +86,41 @@
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif

+/**
+ * Holding structure for one DRM property.
+ */
+struct property_item {
+ drmModePropertyRes *drm_prop;
+ uint64_t value;
+};
+
+/**
+ * List of properties attached to DRM planes
+ */
+enum wdrm_plane_property {
+ WDRM_PLANE_TYPE = 0,
+ WDRM_PLANE__COUNT
+};
+
+/**
+ * Possible values for the WDRM_PLANE_TYPE property.
+ */
+enum wdrm_plane_type {
+ WDRM_PLANE_TYPE_PRIMARY = 0,
+ WDRM_PLANE_TYPE_CURSOR,
+ WDRM_PLANE_TYPE_OVERLAY,
+ WDRM_PLANE_TYPE__COUNT
+};
+
+/**
+ * Holding structure for plane properties.
+ */
+struct plane_properties {
+ struct property_item item[WDRM_PLANE__COUNT];
+ uint64_t typemap[WDRM_PLANE_TYPE__COUNT]; /**< map for type enum */
+ uint64_t value;
+};
+
struct drm_backend {
struct weston_backend base;
struct weston_compositor *compositor;
@@ -115,6 +158,8 @@ struct drm_backend {

int cursors_are_broken;

+ bool universal_planes;
+
int use_pixman;

struct udev_input input;
@@ -188,6 +233,9 @@ struct drm_plane {
struct drm_output *output;
struct drm_backend *backend;

+ enum wdrm_plane_type type;
+ struct plane_properties props;
+
uint32_t possible_crtcs;
uint32_t plane_id;
uint32_t count_formats;
@@ -255,6 +303,342 @@ to_drm_backend(struct weston_compositor *base)
return container_of(base->backend, struct drm_backend, base);
}

+/**
+ * Return a string describing the type of a DRM object
+ */
+static const char *
+drm_object_type_str(uint32_t obj_type)
+{
+ switch (obj_type) {
+ case DRM_MODE_OBJECT_CRTC: return "crtc";
+ case DRM_MODE_OBJECT_CONNECTOR: return "connector";
+ case DRM_MODE_OBJECT_ENCODER: return "encoder";
+ case DRM_MODE_OBJECT_MODE: return "mode";
+ case DRM_MODE_OBJECT_PROPERTY: return "property";
+ case DRM_MODE_OBJECT_FB: return "fb";
+ case DRM_MODE_OBJECT_BLOB: return "blob";
+ case DRM_MODE_OBJECT_PLANE: return "plane";
+ default: return "???";
+ }
+}
+
+/**
+ * Cache a mapping from static values to a DRM property enum
+ *
+ * DRM property enum values are dynamic at runtime; the user must query the
+ * property to find out the desired runtime value for a requested string
+ * name. Using the 'type' field on planes as an example, there is no single
+ * hardcoded constant for primary plane types; instead, the property must be
+ * queried at runtime to find the value associated with the string "Primary".
+ *
+ * This helper queries and caches the enum values, to allow us to use a set
+ * of compile-time-constant enums portably across various implementations.
+ * The values given in enum_names are searched for, and stored in the
+ * same-indexed field of the map array.
+ *
+ * For example, if the DRM driver exposes 'Foo' as value 7 and 'Bar' as value
+ * 19, passing in enum_names containing 'Foo' and 'Bar' in that order, will
+ * populate map with 7 and 19, in that order.
+ *
+ * This should always be used with static C enums to represent each value, e.g.
+ * WDRM_PLANE_MISC_FOO, WDRM_PLANE_MISC_BAR.
+ *
+ * Property lookup is not mandatory and may fail; users should carefully
+ * check the return value, which is a bitmask populated with the indices
+ * of properties which were successfully looked up. Values not successfully
+ * looked up may return 0, which may represent a false positive.
+ *
+ * @param prop DRM enum property to cache map for
+ * @param map Array for enum values; each entry is written for the corresponding
+ * entry in enum_names
+ * @param enum_names List of string values to look up enum values for
+ * @param nenums Length of enum_names and map arrays
+ *
+ * @returns Bitmask of populated entries in map
+ */
+static uint32_t
+drm_property_get_enum_map(const drmModePropertyRes *prop, uint64_t *map,
+ const char * const *enum_names, int nenums)
+{
+ uint32_t valid_mask = 0;
+ int seen = 0;
+ int i, j;
+
+ assert(nenums <= 32 && "update return type");
+ memset(map, 0, nenums * sizeof *map);
+
+ if (!prop) {
+ weston_log("DRM warning: attempting to init property enum, "
+ "that was not found. (e.g. '%s')\n",
+ enum_names[0]);
+ return 0;
+ }
+
+ if (!(prop->flags & DRM_MODE_PROP_ENUM) || prop->count_enums < 1) {
+ weston_log("DRM error: property %d '%s' is not an enum.\n",
+ prop->prop_id, prop->name);
+ return 0;
+ }
+
+ for (i = 0; i < prop->count_enums; i++) {
+ struct drm_mode_property_enum *en = &prop->enums[i];
+
+ for (j = 0; j < nenums; j++) {
+ if (!strcmp(en->name, enum_names[j]))
+ break;
+ }
+
+ if (j == nenums) {
+ weston_log("DRM debug: property %d '%s' "
+ "has unrecognized enum %#" PRIx64 " '%s'\n",
+ prop->prop_id, prop->name,
+ (uint64_t)en->value, en->name);
+ break;
+ }
+
+ map[j] = en->value;
+ valid_mask |= (1U << j);
+ seen++;
+ }
+
+ if (seen != nenums)
+ weston_log("DRM debug: property %d '%s' has %u of %u "
+ "expected enum values.\n",
+ prop->prop_id, prop->name, seen, nenums);
+
+ return valid_mask;
+}
+
+/**
+ * Cache DRM property values
+ *
+ * Given a particular DRM object, find specified properties by name, and
+ * cache their internal property_item representation. Taking a list of
+ * names in prop_names, each corresponding entry in prop_items (matching
+ * index) will be populated with the corresponding DRM property.
+ *
+ * All users of DRM object properties in this file should use this
+ * mechanism.
+ *
+ * Property lookup is not mandatory and may fail; users should carefully
+ * check the return value, which is a bitmask populated with the indices
+ * of properties which were successfully looked up.
+ *
+ * @param ec Internal DRM compositor structure
+ * @param prop_items Array of internal property representations
+ * @param prop_names Array of property names to look up
+ * @param nprops Length of prop_items and prop_names arrays
+ * @param obj_id DRM object ID
+ * @param obj_type DRM object type (DRM_MODE_OBJECT_*)
+ *
+ * @returns Bitmask of populated entries in prop_items
+ */
+static uint32_t
+drm_properties_get_from_obj(struct drm_backend *b,
+ struct property_item *prop_items,
+ const char * const *prop_names,
+ unsigned nprops,
+ uint32_t obj_id, uint32_t obj_type)
+{
+ drmModeObjectProperties *props;
+ drmModePropertyRes *prop;
+ uint32_t valid_mask = 0;
+ unsigned i, j;
+
+ assert(nprops <= 32 && "update return type");
+
+ memset(prop_items, 0, nprops * sizeof *prop_items);
+
+ props = drmModeObjectGetProperties(b->drm.fd, obj_id, obj_type);
+ if (!props) {
+ weston_log("DRM error : get properties for object %u "
+ "of type %#x '%s' failed.\n", obj_id, obj_type,
+ drm_object_type_str(obj_type));
+ return valid_mask;
+ }
+
+ for (i = 0; i < props->count_props; i++) {
+ prop = drmModeGetProperty(b->drm.fd, props->props[i]);
+ if (!prop)
+ continue;
+
+ for (j = 0; j < nprops; j++) {
+ if (strcmp(prop->name, prop_names[j]) == 0)
+ break;
+ }
+
+ if (j == nprops) {
+#ifdef DEBUG
+ weston_log("DRM debug: unrecognized property %u '%s' on"
+ " object %u of type %#x '%s'\n",
+ prop->prop_id, prop->name,
+ obj_id, obj_type,
+ drm_object_type_str(obj_type));
+#endif
+ drmModeFreeProperty(prop);
+ continue;
+ }
+
+ assert(prop_items[j].drm_prop == NULL);
+ prop_items[j].drm_prop = prop;
+ prop_items[j].value = props->prop_values[i];
+ valid_mask |= 1U << j;
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < nprops; i++) {
+ if (prop_items[i].drm_prop == NULL)
+ weston_log("DRM warning: property '%s' missing from obj"
+ " %u of type %#x '%s'\n", prop_names[i],
+ obj_id, obj_type,
+ drm_object_type_str(obj_type));
+ }
+#endif
+
+ drmModeFreeObjectProperties(props);
+
+ return valid_mask;
+}
+
+/**
+ * Frees an array of property_items
+ *
+ * @param items Array to free
+ * @param len Number of items in array
+ */
+static void
+property_item_array_release(struct property_item *items, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (items[i].drm_prop)
+ drmModeFreeProperty(items[i].drm_prop);
+ }
+}
+
+/**
+ * Get one property from a DRM plane
+ *
+ * Retrieve the cached value of a property on a DRM plane. The property passed
+ * must be valid for the plane, i.e. must have been in the return value of
+ * drm_properties_get_from_obj.
+ *
+ * @param plane Plane to retrieve property for
+ * @param prop Property to retrieve
+ */
+static uint64_t
+drm_plane_property_get(struct drm_plane *plane, enum wdrm_plane_property prop)
+{
+ assert(plane->props.item[prop].drm_prop);
+ return plane->props.item[prop].value;
+}
+
+/**
+ * Initialise DRM properties for planes
+ *
+ * Set up the holding structures to track DRM object properties set on planes.
+ * Free the memory allocated here with plane_properties_release.
+ *
+ * @param plane Plane to configure
+ */
+static bool
+plane_properties_init(struct drm_plane *plane)
+{
+ static const char * const plane_property_names[] = {
+ [WDRM_PLANE_TYPE] = "type",
+ };
+ static const char * const plane_type_names[] = {
+ [WDRM_PLANE_TYPE_PRIMARY] = "Primary",
+ [WDRM_PLANE_TYPE_OVERLAY] = "Overlay",
+ [WDRM_PLANE_TYPE_CURSOR] = "Cursor",
+ };
+ uint32_t valid_mask;
+ uint32_t type_mask;
+ uint32_t required_mask;
+
+ static_assert(ARRAY_LENGTH(plane_property_names) == WDRM_PLANE__COUNT,
+ "plane_property_names mismatch with the enum");
+ static_assert(ARRAY_LENGTH(plane_type_names) == WDRM_PLANE_TYPE__COUNT,
+ "plane_type_names mismatch with the enum");
+
+ valid_mask =
+ drm_properties_get_from_obj(plane->backend,
+ plane->props.item,
+ plane_property_names,
+ WDRM_PLANE__COUNT,
+ plane->plane_id,
+ DRM_MODE_OBJECT_PLANE);
+
+ required_mask = 0;
+ if (plane->backend->universal_planes)
+ required_mask |= 1 << WDRM_PLANE_TYPE;
+
+ if ((valid_mask & required_mask) != required_mask) {
+ weston_log("DRM error: failed to look up all plane properties "
+ "(wanted 0x%x got 0x%x) on ID %d\n",
+ required_mask, valid_mask, plane->plane_id);
+ return false;
+ }
+
+ /* Only the universal-plane enum below here. */
+ if (!plane->backend->universal_planes)
+ return true;
+
+ type_mask =
+ drm_property_get_enum_map(plane->props.item[WDRM_PLANE_TYPE].drm_prop,
+ plane->props.typemap,
+ plane_type_names,
+ WDRM_PLANE_TYPE__COUNT);
+
+ required_mask = \
+ (1 << WDRM_PLANE_TYPE_PRIMARY) | \
+ (1 << WDRM_PLANE_TYPE_CURSOR) | \
+ (1 << WDRM_PLANE_TYPE_OVERLAY);
+ if (type_mask != required_mask) {
+ weston_log("DRM error: failed to look up all plane type "
+ "enum members (wanted 0x%x got 0x%x) on ID %d\n",
+ required_mask, type_mask, plane->plane_id);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Free DRM plane properties
+ *
+ * The counterpart to plane_properties_init.
+ *
+ * @param plane Plane to release properties for
+ */
+static void plane_properties_release(struct drm_plane *plane)
+{
+ property_item_array_release(plane->props.item, WDRM_PLANE__COUNT);
+}
+
+/**
+ * Get type for a DRM plane
+ *
+ * Given a drm_plane object, find its type as an internal enum.
+ *
+ * @param plane Plane to find type for
+ * @returns Internal enum for plane type, or OVERLAY if unknown
+ */
+static enum wdrm_plane_type
+drm_plane_get_type(struct drm_plane *plane)
+{
+ uint64_t drm_type = drm_plane_property_get(plane, WDRM_PLANE_TYPE);
+ int i;
+
+ for (i = 0; i < WDRM_PLANE_TYPE__COUNT; i++) {
+ if (plane->props.typemap[i] == drm_type)
+ return i;
+ }
+
+ return WDRM_PLANE_TYPE_OVERLAY;
+}
+
static void
drm_output_set_cursor(struct drm_output *output);

@@ -1608,6 +1992,11 @@ init_drm(struct drm_backend *b, struct udev_device *device)
else
b->cursor_height = 64;

+ ret = drmSetClientCap(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");
+
return 0;
}

@@ -1736,6 +2125,16 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
memcpy(plane->formats, kplane->formats,
kplane->count_formats * sizeof(kplane->formats[0]));

+ if (!plane_properties_init(plane)) {
+ free(plane);
+ return NULL;
+ }
+
+ if (b->universal_planes)
+ plane->type = drm_plane_get_type(plane);
+ else
+ plane->type = WDRM_PLANE_TYPE_OVERLAY;
+
weston_plane_init(&plane->base, b->compositor, 0, 0);
wl_list_insert(&b->sprite_list, &plane->link);

@@ -1760,6 +2159,7 @@ drm_plane_destroy(struct drm_plane *plane)
assert(!plane->fb_pending);
weston_plane_release(&plane->base);
wl_list_remove(&plane->link);
+ plane_properties_release(plane);
free(plane);
}

@@ -1797,6 +2197,12 @@ create_sprites(struct drm_backend *b)
if (!drm_plane)
continue;

+ /* Ignore non-overlay planes for now. */
+ if (drm_plane->type != WDRM_PLANE_TYPE_OVERLAY) {
+ drm_plane_destroy(drm_plane);
+ continue;
+ }
+
weston_compositor_stack_plane(b->compositor, &drm_plane->base,
&b->compositor->primary_plane);
}
--
2.9.3
Daniel Stone
2016-12-09 19:57:42 UTC
Permalink
page_flip_pending is set when we do a no-effect pageflip, and thus don't
need to release the buffer as we don't have a new one pending.

Now we have last/cur/pending properly broken out, we can just use these
consistently, and test if last != current (the effect), rather than the
specific path which triggered it.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1417
---
libweston/compositor-drm.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 382c8e2..bd6170a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -786,6 +786,7 @@ drm_output_repaint(struct weston_output *output_base,
output->fb_current = output->fb_pending;
output->fb_pending = NULL;

+ assert(!output->page_flip_pending);
output->page_flip_pending = 1;

drm_output_set_cursor(output);
@@ -907,12 +908,18 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
*/
fb_id = output->fb_current->fb_id;

+ assert(!output->page_flip_pending);
+ assert(!output->fb_last);
+
if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
weston_log("queueing pageflip failed: %m\n");
goto finish_frame;
}

+ output->fb_last = drm_fb_ref(output->fb_current);
+ output->page_flip_pending = 1;
+
return;

finish_frame:
@@ -973,16 +980,12 @@ page_flip_handler(int fd, unsigned int frame,

drm_output_update_msc(output, frame);

- /* We don't set page_flip_pending on start_repaint_loop, in that case
- * we just want to page flip to the current buffer to get an accurate
- * timestamp */
- if (output->page_flip_pending) {
- drm_fb_unref(output->fb_last);
- output->fb_last = NULL;
- }
-
+ assert(output->page_flip_pending);
output->page_flip_pending = 0;

+ drm_fb_unref(output->fb_last);
+ output->fb_last = NULL;
+
if (output->destroy_pending)
drm_output_destroy(&output->base);
else if (output->disable_pending)
--
2.9.3
Daniel Stone
2016-12-09 19:57:59 UTC
Permalink
Much like we already have to_drm_output and to_drm_backend.

Differential Revision: https://phabricator.freedesktop.org/D1505

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 c98ba25..b33d519 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -393,6 +393,12 @@ to_drm_backend(struct weston_compositor *base)
return container_of(base->backend, struct drm_backend, base);
}

+static inline struct drm_mode *
+to_drm_mode(struct weston_mode *base)
+{
+ return container_of(base, struct drm_mode, base);
+}
+
/**
* Return a string describing the type of a DRM object
*/
@@ -1740,7 +1746,7 @@ drm_output_apply_state(struct drm_output_state *state)
assert(scanout_state->src_w == scanout_state->dest_w << 16);
assert(scanout_state->src_h == scanout_state->dest_h << 16);

- mode = container_of(output->base.current_mode, struct drm_mode, base);
+ mode = to_drm_mode(output->base.current_mode);
if (!scanout_plane->state_cur->fb ||
scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
--
2.9.3
Daniel Stone
2016-12-09 19:57:55 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>

Differential Revision: https://phabricator.freedesktop.org/D1514
---
libweston/compositor-drm.c | 27 ++++-----------------------
1 file changed, 4 insertions(+), 23 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index ec8db31..5959aed 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1743,12 +1743,9 @@ static void
drm_output_start_repaint_loop(struct weston_output *output_base)
{
struct drm_output *output = to_drm_output(output_base);
- struct drm_output_state *output_state;
- struct drm_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;
struct timespec ts, tnow;
struct timespec vbl2now;
int64_t refresh_nsec;
@@ -1798,37 +1795,21 @@ 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);
assert(!output->state_pending);

- output_state =
+ output->state_pending =
drm_output_state_duplicate(output->state_cur,
DRM_OUTPUT_STATE_PRESERVE_PLANES);

- if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
- weston_log("queueing pageflip failed: %m\n");
- drm_output_state_free(output_state);
+ ret = drm_output_apply_state(output->state_pending);
+ if (ret != 0) {
+ weston_log("applying repaint-start state failed: %m\n");
goto finish_frame;
}

- wl_list_for_each(plane_state, &output_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(output_state,
- DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
-
return;

finish_frame:
--
2.9.3
Daniel Stone
2016-12-09 19:57:21 UTC
Permalink
Forcing DPMS on when we lose our session may force an expensive modeset
operation, which is pointless if the next consumer (another compositor,
or the console) is going to do a modeset. These should force DPMS on
regardless.

This actively causes problems for the DRM backend, in that it may
actually require a repaint to set coherent state for DPMS off -> DPMS on
transitions, which is very much not what we want when going offscreen.

As DRM is the only backend which actually implements DPMS, just remove
this call.

Differential Revision: https://phabricator.freedesktop.org/D1483

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

diff --git a/libweston/compositor.c b/libweston/compositor.c
index ad59156..895a040 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -3933,10 +3933,9 @@ weston_compositor_wake(struct weston_compositor *compositor)

switch (old_state) {
case WESTON_COMPOSITOR_SLEEPING:
- weston_compositor_dpms(compositor, WESTON_DPMS_ON);
- /* fall through */
case WESTON_COMPOSITOR_IDLE:
case WESTON_COMPOSITOR_OFFSCREEN:
+ weston_compositor_dpms(compositor, WESTON_DPMS_ON);
wl_signal_emit(&compositor->wake_signal, compositor);
/* fall through */
default:
@@ -3952,10 +3951,6 @@ weston_compositor_wake(struct weston_compositor *compositor)
* This is used for example to prevent further rendering while the
* compositor is shutting down.
*
- * \note When offscreen state is entered, outputs will be powered
- * back on if they were sleeping (in DPMS off mode), even though
- * no rendering will be performed.
- *
* Stops the idle timer.
*/
WL_EXPORT void
@@ -3965,8 +3960,6 @@ weston_compositor_offscreen(struct weston_compositor *compositor)
case WESTON_COMPOSITOR_OFFSCREEN:
return;
case WESTON_COMPOSITOR_SLEEPING:
- weston_compositor_dpms(compositor, WESTON_DPMS_ON);
- /* fall through */
default:
compositor->state = WESTON_COMPOSITOR_OFFSCREEN;
wl_event_source_timer_update(compositor->idle_source, 0);
--
2.9.3
Daniel Stone
2016-12-09 19:57:54 UTC
Permalink
Extend drm_output_state to also cover DPMS, so we can use it to enable
and disable outputs, and always keep a coherent state.

Differential Revision: https://phabricator.freedesktop.org/D1501

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index f1f9bcb..ec8db31 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -231,6 +231,7 @@ struct drm_edid {
struct drm_output_state {
struct drm_output *output;

+ enum dpms_enum dpms;
struct wl_list plane_list;
};

@@ -301,12 +302,11 @@ struct drm_output {
drmModePropertyPtr dpms_prop;
uint32_t gbm_format;

- enum dpms_enum dpms;
-
int vblank_pending;
int page_flip_pending;
int destroy_pending;
int disable_pending;
+ int dpms_off_pending;

struct gbm_surface *gbm_surface;
struct drm_fb *gbm_cursor_fb[2];
@@ -1107,6 +1107,7 @@ drm_output_state_alloc(struct drm_output *output)
struct drm_output_state *state = calloc(1, sizeof(*state));

state->output = output;
+ state->dpms = WESTON_DPMS_OFF;
wl_list_init(&state->plane_list);

return state;
@@ -1163,6 +1164,23 @@ drm_output_state_free(struct drm_output_state *state)
free(state);
}

+static struct drm_output_state *
+drm_output_get_disable_state(struct drm_output *output)
+{
+ struct drm_output_state *state;
+
+ if (!output->state_cur)
+ state = drm_output_state_alloc(output);
+ else
+ state = drm_output_state_duplicate(output->state_cur,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+ state->dpms = WESTON_DPMS_OFF;
+
+ return state;
+}
+
+static int drm_output_apply_state(struct drm_output_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
@@ -1173,6 +1191,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
unsigned int sec, unsigned int usec)
{
struct drm_plane_state *ps;
+ struct drm_output_state *os;
struct timespec ts;

wl_list_for_each(ps, &output->state_cur->plane_list, link)
@@ -1187,6 +1206,9 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
} else if (output->disable_pending) {
weston_output_disable(&output->base);
goto out;
+ } else if (output->dpms_off_pending) {
+ os = drm_output_get_disable_state(output);
+ drm_output_apply_state(os);
}

ts.tv_sec = sec;
@@ -1201,6 +1223,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
out:
output->destroy_pending = 0;
output->disable_pending = 0;
+ output->dpms_off_pending = 0;
}

/**
@@ -1247,7 +1270,6 @@ drm_output_assign_state(struct drm_output_state *state,
}
}

-
static int
drm_view_transform_supported(struct weston_view *ev)
{
@@ -1538,7 +1560,41 @@ drm_output_apply_state(struct drm_output_state *state)
struct drm_mode *mode;
int ret = 0;

- scanout_state = drm_output_state_get_plane(state, scanout_plane);
+ 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);
+
+ 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. */
@@ -1565,7 +1621,6 @@ drm_output_apply_state(struct drm_output_state *state)
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,
@@ -1627,6 +1682,18 @@ drm_output_apply_state(struct drm_output_state *state)
}
}

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

@@ -1655,6 +1722,7 @@ drm_output_repaint(struct weston_output *output_base,
output->state_pending =
drm_output_state_duplicate(output->state_cur,
DRM_OUTPUT_STATE_CLEAR_PLANES);
+ output->state_pending->dpms = WESTON_DPMS_ON;

drm_output_render(output, damage);
scanout_state = drm_output_state_get_plane(output->state_pending,
@@ -1782,6 +1850,9 @@ drm_output_update_msc(struct drm_output *output, unsigned int 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)
{
@@ -1804,9 +1875,6 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int 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)
{
@@ -2760,8 +2828,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);
weston_plane_release(&plane->base);
wl_list_remove(&plane->link);
@@ -2955,22 +3021,42 @@ 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);
int ret;

- if (!output->dpms_prop)
+
+ /* 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;
+ assert(!output->state_cur ||
+ output->state_cur->dpms != level);
+ }
+
+ if (output->state_cur && output->state_cur->dpms == level)
+ return;
+
+ weston_log("asked for DPMS on; scheduling repaint\n");
+ weston_output_schedule_repaint(output_base);
return;
+ }

- ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id,
- output->dpms_prop->prop_id, level);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
+ if (!output->state_cur || output->state_cur->dpms == WESTON_DPMS_OFF)
+ return;
+
+ /* If we've already got a request in the pipeline (common when we've
+ * just finished a repaint cycle, and it's the animation destroy
+ * callback which is asking us to disable), then we need to park our
+ * DPMS request until that request has quiesced. */
+ if (output->state_last) {
+ output->dpms_off_pending = 1;
return;
}

- output->dpms = level;
+ output->state_pending = drm_output_get_disable_state(output);
+ ret = drm_output_apply_state(output->state_pending);
+ if (ret != 0)
+ weston_log("drm_set_dpms: couldn't disable output?\n");
}

static const char * const connector_type_names[] = {
@@ -3778,7 +3864,7 @@ static int
drm_output_disable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
- struct drm_backend *b = to_drm_backend(base->compositor);
+ int ret;

if (output->page_flip_pending || output->vblank_pending) {
output->disable_pending = 1;
@@ -3790,11 +3876,12 @@ 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 = NULL;
+ output->state_pending = drm_output_get_disable_state(output);
+ ret = drm_output_apply_state(output->state_pending);
+ if (ret) {
+ weston_log("Couldn't disable output output %s\n",
+ output->base.name);
+ }

return 0;
}
@@ -4088,6 +4175,8 @@ session_notify(struct wl_listener *listener, void *data)
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
}
+
+ /* XXX: invalidate state_cur everywhere */
}
}
--
2.9.3
Daniel Stone
2016-12-09 19:57:52 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.

Differential Revision: https://phabricator.freedesktop.org/D1499

Signed-off-by: Daniel Stone <***@collabora.com>
---
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 e575429..f0a13e1 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1430,6 +1430,7 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
{
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;

@@ -1440,10 +1441,20 @@ drm_output_render(struct drm_output *output, 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(output, damage);
- else
+ } else {
fb = drm_output_render_gl(output, damage);
+ }

if (!fb) {
drm_plane_state_put_back(scanout_state);
--
2.9.3
Daniel Stone
2016-12-09 19:57:35 UTC
Permalink
When using the Pixman renderer, use drm_fb refcounting explicitly.

Differential Revision: https://phabricator.freedesktop.org/D1492

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a684ac9..950c265 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -491,7 +491,7 @@ drm_fb_unref(struct drm_fb *fb)

switch (fb->type) {
case BUFFER_PIXMAN_DUMB:
- /* nothing: pixman buffers are destroyed manually */
+ drm_fb_destroy_dumb(fb);
break;
case BUFFER_CLIENT:
gbm_bo_destroy(fb->bo);
@@ -652,7 +652,7 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)

output->current_image ^= 1;

- output->next = output->dumb[output->current_image];
+ output->next = drm_fb_ref(output->dumb[output->current_image]);
pixman_renderer_output_set_buffer(&output->base,
output->image[output->current_image]);

@@ -1973,7 +1973,7 @@ drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
err:
for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
if (output->dumb[i])
- drm_fb_destroy_dumb(output->dumb[i]);
+ drm_fb_unref(output->dumb[i]);
if (output->image[i])
pixman_image_unref(output->image[i]);

@@ -1993,8 +1993,8 @@ drm_output_fini_pixman(struct drm_output *output)
pixman_region32_fini(&output->previous_damage);

for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
- drm_fb_destroy_dumb(output->dumb[i]);
pixman_image_unref(output->image[i]);
+ drm_fb_unref(output->dumb[i]);
output->dumb[i] = NULL;
output->image[i] = NULL;
}
--
2.9.3
Pekka Paalanen
2017-02-22 13:31:10 UTC
Permalink
On Fri, 9 Dec 2016 19:57:35 +0000
Post by Daniel Stone
When using the Pixman renderer, use drm_fb refcounting explicitly.
Differential Revision: https://phabricator.freedesktop.org/D1492
---
libweston/compositor-drm.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a684ac9..950c265 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -491,7 +491,7 @@ drm_fb_unref(struct drm_fb *fb)
switch (fb->type) {
- /* nothing: pixman buffers are destroyed manually */
+ drm_fb_destroy_dumb(fb);
break;
gbm_bo_destroy(fb->bo);
@@ -652,7 +652,7 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)
output->current_image ^= 1;
- output->next = output->dumb[output->current_image];
+ output->next = drm_fb_ref(output->dumb[output->current_image]);
pixman_renderer_output_set_buffer(&output->base,
output->image[output->current_image]);
@@ -1973,7 +1973,7 @@ drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
if (output->dumb[i])
- drm_fb_destroy_dumb(output->dumb[i]);
+ drm_fb_unref(output->dumb[i]);
if (output->image[i])
pixman_image_unref(output->image[i]);
@@ -1993,8 +1993,8 @@ drm_output_fini_pixman(struct drm_output *output)
pixman_region32_fini(&output->previous_damage);
for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
- drm_fb_destroy_dumb(output->dumb[i]);
pixman_image_unref(output->image[i]);
+ drm_fb_unref(output->dumb[i]);
output->dumb[i] = NULL;
output->image[i] = NULL;
}
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Daniel Stone
2016-12-09 19:57:53 UTC
Permalink
We can separate repainting into two phases: one performing the actual
repaint (essentially drm_output_render) and populating the plane state
that wasn't already populated by drm_output_assign_planes, and another
applying this state to the device.

Differential Revision: https://phabricator.freedesktop.org/D1500

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index f0a13e1..f1f9bcb 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1527,12 +1527,10 @@ drm_waitvblank_pipe(struct drm_output *output)
}

static int
-drm_output_repaint(struct weston_output *output_base,
- pixman_region32_t *damage)
+drm_output_apply_state(struct drm_output_state *state)
{
- 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_plane_state *scanout_state;
struct drm_plane_state *ps;
@@ -1540,20 +1538,7 @@ drm_output_repaint(struct weston_output *output_base,
struct drm_mode *mode;
int ret = 0;

- if (output->disable_pending || output->destroy_pending)
- goto err;
-
- assert(!output->state_last);
- if (!output->state_pending)
- output->state_pending =
- drm_output_state_duplicate(output->state_cur,
- DRM_OUTPUT_STATE_CLEAR_PLANES);
-
- drm_output_render(output, damage);
- scanout_state = drm_output_state_get_plane(output->state_pending,
- scanout_plane);
- if (!scanout_state || !scanout_state->fb)
- goto err;
+ scanout_state = drm_output_state_get_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. */
@@ -1580,7 +1565,7 @@ 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);
+ output->base.set_dpms(&output->base, WESTON_DPMS_ON);
}

if (drmModePageFlip(backend->drm.fd, output->crtc_id,
@@ -1592,7 +1577,7 @@ drm_output_repaint(struct weston_output *output_base,

assert(!output->page_flip_pending);

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

/*
* Now, update all the sprite surfaces
@@ -1655,6 +1640,37 @@ err:
return -1;
}

+static int
+drm_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct drm_output *output = to_drm_output(output_base);
+ struct drm_plane_state *scanout_state;
+
+ if (output->disable_pending || output->destroy_pending)
+ goto err;
+
+ assert(!output->state_last);
+ if (!output->state_pending)
+ output->state_pending =
+ drm_output_state_duplicate(output->state_cur,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+
+ drm_output_render(output, damage);
+ scanout_state = drm_output_state_get_plane(output->state_pending,
+ output->scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return drm_output_apply_state(output->state_pending);
+
+err:
+ drm_output_state_free(output->state_pending);
+ output->state_pending = NULL;
+ return -1;
+}
+
+
static void
drm_output_start_repaint_loop(struct weston_output *output_base)
{
--
2.9.3
Daniel Stone
2016-12-09 19:57:48 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.

Differential Revision: https://phabricator.freedesktop.org/D1497

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 3dd2924..106d851 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -121,6 +121,19 @@ struct plane_properties {
uint64_t value;
};

+/**
+ * Mode for drm_output_state_duplicate.
+ */
+enum drm_output_state_duplicate_mode {
+ DRM_OUTPUT_STATE_CLEAR_PLANES, /**< reset all planes to off */
+ DRM_OUTPUT_STATE_PRESERVE_PLANES, /**< preserve plane state */
+};
+
+enum drm_output_state_update_mode {
+ DRM_OUTPUT_STATE_UPDATE_SYNCHRONOUS, /**< state already applied */
+ DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS, /**< pending event delivery */
+};
+
struct drm_backend {
struct weston_backend base;
struct weston_compositor *compositor;
@@ -210,6 +223,16 @@ struct drm_edid {
};

/**
+ * Output state holds the dynamic state for one Weston output, i.e. a KMS CRTC,
+ * plus >= 1 each of encoder/connector/plane. Since everything but the planes
+ * is currently statically assigned per-output, we mainly use this to track
+ * plane state.
+ */
+struct drm_output_state {
+ struct drm_output *output;
+};
+
+/**
* A plane represents one buffer, positioned within a CRTC, and stacked
* relative to other planes on the same CRTC.
*
@@ -278,6 +301,10 @@ struct drm_output {
struct drm_fb *fb_current, *fb_pending, *fb_last;
struct backlight *backlight;

+ struct drm_output_state *state_last;
+ struct drm_output_state *state_cur;
+ struct drm_output_state *state_pending;
+
struct drm_fb *dumb[2];
pixman_image_t *image[2];
int current_image;
@@ -645,6 +672,9 @@ drm_output_set_cursor(struct drm_output *output);
static void
drm_output_update_msc(struct drm_output *output, unsigned int seq);

+static void
+drm_output_destroy(struct weston_output *output_base);
+
static int
drm_plane_crtc_supported(struct drm_output *output, struct drm_plane *plane)
{
@@ -905,6 +935,112 @@ drm_fb_unref(struct drm_fb *fb)
}
}

+/**
+ * Allocate a new, empty drm_output_state. This should not generally be used
+ * in the repaint cycle; see drm_output_state_duplicate.
+ */
+static struct drm_output_state *
+drm_output_state_alloc(struct drm_output *output)
+{
+ struct drm_output_state *state = calloc(1, sizeof(*state));
+
+ state->output = output;
+
+ return state;
+}
+
+/**
+ * Duplicate an existing drm_output_state into a new one. This is generally
+ * used during the repaint cycle, to capture the existing state of an output
+ * and modify it to create a new state to be used.
+ *
+ * The mode determines whether the output will be reset to an a blank state,
+ * or an exact mirror of the current state.
+ */
+static struct drm_output_state *
+drm_output_state_duplicate(struct drm_output_state *src,
+ enum drm_output_state_duplicate_mode plane_mode)
+{
+ struct drm_output_state *dst = malloc(sizeof(*dst));
+
+ assert(dst);
+ memcpy(dst, src, sizeof(*dst));
+
+ return dst;
+}
+
+/**
+ * Free an unused drm_output_state.
+ */
+static void
+drm_output_state_free(struct drm_output_state *state)
+{
+ if (!state)
+ return;
+
+ free(state);
+}
+
+/**
+ * Mark a drm_output_state (the output's last state) as complete. This handles
+ * any post-completion actions such as updating the repaint timer, disabling the
+ * output, and finally freeing the state.
+ */
+static void
+drm_output_update_complete(struct drm_output *output, uint32_t flags,
+ unsigned int sec, unsigned int usec)
+{
+ struct timespec ts;
+
+ drm_output_state_free(output->state_last);
+ output->state_last = NULL;
+
+ if (output->destroy_pending) {
+ drm_output_destroy(&output->base);
+ goto out;
+ } else if (output->disable_pending) {
+ weston_output_disable(&output->base);
+ goto out;
+ }
+
+ ts.tv_sec = sec;
+ ts.tv_nsec = usec * 1000;
+ weston_output_finish_frame(&output->base, &ts, flags);
+
+ /* We can't call this from frame_notify, because the output's
+ * repaint needed flag is cleared just after that */
+ if (output->recorder)
+ weston_output_schedule_repaint(&output->base);
+
+out:
+ output->destroy_pending = 0;
+ output->disable_pending = 0;
+}
+
+/**
+ * Mark an output state as current on the output, i.e. it has been
+ * submitted to the kernel. The mode argument determines whether this
+ * update will be applied synchronously (e.g. when calling drmModeSetCrtc),
+ * or asynchronously (in which case we wait for events to complete).
+ */
+static void
+drm_output_assign_state(struct drm_output_state *state,
+ enum drm_output_state_update_mode mode)
+{
+ struct drm_output *output = state->output;
+
+ assert(!output->state_last);
+
+ if (mode == DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+ output->state_last = output->state_cur;
+ else
+ drm_output_state_free(output->state_cur);
+
+ output->state_cur = state;
+ output->state_pending = NULL;
+}
+
+
static int
drm_view_transform_supported(struct weston_view *ev)
{
@@ -943,9 +1079,10 @@ drm_output_check_scanout_format(struct drm_output *output,
}

static struct weston_plane *
-drm_output_prepare_scanout_view(struct drm_output *output,
+drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
+ struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
@@ -1149,13 +1286,19 @@ drm_output_repaint(struct weston_output *output_base,
int ret = 0;

if (output->disable_pending || output->destroy_pending)
- return -1;
+ goto err;
+
+ assert(!output->state_last);
+ if (!output->state_pending)
+ output->state_pending =
+ drm_output_state_duplicate(output->state_cur,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);

assert(!output->fb_last);

drm_output_render(output, damage);
if (!output->fb_pending)
- return -1;
+ goto err;

mode = container_of(output->base.current_mode, struct drm_mode, base);
if (!output->fb_current ||
@@ -1166,7 +1309,7 @@ drm_output_repaint(struct weston_output *output_base,
&mode->mode_info);
if (ret) {
weston_log("set mode failed: %m\n");
- goto err_pageflip;
+ goto err;
}
output_base->set_dpms(output_base, WESTON_DPMS_ON);
}
@@ -1175,7 +1318,7 @@ drm_output_repaint(struct weston_output *output_base,
output->fb_pending->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
weston_log("queueing pageflip failed: %m\n");
- goto err_pageflip;
+ goto err;
}

output->fb_last = output->fb_current;
@@ -1239,14 +1382,19 @@ drm_output_repaint(struct weston_output *output_base,
output->vblank_pending++;
}

+ drm_output_assign_state(output->state_pending,
+ DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+
return 0;

-err_pageflip:
+err:
output->cursor_view = NULL;
if (output->fb_pending) {
drm_fb_unref(output->fb_pending);
output->fb_pending = NULL;
}
+ drm_output_state_free(output->state_pending);
+ output->state_pending = NULL;

return -1;
}
@@ -1255,6 +1403,7 @@ static void
drm_output_start_repaint_loop(struct weston_output *output_base)
{
struct drm_output *output = to_drm_output(output_base);
+ struct drm_output_state *output_state;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
uint32_t fb_id;
@@ -1309,15 +1458,24 @@ drm_output_start_repaint_loop(struct weston_output *output_base)

assert(!output->page_flip_pending);
assert(!output->fb_last);
+ assert(!output->state_last);
+ assert(!output->state_pending);
+
+ output_state =
+ drm_output_state_duplicate(output->state_cur,
+ DRM_OUTPUT_STATE_PRESERVE_PLANES);

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

output->fb_last = drm_fb_ref(output->fb_current);
output->page_flip_pending = 1;
+ drm_output_assign_state(output_state,
+ DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);

return;

@@ -1345,7 +1503,6 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
{
struct drm_plane *s = (struct drm_plane *)data;
struct drm_output *output = s->output;
- struct timespec ts;
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;

@@ -1357,11 +1514,10 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
drm_fb_unref(s->fb_last);
s->fb_last = NULL;

- if (!output->page_flip_pending && !output->vblank_pending) {
- ts.tv_sec = sec;
- ts.tv_nsec = usec * 1000;
- weston_output_finish_frame(&output->base, &ts, flags);
- }
+ if (output->page_flip_pending || output->vblank_pending)
+ return;
+
+ drm_output_update_complete(output, flags, sec, usec);
}

static void
@@ -1372,7 +1528,6 @@ page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void *data)
{
struct drm_output *output = data;
- struct timespec ts;
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
@@ -1385,20 +1540,10 @@ page_flip_handler(int fd, unsigned int frame,
drm_fb_unref(output->fb_last);
output->fb_last = NULL;

- if (output->destroy_pending)
- drm_output_destroy(&output->base);
- else if (output->disable_pending)
- weston_output_disable(&output->base);
- else if (!output->vblank_pending) {
- ts.tv_sec = sec;
- ts.tv_nsec = usec * 1000;
- weston_output_finish_frame(&output->base, &ts, flags);
+ if (output->vblank_pending)
+ return;

- /* We can't call this from frame_notify, because the output's
- * repaint needed flag is cleared just after that */
- if (output->recorder)
- weston_output_schedule_repaint(&output->base);
- }
+ drm_output_update_complete(output, flags, sec, usec);
}

static uint32_t
@@ -1431,9 +1576,10 @@ drm_output_check_plane_format(struct drm_plane *p,
}

static struct weston_plane *
-drm_output_prepare_overlay_view(struct drm_output *output,
+drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
+ struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
struct drm_backend *b = to_drm_backend(ec);
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
@@ -1606,9 +1752,10 @@ drm_output_prepare_overlay_view(struct drm_output *output,
}

static struct weston_plane *
-drm_output_prepare_cursor_view(struct drm_output *output,
+drm_output_prepare_cursor_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
+ struct drm_output *output = output_state->output;
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct wl_shm_buffer *shmbuf;
@@ -1740,10 +1887,16 @@ drm_assign_planes(struct weston_output *output_base)
{
struct drm_backend *b = to_drm_backend(output_base->compositor);
struct drm_output *output = to_drm_output(output_base);
+ struct drm_output_state *state;
struct weston_view *ev, *next;
pixman_region32_t overlap, surface_overlap;
struct weston_plane *primary, *next_plane;

+ assert(!output->state_last);
+ assert(!output->state_pending);
+ state = drm_output_state_duplicate(output->state_cur,
+ DRM_OUTPUT_STATE_CLEAR_PLANES);
+
/*
* Find a surface for each sprite in the output using some heuristics:
* 1) size
@@ -1792,11 +1945,11 @@ drm_assign_planes(struct weston_output *output_base)
if (pixman_region32_not_empty(&surface_overlap))
next_plane = primary;
if (next_plane == NULL)
- next_plane = drm_output_prepare_cursor_view(output, ev);
+ next_plane = drm_output_prepare_cursor_view(state, ev);
if (next_plane == NULL)
- next_plane = drm_output_prepare_scanout_view(output, ev);
+ next_plane = drm_output_prepare_scanout_view(state, ev);
if (next_plane == NULL)
- next_plane = drm_output_prepare_overlay_view(output, ev);
+ next_plane = drm_output_prepare_overlay_view(state, ev);
if (next_plane == NULL)
next_plane = primary;

@@ -1820,6 +1973,8 @@ drm_assign_planes(struct weston_output *output_base)
pixman_region32_fini(&surface_overlap);
}
pixman_region32_fini(&overlap);
+
+ output->state_pending = state;
}

/**
@@ -3120,7 +3275,7 @@ drm_output_destroy(struct weston_output *base)
struct drm_backend *b = to_drm_backend(base->compositor);
drmModeCrtcPtr origcrtc = output->original_crtc;

- if (output->page_flip_pending) {
+ if (output->page_flip_pending || output->vblank_pending) {
output->destroy_pending = 1;
weston_log("destroy output while page flip pending\n");
return;
@@ -3147,6 +3302,10 @@ drm_output_destroy(struct weston_output *base)
b->crtc_allocator &= ~(1 << output->crtc_id);
b->connector_allocator &= ~(1 << output->connector_id);

+ assert(!output->state_last);
+ drm_output_state_free(output->state_cur);
+ assert(!output->state_pending);
+
free(output);
}

@@ -3156,7 +3315,7 @@ drm_output_disable(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);

- if (output->page_flip_pending) {
+ if (output->page_flip_pending || output->vblank_pending) {
output->disable_pending = 1;
return -1;
}
@@ -3169,6 +3328,8 @@ drm_output_disable(struct weston_output *base)
weston_log("Disabling output %s\n", output->base.name);
drmModeSetCrtc(b->drm.fd, output->crtc_id,
0, 0, 0, 0, 0, NULL);
+ drm_output_state_free(output->state_cur);
+ output->state_cur = NULL;

return 0;
}
@@ -3222,6 +3383,8 @@ create_output_for_connector(struct drm_backend *b,
output->disable_pending = 0;
output->original_crtc = NULL;

+ output->state_cur = drm_output_state_alloc(output);
+
b->crtc_allocator |= (1 << output->crtc_id);
b->connector_allocator |= (1 << output->connector_id);
--
2.9.3
Daniel Stone
2016-12-09 19:57:44 UTC
Permalink
From: Pekka Paalanen <***@collabora.co.uk>

This moves the single sprite creation code from create_sprites() into a
new function. The readability clean-up is small, but my intention is to
write an alternate version of create_sprites(), and sharing the single
sprite creation code is useful.

[daniels: Genericised from drm_sprite to drm_plane, moving some of the
logic back into create_sprites(), also symmetrical
drm_plane_destroy.]

Signed-off-by: Pekka Paalanen <***@collabora.co.uk>
Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1409
---
libweston/compositor-drm.c | 188 ++++++++++++++++++++++++++++-----------------
1 file changed, 117 insertions(+), 71 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 1b123b5..9a27f03 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1703,6 +1703,123 @@ init_pixman(struct drm_backend *b)
}

/**
+ * Create a drm_plane for a hardware plane
+ *
+ * Creates one drm_plane structure for a hardware plane, and initialises its
+ * properties and formats.
+ *
+ * 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.
+ *
+ * @param ec Compositor to create plane for
+ * @param kplane DRM plane to create
+ */
+static struct drm_plane *
+drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
+{
+ struct drm_plane *plane;
+
+ plane = zalloc(sizeof(*plane) + ((sizeof(uint32_t)) *
+ kplane->count_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;
+ memcpy(plane->formats, kplane->formats,
+ kplane->count_formats * sizeof(kplane->formats[0]));
+
+ weston_plane_init(&plane->base, b->compositor, 0, 0);
+ wl_list_insert(&b->sprite_list, &plane->link);
+
+ return plane;
+}
+
+/**
+ * Destroy one DRM plane
+ *
+ * Destroy a DRM plane, removing it from screen and releasing its retained
+ * buffers in the process. The counterpart to drm_plane_create.
+ *
+ * @param plane Plane to deallocate (will be freed)
+ */
+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_fb_unref(plane->fb_last);
+ drm_fb_unref(plane->fb_current);
+ assert(!plane->fb_pending);
+ weston_plane_release(&plane->base);
+ wl_list_remove(&plane->link);
+ free(plane);
+}
+
+/**
+ * Initialise sprites (overlay planes)
+ *
+ * Walk the list of provided DRM planes, and add overlay planes.
+ *
+ * Call destroy_sprites to free these planes.
+ *
+ * @param ec Compositor to create sprites for.
+ */
+static void
+create_sprites(struct drm_backend *b)
+{
+ drmModePlaneRes *kplane_res;
+ 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",
+ strerror(errno));
+ return;
+ }
+
+ for (i = 0; i < kplane_res->count_planes; i++) {
+ kplane = drmModeGetPlane(b->drm.fd, kplane_res->planes[i]);
+ if (!kplane)
+ continue;
+
+ drm_plane = drm_plane_create(b, kplane);
+ drmModeFreePlane(kplane);
+ if (!drm_plane)
+ continue;
+
+ weston_compositor_stack_plane(b->compositor, &drm_plane->base,
+ &b->compositor->primary_plane);
+ }
+
+ drmModeFreePlaneResources(kplane_res);
+}
+
+/**
+ * Clean up sprites (overlay planes)
+ *
+ * The counterpart to create_sprites.
+ *
+ * @param compositor Compositor to deallocate sprites for.
+ */
+static void
+destroy_sprites(struct drm_backend *backend)
+{
+ struct drm_plane *plane, *next;
+
+ wl_list_for_each_safe(plane, next, &backend->sprite_list, link)
+ drm_plane_destroy(plane);
+}
+
+/**
* Add a mode to output's mode list
*
* Copy the supplied DRM mode into a Weston mode structure, and add it to the
@@ -2703,77 +2820,6 @@ create_output_for_connector(struct drm_backend *b,
return 0;
}

-static void
-create_sprites(struct drm_backend *b)
-{
- struct drm_plane *plane;
- drmModePlaneRes *kplane_res;
- drmModePlane *kplane;
- uint32_t i;
-
- kplane_res = drmModeGetPlaneResources(b->drm.fd);
- if (!kplane_res) {
- weston_log("failed to get plane resources: %s\n",
- strerror(errno));
- return;
- }
-
- for (i = 0; i < kplane_res->count_planes; i++) {
- kplane = drmModeGetPlane(b->drm.fd, kplane_res->planes[i]);
- if (!kplane)
- continue;
-
- plane = zalloc(sizeof(*plane) + ((sizeof(uint32_t)) *
- kplane->count_formats));
- if (!plane) {
- weston_log("%s: out of memory\n",
- __func__);
- drmModeFreePlane(kplane);
- continue;
- }
-
- plane->possible_crtcs = kplane->possible_crtcs;
- plane->plane_id = kplane->plane_id;
- plane->fb_last = NULL;
- plane->fb_current = NULL;
- plane->fb_pending = NULL;
- plane->backend = b;
- plane->count_formats = kplane->count_formats;
- memcpy(plane->formats, kplane->formats,
- kplane->count_formats * sizeof(kplane->formats[0]));
- drmModeFreePlane(kplane);
- weston_plane_init(&plane->base, b->compositor, 0, 0);
- weston_compositor_stack_plane(b->compositor, &plane->base,
- &b->compositor->primary_plane);
-
- wl_list_insert(&b->sprite_list, &plane->link);
- }
-
- drmModeFreePlaneResources(kplane_res);
-}
-
-static void
-destroy_sprites(struct drm_backend *backend)
-{
- struct drm_plane *plane, *next;
- struct drm_output *output;
-
- output = container_of(backend->compositor->output_list.next,
- struct drm_output, base.link);
-
- wl_list_for_each_safe(plane, next, &backend->sprite_list, link) {
- drmModeSetPlane(backend->drm.fd,
- plane->plane_id,
- output->crtc_id, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0);
- drm_fb_unref(plane->fb_last);
- drm_fb_unref(plane->fb_current);
- assert(!plane->fb_pending);
- weston_plane_release(&plane->base);
- free(plane);
- }
-}
-
static int
create_outputs(struct drm_backend *b, uint32_t option_connector,
struct udev_device *drm_device)
--
2.9.3
Daniel Stone
2016-12-09 19:57:34 UTC
Permalink
Sometimes we need to duplicate an existing drm_fb, e.g. when
pageflipping to the same buffer to kickstart the repaint loop. To handle
situations like these, and simplify resource management for dumb and
cursor buffers, refcount drm_fb.

Differential Revision: https://phabricator.freedesktop.org/D1491

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index eb735b2..a684ac9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -140,6 +140,8 @@ enum drm_fb_type {
struct drm_fb {
enum drm_fb_type type;

+ int refcnt;
+
uint32_t fb_id, stride, handle, size;
const struct pixel_format_info *format;
int width, height;
@@ -302,6 +304,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb)
return NULL;

+ fb->refcnt = 1;
+
fb->format = pixel_format_get_info(format);
if (!fb->format) {
weston_log("failed to look up format 0x%lx\n",
@@ -312,6 +316,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb->format->depth || !fb->format->bpp) {
weston_log("format 0x%lx is not compatible with dumb buffers\n",
(unsigned long) format);
+ goto err_fb;
}

memset(&create_arg, 0, sizeof create_arg);
@@ -384,6 +389,13 @@ err_fb:
}

static struct drm_fb *
+drm_fb_ref(struct drm_fb *fb)
+{
+ fb->refcnt++;
+ return 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)
{
@@ -392,13 +404,14 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
int ret;

if (fb)
- return fb;
+ return drm_fb_ref(fb);

fb = zalloc(sizeof *fb);
if (fb == NULL)
return NULL;

fb->type = type;
+ fb->refcnt = 1;
fb->bo = bo;

fb->width = gbm_bo_get_width(bo);
@@ -472,6 +485,10 @@ drm_fb_unref(struct drm_fb *fb)
if (!fb)
return;

+ assert(fb->refcnt > 0);
+ if (--fb->refcnt > 0)
+ return;
+
switch (fb->type) {
case BUFFER_PIXMAN_DUMB:
/* nothing: pixman buffers are destroyed manually */
--
2.9.3
Pekka Paalanen
2017-02-21 15:19:17 UTC
Permalink
On Fri, 9 Dec 2016 19:57:34 +0000
Post by Daniel Stone
Sometimes we need to duplicate an existing drm_fb, e.g. when
pageflipping to the same buffer to kickstart the repaint loop. To handle
situations like these, and simplify resource management for dumb and
cursor buffers, refcount drm_fb.
Differential Revision: https://phabricator.freedesktop.org/D1491
---
libweston/compositor-drm.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index eb735b2..a684ac9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -140,6 +140,8 @@ enum drm_fb_type {
struct drm_fb {
enum drm_fb_type type;
+ int refcnt;
+
uint32_t fb_id, stride, handle, size;
const struct pixel_format_info *format;
int width, height;
@@ -302,6 +304,8 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb)
return NULL;
+ fb->refcnt = 1;
+
fb->format = pixel_format_get_info(format);
if (!fb->format) {
weston_log("failed to look up format 0x%lx\n",
@@ -312,6 +316,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (!fb->format->depth || !fb->format->bpp) {
weston_log("format 0x%lx is not compatible with dumb buffers\n",
(unsigned long) format);
+ goto err_fb;
Hi,

this hunk belongs in an earlier patch.
Post by Daniel Stone
}
memset(&create_arg, 0, sizeof create_arg);
}
static struct drm_fb *
+drm_fb_ref(struct drm_fb *fb)
+{
+ fb->refcnt++;
+ return 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)
{
@@ -392,13 +404,14 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
int ret;
if (fb)
- return fb;
+ return drm_fb_ref(fb);
This path is now different from before, and requires adding an
equivalent drm_fb_unref() call somewhere, but I don't see it.
Previously unref would have released it immediately, now the first call
will be a no-op.

Or, if this is a bug fix, it requires an explanation in the commit
message how in some cases drm_fb_unref() got called twice.

Maybe this hunk belongs in a different patch instead?
Post by Daniel Stone
fb = zalloc(sizeof *fb);
if (fb == NULL)
return NULL;
fb->type = type;
+ fb->refcnt = 1;
fb->bo = bo;
fb->width = gbm_bo_get_width(bo);
@@ -472,6 +485,10 @@ drm_fb_unref(struct drm_fb *fb)
if (!fb)
return;
+ assert(fb->refcnt > 0);
+ if (--fb->refcnt > 0)
+ return;
+
switch (fb->type) {
/* nothing: pixman buffers are destroyed manually */
It took a while to see the paths:

drm_fb_unref -> gbm_bo_destroy -> drm_fb_destroy_gbm

drm_fb_unref -> gbm_surface_release_buffer
gbm_surface_destroy -> drm_fb_destroy_gbm

drm_output_fini_pixman -> drm_fb_destroy_dumb

The goal is to solidify drm_fb_unref() as the main entry point for
destruction, but it's not quite there yet. Very good.

If you move both of the hunks I pointed out into other patches, then
this patch gets:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>
If that's not the right thing to do, I'll review the next revision.


Thanks,
pq
Daniel Stone
2016-12-09 19:57:57 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.

Differential Revision: https://phabricator.freedesktop.org/D1503

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

diff --git a/configure.ac b/configure.ac
index 7d5eaa1..8fe48b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -204,6 +204,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 mtdev >= 1.1.0])
+ 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 2db48f1..7874c35 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -99,6 +99,16 @@ struct property_item {
*/
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
};

@@ -122,6 +132,37 @@ struct plane_properties {
};

/**
+ * List of properties attached to DRM CRTCs
+ */
+enum wdrm_crtc_property {
+ WDRM_CRTC_MODE_ID = 0,
+ WDRM_CRTC_ACTIVE,
+ WDRM_CRTC__COUNT
+};
+
+/**
+ * List of properties attached to DRM connectors
+ */
+enum wdrm_connector_property {
+ WDRM_CONNECTOR_CRTC_ID = 0,
+ WDRM_CONNECTOR__COUNT
+};
+
+/**
+ * Holding structure for CRTC properties.
+ */
+struct crtc_properties {
+ struct property_item item[WDRM_CRTC__COUNT];
+};
+
+/**
+ * Holding structure for CRTC properties.
+ */
+struct connector_properties {
+ struct property_item item[WDRM_CONNECTOR__COUNT];
+};
+
+/**
* Mode for drm_output_state_duplicate.
*/
enum drm_output_state_duplicate_mode {
@@ -172,6 +213,7 @@ struct drm_backend {
int cursors_are_broken;

bool universal_planes;
+ bool atomic_modeset;

int use_pixman;

@@ -301,6 +343,9 @@ struct drm_output {
drmModePropertyPtr dpms_prop;
uint32_t gbm_format;

+ struct crtc_properties props_crtc;
+ struct connector_properties props_conn;
+
int vblank_pending;
int page_flip_pending;
int destroy_pending;
@@ -591,6 +636,16 @@ plane_properties_init(struct drm_plane *plane)
{
static const char * const plane_property_names[] = {
[WDRM_PLANE_TYPE] = "type",
+ [WDRM_PLANE_SRC_X] = "SRC_X",
+ [WDRM_PLANE_SRC_Y] = "SRC_Y",
+ [WDRM_PLANE_SRC_W] = "SRC_W",
+ [WDRM_PLANE_SRC_H] = "SRC_H",
+ [WDRM_PLANE_CRTC_X] = "CRTC_X",
+ [WDRM_PLANE_CRTC_Y] = "CRTC_Y",
+ [WDRM_PLANE_CRTC_W] = "CRTC_W",
+ [WDRM_PLANE_CRTC_H] = "CRTC_H",
+ [WDRM_PLANE_FB_ID] = "FB_ID",
+ [WDRM_PLANE_CRTC_ID] = "CRTC_ID",
};
static const char * const plane_type_names[] = {
[WDRM_PLANE_TYPE_PRIMARY] = "Primary",
@@ -683,6 +738,84 @@ drm_plane_get_type(struct drm_plane *plane)
return WDRM_PLANE_TYPE_OVERLAY;
}

+/**
+ * Initialise DRM properties for CRTC and connector
+ *
+ * Set up the holding structures to track DRM object properties set on the CRTC
+ * and connector associated with an output.
+ * Free the memory allocated here with output_properties_release.
+ *
+ * @param b DRM backend
+ * @param output Output to configure
+ */
+static bool output_properties_init(struct drm_backend *b,
+ struct drm_output *output)
+{
+ static const char * const crtc_property_names[] = {
+ [WDRM_CRTC_MODE_ID] = "MODE_ID",
+ [WDRM_CRTC_ACTIVE] = "ACTIVE",
+ };
+ static const char * const connector_property_names[] = {
+ [WDRM_CONNECTOR_CRTC_ID] = "CRTC_ID",
+ };
+ uint32_t valid_mask, required_mask;
+
+ static_assert(ARRAY_LENGTH(crtc_property_names) == WDRM_CRTC__COUNT,
+ "crtc_property_names mismatch with the enum");
+ static_assert(WDRM_CRTC__COUNT <= 32,
+ "need more bits for crtc item_valid_mask");
+
+ static_assert(ARRAY_LENGTH(connector_property_names) == WDRM_CONNECTOR__COUNT,
+ "connector_property_names mismatch with the enum");
+ static_assert(WDRM_CONNECTOR__COUNT <= 32,
+ "need more bits for connector item_valid_mask");
+
+ valid_mask = drm_properties_get_from_obj(b,
+ output->props_crtc.item,
+ crtc_property_names,
+ WDRM_CRTC__COUNT,
+ output->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+
+ required_mask = 0;
+ if ((valid_mask & required_mask) != required_mask) {
+ weston_log("DRM error: failed to look up all CRTC properties "
+ "(wanted 0x%x got 0x%x) on ID %d\n",
+ required_mask, valid_mask, output->crtc_id);
+ return false;
+ }
+
+ valid_mask = drm_properties_get_from_obj(b,
+ output->props_conn.item,
+ connector_property_names,
+ WDRM_CONNECTOR__COUNT,
+ output->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ required_mask = 0;
+ if ((valid_mask & required_mask) != required_mask) {
+ weston_log("DRM error: failed to look up all connector properties "
+ "(wanted 0x%x got 0x%x) on ID %d\n",
+ required_mask, valid_mask, output->connector_id);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Free DRM CRTC and connector properties
+ *
+ * The counterpart to output_properties_init.
+ *
+ * @param output Output to release properties for
+ */
+static void output_properties_release(struct drm_output *output)
+{
+ property_item_array_release(output->props_crtc.item, WDRM_CRTC__COUNT);
+ property_item_array_release(output->props_conn.item,
+ WDRM_CONNECTOR__COUNT);
+}
+
static void
drm_output_set_cursor(struct drm_output_state *output_state);

@@ -2563,6 +2696,13 @@ init_drm(struct drm_backend *b, struct udev_device *device)
weston_log("DRM: %s universal planes\n",
b->universal_planes ? "supports" : "does not support");

+#ifdef HAVE_DRM_ATOMIC
+ ret = drmSetClientCap(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;
}

@@ -3819,6 +3959,7 @@ drm_output_destroy(struct weston_output *base)

assert(!output->state_last);
drm_output_state_free(output->state_cur);
+ output_properties_release(output);
assert(!output->state_pending);

free(output);
@@ -3894,6 +4035,7 @@ create_output_for_connector(struct drm_backend *b,
output->base.destroy = drm_output_destroy;
output->base.disable = drm_output_disable;
output->base.name = make_connector_name(connector);
+ output_properties_init(b, output);

output->destroy_pending = 0;
output->disable_pending = 0;
--
2.9.3
Daniel Stone
2016-12-09 19:58:02 UTC
Permalink
Pull this into a helper function, so we can use it everywhere.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1516
---
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 893fdbd..b57e2ee 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1208,6 +1208,92 @@ drm_plane_state_put_back(struct drm_plane_state *state)
}

/**
+ * 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.
*/
static struct drm_plane_state *
@@ -2208,16 +2294,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)
@@ -2319,71 +2402,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.9.3
Daniel Stone
2016-12-09 19:57:58 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.

Differential Revision: https://phabricator.freedesktop.org/D1504

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7874c35..c98ba25 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -228,6 +228,7 @@ struct drm_backend {
struct drm_mode {
struct weston_mode base;
drmModeModeInfo mode_info;
+ uint32_t blob_id;
};

enum drm_fb_type {
@@ -3050,6 +3051,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;
@@ -3059,6 +3061,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)
{
@@ -3794,10 +3808,8 @@ drm_output_set_mode(struct weston_output *base,

err_free:
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);

return -1;
}
--
2.9.3
Daniel Stone
2016-12-09 19:58:10 UTC
Permalink
Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1524
---
libweston/compositor-drm.c | 45 ++++++++++++++++++++++++---------------------
1 file changed, 24 insertions(+), 21 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 72337a6..a2d4c8c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -243,7 +243,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;
@@ -859,7 +862,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);
@@ -878,16 +881,11 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
static int
drm_fb_addfb(struct drm_fb *fb)
{
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
int ret;

- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(fb->fd, fb->width, fb->height,
- fb->format->format, handles, pitches,
- offsets, &fb->fb_id, 0);
+ 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;

@@ -896,9 +894,13 @@ drm_fb_addfb(struct drm_fb *fb)
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->stride, fb->handle, &fb->fb_id);
+ fb->strides[0], fb->handles[0], &fb->fb_id);
return ret;
}

@@ -941,8 +943,8 @@ 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;
@@ -954,7 +956,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
}

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;
@@ -1003,10 +1005,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) {
@@ -1836,7 +1838,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state)

mode = to_drm_mode(output->base.current_mode);
if (!scanout_plane->state_cur->fb ||
- scanout_plane->state_cur->fb->stride != scanout_state->fb->stride) {
+ 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,
@@ -3515,7 +3518,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;
}
@@ -4539,7 +4542,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] "
@@ -4548,7 +4551,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.9.3
Daniel Stone
2016-12-09 19:57:50 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.

Differential Revision: https://phabricator.freedesktop.org/D1498

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 43b36f8..36468b9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -310,7 +310,7 @@ struct drm_output {

struct gbm_surface *gbm_surface;
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;

@@ -685,7 +685,7 @@ drm_plane_get_type(struct drm_plane *plane)
}

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);
@@ -1064,12 +1064,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;

@@ -1078,6 +1077,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);
}

@@ -1518,7 +1534,7 @@ drm_output_repaint(struct weston_output *output_base,
assert(!output->page_flip_pending);
output->page_flip_pending = 1;

- drm_output_set_cursor(output);
+ drm_output_set_cursor(output->state_pending);

/*
* Now, update all the sprite surfaces
@@ -1961,20 +1977,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. */
@@ -2007,89 +2069,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;
-
- 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);
+ plane_state =
+ drm_output_state_get_plane(output_state, output->cursor_plane);

- 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
@@ -2098,6 +2172,7 @@ drm_assign_planes(struct weston_output *output_base)
struct drm_backend *b = to_drm_backend(output_base->compositor);
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state;
+ struct drm_plane_state *plane_state;
struct weston_view *ev, *next;
pixman_region32_t overlap, surface_overlap;
struct weston_plane *primary, *next_plane;
@@ -2123,10 +2198,6 @@ drm_assign_planes(struct weston_output *output_base)
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;

@@ -2170,7 +2241,8 @@ drm_assign_planes(struct weston_output *output_base)
&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 {
@@ -2184,6 +2256,19 @@ drm_assign_planes(struct weston_output *output_base)
}
pixman_region32_fini(&overlap);

+ /* We rely on ev->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;
+ }
+
output->state_pending = state;
}

@@ -2471,34 +2556,62 @@ 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.
*
- * @param ec Compositor to create plane for
- * @param kplane DRM plane to create
+ * @sa drm_output_find_special_plane
+ * @param b DRM backend
+ * @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;
+ int num_formats = (kplane) ? kplane->count_formats : 1;
+
+ /* With universal planes, everything is a DRM plane; without
+ * universal planes, the only DRM planes are overlay planes. */
+ if (b->universal_planes)
+ assert(b->universal_planes && kplane);
+ else
+ assert(!b->universal_planes &&
+ (type == WDRM_PLANE_TYPE_OVERLAY || (output && format)));

- 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]));
+
+ 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]));
+ }
+ else {
+ plane->possible_crtcs = (1 << output->pipe);
+ plane->plane_id = 0;
+ plane->count_formats = 1;
+ plane->formats[0] = format;
+ }

if (!plane_properties_init(plane)) {
free(plane);
@@ -2508,7 +2621,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
if (b->universal_planes)
plane->type = drm_plane_get_type(plane);
else
- plane->type = WDRM_PLANE_TYPE_OVERLAY;
+ plane->type = type;

weston_plane_init(&plane->base, b->compositor, 0, 0);
wl_list_insert(&b->plane_list, &plane->link);
@@ -2517,6 +2630,64 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
}

/**
+ * 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) {
+ if (plane->type != type)
+ continue;
+ if (!drm_plane_crtc_supported(output, plane))
+ continue;
+
+ plane->possible_crtcs = (1 << output->pipe);
+ return plane;
+ }
+
+ return NULL;
+}
+
+/**
* Destroy one DRM plane
*
* Destroy a DRM plane, removing it from screen and releasing its retained
@@ -2565,7 +2736,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;
@@ -2820,6 +2992,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;

@@ -3396,6 +3572,12 @@ drm_output_enable(struct weston_output *base)

output->dpms_prop = drm_get_prop(b->drm.fd, output->connector, "DPMS");

+ /* 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);
+
if (b->use_pixman) {
if (drm_output_init_pixman(output, b) < 0) {
weston_log("Failed to init output pixman state\n");
@@ -3430,11 +3612,15 @@ drm_output_enable(struct weston_output *base)
if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
output->base.connection_internal = 1;

- 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);

@@ -3470,12 +3656,13 @@ drm_output_deinit(struct weston_output *base)
drm_output_fini_egl(output);

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

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

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

static void
@@ -3816,7 +4003,9 @@ session_notify(struct wl_listener *listener, void *data)

wl_list_for_each(output, &compositor->output_list, base.link) {
output->base.repaint_needed = 0;
- 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.9.3
Daniel Stone
2016-12-09 19:57:39 UTC
Permalink
Instead of setting state members directly in the drm_output_render
functions (to paint using Pixman or GL), just return a drm_fb, and let
the core function place it in state.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1419
---
libweston/compositor-drm.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 08634cd..5909239 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -615,11 +615,12 @@ drm_output_prepare_scanout_view(struct drm_output *output,
return &output->fb_plane;
}

-static void
+static struct drm_fb *
drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
{
struct drm_backend *b = to_drm_backend(output->base.compositor);
struct gbm_bo *bo;
+ struct drm_fb *ret;

output->base.compositor->renderer->repaint_output(&output->base,
damage);
@@ -627,20 +628,21 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
bo = gbm_surface_lock_front_buffer(output->gbm_surface);
if (!bo) {
weston_log("failed to lock front buffer: %m\n");
- return;
+ return NULL;
}

- output->fb_pending = drm_fb_get_from_bo(bo, b, output->gbm_format,
- BUFFER_GBM_SURFACE);
- if (!output->fb_pending) {
+ ret = drm_fb_get_from_bo(bo, b, output->gbm_format, BUFFER_GBM_SURFACE);
+ if (!ret) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
- return;
+ return NULL;
}
- output->fb_pending->gbm_surface = output->gbm_surface;
+ ret->gbm_surface = output->gbm_surface;
+
+ return ret;
}

-static void
+static struct drm_fb *
drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)
{
struct weston_compositor *ec = output->base.compositor;
@@ -656,7 +658,6 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)

output->current_image ^= 1;

- output->fb_pending = drm_fb_ref(output->dumb[output->current_image]);
pixman_renderer_output_set_buffer(&output->base,
output->image[output->current_image]);

@@ -664,6 +665,8 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)

pixman_region32_fini(&total_damage);
pixman_region32_fini(&previous_damage);
+
+ return drm_fb_ref(output->dumb[output->current_image]);
}

static void
@@ -671,6 +674,7 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
{
struct weston_compositor *c = output->base.compositor;
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. */
@@ -678,9 +682,13 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
return;

if (b->use_pixman)
- drm_output_render_pixman(output, damage);
+ fb = drm_output_render_pixman(output, damage);
else
- drm_output_render_gl(output, damage);
+ fb = drm_output_render_gl(output, damage);
+
+ if (!fb)
+ return;
+ output->fb_pending = fb;

pixman_region32_subtract(&c->primary_plane.damage,
&c->primary_plane.damage, damage);
--
2.9.3
Daniel Stone
2016-12-09 19:57:51 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>

Differential Revision: https://phabricator.freedesktop.org/D1416
---
libweston/compositor-drm.c | 132 +++++++++++++++++++++++++++++----------------
1 file changed, 87 insertions(+), 45 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 36468b9..e575429 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -315,8 +315,8 @@ struct drm_output {
int current_cursor;

/* Plane currently being directly displayed by KMS */
- struct weston_plane scanout_plane;
- struct drm_fb *fb_current, *fb_pending, *fb_last;
+ struct drm_plane *scanout_plane;
+
struct backlight *backlight;

struct drm_output_state *state_last;
@@ -1242,6 +1242,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;
}
}

@@ -1289,6 +1291,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;
@@ -1342,15 +1346,29 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state,
return NULL;
}

- output->fb_pending = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!output->fb_pending) {
+ state = drm_output_state_get_plane(output_state, scanout_plane);
+ 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 = ev->surface->width << 16;
+ state->src_h = ev->surface->height << 16;

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

static struct drm_fb *
@@ -1411,12 +1429,15 @@ static void
drm_output_render(struct drm_output *output, pixman_region32_t *damage)
{
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(output->state_pending,
+ output->scanout_plane);
+ if (scanout_state->fb)
return;

if (b->use_pixman)
@@ -1424,9 +1445,24 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
else
fb = drm_output_render_gl(output, 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);
@@ -1486,6 +1522,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;
@@ -1500,17 +1538,31 @@ drm_output_repaint(struct weston_output *output_base,
drm_output_state_duplicate(output->state_cur,
DRM_OUTPUT_STATE_CLEAR_PLANES);

- assert(!output->fb_last);
-
drm_output_render(output, damage);
- if (!output->fb_pending)
+ scanout_state = drm_output_state_get_plane(output->state_pending,
+ 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->src_w == scanout_state->dest_w << 16);
+ assert(scanout_state->src_h == scanout_state->dest_h << 16);
+
mode = container_of(output->base.current_mode, struct drm_mode, base);
- if (!output->fb_current ||
- output->fb_current->stride != output->fb_pending->stride) {
+ if (!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) {
@@ -1521,18 +1573,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;

drm_output_set_cursor(output->state_pending);

@@ -1591,10 +1638,6 @@ 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(output->state_pending);
output->state_pending = NULL;

@@ -1607,6 +1650,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *output_state;
struct drm_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;
@@ -1623,11 +1667,13 @@ 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;
}

+ 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);
@@ -1657,10 +1703,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);
assert(!output->state_pending);

@@ -1675,9 +1720,6 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
goto finish_frame;
}

- output->fb_last = drm_fb_ref(output->fb_current);
- output->page_flip_pending = 1;
-
wl_list_for_each(plane_state, &output_state->plane_list, link) {
if (plane_state->plane->type != WDRM_PLANE_TYPE_OVERLAY)
continue;
@@ -1751,9 +1793,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;

@@ -2358,10 +2397,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);
@@ -3572,6 +3607,15 @@ drm_output_enable(struct weston_output *base)

output->dpms_prop = drm_get_prop(b->drm.fd, output->connector, "DPMS");

+ 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);
+ goto err_free;
+ }
+
/* Failing to find a cursor plane is not fatal, as we'll fall back
* to software cursor. */
output->cursor_plane =
@@ -3612,8 +3656,6 @@ drm_output_enable(struct weston_output *base)
if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
output->base.connection_internal = 1;

- weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);
-
if (output->cursor_plane)
weston_compositor_stack_plane(b->compositor,
&output->cursor_plane->base,
@@ -3621,7 +3663,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",
@@ -3655,8 +3698,6 @@ drm_output_deinit(struct weston_output *base)
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);
@@ -4127,7 +4168,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] "
@@ -4136,7 +4178,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.9.3
Daniel Stone
2016-12-09 19:58:18 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>

Differential Revision: https://phabricator.freedesktop.org/D1532
---
libweston/compositor-drm.c | 37 +++++++++++++++++++++++++++++++------
1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 1eb1fbb..6b5c7cb 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1657,6 +1657,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)
@@ -2627,13 +2632,17 @@ err:
}

static struct drm_output_state *
-drm_output_propose_state(struct weston_output *output_base)
+drm_output_propose_state(struct weston_output *output_base,
+ 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);
assert(!output->state_pending);
@@ -2643,6 +2652,7 @@ drm_output_propose_state(struct weston_output *output_base)
/*
* Find a surface for each sprite in the output using some heuristics:
* 1) size
+
* 2) frequency of update
* 3) opacity (though some hw might support alpha blending)
* 4) clipping (this can be fixed with color keys)
@@ -2695,24 +2705,38 @@ drm_output_propose_state(struct weston_output *output_base)
&occluded_region,
&ev->transform.boundingbox);

- 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 (!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,
&ev->transform.boundingbox);
+ }
}
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
@@ -2725,7 +2749,8 @@ drm_assign_planes(struct weston_output *output_base)
struct weston_view *ev;
struct weston_plane *primary = &output_base->compositor->primary_plane;

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

wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct drm_plane *target_plane = NULL;
--
2.9.3
Daniel Stone
2016-12-09 19:58:09 UTC
Permalink
We currently do the same thing in two places, and will soon have a
third.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1523
---
libweston/compositor-drm.c | 63 +++++++++++++++++++++++-----------------------
1 file changed, 31 insertions(+), 32 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5fff646..72337a6 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -875,6 +875,33 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
drm_fb_destroy(fb);
}

+static int
+drm_fb_addfb(struct drm_fb *fb)
+{
+ uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
+ int ret;
+
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;
+
+ ret = drmModeAddFB2(fb->fd, fb->width, fb->height,
+ fb->format->format, handles, pitches,
+ 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;
+
+ ret = drmModeAddFB(fb->fd, fb->width, fb->height,
+ fb->format->depth, fb->format->bpp,
+ fb->stride, fb->handle, &fb->fb_id);
+ return ret;
+}
+
static struct drm_fb *
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
uint32_t format)
@@ -885,12 +912,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);
@@ -923,22 +948,10 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
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;
@@ -976,8 +989,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)
return drm_fb_ref(fb);
@@ -1015,19 +1026,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;
}
--
2.9.3
Daniel Stone
2016-12-09 19:58:00 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.

Differential Revision: https://phabricator.freedesktop.org/D1507

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>
---
libweston/compositor-drm.c | 208 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 203 insertions(+), 5 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b33d519..2f9415c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -349,6 +349,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;
@@ -1376,6 +1377,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);
@@ -1388,6 +1390,9 @@ drm_output_assign_state(struct drm_output_state *state,
output->state_cur = state;
output->state_pending = NULL;

+ 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
@@ -1399,7 +1404,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)
@@ -1686,7 +1692,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);
@@ -1844,6 +1850,168 @@ 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 property_item *item = &output->props_crtc.item[prop];
+ int ret;
+
+ if (!item)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->crtc_id,
+ item->drm_prop->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 property_item *item = &output->props_conn.item[prop];
+ int ret;
+
+ if (!item)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->connector_id,
+ item->drm_prop->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 property_item *item = &plane->props.item[prop];
+ int ret;
+
+ if (!item)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, plane->plane_id,
+ item->drm_prop->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)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ struct drm_plane_state *plane_state;
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+ struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
+ uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ int ret = 0;
+
+ if (!req)
+ return -1;
+
+ 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;
+ }
+ }
+
+ if (drmModeAtomicCommit(backend->drm.fd, req, flags, output) != 0) {
+ weston_log("couldn't commit new state: %m\n");
+ goto err;
+ }
+
+ drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+
+ return 0;
+
+err:
+ drm_output_state_free(state);
+ output->state_pending = NULL;
+ return -1;
+}
+#endif
+
+static int
+drm_output_apply_state(struct drm_output_state *state)
+{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = to_drm_backend(state->output->base.compositor);
+
+ if (b->atomic_modeset)
+ return drm_output_apply_state_atomic(state);
+ else
+#endif
+ return drm_output_apply_state_legacy(state);
+}
+
static int
drm_output_repaint(struct weston_output *output_base,
pixman_region32_t *damage)
@@ -1977,9 +2145,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);
@@ -1997,12 +2168,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;

@@ -2012,6 +2185,25 @@ page_flip_handler(int fd, unsigned int frame,
drm_output_update_complete(output, flags, sec, usec);
}

+static void
+atomic_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->atomic_complete_pending);
+ output->atomic_complete_pending = 0;
+
+ drm_output_update_complete(output, flags, sec, usec);
+}
+
static uint32_t
drm_output_check_plane_format(struct drm_plane *p,
struct weston_view *ev, struct gbm_bo *bo)
@@ -2633,11 +2825,15 @@ 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)
{
+ struct drm_backend *b = data;
drmEventContext evctx;

memset(&evctx, 0, sizeof evctx);
evctx.version = DRM_EVENT_CONTEXT_VERSION;
- evctx.page_flip_handler = page_flip_handler;
+ if (b->atomic_modeset)
+ evctx.page_flip_handler = atomic_flip_handler;
+ else
+ evctx.page_flip_handler = page_flip_handler;
evctx.vblank_handler = vblank_handler;
drmHandleEvent(fd, &evctx);

@@ -3956,7 +4152,8 @@ drm_output_destroy(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 || 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;
@@ -3989,7 +4186,8 @@ drm_output_disable(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
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;
}
--
2.9.3
Daniel Stone
2016-12-09 19:57:45 UTC
Permalink
All planes being displayed have a framebuffer. What makes 'fb_plane'
special is that it's being displayed as the primary plane by KMS.

Previous patchsets renamed this to 'primary_plane' to match the KMS
terminology, namely the CRTC's base plane, which is controlled by
drmModeSetCrtc in the legacy API, and identified by PLANE_TYPE ==
"Primary" in the universal-plane API.

However, Weston uses 'primary_plane' internally to refer to the case
where client content is _not_ directly displayed on a plane, but
composited via the renderer, with the result of the compositing then
shown.

Rename to 'scanout_plane' as our least-ambiguous name, and document it a
bit.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1415
---
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 9a27f03..e4d6743 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -225,7 +225,8 @@ struct drm_output {
struct weston_view *cursor_view;
int current_cursor;

- struct weston_plane fb_plane;
+ /* Plane currently being directly displayed by KMS */
+ struct weston_plane scanout_plane;
struct drm_fb *fb_current, *fb_pending, *fb_last;
struct backlight *backlight;

@@ -623,7 +624,7 @@ drm_output_prepare_scanout_view(struct drm_output *output,

drm_fb_set_buffer(output->fb_pending, buffer);

- return &output->fb_plane;
+ return &output->scanout_plane;
}

static struct drm_fb *
@@ -2656,10 +2657,10 @@ drm_output_enable(struct weston_output *base)

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

weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL);
- weston_compositor_stack_plane(b->compositor, &output->fb_plane,
+ weston_compositor_stack_plane(b->compositor, &output->scanout_plane,
&b->compositor->primary_plane);

weston_log("Output %s, (connector %d, crtc %d)\n",
@@ -2693,7 +2694,7 @@ drm_output_deinit(struct weston_output *base)
else
drm_output_fini_egl(output);

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

drmModeFreeProperty(output->dpms_prop);
--
2.9.3
Daniel Stone
2016-12-09 19:58:03 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>

Differential Revision: https://phabricator.freedesktop.org/D1517
---
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 b57e2ee..cd89083 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2294,7 +2294,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;
@@ -2320,13 +2319,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;

@@ -2355,6 +2347,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:
@@ -2373,7 +2371,7 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
};

if (dmabuf->attributes.n_planes != 1 || dmabuf->attributes.offset[0] != 0)
- return NULL;
+ goto err;

bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
GBM_BO_USE_SCANOUT);
@@ -2389,8 +2387,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++)
@@ -2401,15 +2403,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.9.3
Daniel Stone
2016-12-09 19:58:06 UTC
Permalink
... in order to be able to use it from scanout as well.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1520
---
libweston/compositor-drm.c | 200 ++++++++++++++++++++++++---------------------
1 file changed, 109 insertions(+), 91 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 1dc63c8..59a764d 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1293,6 +1293,99 @@ drm_plane_state_coords_for_view(struct drm_plane_state *state,
pixman_region32_fini(&src_rect);
}

+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
+ };
+
+ if (dmabuf->attributes.n_planes != 1 ||
+ dmabuf->attributes.offset[0])
+ return NULL;
+
+ 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.
*/
@@ -1505,28 +1598,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)
@@ -2275,31 +2346,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) {
@@ -2314,6 +2370,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;
@@ -2324,65 +2388,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
- };
-
- if (dmabuf->attributes.n_planes != 1 || dmabuf->attributes.offset[0] != 0)
- 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.9.3
Daniel Stone
2016-12-09 19:58:14 UTC
Permalink
Make it a bit more clear what the purpose of the variable is.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1528
---
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 b7ebb2f..79f2941 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2637,7 +2637,7 @@ drm_assign_planes(struct weston_output *output_base)
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);
@@ -2658,7 +2658,7 @@ drm_assign_planes(struct weston_output *output_base)
* 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) {
@@ -2682,7 +2682,7 @@ drm_assign_planes(struct weston_output *output_base)
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;
@@ -2700,7 +2700,8 @@ drm_assign_planes(struct weston_output *output_base)
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 ||
@@ -2717,7 +2718,7 @@ drm_assign_planes(struct weston_output *output_base)

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

/* We rely on ev->cursor_view being both an accurate reflection of the
* cursor plane's state, but also being maintained across repaints to
--
2.9.3
Daniel Stone
2016-12-09 19:58:23 UTC
Permalink
Now that we can sensibly test proposed plane configurations with atomic,
sprites are not broken.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1537
---
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 e615eb5..d1f7f0f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -3111,6 +3111,17 @@ init_drm(struct drm_backend *b, struct udev_device *device)
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;
}

@@ -4972,17 +4983,6 @@ drm_backend_create(struct weston_compositor *compositor,
if (b == NULL)
return NULL;

- /*
- * 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->use_current_mode = config->use_current_mode;
--
2.9.3
Daniel Stone
2016-12-09 19:58:01 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>

Differential Revision: https://phabricator.freedesktop.org/D1515
---
libweston/compositor-drm.c | 114 +++++++++++++++++----------------------------
1 file changed, 43 insertions(+), 71 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 2f9415c..893fdbd 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -984,7 +984,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 };
@@ -1005,16 +1005,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 ||
@@ -1025,13 +1028,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");
@@ -1422,34 +1426,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);
-
- 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);
+ /* 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))
- format = GBM_FORMAT_XRGB8888;
-
- pixman_region32_fini(&r);
- }
+ if (!pixman_region32_not_empty(&r))
+ ret = true;

- if (output->gbm_format == format)
- return format;
+ pixman_region32_fini(&r);

- return 0;
+ return ret;
}

static struct weston_plane *
@@ -1463,7 +1459,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))
@@ -1507,17 +1502,20 @@ 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 = drm_output_state_get_plane(output_state, scanout_plane);
+ 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 = drm_output_state_get_plane(output_state, scanout_plane);
- 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;
}

@@ -1554,7 +1552,7 @@ drm_output_render_gl(struct drm_output *output, 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);
@@ -2204,35 +2202,6 @@ atomic_flip_handler(int fd, unsigned int frame,
drm_output_update_complete(output, flags, sec, usec);
}

-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)
@@ -2248,8 +2217,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;
@@ -2335,12 +2304,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);
@@ -3488,8 +3461,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.9.3
Daniel Stone
2016-12-09 19:58:07 UTC
Permalink
Use the same codepath, which has the added advantage of being able to
import dmabufs.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1521
---
libweston/compositor-drm.c | 54 +++++++++++-----------------------------------
1 file changed, 12 insertions(+), 42 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 59a764d..ba305ad 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1603,29 +1603,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);

@@ -1638,34 +1636,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;
-
- 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 = drm_output_state_get_plane(output_state, scanout_plane);
- 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.9.3
Daniel Stone
2016-12-09 19:58:13 UTC
Permalink
Nothing in this loop reorders views within the compositor's view_list.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1527
---
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 a36d27c..b7ebb2f 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2636,7 +2636,7 @@ drm_assign_planes(struct weston_output *output_base)
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;

@@ -2661,7 +2661,7 @@ drm_assign_planes(struct weston_output *output_base)
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.9.3
Daniel Stone
2016-12-09 19:57:47 UTC
Permalink
Retain drm_plane tracking objects for all actual DRM planes when using
universal planes, not just overlay planes. Rename uses of 'sprite' to
'plane' to make it clear that it can now be any kind of plane, not just
an overlay/sprite.

These are currently unused.

Differential Revision: https://phabricator.freedesktop.org/D1496

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 46fcf0a..3dd2924 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -152,7 +152,7 @@ struct drm_backend {
int min_height, max_height;
int no_addfb2;

- struct wl_list sprite_list;
+ struct wl_list plane_list;
int sprites_are_broken;
int sprites_hidden;

@@ -1144,7 +1144,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 *s;
+ struct drm_plane *p;
struct drm_mode *mode;
int ret = 0;

@@ -1190,26 +1190,31 @@ drm_output_repaint(struct weston_output *output_base,
/*
* Now, update all the sprite surfaces
*/
- wl_list_for_each(s, &backend->sprite_list, link) {
+ wl_list_for_each(p, &backend->plane_list, link) {
uint32_t flags = 0, fb_id = 0;
drmVBlank vbl = {
.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
.request.sequence = 1,
};

- if ((!s->fb_current && !s->fb_pending) ||
- !drm_plane_crtc_supported(output, s))
+ if (p->type != WDRM_PLANE_TYPE_OVERLAY)
+ continue;
+
+ /* XXX: Set output much earlier, so we don't attempt to place
+ * planes on entirely the wrong output. */
+ if ((!p->fb_current && !p->fb_pending) ||
+ !drm_plane_crtc_supported(output, p))
continue;

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

- ret = drmModeSetPlane(backend->drm.fd, s->plane_id,
+ ret = drmModeSetPlane(backend->drm.fd, p->plane_id,
output->crtc_id, fb_id, flags,
- s->dest_x, s->dest_y,
- s->dest_w, s->dest_h,
- s->src_x, s->src_y,
- s->src_w, s->src_h);
+ p->dest_x, p->dest_y,
+ p->dest_w, p->dest_h,
+ p->src_x, p->src_y,
+ p->src_w, p->src_h);
if (ret)
weston_log("setplane failed: %d: %s\n",
ret, strerror(errno));
@@ -1220,17 +1225,17 @@ 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)s;
+ vbl.request.signal = (unsigned long) p;
ret = drmWaitVBlank(backend->drm.fd, &vbl);
if (ret) {
weston_log("vblank event request failed: %d: %s\n",
ret, strerror(errno));
}

- s->output = output;
- s->fb_last = s->fb_current;
- s->fb_current = s->fb_pending;
- s->fb_pending = NULL;
+ p->output = output;
+ p->fb_last = p->fb_current;
+ p->fb_current = p->fb_pending;
+ p->fb_pending = NULL;
output->vblank_pending++;
}

@@ -1469,7 +1474,10 @@ drm_output_prepare_overlay_view(struct drm_output *output,
if (ev->alpha != 1.0f)
return NULL;

- wl_list_for_each(p, &b->sprite_list, link) {
+ wl_list_for_each(p, &b->plane_list, link) {
+ if (p->type != WDRM_PLANE_TYPE_OVERLAY)
+ continue;
+
if (!drm_plane_crtc_supported(output, p))
continue;

@@ -2136,7 +2144,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
plane->type = WDRM_PLANE_TYPE_OVERLAY;

weston_plane_init(&plane->base, b->compositor, 0, 0);
- wl_list_insert(&b->sprite_list, &plane->link);
+ wl_list_insert(&b->plane_list, &plane->link);

return plane;
}
@@ -2197,14 +2205,10 @@ create_sprites(struct drm_backend *b)
if (!drm_plane)
continue;

- /* Ignore non-overlay planes for now. */
- if (drm_plane->type != WDRM_PLANE_TYPE_OVERLAY) {
- drm_plane_destroy(drm_plane);
- continue;
- }
-
- weston_compositor_stack_plane(b->compositor, &drm_plane->base,
- &b->compositor->primary_plane);
+ if (drm_plane->type == WDRM_PLANE_TYPE_OVERLAY)
+ weston_compositor_stack_plane(b->compositor,
+ &drm_plane->base,
+ &b->compositor->primary_plane);
}

drmModeFreePlaneResources(kplane_res);
@@ -2222,7 +2226,7 @@ destroy_sprites(struct drm_backend *backend)
{
struct drm_plane *plane, *next;

- wl_list_for_each_safe(plane, next, &backend->sprite_list, link)
+ wl_list_for_each_safe(plane, next, &backend->plane_list, link)
drm_plane_destroy(plane);
}

@@ -3415,7 +3419,7 @@ session_notify(struct wl_listener *listener, void *data)
{
struct weston_compositor *compositor = data;
struct drm_backend *b = to_drm_backend(compositor);
- struct drm_plane *sprite;
+ struct drm_plane *plane;
struct drm_output *output;

if (compositor->session_active) {
@@ -3445,12 +3449,16 @@ session_notify(struct wl_listener *listener, void *data)
output = container_of(compositor->output_list.next,
struct drm_output, base.link);

- wl_list_for_each(sprite, &b->sprite_list, link)
+ wl_list_for_each(plane, &b->plane_list, link) {
+ if (plane->type != WDRM_PLANE_TYPE_OVERLAY)
+ continue;
+
drmModeSetPlane(b->drm.fd,
- sprite->plane_id,
+ plane->plane_id,
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- };
+ }
+ }
}

/*
@@ -3785,7 +3793,7 @@ drm_backend_create(struct weston_compositor *compositor,

weston_setup_vt_switch_bindings(compositor);

- wl_list_init(&b->sprite_list);
+ wl_list_init(&b->plane_list);
create_sprites(b);

if (udev_input_init(&b->input,
--
2.9.3
Daniel Stone
2016-12-09 19:58:11 UTC
Permalink
From: Tomohito Esaki <***@igel.co.jp>

Rather than relying on GBM for dmabuf import, perform the import
directly through libdrm. This creates a specialised drm_fb type for
dmabufs.

This also supports multi-planar dmabuf.

[daniels: Rebased on top of recent drm_fb/drm_plane_state/etc.]

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1525
---
configure.ac | 3 --
libweston/compositor-drm.c | 131 ++++++++++++++++++++++++++++++++-------------
2 files changed, 95 insertions(+), 39 deletions(-)

diff --git a/configure.ac b/configure.ac
index 8fe48b6..54c6fcd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -207,9 +207,6 @@ if test x$enable_drm_compositor = xyes; then
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])])
fi


diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a2d4c8c..e6f94af 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -233,6 +233,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 */
@@ -986,6 +987,82 @@ drm_fb_ref(struct drm_fb *fb)
return fb;
}

+static void
+drm_fb_destroy_dmabuf(struct drm_fb *fb)
+{
+ unsigned int i;
+
+ /* Unlike GBM buffers, where destroying the BO also closes the GEM
+ * handle, we fully control the handle lifetime for dmabuf buffers. */
+ for (i = 0; i < ARRAY_LENGTH(fb->handles); i++) {
+ struct drm_gem_close gem_close = { .handle = fb->handles[i] };
+ if (!gem_close.handle)
+ continue;
+ (void) drmIoctl(fb->fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+ }
+
+ 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;
+ int i, ret;
+
+ fb = zalloc(sizeof *fb);
+ if (fb == NULL)
+ return NULL;
+
+ fb->refcnt = 1;
+ fb->type = BUFFER_DMABUF;
+
+ 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->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++) {
+ ret = drmPrimeFDToHandle(backend->drm.fd,
+ dmabuf->attributes.fd[i],
+ &fb->handles[i]);
+ if (ret)
+ 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)
@@ -1070,6 +1147,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;
@@ -1300,9 +1380,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))
@@ -1317,48 +1397,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
- };
-
- if (dmabuf->attributes.n_planes != 1 ||
- dmabuf->attributes.offset[0])
+ fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque);
+ if (!fb)
return NULL;
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
- GBM_BO_USE_SCANOUT);
-#else
- return NULL;
-#endif
} 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.9.3
Daniel Stone
2016-12-09 19:58:22 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>

Differential Revision: https://phabricator.freedesktop.org/D1536
---
libweston/compositor-drm.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 7c8b5d9..e615eb5 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1673,6 +1673,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;
@@ -1684,7 +1685,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;
}
@@ -1709,15 +1710,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;

@@ -2475,8 +2479,9 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
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;
}
--
2.9.3
Daniel Stone
2016-12-09 19:58:08 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>

Differential Revision: https://phabricator.freedesktop.org/D1522
---
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 ba305ad..5fff646 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -204,7 +204,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;
@@ -886,6 +885,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)
@@ -925,23 +925,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,
@@ -1026,24 +1015,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.9.3
Daniel Stone
2016-12-09 19:58:17 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>

Differential Revision: https://phabricator.freedesktop.org/D1531
---
libweston/compositor-drm.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index dc68768..1eb1fbb 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -2632,7 +2632,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);
@@ -2654,9 +2654,11 @@ 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;
+ bool occluded = false;

/* If this view doesn't touch our output at all, there's no
* reason to do anything with it. */
@@ -2668,6 +2670,16 @@ 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(&surface_overlap);
+ pixman_region32_subtract(&surface_overlap,
+ &ev->transform.boundingbox,
+ &occluded_region);
+ occluded = !pixman_region32_not_empty(&surface_overlap);
+ pixman_region32_fini(&surface_overlap);
+ if (occluded)
+ 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. */
@@ -2678,6 +2690,11 @@ drm_output_propose_state(struct weston_output *output_base)
next_plane = primary;
pixman_region32_fini(&surface_overlap);

+ if (drm_view_is_opaque(ev))
+ pixman_region32_union(&occluded_region,
+ &occluded_region,
+ &ev->transform.boundingbox);
+
if (next_plane == NULL)
next_plane = drm_output_prepare_cursor_view(state, ev);
if (next_plane == NULL)
@@ -2693,6 +2710,7 @@ drm_output_propose_state(struct weston_output *output_base)
&ev->transform.boundingbox);
}
pixman_region32_fini(&renderer_region);
+ pixman_region32_fini(&occluded_region);

return state;
}
--
2.9.3
Daniel Stone
2016-12-09 19:58:12 UTC
Permalink
Use the new drmModeAddFB2WithModifiers interface to import buffers with
modifiers.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1526
---
configure.ac | 3 +++
libweston/compositor-drm.c | 25 ++++++++++++++++++++++++-
2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 54c6fcd..74fe7cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -207,6 +207,9 @@ if test x$enable_drm_compositor = xyes; then
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_MODIFIERS, [libdrm >= 2.4.71],
+ [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 e6f94af..a36d27c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -249,6 +249,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;
@@ -882,7 +883,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) {
+ /* 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,
@@ -1023,6 +1045,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[0];
fb->size = 0;
fb->fd = backend->drm.fd;
--
2.9.3
Daniel Stone
2016-12-09 19:58:04 UTC
Permalink
Use the shiny new helper we have for working through scanout as well.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1518
---
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 cd89083..202772b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1505,13 +1505,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)
{
@@ -1543,7 +1536,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. */
@@ -1559,67 +1551,55 @@ 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;

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)
- return NULL;
+ goto err;

state = drm_output_state_get_plane(output_state, scanout_plane);
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 = ev->surface->width << 16;
- state->src_h = ev->surface->height << 16;
-
- state->dest_x = 0;
- state->dest_y = 0;
- state->dest_w = output->base.width;
- state->dest_h = output->base.height;
-
return &scanout_plane->base;
+
+err:
+ drm_plane_state_put_back(state);
+ return NULL;
}

static struct drm_fb *
--
2.9.3
Daniel Stone
2016-12-09 19:57:49 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>

Differential Revision: https://phabricator.freedesktop.org/D1412
---
libweston/compositor-drm.c | 330 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 270 insertions(+), 60 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 106d851..43b36f8 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -230,6 +230,29 @@ struct drm_edid {
*/
struct drm_output_state {
struct drm_output *output;
+
+ 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.
+ */
+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;
+ uint32_t dest_x, dest_y;
+ uint32_t dest_w, dest_h;
+
+ bool complete;
+
+ struct wl_list link; /* drm_output_state::plane_list */
};

/**
@@ -248,14 +271,12 @@ struct drm_output_state {
* are referred to as 'sprites'.
*/
struct drm_plane {
- struct wl_list link;
-
struct weston_plane base;

- struct drm_fb *fb_current, *fb_pending, *fb_last;
- struct drm_output *output;
struct drm_backend *backend;

+ struct drm_plane_state *state_cur;
+
enum wdrm_plane_type type;
struct plane_properties props;

@@ -263,10 +284,7 @@ struct drm_plane {
uint32_t plane_id;
uint32_t count_formats;

- 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[];
};
@@ -936,6 +954,134 @@ 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 = calloc(1, 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 output state.
+ */
+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);
+ memcpy(dst, src, sizeof(*dst));
+
+ 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.
*/
@@ -945,6 +1091,7 @@ drm_output_state_alloc(struct drm_output *output)
struct drm_output_state *state = calloc(1, sizeof(*state));

state->output = output;
+ wl_list_init(&state->plane_list);

return state;
}
@@ -962,9 +1109,23 @@ 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);
memcpy(dst, src, sizeof(*dst));
+ 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;
}
@@ -975,9 +1136,14 @@ 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);
+
free(state);
}

@@ -990,8 +1156,12 @@ 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;

+ 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;

@@ -1028,6 +1198,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);

@@ -1038,6 +1209,24 @@ drm_output_assign_state(struct drm_output_state *state,

output->state_cur = state;
output->state_pending = NULL;
+
+ /* 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++;
+ }
}


@@ -1281,6 +1470,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;
@@ -1333,31 +1523,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, &output->state_pending->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;

- /* XXX: Set output much earlier, so we don't attempt to place
- * planes on entirely the wrong output. */
- 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));
@@ -1368,18 +1560,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++;
}

drm_output_assign_state(output->state_pending,
@@ -1404,6 +1590,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
{
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *output_state;
+ struct drm_plane_state *plane_state;
struct drm_backend *backend =
to_drm_backend(output_base->compositor);
uint32_t fb_id;
@@ -1474,6 +1661,18 @@ 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, &output_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(output_state,
DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);

@@ -1501,8 +1700,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;

@@ -1510,9 +1710,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;
@@ -1585,8 +1783,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;
@@ -1627,14 +1825,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))) {
@@ -1660,28 +1866,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;
@@ -1702,10 +1906,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);
@@ -1742,13 +1946,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 *
@@ -2285,6 +2495,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]));

@@ -2317,9 +2529,7 @@ 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_fb_unref(plane->fb_last);
- drm_fb_unref(plane->fb_current);
- assert(!plane->fb_pending);
+ drm_plane_state_free(plane->state_cur, true);
weston_plane_release(&plane->base);
wl_list_remove(&plane->link);
plane_properties_release(plane);
--
2.9.3
Daniel Stone
2016-12-09 19:58:15 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>

Differential Revision: https://phabricator.freedesktop.org/D1529
---
libweston/compositor-drm.c | 106 ++++++++++++++++++++++++++++++---------------
1 file changed, 72 insertions(+), 34 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 79f2941..be2b4ea 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -293,6 +293,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;
uint32_t dest_x, dest_y;
@@ -1685,6 +1687,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);

@@ -2426,6 +2429,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);
@@ -2562,6 +2566,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]);
@@ -2629,16 +2634,14 @@ err:
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
}

-static void
-drm_assign_planes(struct weston_output *output_base)
+static struct drm_output_state *
+drm_output_propose_state(struct weston_output *output_base)
{
- struct drm_backend *b = to_drm_backend(output_base->compositor);
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state;
- struct 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);
assert(!output->state_pending);
@@ -2659,35 +2662,21 @@ drm_assign_planes(struct weston_output *output_base)
* 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)
@@ -2697,17 +2686,69 @@ drm_assign_planes(struct weston_output *output_base)
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)
+{
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+ struct drm_output *output = to_drm_output(output_base);
+ struct drm_output_state *state;
+ struct 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);
+
+ 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 (next_plane == primary ||
- (output->cursor_plane &&
- next_plane == &output->cursor_plane->base)) {
- /* cursor plane involves a copy */
+ 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
@@ -2715,10 +2756,7 @@ drm_assign_planes(struct weston_output *output_base)
*/
ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
}
-
- pixman_region32_fini(&surface_overlap);
}
- pixman_region32_fini(&renderer_region);

/* We rely on ev->cursor_view being both an accurate reflection of the
* cursor plane's state, but also being maintained across repaints to
--
2.9.3
Daniel Stone
2016-12-09 19:58:19 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>

Differential Revision: https://phabricator.freedesktop.org/D1533
---
libweston/compositor-drm.c | 58 +++++++++++++++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 16 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 6b5c7cb..5db0a1a 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1561,7 +1561,12 @@ drm_output_get_disable_state(struct drm_output *output)
return state;
}

-static int drm_output_apply_state(struct drm_output_state *state);
+enum drm_output_apply_state_mode {
+ DRM_OUTPUT_APPLY_STATE_TEST,
+ DRM_OUTPUT_APPLY_STATE_REAL,
+};
+static int drm_output_apply_state(struct drm_output_state *state,
+ enum drm_output_apply_state_mode mode);

/**
* Mark a drm_output_state (the output's last state) as complete. This handles
@@ -1590,7 +1595,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
goto out;
} else if (output->dpms_off_pending) {
os = drm_output_get_disable_state(output);
- drm_output_apply_state(os);
+ drm_output_apply_state(os, DRM_OUTPUT_APPLY_STATE_REAL);
}

ts.tv_sec = sec;
@@ -1862,7 +1867,8 @@ drm_waitvblank_pipe(struct drm_output *output)
}

static int
-drm_output_apply_state_legacy(struct drm_output_state *state)
+drm_output_apply_state_legacy(struct drm_output_state *state,
+ enum drm_output_apply_state_mode mode)
{
struct drm_output *output = state->output;
struct drm_backend *backend = to_drm_backend(output->base.compositor);
@@ -1870,9 +1876,14 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
- struct drm_mode *mode;
+ struct drm_mode *display_mode;
int ret = 0;

+ /* The legacy DRM API gives us no way to test commits without actually
+ * applying them. */
+ if (mode == DRM_OUTPUT_APPLY_STATE_TEST)
+ return 0;
+
if (state->dpms != WESTON_DPMS_ON) {
wl_list_for_each(ps, &state->plane_list, link) {
p = ps->plane;
@@ -1922,7 +1933,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
assert(scanout_state->src_w == scanout_state->dest_w << 16);
assert(scanout_state->src_h == scanout_state->dest_h << 16);

- mode = to_drm_mode(output->base.current_mode);
+ display_mode = to_drm_mode(output->base.current_mode);
if (!scanout_plane->state_cur->fb ||
scanout_plane->state_cur->fb->strides[0] !=
scanout_state->fb->strides[0]) {
@@ -1930,7 +1941,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
scanout_state->fb->fb_id,
0, 0,
&output->connector_id, 1,
- &mode->mode_info);
+ &display_mode->mode_info);
if (ret) {
weston_log("set mode failed: %m\n");
goto err;
@@ -2086,14 +2097,15 @@ drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode)
}

static int
-drm_output_apply_state_atomic(struct drm_output_state *state)
+drm_output_apply_state_atomic(struct drm_output_state *state,
+ enum drm_output_apply_state_mode mode)
{
struct drm_output *output = state->output;
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;
drmModeAtomicReq *req = drmModeAtomicAlloc();
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
- uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ uint32_t flags = 0;
int ret = 0;

if (!req)
@@ -2154,7 +2166,16 @@ drm_output_apply_state_atomic(struct drm_output_state *state)
}
}

- if (drmModeAtomicCommit(backend->drm.fd, req, flags, output) != 0) {
+ if (mode == DRM_OUTPUT_APPLY_STATE_TEST)
+ flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+ else
+ flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+
+ ret = drmModeAtomicCommit(backend->drm.fd, req, flags, output);
+ if (mode == DRM_OUTPUT_APPLY_STATE_TEST)
+ return ret;
+
+ if (ret != 0) {
weston_log("couldn't commit new state: %m\n");
goto err;
}
@@ -2171,16 +2192,17 @@ err:
#endif

static int
-drm_output_apply_state(struct drm_output_state *state)
+drm_output_apply_state(struct drm_output_state *state,
+ enum drm_output_apply_state_mode mode)
{
#ifdef HAVE_DRM_ATOMIC
struct drm_backend *b = to_drm_backend(state->output->base.compositor);

if (b->atomic_modeset)
- return drm_output_apply_state_atomic(state);
+ return drm_output_apply_state_atomic(state, mode);
else
#endif
- return drm_output_apply_state_legacy(state);
+ return drm_output_apply_state_legacy(state, mode);
}

static int
@@ -2206,7 +2228,8 @@ drm_output_repaint(struct weston_output *output_base,
if (!scanout_state || !scanout_state->fb)
goto err;

- return drm_output_apply_state(output->state_pending);
+ return drm_output_apply_state(output->state_pending,
+ DRM_OUTPUT_APPLY_STATE_REAL);

err:
drm_output_state_free(output->state_pending);
@@ -2280,7 +2303,8 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
drm_output_state_duplicate(output->state_cur,
DRM_OUTPUT_STATE_PRESERVE_PLANES);

- ret = drm_output_apply_state(output->state_pending);
+ ret = drm_output_apply_state(output->state_pending,
+ DRM_OUTPUT_APPLY_STATE_REAL);
if (ret != 0) {
weston_log("applying repaint-start state failed: %m\n");
goto finish_frame;
@@ -3495,7 +3519,8 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
}

output->state_pending = drm_output_get_disable_state(output);
- ret = drm_output_apply_state(output->state_pending);
+ ret = drm_output_apply_state(output->state_pending,
+ DRM_OUTPUT_APPLY_STATE_REAL);
if (ret != 0)
weston_log("drm_set_dpms: couldn't disable output?\n");
}
@@ -4304,7 +4329,8 @@ drm_output_disable(struct weston_output *base)
output->disable_pending = 0;

output->state_pending = drm_output_get_disable_state(output);
- ret = drm_output_apply_state(output->state_pending);
+ ret = drm_output_apply_state(output->state_pending,
+ DRM_OUTPUT_APPLY_STATE_REAL);
if (ret) {
weston_log("Couldn't disable output output %s\n",
output->base.name);
--
2.9.3
Daniel Stone
2016-12-09 19:57:30 UTC
Permalink
Rather than magically trying to infer what the buffer is and what we
should do with it when we go to destroy it, add an explicit type
instead.

Differential Revision: https://phabricator.freedesktop.org/D1488

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 4ef7343..217db32 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -129,11 +129,19 @@ struct drm_mode {
drmModeModeInfo mode_info;
};

+enum drm_fb_type {
+ BUFFER_INVALID = 0, /**< never used */
+ BUFFER_CLIENT, /**< directly sourced from client */
+ BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
+ BUFFER_GBM_SURFACE, /**< internal EGL rendering */
+};
+
struct drm_fb {
+ enum drm_fb_type type;
+
uint32_t fb_id, stride, handle, size;
int width, height;
int fd;
- int is_client_buffer;
struct weston_buffer_reference buffer_ref;

/* Used by gbm fbs */
@@ -290,6 +298,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (ret)
goto err_fb;

+ fb->type = BUFFER_PIXMAN_DUMB;
fb->handle = create_arg.handle;
fb->stride = create_arg.pitch;
fb->size = create_arg.size;
@@ -352,6 +361,8 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
{
struct drm_mode_destroy_dumb destroy_arg;

+ assert(fb->type == BUFFER_PIXMAN_DUMB);
+
if (!fb->map)
return;

@@ -370,8 +381,8 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
}

static struct drm_fb *
-drm_fb_get_from_bo(struct gbm_bo *bo,
- struct drm_backend *backend, uint32_t format)
+drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
+ uint32_t format, 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 };
@@ -384,6 +395,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
if (fb == NULL)
return NULL;

+ fb->type = type;
fb->bo = bo;

fb->width = gbm_bo_get_width(bo);
@@ -440,9 +452,6 @@ static void
drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
{
assert(fb->buffer_ref.buffer == NULL);
-
- fb->is_client_buffer = 1;
-
weston_buffer_reference(&fb->buffer_ref, buffer);
}

@@ -452,15 +461,19 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
if (!fb)
return;

- if (fb->map &&
- (fb != output->dumb[0] && fb != output->dumb[1])) {
- drm_fb_destroy_dumb(fb);
- } else if (fb->bo) {
- if (fb->is_client_buffer)
- gbm_bo_destroy(fb->bo);
- else
- gbm_surface_release_buffer(output->gbm_surface,
- fb->bo);
+ switch (fb->type) {
+ case BUFFER_PIXMAN_DUMB:
+ /* nothing: pixman buffers are destroyed manually */
+ break;
+ case BUFFER_CLIENT:
+ gbm_bo_destroy(fb->bo);
+ break;
+ case BUFFER_GBM_SURFACE:
+ gbm_surface_release_buffer(output->gbm_surface, fb->bo);
+ break;
+ default:
+ assert(NULL);
+ break;
}
}

@@ -559,7 +572,7 @@ drm_output_prepare_scanout_view(struct drm_output *output,
return NULL;
}

- output->next = drm_fb_get_from_bo(bo, b, format);
+ output->next = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
if (!output->next) {
gbm_bo_destroy(bo);
return NULL;
@@ -585,7 +598,8 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
return;
}

- output->next = drm_fb_get_from_bo(bo, b, output->gbm_format);
+ output->next = drm_fb_get_from_bo(bo, b, output->gbm_format,
+ BUFFER_GBM_SURFACE);
if (!output->next) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
@@ -1054,7 +1068,7 @@ drm_output_prepare_overlay_view(struct drm_output *output,
return NULL;
}

- s->next = drm_fb_get_from_bo(bo, b, format);
+ s->next = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
if (!s->next) {
gbm_bo_destroy(bo);
return NULL;
--
2.9.3
Pekka Paalanen
2017-02-21 13:29:09 UTC
Permalink
On Fri, 9 Dec 2016 19:57:30 +0000
Post by Daniel Stone
Rather than magically trying to infer what the buffer is and what we
should do with it when we go to destroy it, add an explicit type
instead.
Differential Revision: https://phabricator.freedesktop.org/D1488
---
libweston/compositor-drm.c | 50 +++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 18 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 4ef7343..217db32 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -129,11 +129,19 @@ struct drm_mode {
drmModeModeInfo mode_info;
};
+enum drm_fb_type {
+ BUFFER_INVALID = 0, /**< never used */
+ BUFFER_CLIENT, /**< directly sourced from client */
+ BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
+ BUFFER_GBM_SURFACE, /**< internal EGL rendering */
+};
Hi,

cool.
Post by Daniel Stone
+
struct drm_fb {
+ enum drm_fb_type type;
+
uint32_t fb_id, stride, handle, size;
int width, height;
int fd;
- int is_client_buffer;
struct weston_buffer_reference buffer_ref;
/* Used by gbm fbs */
@@ -290,6 +298,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
if (ret)
goto err_fb;
+ fb->type = BUFFER_PIXMAN_DUMB;
fb->handle = create_arg.handle;
fb->stride = create_arg.pitch;
fb->size = create_arg.size;
@@ -352,6 +361,8 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
{
struct drm_mode_destroy_dumb destroy_arg;
+ assert(fb->type == BUFFER_PIXMAN_DUMB);
+
if (!fb->map)
return;
@@ -370,8 +381,8 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
}
static struct drm_fb *
-drm_fb_get_from_bo(struct gbm_bo *bo,
- struct drm_backend *backend, uint32_t format)
+drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
+ uint32_t format, 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 };
For the shortcut return:
assert(type == fb->type)?
Post by Daniel Stone
@@ -384,6 +395,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo,
if (fb == NULL)
return NULL;
+ fb->type = type;
fb->bo = bo;
fb->width = gbm_bo_get_width(bo);
@@ -440,9 +452,6 @@ static void
drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
{
assert(fb->buffer_ref.buffer == NULL);
-
- fb->is_client_buffer = 1;
-
assert(fb->type == BUFFER_CLIENT)?
Post by Daniel Stone
weston_buffer_reference(&fb->buffer_ref, buffer);
}
@@ -452,15 +461,19 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
if (!fb)
return;
- if (fb->map &&
- (fb != output->dumb[0] && fb != output->dumb[1])) {
- drm_fb_destroy_dumb(fb);
This piece sent me into a recursive "well, actually..." loop.

It looked like dead code at first hand, as this gets hit when
output->dumb and fb don't match. When would that be? I would guess when
video mode changed.

I think changing resolutions would hit this path, when flipping to a
new dumb buffer of a different size than one coming out of scanout
which cannot be destroyed until pageflip completed.

Except I am wrong in a couple of ways: destroying the buffer is fine,
the kernel will keep it referenced as long as necessary anyway. And,
drm_output_switch_mode() does destroy everything immediately.

So this bit is ok. Unless there is a third well-actually.
Post by Daniel Stone
- } else if (fb->bo) {
- if (fb->is_client_buffer)
- gbm_bo_destroy(fb->bo);
- else
- gbm_surface_release_buffer(output->gbm_surface,
- fb->bo);
+ switch (fb->type) {
+ /* nothing: pixman buffers are destroyed manually */
+ break;
+ gbm_bo_destroy(fb->bo);
+ break;
+ gbm_surface_release_buffer(output->gbm_surface, fb->bo);
+ break;
+ assert(NULL);
+ break;
}
}
@@ -559,7 +572,7 @@ drm_output_prepare_scanout_view(struct drm_output *output,
return NULL;
}
- output->next = drm_fb_get_from_bo(bo, b, format);
+ output->next = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
if (!output->next) {
gbm_bo_destroy(bo);
return NULL;
@@ -585,7 +598,8 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
return;
}
- output->next = drm_fb_get_from_bo(bo, b, output->gbm_format);
+ output->next = drm_fb_get_from_bo(bo, b, output->gbm_format,
+ BUFFER_GBM_SURFACE);
if (!output->next) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
@@ -1054,7 +1068,7 @@ drm_output_prepare_overlay_view(struct drm_output *output,
return NULL;
}
- s->next = drm_fb_get_from_bo(bo, b, format);
+ s->next = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
if (!s->next) {
gbm_bo_destroy(bo);
return NULL;
Yeah, it looks fine, I only proposed some added asserts which are
non-essential, so:
Reviewed-by: Pekka Paalanen <***@collabora.co.uk>


Thanks,
pq
Pekka Paalanen
2017-02-22 13:45:54 UTC
Permalink
On Tue, 21 Feb 2017 15:29:09 +0200
Post by Pekka Paalanen
On Fri, 9 Dec 2016 19:57:30 +0000
Post by Daniel Stone
Rather than magically trying to infer what the buffer is and what we
should do with it when we go to destroy it, add an explicit type
instead.
Differential Revision: https://phabricator.freedesktop.org/D1488
---
libweston/compositor-drm.c | 50 +++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 18 deletions(-)
weston_buffer_reference(&fb->buffer_ref, buffer);
}
@@ -452,15 +461,19 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
if (!fb)
return;
- if (fb->map &&
- (fb != output->dumb[0] && fb != output->dumb[1])) {
- drm_fb_destroy_dumb(fb);
This piece sent me into a recursive "well, actually..." loop.
It looked like dead code at first hand, as this gets hit when
output->dumb and fb don't match. When would that be? I would guess when
video mode changed.
I think changing resolutions would hit this path, when flipping to a
new dumb buffer of a different size than one coming out of scanout
which cannot be destroyed until pageflip completed.
Except I am wrong in a couple of ways: destroying the buffer is fine,
the kernel will keep it referenced as long as necessary anyway. And,
drm_output_switch_mode() does destroy everything immediately.
So this bit is ok. Unless there is a third well-actually.
And there is it:

< MrCooper> pq: IME KMS framebuffers aren't reference-counted in the
kernel; destroying an fb which is still being scanned out by any CRTCs
turns off those CRTCs

Is it then so, that drm_output_switch_mode() is wrong to destroy the FB
immediately before even flipping a new one, but as it does so, there is
no leak introduced by removing the above drm_fb_destroy_dumb() call?

I.e. the patch does not regress anything because of a bug elsewhere?

If that is so, my R-b stands, as fixing switch_mode is a whole another
matter. Also the refcounting introduced later in this series offer a
much better basis for fixing switch_mode.


Thanks,
pq

Daniel Stone
2016-12-09 19:58:20 UTC
Permalink
Return a pointer to the plane state, rather than indirecting via a
weston_plane.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1534
---
libweston/compositor-drm.c | 52 ++++++++++++++++++++++++----------------------
1 file changed, 27 insertions(+), 25 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5db0a1a..e51a5b2 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1667,7 +1667,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)
{
@@ -1706,7 +1706,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);
@@ -2399,7 +2399,7 @@ atomic_flip_handler(int fd, unsigned int frame,
drm_output_update_complete(output, flags, sec, usec);
}

-static struct weston_plane *
+static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
@@ -2462,7 +2462,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);
@@ -2506,7 +2506,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)
{
@@ -2595,7 +2595,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);
@@ -2664,7 +2664,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;

@@ -2691,7 +2690,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;
bool occluded = false;

/* If this view doesn't touch our output at all, there's no
@@ -2702,7 +2702,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(&surface_overlap);
@@ -2721,9 +2721,12 @@ drm_output_propose_state(struct weston_output *output_base,
pixman_region32_intersect(&surface_overlap, &renderer_region,
&ev->transform.boundingbox);
if (pixman_region32_not_empty(&surface_overlap))
- next_plane = primary;
+ force_renderer = true;
pixman_region32_fini(&surface_overlap);

+ if (force_renderer && !renderer_ok)
+ goto err;
+
if (drm_view_is_opaque(ev))
pixman_region32_union(&occluded_region,
&occluded_region,
@@ -2732,24 +2735,23 @@ drm_output_propose_state(struct weston_output *output_base,
/* 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 (!force_renderer && !b->cursors_are_broken)
+ ps = drm_output_prepare_cursor_view(state, ev);
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);
+ 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 (!next_plane || next_plane == primary) {
- if (!renderer_ok)
- goto err;
+ if (ps)
+ continue;
+ if (!renderer_ok)
+ goto err;

- pixman_region32_union(&renderer_region,
- &renderer_region,
- &ev->transform.boundingbox);
- }
+ pixman_region32_union(&renderer_region,
+ &renderer_region,
+ &ev->transform.boundingbox);
}
pixman_region32_fini(&renderer_region);
pixman_region32_fini(&occluded_region);
--
2.9.3
Daniel Stone
2016-12-09 19:58:21 UTC
Permalink
Generate an output state in two stages. Firstly, attempt to run through
and generate a configuration with all views in planes. If this succeeds,
we can bypass the renderer completely.

If this fails, we know we need to have the renderer output used as a
base on the scanout plane, and can incrementally attempt to construct a
working state, by trying the combination of our old renderer state with
planes added one-by-one. If they pass the test commit stage, we can use
it, so we stash that state and continue on.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1535
---
libweston/compositor-drm.c | 135 +++++++++++++++++++++++++++++++++------------
1 file changed, 101 insertions(+), 34 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e51a5b2..7c8b5d9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1669,12 +1669,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)
@@ -1687,7 +1690,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;
}
@@ -1706,10 +1718,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_output_apply_state(output_state, mode);
+ 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;
}

@@ -1780,7 +1803,9 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
* want to render. */
scanout_state = drm_output_state_get_plane(output->state_pending,
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) &&
@@ -1803,6 +1828,7 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
return;
}

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

@@ -2401,24 +2427,24 @@ atomic_flip_handler(int fd, unsigned int frame,

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;
struct drm_backend *b = to_drm_backend(ec);
struct drm_plane *p;
- 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)
return NULL;

wl_list_for_each(p, &b->plane_list, link) {
+ struct drm_plane_state *state = NULL;
+
if (p->type != WDRM_PLANE_TYPE_OVERLAY)
continue;

@@ -2444,28 +2470,30 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
continue;
}

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

- /* No sprites available */
- if (!state) {
- drm_fb_unref(fb);
- return NULL;
- }
+ 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;
+ }

- 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)
+ return state;

- 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_output_apply_state(output_state, mode);
+ if (ret == 0)
+ return state;

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

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

@@ -2665,12 +2693,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);
assert(!output->state_pending);
state = drm_output_state_duplicate(output->state_cur,
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:
@@ -2704,6 +2762,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(&surface_overlap);
pixman_region32_subtract(&surface_overlap,
@@ -2737,17 +2798,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)
- 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)
continue;
- if (!renderer_ok)
- goto err;

pixman_region32_union(&renderer_region,
&renderer_region,
@@ -2756,6 +2813,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_output_apply_state(state, DRM_OUTPUT_APPLY_STATE_TEST);
+ if (ret != 0)
+ goto err;
+
return state;

err:
@@ -2776,7 +2838,12 @@ drm_assign_planes(struct weston_output *output_base)
struct weston_plane *primary = &output_base->compositor->primary_plane;

state = drm_output_propose_state(output_base,
- DRM_OUTPUT_PROPOSE_STATE_MIXED);
+ DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+ if (!state)
+ state = drm_output_propose_state(output_base,
+ 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.9.3
Fabien DESSENNE
2017-01-19 10:52:43 UTC
Permalink
Hi Daniel
Post by Daniel Stone
Generate an output state in two stages. Firstly, attempt to run through
and generate a configuration with all views in planes. If this succeeds,
we can bypass the renderer completely.
If this fails, we know we need to have the renderer output used as a
base on the scanout plane, and can incrementally attempt to construct a
working state, by trying the combination of our old renderer state with
planes added one-by-one. If they pass the test commit stage, we can use
it, so we stash that state and continue on.
Differential Revision: https://phabricator.freedesktop.org/D1535
---
libweston/compositor-drm.c | 135 +++++++++++++++++++++++++++++++++------------
1 file changed, 101 insertions(+), 34 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index e51a5b2..7c8b5d9 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1669,12 +1669,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)
@@ -1687,7 +1690,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;
}
@@ -1706,10 +1718,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_output_apply_state(output_state, mode);
"mode" is not the correct parameter (it is an 'enum
drm_output_propose_state_mode' and drm_output_apply_state expects an
'enum drm_output_apply_state_mode mode').
Use DRM_OUTPUT_APPLY_STATE_TEST instead of mode
Post by Daniel Stone
+ 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;
}
@@ -1780,7 +1803,9 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
* want to render. */
scanout_state = drm_output_state_get_plane(output->state_pending,
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) &&
@@ -1803,6 +1828,7 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
return;
}
+ drm_fb_unref(scanout_state->fb);
scanout_state->fb = fb;
scanout_state->output = output;
@@ -2401,24 +2427,24 @@ atomic_flip_handler(int fd, unsigned int frame,
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;
struct drm_backend *b = to_drm_backend(ec);
struct drm_plane *p;
- 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)
return NULL;
wl_list_for_each(p, &b->plane_list, link) {
+ struct drm_plane_state *state = NULL;
+
if (p->type != WDRM_PLANE_TYPE_OVERLAY)
continue;
@@ -2444,28 +2470,30 @@ drm_output_prepare_overlay_view(struct drm_output_state *output_state,
continue;
}
- break;
- }
+ state->fb = fb;
+ state->ev = ev;
+ state->output = output;
- /* No sprites available */
- if (!state) {
- drm_fb_unref(fb);
- return NULL;
- }
+ 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;
+ }
- 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)
+ return state;
- 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_output_apply_state(output_state, mode);
Same remark here
Post by Daniel Stone
+ if (ret == 0)
+ return state;
- return state;
+ drm_plane_state_put_back(state);
+ }
- drm_plane_state_put_back(state);
+ drm_fb_unref(fb);
return NULL;
}
@@ -2665,12 +2693,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);
assert(!output->state_pending);
state = drm_output_state_duplicate(output->state_cur,
DRM_OUTPUT_STATE_CLEAR_PLANES);
+ if (!planes_ok)
+ return state;
+
+ if (duplicate_renderer)
+ drm_plane_state_duplicate(state,
+ output->scanout_plane->state_cur);
/*
@@ -2704,6 +2762,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(&surface_overlap);
pixman_region32_subtract(&surface_overlap,
@@ -2737,17 +2798,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)
- 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)
continue;
- if (!renderer_ok)
- goto err;
pixman_region32_union(&renderer_region,
&renderer_region,
@@ -2756,6 +2813,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_output_apply_state(state, DRM_OUTPUT_APPLY_STATE_TEST);
+ if (ret != 0)
+ goto err;
+
return state;
@@ -2776,7 +2838,12 @@ drm_assign_planes(struct weston_output *output_base)
struct weston_plane *primary = &output_base->compositor->primary_plane;
state = drm_output_propose_state(output_base,
- DRM_OUTPUT_PROPOSE_STATE_MIXED);
+ DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
+ if (!state)
+ state = drm_output_propose_state(output_base,
+ DRM_OUTPUT_PROPOSE_STATE_MIXED);
+
+ assert(state);
wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct drm_plane *target_plane = NULL;
Fabien
BR
Daniel Stone
2017-01-19 10:55:21 UTC
Permalink
Hi Fabien,
Post by Fabien DESSENNE
Post by Daniel Stone
@@ -1706,10 +1718,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_output_apply_state(output_state, mode);
"mode" is not the correct parameter (it is an 'enum
drm_output_propose_state_mode' and drm_output_apply_state expects an
'enum drm_output_apply_state_mode mode').
Use DRM_OUTPUT_APPLY_STATE_TEST instead of mode
Thanks very much for the review. I actually noticed this a couple of
days ago when working on the global-modeset patches and have these
fixed up locally.

Cheers,
Daniel
Daniel Stone
2016-12-09 19:57:22 UTC
Permalink
No need to walk the CRTC list every time looking for CRTC indices, when we
already have the CRTC index stashed away. Taking the plane as an argument
also simplifies things a little for callers, and future-proofs for a
potential future KMS API which passes a list of supported CRTC IDs rather
than a bitmask of supported CRTC indices.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1407
---
libweston/compositor-drm.c | 20 ++++----------------
1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d577c05..2d5faa0 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -234,21 +234,9 @@ static void
drm_output_update_msc(struct drm_output *output, unsigned int seq);

static int
-drm_sprite_crtc_supported(struct drm_output *output, uint32_t supported)
+drm_sprite_crtc_supported(struct drm_output *output, struct drm_sprite *sprite)
{
- struct weston_compositor *ec = output->base.compositor;
- struct drm_backend *b = to_drm_backend(ec);
- int crtc;
-
- for (crtc = 0; crtc < b->num_crtcs; crtc++) {
- if (b->crtcs[crtc] != output->crtc_id)
- continue;
-
- if (supported & (1 << crtc))
- return -1;
- }
-
- return 0;
+ return !!(sprite->possible_crtcs & (1 << output->pipe));
}

static void
@@ -716,7 +704,7 @@ drm_output_repaint(struct weston_output *output_base,
};

if ((!s->current && !s->next) ||
- !drm_sprite_crtc_supported(output, s->possible_crtcs))
+ !drm_sprite_crtc_supported(output, s))
continue;

if (s->next && !backend->sprites_hidden)
@@ -991,7 +979,7 @@ drm_output_prepare_overlay_view(struct drm_output *output,
return NULL;

wl_list_for_each(s, &b->sprite_list, link) {
- if (!drm_sprite_crtc_supported(output, s->possible_crtcs))
+ if (!drm_sprite_crtc_supported(output, s))
continue;

if (!s->next) {
--
2.9.3
Daniel Stone
2016-12-09 19:57:56 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.

XXX: This breaks gamma. Need to work around that.

Differential Revision: https://phabricator.freedesktop.org/D1502

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

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5959aed..2db48f1 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -297,7 +297,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;
drmModePropertyPtr dpms_prop;
uint32_t gbm_format;
@@ -1513,8 +1512,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,
@@ -3630,8 +3627,6 @@ drm_output_set_mode(struct weston_output *base,
output->base.serial_number = "unknown";
wl_list_init(&output->base.mode_list);

- output->original_crtc = drmModeGetCrtc(b->drm.fd, output->crtc_id);
-
if (connector_get_current_mode(output->connector, b->drm.fd, &crtc_mode) < 0)
goto err_free;

@@ -3658,9 +3653,6 @@ drm_output_set_mode(struct weston_output *base,
return 0;

err_free:
- drmModeFreeCrtc(output->original_crtc);
- output->original_crtc = NULL;
-
wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
base.link) {
wl_list_remove(&drm_mode->base.link);
@@ -3741,7 +3733,7 @@ drm_output_enable(struct weston_output *base)
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.gamma_size = 0; /* XXX */
output->base.set_gamma = drm_output_set_gamma;

output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel);
@@ -3805,7 +3797,6 @@ drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- drmModeCrtcPtr origcrtc = output->original_crtc;

if (output->page_flip_pending || output->vblank_pending) {
output->destroy_pending = 1;
@@ -3816,14 +3807,6 @@ drm_output_destroy(struct weston_output *base)
if (output->base.enabled)
drm_output_deinit(&output->base);

- 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);
- }
-
weston_output_destroy(&output->base);

drmModeFreeConnector(output->connector);
@@ -3914,7 +3897,6 @@ create_output_for_connector(struct drm_backend *b,

output->destroy_pending = 0;
output->disable_pending = 0;
- output->original_crtc = NULL;

output->state_cur = drm_output_state_alloc(output);
--
2.9.3
Armin Krezović
2016-12-09 21:16:42 UTC
Permalink
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.
XXX: This breaks gamma. Need to work around that.
Differential Revision: https://phabricator.freedesktop.org/D1502
---
libweston/compositor-drm.c | 20 +-------------------
1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 5959aed..2db48f1 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -297,7 +297,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;
drmModePropertyPtr dpms_prop;
uint32_t gbm_format;
@@ -1513,8 +1512,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,
@@ -3630,8 +3627,6 @@ drm_output_set_mode(struct weston_output *base,
output->base.serial_number = "unknown";
wl_list_init(&output->base.mode_list);
- output->original_crtc = drmModeGetCrtc(b->drm.fd, output->crtc_id);
-
Can't you just store gamma_size from here? No need to store the original_crtc,
just get the gamma value and let it go.
Post by Daniel Stone
if (connector_get_current_mode(output->connector, b->drm.fd, &crtc_mode) < 0)
goto err_free;
@@ -3658,9 +3653,6 @@ drm_output_set_mode(struct weston_output *base,
return 0;
- drmModeFreeCrtc(output->original_crtc);
- output->original_crtc = NULL;
-
wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
base.link) {
wl_list_remove(&drm_mode->base.link);
@@ -3741,7 +3733,7 @@ drm_output_enable(struct weston_output *base)
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.gamma_size = 0; /* XXX */
output->base.set_gamma = drm_output_set_gamma;
output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel);
@@ -3805,7 +3797,6 @@ drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- drmModeCrtcPtr origcrtc = output->original_crtc;
if (output->page_flip_pending || output->vblank_pending) {
output->destroy_pending = 1;
@@ -3816,14 +3807,6 @@ drm_output_destroy(struct weston_output *base)
if (output->base.enabled)
drm_output_deinit(&output->base);
- 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);
- }
-
weston_output_destroy(&output->base);
drmModeFreeConnector(output->connector);
@@ -3914,7 +3897,6 @@ create_output_for_connector(struct drm_backend *b,
output->destroy_pending = 0;
output->disable_pending = 0;
- output->original_crtc = NULL;
output->state_cur = drm_output_state_alloc(output);
Daniel Stone
2016-12-09 19:58:16 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>

Differential Revision: https://phabricator.freedesktop.org/D1530
---
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 be2b4ea..dc68768 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -1409,10 +1409,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;

@@ -2504,10 +2500,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;
@@ -2666,13 +2658,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);
@@ -2711,6 +2712,11 @@ drm_assign_planes(struct weston_output *output_base)
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.9.3
Daniel Stone
2016-12-09 19:57:37 UTC
Permalink
'next' is used as a framebuffer which has either been rendered but not
had a configuration request (pageflip or CRTC set) applied to it, or
when for a framebuffer that has had configuration requested but not
applied (delayed pageflip where the event has not been applied).

'current' is used as the last framebuffer for which we know
configuration has been fully applied, i.e. CRTC set executed or pageflip
requested and event received.

Rename these members to fb_current and fb_pending.

Signed-off-by: Daniel Stone <***@collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1410
---
libweston/compositor-drm.c | 86 +++++++++++++++++++++++-----------------------
1 file changed, 43 insertions(+), 43 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 557b97d..8071737 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -190,7 +190,7 @@ struct drm_output {
int current_cursor;

struct weston_plane fb_plane;
- struct drm_fb *current, *next;
+ struct drm_fb *fb_current, *fb_pending;
struct backlight *backlight;

struct drm_fb *dumb[2];
@@ -211,7 +211,7 @@ struct drm_sprite {

struct weston_plane plane;

- struct drm_fb *current, *next;
+ struct drm_fb *fb_current, *fb_pending;
struct drm_output *output;
struct drm_backend *backend;

@@ -604,13 +604,13 @@ drm_output_prepare_scanout_view(struct drm_output *output,
return NULL;
}

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

- drm_fb_set_buffer(output->next, buffer);
+ drm_fb_set_buffer(output->fb_pending, buffer);

return &output->fb_plane;
}
@@ -630,14 +630,14 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
return;
}

- output->next = drm_fb_get_from_bo(bo, b, output->gbm_format,
- BUFFER_GBM_SURFACE);
- if (!output->next) {
+ output->fb_pending = drm_fb_get_from_bo(bo, b, output->gbm_format,
+ BUFFER_GBM_SURFACE);
+ if (!output->fb_pending) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
return;
}
- output->next->gbm_surface = output->gbm_surface;
+ output->fb_pending->gbm_surface = output->gbm_surface;
}

static void
@@ -656,7 +656,7 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)

output->current_image ^= 1;

- output->next = drm_fb_ref(output->dumb[output->current_image]);
+ output->fb_pending = drm_fb_ref(output->dumb[output->current_image]);
pixman_renderer_output_set_buffer(&output->base,
output->image[output->current_image]);

@@ -742,16 +742,16 @@ drm_output_repaint(struct weston_output *output_base,
if (output->disable_pending || output->destroy_pending)
return -1;

- if (!output->next)
+ if (!output->fb_pending)
drm_output_render(output, damage);
- if (!output->next)
+ if (!output->fb_pending)
return -1;

mode = container_of(output->base.current_mode, struct drm_mode, base);
- if (!output->current ||
- output->current->stride != output->next->stride) {
+ if (!output->fb_current ||
+ output->fb_current->stride != output->fb_pending->stride) {
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- output->next->fb_id, 0, 0,
+ output->fb_pending->fb_id, 0, 0,
&output->connector_id, 1,
&mode->mode_info);
if (ret) {
@@ -762,7 +762,7 @@ drm_output_repaint(struct weston_output *output_base,
}

if (drmModePageFlip(backend->drm.fd, output->crtc_id,
- output->next->fb_id,
+ output->fb_pending->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
weston_log("queueing pageflip failed: %m\n");
goto err_pageflip;
@@ -782,12 +782,12 @@ drm_output_repaint(struct weston_output *output_base,
.request.sequence = 1,
};

- if ((!s->current && !s->next) ||
+ if ((!s->fb_current && !s->fb_pending) ||
!drm_sprite_crtc_supported(output, s))
continue;

- if (s->next && !backend->sprites_hidden)
- fb_id = s->next->fb_id;
+ if (s->fb_pending && !backend->sprites_hidden)
+ fb_id = s->fb_pending->fb_id;

ret = drmModeSetPlane(backend->drm.fd, s->plane_id,
output->crtc_id, fb_id, flags,
@@ -820,9 +820,9 @@ drm_output_repaint(struct weston_output *output_base,

err_pageflip:
output->cursor_view = NULL;
- if (output->next) {
- drm_fb_unref(output->next);
- output->next = NULL;
+ if (output->fb_pending) {
+ drm_fb_unref(output->fb_pending);
+ output->fb_pending = NULL;
}

return -1;
@@ -848,7 +848,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
if (output->disable_pending || output->destroy_pending)
return;

- if (!output->current) {
+ if (!output->fb_current) {
/* We can't page flip if there's no mode set */
goto finish_frame;
}
@@ -882,7 +882,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base)
/* Immediate query didn't provide valid timestamp.
* Use pageflip fallback.
*/
- fb_id = output->current->fb_id;
+ fb_id = output->fb_current->fb_id;

if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
@@ -923,9 +923,9 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
drm_output_update_msc(output, frame);
output->vblank_pending = 0;

- drm_fb_unref(s->current);
- s->current = s->next;
- s->next = NULL;
+ drm_fb_unref(s->fb_current);
+ s->fb_current = s->fb_pending;
+ s->fb_pending = NULL;

if (!output->page_flip_pending) {
ts.tv_sec = sec;
@@ -953,9 +953,9 @@ page_flip_handler(int fd, unsigned int frame,
* we just want to page flip to the current buffer to get an accurate
* timestamp */
if (output->page_flip_pending) {
- drm_fb_unref(output->current);
- output->current = output->next;
- output->next = NULL;
+ drm_fb_unref(output->fb_current);
+ output->fb_current = output->fb_pending;
+ output->fb_pending = NULL;
}

output->page_flip_pending = 0;
@@ -1053,7 +1053,7 @@ drm_output_prepare_overlay_view(struct drm_output *output,
if (!drm_sprite_crtc_supported(output, s))
continue;

- if (!s->next) {
+ if (!s->fb_pending) {
found = 1;
break;
}
@@ -1101,13 +1101,13 @@ drm_output_prepare_overlay_view(struct drm_output *output,
return NULL;
}

- s->next = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!s->next) {
+ s->fb_pending = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
+ if (!s->fb_pending) {
gbm_bo_destroy(bo);
return NULL;
}

- drm_fb_set_buffer(s->next, ev->surface->buffer_ref.buffer);
+ drm_fb_set_buffer(s->fb_pending, ev->surface->buffer_ref.buffer);

box = pixman_region32_extents(&ev->transform.boundingbox);
s->plane.x = box->x1;
@@ -1475,9 +1475,9 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;

/* reset rendering stuff. */
- drm_fb_unref(output->current);
- drm_fb_unref(output->next);
- output->current = output->next = NULL;
+ drm_fb_unref(output->fb_current);
+ drm_fb_unref(output->fb_pending);
+ output->fb_current = output->fb_pending = NULL;

if (b->use_pixman) {
drm_output_fini_pixman(output);
@@ -2693,8 +2693,8 @@ create_sprites(struct drm_backend *b)

sprite->possible_crtcs = plane->possible_crtcs;
sprite->plane_id = plane->plane_id;
- sprite->current = NULL;
- sprite->next = NULL;
+ sprite->fb_current = NULL;
+ sprite->fb_pending = NULL;
sprite->backend = b;
sprite->count_formats = plane->count_formats;
memcpy(sprite->formats, plane->formats,
@@ -2724,8 +2724,8 @@ destroy_sprites(struct drm_backend *backend)
sprite->plane_id,
output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- drm_fb_unref(sprite->current);
- drm_fb_unref(sprite->next);
+ drm_fb_unref(sprite->fb_current);
+ drm_fb_unref(sprite->fb_pending);
weston_plane_release(&sprite->plane);
free(sprite);
}
@@ -3061,7 +3061,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data)
if (!output->recorder)
return;

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

ret = vaapi_recorder_frame(output->recorder, fd,
- output->current->stride);
+ output->fb_current->stride);
if (ret < 0) {
weston_log("[libva recorder] aborted: %m\n");
recorder_destroy(output);
--
2.9.3
Armin Krezović
2016-12-09 20:37:28 UTC
Permalink
Rather than duplicating knowledge of pixel formats across several
components, create a custom central repository.
Hi,
Differential Revision: https://phabricator.freedesktop.org/D1511
---
libweston/pixel-formats.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++
libweston/pixel-formats.h | 112 +++++++++++++
2 files changed, 510 insertions(+)
create mode 100644 libweston/pixel-formats.c
create mode 100644 libweston/pixel-formats.h
Where are corresponding build system modifications?
diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c
new file mode 100644
index 0000000..9c70e73
--- /dev/null
+++ b/libweston/pixel-formats.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright © 2016 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <drm/drm_fourcc.h>
+
+#include "helpers.h"
+#include "wayland-util.h"
+#include "pixel-formats.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
Is it supposed to work without EGL/GLESv2 enabled?
+#include "weston-egl-ext.h"
+
+/**
+ * Table of DRM formats supported by Weston; RGB, ARGB and YUV formats are
+ * supported. Indexed/greyscale formats, and formats not containing complete
+ * colour channels, are not supported.
+ */
I expected something using this immediately. I suggest you squash it with something
else that uses this.
Daniel Stone
2016-12-09 21:30:32 UTC
Permalink
Hi,
Post by Armin Krezović
libweston/pixel-formats.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++
libweston/pixel-formats.h | 112 +++++++++++++
Where are corresponding build system modifications?
Missing, apparently ... :\
Post by Armin Krezović
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
Is it supposed to work without EGL/GLESv2 enabled?
Not really, no; note that the DRM backend has a hard dependency on GBM
and the Wayland backend has a hard dependency on libwayland-egl, so
this only really affects people building the X11/headless backends
only with no EGL. I suppose we could do a giant #ifdef tree, or just
pull all the tokens we use into weston-egl-ext.h and use that. Do you
have any preferences?
Post by Armin Krezović
+/**
+ * Table of DRM formats supported by Weston; RGB, ARGB and YUV formats are
+ * supported. Indexed/greyscale formats, and formats not containing complete
+ * colour channels, are not supported.
+ */
I expected something using this immediately. I suggest you squash it with something
else that uses this.
It could be squashed with 'Store format in drm_fb', but as the
DRM-specific parts of the series were getting very little review, and
it can also be useful for gl-renderer's SHM uploads in particular, I
didn't want to muddle it in with the rest of the series. Depending on
how that goes (whether I get to porting gl-renderer, if earlier parts
of the series get reviewed so we can merge this, etc), it can be
squashed into its first user.

Cheers,
Daniel
Armin Krezović
2016-12-09 22:05:51 UTC
Permalink
Post by Daniel Stone
Hi,
Hi,
Post by Daniel Stone
Post by Armin Krezović
libweston/pixel-formats.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++
libweston/pixel-formats.h | 112 +++++++++++++
Where are corresponding build system modifications?
Missing, apparently ... :\
Post by Armin Krezović
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
Is it supposed to work without EGL/GLESv2 enabled?
Not really, no; note that the DRM backend has a hard dependency on GBM
and the Wayland backend has a hard dependency on libwayland-egl, so
this only really affects people building the X11/headless backends
only with no EGL. I suppose we could do a giant #ifdef tree, or just
pull all the tokens we use into weston-egl-ext.h and use that. Do you
have any preferences?
Well, if my guess is correct, this is supposed to be part of libweston, no?

I don't think some people would like to have hard EGL dep on libweston itself,
and conditionally building this into the library might produce incompatible
lib with and without --disable-egl using the same source.

That said, I'm all for importing the needed bits into weston-egl-ext.h (if
you prefer to #ifdef a lot, go ahead).
Post by Daniel Stone
Post by Armin Krezović
+/**
+ * Table of DRM formats supported by Weston; RGB, ARGB and YUV formats are
+ * supported. Indexed/greyscale formats, and formats not containing complete
+ * colour channels, are not supported.
+ */
I expected something using this immediately. I suggest you squash it with something
else that uses this.
It could be squashed with 'Store format in drm_fb', but as the
DRM-specific parts of the series were getting very little review, and
it can also be useful for gl-renderer's SHM uploads in particular, I
didn't want to muddle it in with the rest of the series. Depending on
how that goes (whether I get to porting gl-renderer, if earlier parts
of the series get reviewed so we can merge this, etc), it can be
squashed into its first user.
Well, you could've squashed it into that patch and sent it (from what I've
seen it doesn't involve any new code and doesn't depend on anything else),
and I can help you with review and gl-renderer porting, as this can be
landed independently from rest of the series.

Even if they don't fit together, at least make the patch that uses this a
next one, so someone can take a look at example usage without having to
dig through whole series to find out what is using this.
Post by Daniel Stone
Cheers,
Daniel
Daniel Stone
2016-12-09 21:48:08 UTC
Permalink
Hi,
Post by Daniel Stone
This is v2 of the atomic patchset, which incorporates quite a few
fixups on the original code, and extends it far enough to flip
sprites_are_broken off for atomic. \o/
And it lives here:
git://git.collabora.com/git/user/daniels/weston#wip/phab/T7595-atomic-modeset
Post by Daniel Stone
First we try to construct a view made entirely out of planes and
nothing else; if this succeeds, we just use that and we don't need to
render anything. Failing that, we try to incrementally build a state,
trying one view at a time on one plane at a time, and seeing if that
changes anything. Doing this is what lets us flip sprites_are_broken,
because we can not only generate an optimal configuration, but make
sure it'll actually work when we do it.
There are a couple of things I'm not entirely happy with here in
hindsight, that a nice long walk has helped clarify.

The duplicate_renderer dance in drm_output_propose_state /
drm_output_prepare_scanout_view is messy, and in fact leaks the old
state when we successfully promote a view to scanout. I did play
around with special-casing the drm_plane_state_*() API to deal with
this, but didn't like the non-obviousness it introduced. I think a
cleaner solution might just be to duplicate the entire output_state;
I'll have another play around with both (and a clearer head) next
week.

The 'test plane states' commit is obscured by the fact overlay
assignment moves from picking one plane early and sticking to that, to
testing all the planes in a loop. I was trying to avoid exploding the
series into too many patches, but may have got the balance wrong
there. I'll probably change that in the next iteration.

I think we also need to delay start_repaint_loop() if we have a DPMS
off that hasn't completed; in testing just now I was able to hit a
rare breakage (hooray for asserts!) which I suspect was exactly this.

Short of this, any general commments welcome, as well as review of the
earlier patches in the series (thanks Armin!), so we can start landing
the less dangerous/controversial pieces and trim the series down a
bit. Testing on exotic platforms is always good too: I have Intel,
Rockchip (with the Mali driver), and Raspberry Pi (less interesting as
it can't put client surfaces in planes) here, and hopefully will have
Freedreno running next week. Maybe Tegra if Alexandre posts a tree
with that working too.

Cheers,
Daniel
Pekka Paalanen
2016-12-14 15:59:38 UTC
Permalink
On Fri, 9 Dec 2016 19:57:16 +0000
Rather than duplicating knowledge of pixel formats across several
components, create a custom central repository.
Differential Revision: https://phabricator.freedesktop.org/D1511
---
libweston/pixel-formats.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++
libweston/pixel-formats.h | 112 +++++++++++++
2 files changed, 510 insertions(+)
create mode 100644 libweston/pixel-formats.c
create mode 100644 libweston/pixel-formats.h
diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c
new file mode 100644
index 0000000..9c70e73
--- /dev/null
+++ b/libweston/pixel-formats.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright © 2016 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <drm/drm_fourcc.h>
+
+#include "helpers.h"
+#include "wayland-util.h"
+#include "pixel-formats.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "weston-egl-ext.h"
+
+/**
+ * Table of DRM formats supported by Weston; RGB, ARGB and YUV formats are
+ * supported. Indexed/greyscale formats, and formats not containing complete
+ * colour channels, are not supported.
+ */
+static const struct pixel_format_info pixel_format_table[] = {
+ {
+ .format = DRM_FORMAT_XRGB4444,
+ },
+ {
+ .format = DRM_FORMAT_ARGB4444,
+ .opaque_substitute = DRM_FORMAT_XRGB4444,
+ },
+ {
+ .format = DRM_FORMAT_XBGR4444,
+ },
+ {
+ .format = DRM_FORMAT_ABGR4444,
+ .opaque_substitute = DRM_FORMAT_XBGR4444,
+ },
+ {
+ .format = DRM_FORMAT_RGBX4444,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
Could there be any concern about sampling garbage as alpha?
Should we have a flag 'ignore_alpha'?
+ },
+ {
+ .format = DRM_FORMAT_RGBA4444,
+ .opaque_substitute = DRM_FORMAT_RGBX4444,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
+ {
+ .format = DRM_FORMAT_BGRX4444,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
+ {
+ .format = DRM_FORMAT_BGRA4444,
+ .opaque_substitute = DRM_FORMAT_BGRX4444,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
+ {
+ .format = DRM_FORMAT_XRGB1555,
+ .depth = 15,
+ .bpp = 16,
+ },
+ {
+ .format = DRM_FORMAT_ARGB1555,
+ .opaque_substitute = DRM_FORMAT_XRGB1555,
+ },
+ {
+ .format = DRM_FORMAT_XBGR1555,
+ },
+ {
+ .format = DRM_FORMAT_ABGR1555,
+ .opaque_substitute = DRM_FORMAT_XBGR1555,
+ },
+ {
+ .format = DRM_FORMAT_RGBX5551,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_5_5_5_1,
+ },
+ {
+ .format = DRM_FORMAT_RGBA5551,
+ .opaque_substitute = DRM_FORMAT_RGBX5551,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_5_5_5_1,
+ },
+ {
+ .format = DRM_FORMAT_BGRX5551,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_5_5_5_1,
+ },
+ {
+ .format = DRM_FORMAT_BGRA5551,
+ .opaque_substitute = DRM_FORMAT_BGRX5551,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_5_5_5_1,
+ },
+ {
+ .format = DRM_FORMAT_RGB565,
+ .depth = 16,
+ .bpp = 16,
+ .gl_type = GL_RGB,
+ .gl_type = GL_UNSIGNED_SHORT_5_6_5,
+ },
+ {
+ .format = DRM_FORMAT_BGR565,
+ },
+ {
+ .format = DRM_FORMAT_RGB888,
+ },
+ {
+ .format = DRM_FORMAT_BGR888,
+ .gl_type = GL_RGB,
+ .gl_format = GL_UNSIGNED_BYTE,
How do the 24-bpp formats actually work? Do we really imagine there is
a 24-bit word and then address the bits of that?

Yes, I'm thinking about big-endian.
+ },
+ {
+ .format = DRM_FORMAT_XRGB8888,
+ .depth = 24,
+ .bpp = 32,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
GL info incorrect for big-endian.
+ },
+ {
+ .format = DRM_FORMAT_ARGB8888,
+ .opaque_substitute = DRM_FORMAT_XRGB8888,
+ .depth = 32,
+ .bpp = 32,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
GL info incorrect for big-endian.

Well, yeah, you know.

< daniels> happy to drop a #if __BYTE_ORDER__ == BIG_ENDIAN #error
reverse literally every single one of these #endif in there

Please do. :-)
+ },
+ {
+ .format = DRM_FORMAT_XBGR8888,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_BYTE,
+ },
+ {
+ .format = DRM_FORMAT_ABGR8888,
+ .opaque_substitute = DRM_FORMAT_XBGR8888,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_BYTE,
+ },
+ {
+ .format = DRM_FORMAT_RGBX8888,
+ },
+ {
+ .format = DRM_FORMAT_RGBA8888,
+ .opaque_substitute = DRM_FORMAT_RGBX8888,
+ },
+ {
+ .format = DRM_FORMAT_BGRX8888,
+ },
+ {
+ .format = DRM_FORMAT_BGRA8888,
+ .opaque_substitute = DRM_FORMAT_BGRX8888,
+ },
+ {
+ .format = DRM_FORMAT_XRGB2101010,
+ .depth = 30,
+ .bpp = 32,
+ },
+ {
+ .format = DRM_FORMAT_ARGB2101010,
+ .opaque_substitute = DRM_FORMAT_XRGB2101010,
+ .gl_type = GL_BGRA_EXT,
+ .gl_format = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
I suspect the GL format is not right...

"The elements in a reversed packed pixel are ordered such that the
first element is in the least-significant bits, ..."

So, B would be the 2 LSB, G the next higher 10 bits, etc.

The DRM format says A is the 2 MSB, right? That would make
GL_UNSIGNED_INT_2_10_10_10_EXT for the bits, and GL_ARGB for the order
(which might not exist?).

Or, GL_UNSIGNED_INT_10_10_10_2_REV (does this exist?) and GL_BGRA_EXT.
+ },
+ {
+ .format = DRM_FORMAT_XBGR2101010,
+ .gl_type = GL_RGB,
+ .gl_format = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
I think this might be illegal, having only 3 elements in GL_RGB, but 4
in the bit format.
+ },
+ {
+ .format = DRM_FORMAT_ABGR2101010,
+ .opaque_substitute = DRM_FORMAT_XBGR2101010,
+ .gl_type = GL_RGBA,
+ .gl_format = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
Looks flawed as earlier.
+ },
+ {
+ .format = DRM_FORMAT_RGBX1010102,
+ },
+ {
+ .format = DRM_FORMAT_RGBA1010102,
+ .opaque_substitute = DRM_FORMAT_RGBX1010102,
+ },
+ {
+ .format = DRM_FORMAT_BGRX1010102,
+ },
+ {
+ .format = DRM_FORMAT_BGRA1010102,
+ .opaque_substitute = DRM_FORMAT_BGRX1010102,
+ },
+ {
+ .format = DRM_FORMAT_YUYV,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .hsub = 2,
Should chroma_order and luma_chroma_order be set? I suppose 0 happens
to be a right value and it gets written implicitly.
+ },
+ {
+ .format = DRM_FORMAT_YVYU,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
Should these entries have also all the information we now have in the
yuv_formats table in gl-renderer.c?
+ },
+ {
+ .format = DRM_FORMAT_UYVY,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .luma_chroma_order = ORDER_CHROMA_LUMA,
+ },
+ {
+ .format = DRM_FORMAT_VYUY,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .luma_chroma_order = ORDER_CHROMA_LUMA,
+ .chroma_order = ORDER_VU,
+ },
+ {
+ /* our one format with an alpha channel and no opaque equiv;
+ * also cannot be trivially converted to RGB without writing
+ * a new shader in gl-renderer */
+ .format = DRM_FORMAT_AYUV,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ },
+ {
+ .format = DRM_FORMAT_NV12,
+ .sampler_type = EGL_TEXTURE_Y_UV_WL,
+ .num_planes = 2,
+ .hsub = 2,
+ .vsub = 2,
+ },
+ {
+ .format = DRM_FORMAT_NV21,
+ .sampler_type = EGL_TEXTURE_Y_UV_WL,
+ .num_planes = 2,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
+ .vsub = 2,
+ },
+ {
+ .format = DRM_FORMAT_NV16,
+ .sampler_type = EGL_TEXTURE_Y_UV_WL,
+ .num_planes = 2,
+ .hsub = 2,
+ .vsub = 1,
+ },
+ {
+ .format = DRM_FORMAT_NV61,
+ .sampler_type = EGL_TEXTURE_Y_UV_WL,
+ .num_planes = 2,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
+ .vsub = 1,
+ },
+ {
+ .format = DRM_FORMAT_NV24,
+ .sampler_type = EGL_TEXTURE_Y_UV_WL,
+ .num_planes = 2,
+ },
+ {
+ .format = DRM_FORMAT_NV42,
+ .sampler_type = EGL_TEXTURE_Y_UV_WL,
+ .num_planes = 2,
+ .chroma_order = ORDER_VU,
+ },
+ {
+ .format = DRM_FORMAT_YUV410,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .hsub = 4,
+ .vsub = 4,
+ },
+ {
+ .format = DRM_FORMAT_YVU410,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .chroma_order = ORDER_VU,
+ .hsub = 4,
+ .vsub = 4,
+ },
+ {
+ .format = DRM_FORMAT_YUV411,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .hsub = 4,
+ .vsub = 1,
+ },
+ {
+ .format = DRM_FORMAT_YVU411,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .chroma_order = ORDER_VU,
+ .hsub = 4,
+ .vsub = 1,
+ },
+ {
+ .format = DRM_FORMAT_YUV420,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .hsub = 2,
+ .vsub = 2,
+ },
+ {
+ .format = DRM_FORMAT_YVU420,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
+ .vsub = 2,
+ },
+ {
+ .format = DRM_FORMAT_YUV422,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .hsub = 2,
+ .vsub = 1,
+ },
+ {
+ .format = DRM_FORMAT_YVU422,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
+ .vsub = 1,
+ },
+ {
+ .format = DRM_FORMAT_YUV444,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ },
+ {
+ .format = DRM_FORMAT_YVU444,
+ .sampler_type = EGL_TEXTURE_Y_U_V_WL,
+ .num_planes = 3,
+ .chroma_order = ORDER_VU,
+ },
+};
+
+WL_EXPORT const struct pixel_format_info *
+pixel_format_get_info(uint32_t format)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH(pixel_format_table); i++) {
+ if (pixel_format_table[i].format == format)
+ return &pixel_format_table[i];
+ }
+
+ return NULL;
+}
+
+WL_EXPORT unsigned int
+pixel_format_get_plane_count(const struct pixel_format_info *info)
+{
+ return info->num_planes ? info->num_planes : 1;
+}
+
+WL_EXPORT bool
+pixel_format_is_opaque(const struct pixel_format_info *info)
+{
+ return !!info->opaque_substitute;
Um, if there is an opaque substitute, you say the original format is
opaque?
Shouldn't it be the opposite? And even then I'm not sure it's accurate.
+}
+
+WL_EXPORT const struct pixel_format_info *
+pixel_format_get_opaque_substitute(const struct pixel_format_info *info)
+{
+ if (!info->opaque_substitute)
+ return info;
What does this help? Intuitively I'd return NULL if there is no opaque
substitute (and the format is not opaque itself?).
+ else
+ return pixel_format_get_info(info->opaque_substitute);
+}
diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h
new file mode 100644
index 0000000..8ab5035
--- /dev/null
+++ b/libweston/pixel-formats.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2016 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+/**
+ * Contains information about pixel formats, mapping format codes from
+ * wl_shm and drm_fourcc.h (which are deliberately identical) into various
Except for WL_SHM_ARGB8888 and WL_SHM_XRGB8888. \o/
+ * sets of information. Helper functions are provided for dealing with these
+ * raw structures.
+ */
+struct pixel_format_info {
+ /** DRM/wl_shm format code */
+ uint32_t format;
+
+ /** If non-zero, number of planes in base (non-modified) format. */
+ int num_planes;
+
+ /** If format contains alpha channel, opaque equivalent of format,
+ * i.e. alpha channel replaced with X. */
+ uint32_t opaque_substitute;
+
+ /** How the format should be sampled, expressed in terms of tokens
+ * from the EGL_WL_bind_wayland_display extension. If not set,
+ * assumed to be either RGB or RGBA, depending on whether or not
+ * the format contains an alpha channel. */
+ uint32_t sampler_type;
+
+ /** GL format, if data can be natively/directly uploaded. Note that
+ * whilst DRM formats are little-endian unless explicitly specified,
+ * (i.e. DRM_FORMAT_ARGB8888 is stored BGRA as sequential bytes in
+ * memory), GL uses the sequential byte order, so that format maps to
+ * GL_BGRA_EXT plus GL_UNSIGNED_BYTE. To add to the confusion, the
+ * explicitly-sized types (e.g. GL_UNSIGNED_SHORT_5_5_5_1) read in
+ * this big-endian order, so for these types, the GL format descriptor
+ * matches the order in the DRM format. */
Do mention the fun on big-endian. ;-)
+ int gl_format;
+
+ /** GL data type, if data can be natively/directly uploaded. */
+ int gl_type;
+
+ /** If set, this format can be used with the legacy drmModeAddFB()
+ * function (not AddFB2), using this and the bpp member. */
+ int depth;
+
+ /** See 'depth' member above. */
+ int bpp;
+
+ /** Horizontal subsampling; if non-zero, divide the width by this
+ * member to obtain the number of columns in the source buffer for
+ * secondary planes only. Stride is not affected by horizontal
+ * subsampling. */
+ int hsub;
+
+ /** Horizontal subsampling; if non-zero, divide the height by this
Should be vertical.
+ * member to obtain the number of rows in the source buffer for
+ * secondary planes only. */
+ int vsub;
+
+ /* Ordering of chroma components. */
+ enum {
+ ORDER_UV = 0,
+ ORDER_VU,
+ } chroma_order;
+
+ /* If packed YUV (num_planes == 1), ordering of luma/chroma
+ * components. */
+ enum {
+ ORDER_LUMA_CHROMA = 0,
+ ORDER_CHROMA_LUMA,
+ } luma_chroma_order;
+};
+
+const struct pixel_format_info *pixel_format_get_info(uint32_t format);
+
+unsigned int
+pixel_format_get_plane_count(const struct pixel_format_info *format);
+
+bool pixel_format_is_opaque(const struct pixel_format_info *format);
+
+const struct pixel_format_info *
+pixel_format_get_opaque_substitute(const struct pixel_format_info *format);
+
+unsigned int
+pixel_format_width_for_plane(const struct pixel_format_info *format,
+ unsigned int plane);
+unsigned int
+pixel_format_height_for_plane(const struct pixel_format_info *format,
+ unsigned int plane);
Very nice, very boring. Hurrah!

Sure there isn't something we could simply steal from?

Btw. the functions are missing docs. Good that the struct has it already.


Thanks,
pq
Daniel Stone
2017-02-13 18:24:58 UTC
Permalink
Hi,
Post by Pekka Paalanen
On Fri, 9 Dec 2016 19:57:16 +0000
+ {
+ .format = DRM_FORMAT_RGBX4444,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
Could there be any concern about sampling garbage as alpha?
Should we have a flag 'ignore_alpha'?
I could add an explicit .you_must_ignore_alpha_no_im_serious = 1 to
opaque formats, but given that there's already a helper to query
whether or not the format is opaque, it seems a bit overkill.
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_BGRX4444,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
+ {
+ .format = DRM_FORMAT_BGRA4444,
+ .opaque_substitute = DRM_FORMAT_BGRX4444,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
Turns out that BGRA_EXT is only considered valid for UNSIGNED_BYTE, so
I've axed all users of BGRA_EXT except for ARGB8888/XRGB8888.
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_BGR888,
+ .gl_type = GL_RGB,
+ .gl_format = GL_UNSIGNED_BYTE,
How do the 24-bpp formats actually work? Do we really imagine there is
a 24-bit word and then address the bits of that?
Yes, I'm thinking about big-endian.
Not a word as such. To the very best of my understanding,
UNSIGNED_BYTE loads each component in left-to-right order (in this
case, R then G then B) one byte at a time, starting from lower to
higher memory address. IOW, GL_RGB + GL_UNSIGNED_BYTE will always,
regardless of endianness, load DRM_FORMAT_BGR888 as defined to be
little-endian.
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_XRGB8888,
+ .depth = 24,
+ .bpp = 32,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
GL info incorrect for big-endian.
As above, I don't believe that to be the case.
Post by Pekka Paalanen
+ },
+ {
+ .format = DRM_FORMAT_ARGB8888,
+ .opaque_substitute = DRM_FORMAT_XRGB8888,
+ .depth = 32,
+ .bpp = 32,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
GL info incorrect for big-endian.
Well, yeah, you know.
< daniels> happy to drop a #if __BYTE_ORDER__ == BIG_ENDIAN #error
reverse literally every single one of these #endif in there
Please do. :-)
I went with a different approach; see below. (Also note that
gl-renderer is absolutely broken _today_ if this is the case, since it
maps ARGB8888 -> GL_BGRA_EXT + GL_UNSIGNED_BYTE without regard to
endianness. But I don't think it is, as above.)
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_ARGB2101010,
+ .opaque_substitute = DRM_FORMAT_XRGB2101010,
+ .gl_type = GL_BGRA_EXT,
+ .gl_format = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
I suspect the GL format is not right...
"The elements in a reversed packed pixel are ordered such that the
first element is in the least-significant bits, ..."
So, B would be the 2 LSB, G the next higher 10 bits, etc.
The DRM format says A is the 2 MSB, right? That would make
GL_UNSIGNED_INT_2_10_10_10_EXT for the bits, and GL_ARGB for the order
(which might not exist?).
Or, GL_UNSIGNED_INT_10_10_10_2_REV (does this exist?) and GL_BGRA_EXT.
Mind you, BGRA_EXT can't be used for anything other than
UNSIGNED_BYTE, so I just axed the GL equivalence for all the 10bpc
formats as there is none. You're right that the format definition was
confused even if this were the case though.
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_YUYV,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .hsub = 2,
Should chroma_order and luma_chroma_order be set? I suppose 0 happens
to be a right value and it gets written implicitly.
Exactly.
Post by Pekka Paalanen
+ },
+ {
+ .format = DRM_FORMAT_YVYU,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
Should these entries have also all the information we now have in the
yuv_formats table in gl-renderer.c?
What in particular do you feel it's missing? Per-plane subsampling values?
Post by Pekka Paalanen
+WL_EXPORT bool
+pixel_format_is_opaque(const struct pixel_format_info *info)
+{
+ return !!info->opaque_substitute;
Um, if there is an opaque substitute, you say the original format is
opaque?
Shouldn't it be the opposite? And even then I'm not sure it's accurate.
You're right that it's currently inverted, but when fixed, this only
returns incorrect information for AYUV (IIRC). But AYUV isn't actually
supported, so ...
Post by Pekka Paalanen
+WL_EXPORT const struct pixel_format_info *
+pixel_format_get_opaque_substitute(const struct pixel_format_info *info)
+{
+ if (!info->opaque_substitute)
+ return info;
What does this help? Intuitively I'd return NULL if there is no opaque
substitute (and the format is not opaque itself?).
If we've already determined through the surface's opaque region that
we can address the entire buffer as opaque, then we would have to have
something like:
if (surface_is_completely_opaque(surface)) {
if (pixel_format_is_opaque(format))
format = pixel_format_get_opaque_substitute(format);
[...]
}

This simplifies that slightly.
Post by Pekka Paalanen
+/**
+ * Contains information about pixel formats, mapping format codes from
+ * wl_shm and drm_fourcc.h (which are deliberately identical) into various
Except for WL_SHM_ARGB8888 and WL_SHM_XRGB8888. \o/
Ugh, that again.
Post by Pekka Paalanen
+ /** GL format, if data can be natively/directly uploaded. Note that
+ * whilst DRM formats are little-endian unless explicitly specified,
+ * (i.e. DRM_FORMAT_ARGB8888 is stored BGRA as sequential bytes in
+ * memory), GL uses the sequential byte order, so that format maps to
+ * GL_BGRA_EXT plus GL_UNSIGNED_BYTE. To add to the confusion, the
+ * explicitly-sized types (e.g. GL_UNSIGNED_SHORT_5_5_5_1) read in
+ * this big-endian order, so for these types, the GL format descriptor
+ * matches the order in the DRM format. */
Do mention the fun on big-endian. ;-)
Explicitly? What should I add?
Post by Pekka Paalanen
+ /** Horizontal subsampling; if non-zero, divide the height by this
Should be vertical.
Yep.
Post by Pekka Paalanen
Sure there isn't something we could simply steal from?
There's drm_fourcc.c in the kernel, but that didn't include anything
about how YUV formats are organised, which is pretty important for us
if we want to convert. It also lacks any indication of the translation
to GL (useful for gl-renderer), or drmModeAddFB (not AddFB2) formats.
Given that, I just went with something separate.
Post by Pekka Paalanen
Btw. the functions are missing docs. Good that the struct has it already.
I tried to make them simple enough that any documentation would be
insulting, but fair enough. Will add for v2.

Cheers,
Daniel
Pekka Paalanen
2017-02-14 10:30:25 UTC
Permalink
On Mon, 13 Feb 2017 18:24:58 +0000
Post by Daniel Stone
Hi,
Post by Pekka Paalanen
On Fri, 9 Dec 2016 19:57:16 +0000
+ {
+ .format = DRM_FORMAT_RGBX4444,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
Could there be any concern about sampling garbage as alpha?
Should we have a flag 'ignore_alpha'?
I could add an explicit .you_must_ignore_alpha_no_im_serious = 1 to
opaque formats, but given that there's already a helper to query
whether or not the format is opaque, it seems a bit overkill.
Ah yes, I missed that point. Very good, although it assumes that all
non-opaque formats do have an opaque substitute, which fails on AYUV.
Post by Daniel Stone
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_BGRX4444,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
+ {
+ .format = DRM_FORMAT_BGRA4444,
+ .opaque_substitute = DRM_FORMAT_BGRX4444,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ },
Turns out that BGRA_EXT is only considered valid for UNSIGNED_BYTE, so
I've axed all users of BGRA_EXT except for ARGB8888/XRGB8888.
D'oh.
Post by Daniel Stone
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_BGR888,
+ .gl_type = GL_RGB,
+ .gl_format = GL_UNSIGNED_BYTE,
How do the 24-bpp formats actually work? Do we really imagine there is
a 24-bit word and then address the bits of that?
Yes, I'm thinking about big-endian.
Not a word as such. To the very best of my understanding,
UNSIGNED_BYTE loads each component in left-to-right order (in this
case, R then G then B) one byte at a time, starting from lower to
higher memory address. IOW, GL_RGB + GL_UNSIGNED_BYTE will always,
regardless of endianness, load DRM_FORMAT_BGR888 as defined to be
little-endian.
Yes, the GL side is clear. The unclear part was DRM_FORMAT_BGR888 which
is defined as little-endian, but since there is no such thing as a
24-bit word, what does "little-endian" mean in that case?

#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */

I think that actually tries to say that there is a 24-bit word, which
is kind of like a 32-bit word for the byte order, except bits 24-31 do
not exist.

Yeah, hair-splitting, mostly on how the DRM description should be read.
Post by Daniel Stone
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_XRGB8888,
+ .depth = 24,
+ .bpp = 32,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
GL info incorrect for big-endian.
As above, I don't believe that to be the case.
I think you're right. DRM formats are always explicitly little-endian
regardless of the machine endianess. I was probably mislead by Pixman
formats which are machine-endian.

IOW, Pixman <-> DRM format conversion depends on machine endianess.

That makes drm_output_init_pixman() in upstream master broken for
big-endian, because the translation from GBM formats (identical to DRM
formats, except GBM_BO formats??) to Pixman formats does not take
machine endianess into account.

That actually raises a fun question about wl_shm formats. If Pixman
formats are machine-endian, and wl_shm formats are like DRM formats
little-endian, what happens with CAIRO_FORMAT_ARGB32 drawing?

https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t
says it's machine-endian.

I.e. Cairo on big-endian may not work with wl_shm, because the format
is not the ones guaranteed to work in the protocol spec or
libwayland-server.

IOW, Pixman <-> wl_shm format conversion depends on machine endianess.

Or, are the special format codes WL_SHM_FORMAT_ARGB8888 and
WL_SHM_FORMAT_XRGB8888 machine-endian unlike everything else?
If these were originally designed to match Cairo formats, these would
need to be machine-endian!
Post by Daniel Stone
Post by Pekka Paalanen
+ },
+ {
+ .format = DRM_FORMAT_ARGB8888,
+ .opaque_substitute = DRM_FORMAT_XRGB8888,
+ .depth = 32,
+ .bpp = 32,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
GL info incorrect for big-endian.
Well, yeah, you know.
< daniels> happy to drop a #if __BYTE_ORDER__ == BIG_ENDIAN #error
reverse literally every single one of these #endif in there
Please do. :-)
I went with a different approach; see below. (Also note that
gl-renderer is absolutely broken _today_ if this is the case, since it
maps ARGB8888 -> GL_BGRA_EXT + GL_UNSIGNED_BYTE without regard to
endianness. But I don't think it is, as above.)
Yeah, seeing there actually are people using big-endian still
( https://bugs.freedesktop.org/show_bug.cgi?id=99638 ).

I've always believed gl-renderer to be broken in that exact place you
mention, but now we have the question of what endianess was used to
define the first wl_shm formats.
Post by Daniel Stone
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_ARGB2101010,
+ .opaque_substitute = DRM_FORMAT_XRGB2101010,
+ .gl_type = GL_BGRA_EXT,
+ .gl_format = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
I suspect the GL format is not right...
"The elements in a reversed packed pixel are ordered such that the
first element is in the least-significant bits, ..."
So, B would be the 2 LSB, G the next higher 10 bits, etc.
The DRM format says A is the 2 MSB, right? That would make
GL_UNSIGNED_INT_2_10_10_10_EXT for the bits, and GL_ARGB for the order
(which might not exist?).
Or, GL_UNSIGNED_INT_10_10_10_2_REV (does this exist?) and GL_BGRA_EXT.
Mind you, BGRA_EXT can't be used for anything other than
UNSIGNED_BYTE, so I just axed the GL equivalence for all the 10bpc
formats as there is none. You're right that the format definition was
confused even if this were the case though.
Ok. No way to render 10bpc and put it out to KMS then? :-/
Well, in GLESv2 and possibly aside from converting in shader.
Post by Daniel Stone
Post by Pekka Paalanen
+ {
+ .format = DRM_FORMAT_YUYV,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .hsub = 2,
Should chroma_order and luma_chroma_order be set? I suppose 0 happens
to be a right value and it gets written implicitly.
Exactly.
Post by Pekka Paalanen
+ },
+ {
+ .format = DRM_FORMAT_YVYU,
+ .sampler_type = EGL_TEXTURE_Y_XUXV_WL,
+ .num_planes = 1,
+ .chroma_order = ORDER_VU,
+ .hsub = 2,
Should these entries have also all the information we now have in the
yuv_formats table in gl-renderer.c?
What in particular do you feel it's missing? Per-plane subsampling values?
I suppose that was it, I can't remember. It probably wasn't obvious how
to infer all that info, or whether it just didn't belong here.
Post by Daniel Stone
Post by Pekka Paalanen
+WL_EXPORT bool
+pixel_format_is_opaque(const struct pixel_format_info *info)
+{
+ return !!info->opaque_substitute;
Um, if there is an opaque substitute, you say the original format is
opaque?
Shouldn't it be the opposite? And even then I'm not sure it's accurate.
You're right that it's currently inverted, but when fixed, this only
returns incorrect information for AYUV (IIRC). But AYUV isn't actually
supported, so ...
AhhhHA! Seems a little... like burying in a mine in a field and
thinking I'm never going to go there. ;-)
Post by Daniel Stone
Post by Pekka Paalanen
+WL_EXPORT const struct pixel_format_info *
+pixel_format_get_opaque_substitute(const struct pixel_format_info *info)
+{
+ if (!info->opaque_substitute)
+ return info;
What does this help? Intuitively I'd return NULL if there is no opaque
substitute (and the format is not opaque itself?).
If we've already determined through the surface's opaque region that
we can address the entire buffer as opaque, then we would have to have
if (surface_is_completely_opaque(surface)) {
if (pixel_format_is_opaque(format))
format = pixel_format_get_opaque_substitute(format);
[...]
}
This simplifies that slightly.
Perhaps, but IMO it makes little sense from API definition point of
view. That is, I think it is surprising if you didn't read the
implementation.

But again this would only be a problem for AYUV which is both not
opaque and does not have an opaque substitute.

(Missed a ! there btw.)
Post by Daniel Stone
Post by Pekka Paalanen
+/**
+ * Contains information about pixel formats, mapping format codes from
+ * wl_shm and drm_fourcc.h (which are deliberately identical) into various
Except for WL_SHM_ARGB8888 and WL_SHM_XRGB8888. \o/
Ugh, that again.
Hmm, I was thinking only about the literal enum values differing, but
now there is the question if the formatf also differ by endianess
definition.
Post by Daniel Stone
Post by Pekka Paalanen
+ /** GL format, if data can be natively/directly uploaded. Note that
+ * whilst DRM formats are little-endian unless explicitly specified,
+ * (i.e. DRM_FORMAT_ARGB8888 is stored BGRA as sequential bytes in
+ * memory), GL uses the sequential byte order, so that format maps to
+ * GL_BGRA_EXT plus GL_UNSIGNED_BYTE. To add to the confusion, the
+ * explicitly-sized types (e.g. GL_UNSIGNED_SHORT_5_5_5_1) read in
+ * this big-endian order, so for these types, the GL format descriptor
+ * matches the order in the DRM format. */
Do mention the fun on big-endian. ;-)
Explicitly? What should I add?
That was probably a reference to the brainfart of thinking Pixman
formats instead of DRM formats.
Post by Daniel Stone
Post by Pekka Paalanen
+ /** Horizontal subsampling; if non-zero, divide the height by this
Should be vertical.
Yep.
Post by Pekka Paalanen
Sure there isn't something we could simply steal from?
There's drm_fourcc.c in the kernel, but that didn't include anything
about how YUV formats are organised, which is pretty important for us
if we want to convert. It also lacks any indication of the translation
to GL (useful for gl-renderer), or drmModeAddFB (not AddFB2) formats.
Given that, I just went with something separate.
Ok.
Post by Daniel Stone
Post by Pekka Paalanen
Btw. the functions are missing docs. Good that the struct has it already.
I tried to make them simple enough that any documentation would be
insulting, but fair enough. Will add for v2.
Cool, there was at least that one surprise. :-)


Thanksl,
pq
Loading...