wayland: support multiple devices and tranches when querying formats

The multi device part is mostly theoretical, but it turns out that
plasma does send multiple tranches to the same device so we have to take
that into account. And that means taking into account multiple devices
as well. In theory, a compositor should give us tranches for any device
that will work so as long as we find a match it should be OK. This is
still technically incomplete since mpv's core wouldn't react to a device
being hotplugged.
This commit is contained in:
Dudemanguy 2024-09-30 22:11:16 -05:00
parent 7ca5fd2ad2
commit 0d7b4d64a5
6 changed files with 124 additions and 79 deletions

View File

@ -44,8 +44,8 @@ static bool map(struct ra_hwdec_mapper *mapper,
if (mapper_p->desc.nb_layers != 1) {
MP_VERBOSE(mapper, "Mapped surface has separate layers - expected composed layers.\n");
return false;
} else if (!ra_compatible_format(mapper->ra, mapper->src->params.hw_subfmt,
drm_format, mapper_p->desc.objects[0].format_modifier)) {
} else if (!ra_compatible_format(mapper->ra, drm_format,
mapper_p->desc.objects[0].format_modifier)) {
MP_VERBOSE(mapper, "Mapped surface with format %s; drm format '%s(%016" PRIx64 ")' "
"is not supported by compositor and GPU combination.\n",
mp_imgfmt_to_name(mapper->src->params.hw_subfmt),

View File

@ -198,7 +198,7 @@ static void vaapi_dmabuf_importer(struct buffer *buf, struct mp_image *src,
goto done;
}
buf->drm_format = desc.layers[layer_no].drm_format;
if (!ra_compatible_format(p->ctx->ra, src->params.hw_subfmt, buf->drm_format, desc.objects[0].drm_format_modifier)) {
if (!ra_compatible_format(p->ctx->ra, buf->drm_format, desc.objects[0].drm_format_modifier)) {
MP_VERBOSE(vo, "%s(%016" PRIx64 ") is not supported.\n",
mp_tag_str(buf->drm_format), desc.objects[0].drm_format_modifier);
buf->drm_format = 0;
@ -680,7 +680,7 @@ static int reconfig(struct vo *vo, struct mp_image *img)
return VO_ERROR;
}
if (!ra_compatible_format(p->ctx->ra, img->params.hw_subfmt, p->drm_format, p->drm_modifier)) {
if (!ra_compatible_format(p->ctx->ra, p->drm_format, p->drm_modifier)) {
MP_ERR(vo, "Format '%s' with modifier '(%016" PRIx64 ")' is not supported by"
" the compositor.\n", mp_tag_str(p->drm_format), p->drm_modifier);
return VO_ERROR;

View File

@ -47,6 +47,7 @@
#include "fractional-scale-v1.h"
#if HAVE_DRM
#include <drm_fourcc.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#endif
@ -147,6 +148,12 @@ static const struct mp_keymap keymap[] = {
{0, 0}
};
struct compositor_format {
uint32_t format;
uint32_t padding;
uint64_t modifier;
};
struct vo_wayland_feedback_pool {
struct wp_presentation_feedback **fback;
struct vo_wayland_state *wl;
@ -199,6 +206,15 @@ struct vo_wayland_seat {
int num_keyboard_entering_keys;
};
struct vo_wayland_tranche {
struct drm_format *compositor_formats;
int num_compositor_formats;
uint32_t *planar_formats;
int num_planar_formats;
dev_t device_id;
struct wl_list link;
};
static bool single_output_spanned(struct vo_wayland_state *wl);
static int check_for_resize(struct vo_wayland_state *wl, int edge_pixels,
@ -1383,15 +1399,20 @@ static void main_device(void *data,
struct wl_array *device)
{
struct vo_wayland_state *wl = data;
wl->add_tranche = true;
// Remove any old devices and tranches if we get this again.
struct vo_wayland_tranche *tranche, *tranche_tmp;
wl_list_for_each_safe(tranche, tranche_tmp, &wl->tranche_list, link) {
wl_list_remove(&tranche->link);
talloc_free(tranche);
}
}
static void tranche_done(void *data,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
{
struct vo_wayland_state *wl = data;
wl->add_tranche = false;
wl->current_tranche = NULL;
}
static void tranche_target_device(void *data,
@ -1399,15 +1420,18 @@ static void tranche_target_device(void *data,
struct wl_array *device)
{
struct vo_wayland_state *wl = data;
// Only use the first tranche device we get.
if (wl->add_tranche) {
dev_t *id;
wl_array_for_each(id, device) {
memcpy(&wl->target_device_id, id, sizeof(dev_t));
break;
}
get_planar_drm_formats(wl);
struct vo_wayland_tranche *tranche = talloc_zero(wl, struct vo_wayland_tranche);
dev_t *id;
wl_array_for_each(id, device) {
memcpy(&tranche->device_id, id, sizeof(dev_t));
break;
}
static_assert(sizeof(tranche->device_id) == sizeof(dev_t), "");
wl->current_tranche = tranche;
get_planar_drm_formats(wl);
wl_list_insert(&wl->tranche_list, &tranche->link);
}
static void tranche_formats(void *data,
@ -1416,22 +1440,22 @@ static void tranche_formats(void *data,
{
struct vo_wayland_state *wl = data;
// Only grab formats from the first tranche and ignore the rest.
if (!wl->add_tranche)
return;
// Should never happen.
if (!wl->compositor_format_map) {
MP_WARN(wl, "Compositor did not send a format and modifier table!\n");
return;
}
const compositor_format *formats = wl->compositor_format_map;
MP_RESIZE_ARRAY(wl, wl->compositor_formats, indices->size);
wl->num_compositor_formats = 0;
struct vo_wayland_tranche *tranche = wl->current_tranche;
if (!tranche)
return;
const struct compositor_format *formats = wl->compositor_format_map;
uint16_t *index;
MP_DBG(wl, "Querying available drm format and modifier pairs from tranche on device '%lu'\n",
tranche->device_id);
wl_array_for_each(index, indices) {
MP_TARRAY_APPEND(wl, wl->compositor_formats, wl->num_compositor_formats,
MP_TARRAY_APPEND(tranche, tranche->compositor_formats, tranche->num_compositor_formats,
(struct drm_format) {
formats[*index].format,
formats[*index].modifier,
@ -1445,6 +1469,9 @@ static void tranche_flags(void *data,
struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1,
uint32_t flags)
{
struct vo_wayland_state *wl = data;
if (flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT)
MP_DBG(wl, "Tranche has direct scanout.\n");
}
static const struct zwp_linux_dmabuf_feedback_v1_listener dmabuf_feedback_listener = {
@ -1806,6 +1833,19 @@ static void add_feedback(struct vo_wayland_feedback_pool *fback_pool,
}
}
static bool devices_are_equal(dev_t a, dev_t b)
{
bool ret = false;
#if HAVE_DRM
drmDevice *deviceA, *deviceB;
if (!drmGetDeviceFromDevId(a, 0, &deviceA) && !drmGetDeviceFromDevId(b, 0, &deviceB))
ret = drmDevicesEqual(deviceA, deviceB);
drmFreeDevice(&deviceA);
drmFreeDevice(&deviceB);
#endif
return ret;
}
static void do_minimize(struct vo_wayland_state *wl)
{
if (wl->opts->window_minimized)
@ -1831,12 +1871,23 @@ static char **get_displays_spanned(struct vo_wayland_state *wl)
static void get_planar_drm_formats(struct vo_wayland_state *wl)
{
#if HAVE_DRM
struct vo_wayland_tranche *tranche;
wl_list_for_each(tranche, &wl->tranche_list, link) {
// If there is a device equality, just copy the pointer over.
if (devices_are_equal(tranche->device_id, wl->current_tranche->device_id)) {
wl->current_tranche->planar_formats = tranche->planar_formats;
wl->current_tranche->num_planar_formats = tranche->num_planar_formats;
return;
}
}
tranche = wl->current_tranche;
drmDevice *device = NULL;
drmModePlaneRes *res = NULL;
drmModePlane *plane = NULL;
if (drmGetDeviceFromDevId(wl->target_device_id, 0, &device) != 0) {
MP_WARN(wl, "Unable to get drm device from device id: %s\n", mp_strerror(errno));
if (drmGetDeviceFromDevId(tranche->device_id, 0, &device) != 0) {
MP_VERBOSE(wl, "Unable to get drm device from device id: %s\n", mp_strerror(errno));
goto done;
}
@ -1850,30 +1901,30 @@ static void get_planar_drm_formats(struct vo_wayland_state *wl)
}
if (!path || !path[0]) {
MP_WARN(wl, "Unable to find a valid drm device node.\n");
MP_VERBOSE(wl, "Unable to find a valid drm device node.\n");
goto done;
}
int fd = open(path, O_RDWR | O_CLOEXEC);
if (fd < 0) {
MP_WARN(wl, "Unable to open DRM node path '%s': %s\n", path, mp_strerror(errno));
MP_VERBOSE(wl, "Unable to open DRM node path '%s': %s\n", path, mp_strerror(errno));
goto done;
}
// Need to set this in order to access plane information.
if (drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
MP_WARN(wl, "Unable to set DRM atomic cap: %s\n", mp_strerror(errno));
MP_VERBOSE(wl, "Unable to set DRM atomic cap: %s\n", mp_strerror(errno));
goto done;
}
res = drmModeGetPlaneResources(fd);
if (!res) {
MP_WARN(wl, "Unable to get DRM plane resources: %s\n", mp_strerror(errno));
MP_VERBOSE(wl, "Unable to get DRM plane resources: %s\n", mp_strerror(errno));
goto done;
}
if (!res->count_planes) {
MP_WARN(wl, "No DRM planes were found.\n");
MP_VERBOSE(wl, "No DRM planes were found.\n");
goto done;
}
@ -1908,20 +1959,17 @@ static void get_planar_drm_formats(struct vo_wayland_state *wl)
}
if (index == -1) {
MP_WARN(wl, "Unable to get DRM plane: %s\n", mp_strerror(errno));
MP_VERBOSE(wl, "Unable to get DRM plane: %s\n", mp_strerror(errno));
goto done;
}
plane = drmModeGetPlane(fd, res->planes[index]);
wl->num_planar_formats = plane->count_formats;
tranche->num_planar_formats = plane->count_formats;
tranche->planar_formats = talloc_zero_array(tranche, int, tranche->num_planar_formats);
if (wl->planar_formats)
talloc_free(wl->planar_formats);
wl->planar_formats = talloc_zero_array(wl, int, wl->num_planar_formats);
for (int i = 0; i < wl->num_planar_formats; ++i) {
for (int i = 0; i < tranche->num_planar_formats; ++i) {
MP_DBG(wl, "DRM primary plane supports drm format: %s\n", mp_tag_str(plane->formats[i]));
wl->planar_formats[i] = plane->formats[i];
tranche->planar_formats[i] = plane->formats[i];
}
done:
@ -2722,6 +2770,35 @@ void vo_wayland_handle_scale(struct vo_wayland_state *wl)
lround(mp_rect_h(wl->geometry) / wl->scaling_factor));
}
bool vo_wayland_valid_format(struct vo_wayland_state *wl, uint32_t drm_format, uint64_t modifier)
{
#if HAVE_DRM
// Tranches are grouped by preference and the first tranche is at the end of
// the list. It doesn't really matter for us since we search everything
// anyways, but might as well start from the most preferred tranche.
struct vo_wayland_tranche *tranche;
wl_list_for_each_reverse(tranche, &wl->tranche_list, link) {
bool supported_compositor_format = false;
struct drm_format *formats = tranche->compositor_formats;
for (int i = 0; i < tranche->num_compositor_formats; ++i) {
if (formats[i].format != drm_format)
continue;
if (modifier == formats[i].modifier && modifier != DRM_FORMAT_MOD_INVALID)
return true;
supported_compositor_format = true;
}
if (supported_compositor_format && tranche->planar_formats) {
for (int i = 0; i < tranche->num_planar_formats; i++) {
if (drm_format == tranche->planar_formats[i])
return true;
}
}
}
#endif
return false;
}
bool vo_wayland_init(struct vo *vo)
{
if (!getenv("WAYLAND_DISPLAY"))
@ -2749,6 +2826,7 @@ bool vo_wayland_init(struct vo *vo)
wl_list_init(&wl->output_list);
wl_list_init(&wl->seat_list);
wl_list_init(&wl->tranche_list);
if (!wl->display)
goto err;

View File

@ -22,13 +22,9 @@
#include "input/event.h"
#include "vo.h"
struct compositor_format;
struct vo_wayland_seat;
typedef struct {
uint32_t format;
uint32_t padding;
uint64_t modifier;
} compositor_format;
struct vo_wayland_tranche;
struct drm_format {
uint32_t format;
@ -107,16 +103,12 @@ struct vo_wayland_state {
struct zwp_idle_inhibitor_v1 *idle_inhibitor;
/* linux-dmabuf */
dev_t target_device_id;
struct wl_list tranche_list;
struct vo_wayland_tranche *current_tranche;
struct zwp_linux_dmabuf_v1 *dmabuf;
struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback;
bool add_tranche;
compositor_format *compositor_format_map;
struct compositor_format *compositor_format_map;
uint32_t compositor_format_size;
struct drm_format *compositor_formats;
int num_compositor_formats;
uint32_t *planar_formats;
int num_planar_formats;
/* presentation-time */
struct wp_presentation *presentation;
@ -168,6 +160,7 @@ struct vo_wayland_state {
};
bool vo_wayland_check_visible(struct vo *vo);
bool vo_wayland_valid_format(struct vo_wayland_state *wl, uint32_t drm_format, uint64_t modifier);
bool vo_wayland_init(struct vo *vo);
bool vo_wayland_reconfig(struct vo *vo);

View File

@ -15,8 +15,6 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <drm_fourcc.h>
#include "video/out/wayland_common.h"
#include "video/out/gpu/ra.h"
#include "ra_wldmabuf.h"
@ -30,35 +28,11 @@ static void destroy(struct ra *ra)
talloc_free(ra->priv);
}
bool ra_compatible_format(struct ra *ra, int imgfmt, uint32_t drm_format, uint64_t modifier)
bool ra_compatible_format(struct ra *ra, uint32_t drm_format, uint64_t modifier)
{
struct priv *p = ra->priv;
struct vo_wayland_state *wl = p->vo->wl;
struct drm_format *formats = wl->compositor_formats;
// Always check if the compositor supports the format.
bool supported_compositor_format = false;
for (int i = 0; i < wl->num_compositor_formats; ++i) {
if (formats[i].format != drm_format || formats[i].modifier == DRM_FORMAT_MOD_INVALID)
continue;
if (modifier == formats[i].modifier)
return true;
supported_compositor_format = true;
}
if (!supported_compositor_format)
return false;
// If the compositor supports the format but there are no valid modifiers,
// see if this is a planar format which can be still be supported.
if (wl->planar_formats) {
for (int i = 0; i < wl->num_planar_formats; i++) {
if (drm_format == wl->planar_formats[i])
return true;
}
}
return false;
return vo_wayland_valid_format(wl, drm_format, modifier);
}
static struct ra_fns ra_fns_wldmabuf = {

View File

@ -19,5 +19,5 @@
#include "video/out/wayland_common.h"
struct ra *ra_create_wayland(struct mp_log *log, struct vo *vo);
bool ra_compatible_format(struct ra *ra, int imgfmt, uint32_t drm_format, uint64_t modifier);
bool ra_compatible_format(struct ra *ra, uint32_t drm_format, uint64_t modifier);
bool ra_is_wldmabuf(struct ra *ra);