diff --git a/configure.ac b/configure.ac
index a7491ed42..c566063b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,7 +75,8 @@ m4_define([libva_lt_age],
[m4_eval(libva_binary_age - libva_interface_age)])
# libdrm minimun version requirement
-m4_define([libdrm_version], [2.4.60])
+# 2.4.75 for drmGetDevices2
+m4_define([libdrm_version], [2.4.75])
# Wayland minimum version number
# 1.11.0 for wl_proxy_create_wrapper
diff --git a/meson.build b/meson.build
index a0026d99d..9e42ab40c 100644
--- a/meson.build
+++ b/meson.build
@@ -85,7 +85,7 @@ cc = meson.get_compiler('c')
dl_dep = cc.find_library('dl', required : false)
WITH_DRM = not get_option('disable_drm') and (host_machine.system() != 'windows')
-libdrm_dep = dependency('libdrm', version : '>= 2.4.60', required : (host_machine.system() != 'windows'))
+libdrm_dep = dependency('libdrm', version : '>= 2.4.75', required : (host_machine.system() != 'windows'))
WITH_X11 = false
if get_option('with_x11') != 'no'
diff --git a/va/meson.build b/va/meson.build
index 5e969eb83..e15f8fe3e 100644
--- a/va/meson.build
+++ b/va/meson.build
@@ -228,6 +228,7 @@ endif
if WITH_WAYLAND
libva_wayland_sources = [
'wayland/va_wayland.c',
+ 'wayland/va_wayland_linux_dmabuf.c',
'wayland/va_wayland_drm.c',
'wayland/va_wayland_emgd.c',
'drm/va_drm_utils.c',
@@ -241,24 +242,31 @@ if WITH_WAYLAND
libva_headers_subproject += libva_wayland_headers
libva_wayland_headers_priv = [
+ 'wayland/va_wayland_linux_dmabuf.h',
'wayland/va_wayland_drm.h',
'wayland/va_wayland_emgd.h',
'wayland/va_wayland_private.h',
]
- protocol_files = [
- custom_target(
- 'wayland-drm-client-protocol.c',
- output : 'wayland-drm-client-protocol.c',
- input : 'wayland/wayland-drm.xml',
- command : [wl_scanner, 'private-code', '@INPUT@', '@OUTPUT@']),
-
- custom_target(
- 'wayland-drm-client-protocol.h',
- output : 'wayland-drm-client-protocol.h',
- input : 'wayland/wayland-drm.xml',
+ # XXX: grab linux-dmabuf-v1.xml from wayland-protocols
+ protocols = {
+ 'wayland-drm': 'wayland/wayland-drm.xml',
+ 'linux-dmabuf-v1': 'wayland/linux-dmabuf-v1.xml',
+ }
+
+ protocol_files = []
+ foreach name, xml : protocols
+ protocol_files += custom_target(
+ name + '-client-protocol.c',
+ output : name + '-client-protocol.c',
+ input : xml,
+ command : [wl_scanner, 'private-code', '@INPUT@', '@OUTPUT@'])
+ protocol_files += custom_target(
+ name + '-client-protocol.h',
+ output : name + '-client-protocol.h',
+ input : xml,
command : [wl_scanner, 'client-header', '@INPUT@', '@OUTPUT@'])
- ]
+ endforeach
install_headers(libva_wayland_headers, subdir : 'va')
diff --git a/va/wayland/Makefile.am b/va/wayland/Makefile.am
index d1638870a..1f61aa4d2 100644
--- a/va/wayland/Makefile.am
+++ b/va/wayland/Makefile.am
@@ -29,6 +29,7 @@ AM_CPPFLAGS = \
source_c = \
va_wayland.c \
+ va_wayland_linux_dmabuf.c \
va_wayland_drm.c \
va_wayland_emgd.c \
../drm/va_drm_utils.c \
@@ -40,16 +41,19 @@ source_h = \
$(NULL)
source_h_priv = \
+ va_wayland_linux_dmabuf.h \
va_wayland_drm.h \
va_wayland_emgd.h \
va_wayland_private.h \
$(NULL)
protocol_source_c = \
+ linux-dmabuf-v1-client-protocol.c \
wayland-drm-client-protocol.c \
$(NULL)
protocol_source_h = \
+ linux-dmabuf-v1-client-protocol.h \
wayland-drm-client-protocol.h \
$(NULL)
@@ -61,6 +65,7 @@ noinst_HEADERS = $(source_h_priv)
# Wayland protocol
va_wayland_drm.c: $(protocol_source_h)
+va_wayland_linux_dmabuf.c: $(protocol_source_h)
%-client-protocol.h : %.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
%-client-protocol.c : %.xml
@@ -68,6 +73,7 @@ va_wayland_drm.c: $(protocol_source_h)
EXTRA_DIST = \
wayland-drm.xml \
+ linux-dmabuf-v1.xml \
$(NULL)
BUILT_SOURCES = $(protocol_source_h) $(protocol_source_c)
diff --git a/va/wayland/linux-dmabuf-v1.xml b/va/wayland/linux-dmabuf-v1.xml
new file mode 100644
index 000000000..38e06f5b6
--- /dev/null
+++ b/va/wayland/linux-dmabuf-v1.xml
@@ -0,0 +1,585 @@
+
+
+
+
+ Copyright © 2014, 2015 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
+ Software is furnished to do so, subject to the following conditions:
+
+ 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.
+
+
+
+
+ Following the interfaces from:
+ https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
+ https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
+ and the Linux DRM sub-system's AddFb2 ioctl.
+
+ This interface offers ways to create generic dmabuf-based wl_buffers.
+
+ Clients can use the get_surface_feedback request to get dmabuf feedback
+ for a particular surface. If the client wants to retrieve feedback not
+ tied to a surface, they can use the get_default_feedback request.
+
+ The following are required from clients:
+
+ - Clients must ensure that either all data in the dma-buf is
+ coherent for all subsequent read access or that coherency is
+ correctly handled by the underlying kernel-side dma-buf
+ implementation.
+
+ - Don't make any more attachments after sending the buffer to the
+ compositor. Making more attachments later increases the risk of
+ the compositor not being able to use (re-import) an existing
+ dmabuf-based wl_buffer.
+
+ The underlying graphics stack must ensure the following:
+
+ - The dmabuf file descriptors relayed to the server will stay valid
+ for the whole lifetime of the wl_buffer. This means the server may
+ at any time use those fds to import the dmabuf into any kernel
+ sub-system that might accept it.
+
+ However, when the underlying graphics stack fails to deliver the
+ promise, because of e.g. a device hot-unplug which raises internal
+ errors, after the wl_buffer has been successfully created the
+ compositor must not raise protocol errors to the client when dmabuf
+ import later fails.
+
+ To create a wl_buffer from one or more dmabufs, a client creates a
+ zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
+ request. All planes required by the intended format are added with
+ the 'add' request. Finally, a 'create' or 'create_immed' request is
+ issued, which has the following outcome depending on the import success.
+
+ The 'create' request,
+ - on success, triggers a 'created' event which provides the final
+ wl_buffer to the client.
+ - on failure, triggers a 'failed' event to convey that the server
+ cannot use the dmabufs received from the client.
+
+ For the 'create_immed' request,
+ - on success, the server immediately imports the added dmabufs to
+ create a wl_buffer. No event is sent from the server in this case.
+ - on failure, the server can choose to either:
+ - terminate the client by raising a fatal error.
+ - mark the wl_buffer as failed, and send a 'failed' event to the
+ client. If the client uses a failed wl_buffer as an argument to any
+ request, the behaviour is compositor implementation-defined.
+
+ For all DRM formats and unless specified in another protocol extension,
+ pre-multiplied alpha is used for pixel values.
+
+ Unless specified otherwise in another protocol extension, implicit
+ synchronization is used. In other words, compositors and clients must
+ wait and signal fences implicitly passed via the DMA-BUF's reservation
+ mechanism.
+
+
+
+
+ Objects created through this interface, especially wl_buffers, will
+ remain valid.
+
+
+
+
+
+ This temporary object is used to collect multiple dmabuf handles into
+ a single batch to create a wl_buffer. It can only be used once and
+ should be destroyed after a 'created' or 'failed' event has been
+ received.
+
+
+
+
+
+
+ This event advertises one buffer format that the server supports.
+ All the supported formats are advertised once when the client
+ binds to this interface. A roundtrip after binding guarantees
+ that the client has received all supported formats.
+
+ For the definition of the format codes, see the
+ zwp_linux_buffer_params_v1::create request.
+
+ Starting version 4, the format event is deprecated and must not be
+ sent by compositors. Instead, use get_default_feedback or
+ get_surface_feedback.
+
+
+
+
+
+
+ This event advertises the formats that the server supports, along with
+ the modifiers supported for each format. All the supported modifiers
+ for all the supported formats are advertised once when the client
+ binds to this interface. A roundtrip after binding guarantees that
+ the client has received all supported format-modifier pairs.
+
+ For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi ==
+ 0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event.
+ It indicates that the server can support the format with an implicit
+ modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it
+ is as if no explicit modifier is specified. The effective modifier
+ will be derived from the dmabuf.
+
+ A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for
+ a given format supports both explicit modifiers and implicit modifiers.
+
+ For the definition of the format and modifier codes, see the
+ zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add
+ requests.
+
+ Starting version 4, the modifier event is deprecated and must not be
+ sent by compositors. Instead, use get_default_feedback or
+ get_surface_feedback.
+
+
+
+
+
+
+
+
+
+
+ This request creates a new wp_linux_dmabuf_feedback object not bound
+ to a particular surface. This object will deliver feedback about dmabuf
+ parameters to use if the client doesn't support per-surface feedback
+ (see get_surface_feedback).
+
+
+
+
+
+
+ This request creates a new wp_linux_dmabuf_feedback object for the
+ specified wl_surface. This object will deliver feedback about dmabuf
+ parameters to use for buffers attached to this surface.
+
+ If the surface is destroyed before the wp_linux_dmabuf_feedback object,
+ the feedback object becomes inert.
+
+
+
+
+
+
+
+
+ This temporary object is a collection of dmabufs and other
+ parameters that together form a single logical buffer. The temporary
+ object may eventually create one wl_buffer unless cancelled by
+ destroying it before requesting 'create'.
+
+ Single-planar formats only require one dmabuf, however
+ multi-planar formats may require more than one dmabuf. For all
+ formats, an 'add' request must be called once per plane (even if the
+ underlying dmabuf fd is identical).
+
+ You must use consecutive plane indices ('plane_idx' argument for 'add')
+ from zero to the number of planes used by the drm_fourcc format code.
+ All planes required by the format must be given exactly once, but can
+ be given in any order. Each plane index can be set only once.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cleans up the temporary data sent to the server for dmabuf-based
+ wl_buffer creation.
+
+
+
+
+
+ This request adds one dmabuf to the set in this
+ zwp_linux_buffer_params_v1.
+
+ The 64-bit unsigned value combined from modifier_hi and modifier_lo
+ is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
+ fb modifier, which is defined in drm_mode.h of Linux UAPI.
+ This is an opaque token. Drivers use this token to express tiling,
+ compression, etc. driver-specific modifications to the base format
+ defined by the DRM fourcc code.
+
+ Starting from version 4, the invalid_format protocol error is sent if
+ the format + modifier pair was not advertised as supported.
+
+ Starting from version 5, the invalid_format protocol error is sent if
+ all planes don't use the same modifier.
+
+ This request raises the PLANE_IDX error if plane_idx is too large.
+ The error PLANE_SET is raised if attempting to set a plane that
+ was already set.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This asks for creation of a wl_buffer from the added dmabuf
+ buffers. The wl_buffer is not created immediately but returned via
+ the 'created' event if the dmabuf sharing succeeds. The sharing
+ may fail at runtime for reasons a client cannot predict, in
+ which case the 'failed' event is triggered.
+
+ The 'format' argument is a DRM_FORMAT code, as defined by the
+ libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
+ authoritative source on how the format codes should work.
+
+ The 'flags' is a bitfield of the flags defined in enum "flags".
+ 'y_invert' means the that the image needs to be y-flipped.
+
+ Flag 'interlaced' means that the frame in the buffer is not
+ progressive as usual, but interlaced. An interlaced buffer as
+ supported here must always contain both top and bottom fields.
+ The top field always begins on the first pixel row. The temporal
+ ordering between the two fields is top field first, unless
+ 'bottom_first' is specified. It is undefined whether 'bottom_first'
+ is ignored if 'interlaced' is not set.
+
+ This protocol does not convey any information about field rate,
+ duration, or timing, other than the relative ordering between the
+ two fields in one buffer. A compositor may have to estimate the
+ intended field rate from the incoming buffer rate. It is undefined
+ whether the time of receiving wl_surface.commit with a new buffer
+ attached, applying the wl_surface state, wl_surface.frame callback
+ trigger, presentation, or any other point in the compositor cycle
+ is used to measure the frame or field times. There is no support
+ for detecting missed or late frames/fields/buffers either, and
+ there is no support whatsoever for cooperating with interlaced
+ compositor output.
+
+ The composited image quality resulting from the use of interlaced
+ buffers is explicitly undefined. A compositor may use elaborate
+ hardware features or software to deinterlace and create progressive
+ output frames from a sequence of interlaced input buffers, or it
+ may produce substandard image quality. However, compositors that
+ cannot guarantee reasonable image quality in all cases are recommended
+ to just reject all interlaced buffers.
+
+ Any argument errors, including non-positive width or height,
+ mismatch between the number of planes and the format, bad
+ format, bad offset or stride, may be indicated by fatal protocol
+ errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
+ OUT_OF_BOUNDS.
+
+ Dmabuf import errors in the server that are not obvious client
+ bugs are returned via the 'failed' event as non-fatal. This
+ allows attempting dmabuf sharing and falling back in the client
+ if it fails.
+
+ This request can be sent only once in the object's lifetime, after
+ which the only legal request is destroy. This object should be
+ destroyed after issuing a 'create' request. Attempting to use this
+ object after issuing 'create' raises ALREADY_USED protocol error.
+
+ It is not mandatory to issue 'create'. If a client wants to
+ cancel the buffer creation, it can just destroy this object.
+
+
+
+
+
+
+
+
+
+ This event indicates that the attempted buffer creation was
+ successful. It provides the new wl_buffer referencing the dmabuf(s).
+
+ Upon receiving this event, the client should destroy the
+ zwp_linux_buffer_params_v1 object.
+
+
+
+
+
+
+ This event indicates that the attempted buffer creation has
+ failed. It usually means that one of the dmabuf constraints
+ has not been fulfilled.
+
+ Upon receiving this event, the client should destroy the
+ zwp_linux_buffer_params_v1 object.
+
+
+
+
+
+ This asks for immediate creation of a wl_buffer by importing the
+ added dmabufs.
+
+ In case of import success, no event is sent from the server, and the
+ wl_buffer is ready to be used by the client.
+
+ Upon import failure, either of the following may happen, as seen fit
+ by the implementation:
+ - the client is terminated with one of the following fatal protocol
+ errors:
+ - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
+ in case of argument errors such as mismatch between the number
+ of planes and the format, bad format, non-positive width or
+ height, or bad offset or stride.
+ - INVALID_WL_BUFFER, in case the cause for failure is unknown or
+ plaform specific.
+ - the server creates an invalid wl_buffer, marks it as failed and
+ sends a 'failed' event to the client. The result of using this
+ invalid wl_buffer as an argument in any request by the client is
+ defined by the compositor implementation.
+
+ This takes the same arguments as a 'create' request, and obeys the
+ same restrictions.
+
+
+
+
+
+
+
+
+
+
+
+ This object advertises dmabuf parameters feedback. This includes the
+ preferred devices and the supported formats/modifiers.
+
+ The parameters are sent once when this object is created and whenever they
+ change. The done event is always sent once after all parameters have been
+ sent. When a single parameter changes, all parameters are re-sent by the
+ compositor.
+
+ Compositors can re-send the parameters when the current client buffer
+ allocations are sub-optimal. Compositors should not re-send the
+ parameters if re-allocating the buffers would not result in a more optimal
+ configuration. In particular, compositors should avoid sending the exact
+ same parameters multiple times in a row.
+
+ The tranche_target_device and tranche_formats events are grouped by
+ tranches of preference. For each tranche, a tranche_target_device, one
+ tranche_flags and one or more tranche_formats events are sent, followed
+ by a tranche_done event finishing the list. The tranches are sent in
+ descending order of preference. All formats and modifiers in the same
+ tranche have the same preference.
+
+ To send parameters, the compositor sends one main_device event, tranches
+ (each consisting of one tranche_target_device event, one tranche_flags
+ event, tranche_formats events and then a tranche_done event), then one
+ done event.
+
+
+
+
+ Using this request a client can tell the server that it is not going to
+ use the wp_linux_dmabuf_feedback object anymore.
+
+
+
+
+
+ This event is sent after all parameters of a wp_linux_dmabuf_feedback
+ object have been sent.
+
+ This allows changes to the wp_linux_dmabuf_feedback parameters to be
+ seen as atomic, even if they happen via multiple events.
+
+
+
+
+
+ This event provides a file descriptor which can be memory-mapped to
+ access the format and modifier table.
+
+ The table contains a tightly packed array of consecutive format +
+ modifier pairs. Each pair is 16 bytes wide. It contains a format as a
+ 32-bit unsigned integer, followed by 4 bytes of unused padding, and a
+ modifier as a 64-bit unsigned integer. The native endianness is used.
+
+ The client must map the file descriptor in read-only private mode.
+
+ Compositors are not allowed to mutate the table file contents once this
+ event has been sent. Instead, compositors must create a new, separate
+ table file and re-send feedback parameters. Compositors are allowed to
+ store duplicate format + modifier pairs in the table.
+
+
+
+
+
+
+
+ This event advertises the main device that the server prefers to use
+ when direct scan-out to the target device isn't possible. The
+ advertised main device may be different for each
+ wp_linux_dmabuf_feedback object, and may change over time.
+
+ There is exactly one main device. The compositor must send at least
+ one preference tranche with tranche_target_device equal to main_device.
+
+ Clients need to create buffers that the main device can import and
+ read from, otherwise creating the dmabuf wl_buffer will fail (see the
+ wp_linux_buffer_params.create and create_immed requests for details).
+ The main device will also likely be kept active by the compositor,
+ so clients can use it instead of waking up another device for power
+ savings.
+
+ In general the device is a DRM node. The DRM node type (primary vs.
+ render) is unspecified. Clients must not rely on the compositor sending
+ a particular node type. Clients cannot check two devices for equality
+ by comparing the dev_t value.
+
+ If explicit modifiers are not supported and the client performs buffer
+ allocations on a different device than the main device, then the client
+ must force the buffer to have a linear layout.
+
+
+
+
+
+
+ This event splits tranche_target_device and tranche_formats events in
+ preference tranches. It is sent after a set of tranche_target_device
+ and tranche_formats events; it represents the end of a tranche. The
+ next tranche will have a lower preference.
+
+
+
+
+
+ This event advertises the target device that the server prefers to use
+ for a buffer created given this tranche. The advertised target device
+ may be different for each preference tranche, and may change over time.
+
+ There is exactly one target device per tranche.
+
+ The target device may be a scan-out device, for example if the
+ compositor prefers to directly scan-out a buffer created given this
+ tranche. The target device may be a rendering device, for example if
+ the compositor prefers to texture from said buffer.
+
+ The client can use this hint to allocate the buffer in a way that makes
+ it accessible from the target device, ideally directly. The buffer must
+ still be accessible from the main device, either through direct import
+ or through a potentially more expensive fallback path. If the buffer
+ can't be directly imported from the main device then clients must be
+ prepared for the compositor changing the tranche priority or making
+ wl_buffer creation fail (see the wp_linux_buffer_params.create and
+ create_immed requests for details).
+
+ If the device is a DRM node, the DRM node type (primary vs. render) is
+ unspecified. Clients must not rely on the compositor sending a
+ particular node type. Clients cannot check two devices for equality by
+ comparing the dev_t value.
+
+ This event is tied to a preference tranche, see the tranche_done event.
+
+
+
+
+
+
+ This event advertises the format + modifier combinations that the
+ compositor supports.
+
+ It carries an array of indices, each referring to a format + modifier
+ pair in the last received format table (see the format_table event).
+ Each index is a 16-bit unsigned integer in native endianness.
+
+ For legacy support, DRM_FORMAT_MOD_INVALID is an allowed modifier.
+ It indicates that the server can support the format with an implicit
+ modifier. When a buffer has DRM_FORMAT_MOD_INVALID as its modifier, it
+ is as if no explicit modifier is specified. The effective modifier
+ will be derived from the dmabuf.
+
+ A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for
+ a given format supports both explicit modifiers and implicit modifiers.
+
+ Compositors must not send duplicate format + modifier pairs within the
+ same tranche or across two different tranches with the same target
+ device and flags.
+
+ This event is tied to a preference tranche, see the tranche_done event.
+
+ For the definition of the format and modifier codes, see the
+ wp_linux_buffer_params.create request.
+
+
+
+
+
+
+
+
+
+
+ This event sets tranche-specific flags.
+
+ The scanout flag is a hint that direct scan-out may be attempted by the
+ compositor on the target device if the client appropriately allocates a
+ buffer. How to allocate a buffer that can be scanned out on the target
+ device is implementation-defined.
+
+ This event is tied to a preference tranche, see the tranche_done event.
+
+
+
+
+
+
diff --git a/va/wayland/va_wayland.c b/va/wayland/va_wayland.c
index 95ac3c36b..759b05517 100644
--- a/va/wayland/va_wayland.c
+++ b/va/wayland/va_wayland.c
@@ -27,6 +27,7 @@
#include "sysdeps.h"
#include
#include "va_wayland.h"
+#include "va_wayland_linux_dmabuf.h"
#include "va_wayland_drm.h"
#include "va_wayland_emgd.h"
#include "va_wayland_private.h"
@@ -90,6 +91,10 @@ struct va_wayland_backend {
};
static const struct va_wayland_backend g_backends[] = {
+ {
+ va_wayland_linux_dmabuf_create,
+ va_wayland_linux_dmabuf_destroy
+ },
{
va_wayland_drm_create,
va_wayland_drm_destroy
diff --git a/va/wayland/va_wayland_linux_dmabuf.c b/va/wayland/va_wayland_linux_dmabuf.c
new file mode 100644
index 000000000..27e357c8a
--- /dev/null
+++ b/va/wayland/va_wayland_linux_dmabuf.c
@@ -0,0 +1,364 @@
+/*
+ * va_wayland_drm.c - Wayland/linux-dmabuf helpers
+ *
+ * Copyright (c) 2024 Simon Ser
+ *
+ * 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, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS 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 "sysdeps.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include "va_drmcommon.h"
+#include "drm/va_drm_utils.h"
+#include "va_wayland_linux_dmabuf.h"
+#include "va_wayland_private.h"
+#include "linux-dmabuf-v1-client-protocol.h"
+
+typedef struct va_wayland_linux_dmabuf_context {
+ struct va_wayland_context base;
+ bool has_linux_dmabuf;
+ bool default_feedback_done;
+} VADisplayContextWaylandLinuxDmabuf;
+
+static void
+feedback_handle_done(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback
+)
+{
+ VADisplayContextP const pDisplayContext = data;
+ struct va_wayland_linux_dmabuf_context *wl_linux_dmabuf_ctx = pDisplayContext->opaque;
+
+ wl_linux_dmabuf_ctx->default_feedback_done = true;
+
+ zwp_linux_dmabuf_feedback_v1_destroy(feedback);
+}
+
+static void
+feedback_handle_format_table(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ int fd,
+ uint32_t size
+)
+{
+ close(fd);
+}
+
+/* XXX: replace with drmGetDeviceFromDevId() */
+static drmDevice *
+get_drm_device_from_dev_id(dev_t dev_id)
+{
+ uint32_t flags = 0;
+ int devices_len, i, node_type;
+ drmDevice *match = NULL, *dev;
+ struct stat statbuf;
+
+ devices_len = drmGetDevices2(flags, NULL, 0);
+ if (devices_len < 0) {
+ return NULL;
+ }
+ drmDevice **devices = calloc(devices_len, sizeof(*devices));
+ if (devices == NULL) {
+ return NULL;
+ }
+ devices_len = drmGetDevices2(flags, devices, devices_len);
+ if (devices_len < 0) {
+ free(devices);
+ return NULL;
+ }
+
+ for (i = 0; i < devices_len; i++) {
+ dev = devices[i];
+ for (node_type = 0; node_type < DRM_NODE_MAX; node_type++) {
+ if (!(dev->available_nodes & (1 << node_type)))
+ continue;
+
+ if (stat(dev->nodes[node_type], &statbuf) != 0) {
+ va_wayland_error("stat() failed for %s", dev->nodes[node_type]);
+ continue;
+ }
+
+ if (statbuf.st_rdev == dev_id) {
+ match = dev;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < devices_len; i++) {
+ dev = devices[i];
+ if (dev != match)
+ drmFreeDevice(&dev);
+ }
+ free(devices);
+
+ return match;
+}
+
+static void
+feedback_handle_main_device(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ struct wl_array *device_array
+)
+{
+ dev_t dev_id;
+ drmDevice *dev;
+ const char *dev_path;
+ VADisplayContextP const pDisplayContext = data;
+ VADriverContextP const ctx = pDisplayContext->pDriverContext;
+ struct drm_state * const drm_state = ctx->drm_state;
+
+ assert(device_array->size == sizeof(dev_id));
+ memcpy(&dev_id, device_array->data, sizeof(dev_id));
+
+ dev = get_drm_device_from_dev_id(dev_id);
+ if (!dev) {
+ va_wayland_error("failed to get DRM device from device ID");
+ return;
+ }
+
+ if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
+ goto end;
+
+ dev_path = dev->nodes[DRM_NODE_RENDER];
+ drm_state->fd = open(dev_path, O_RDWR | O_CLOEXEC);
+ if (drm_state->fd < 0) {
+ va_wayland_error("failed to open %s", dev_path);
+ goto end;
+ }
+
+ drm_state->auth_type = VA_DRM_AUTH_CUSTOM;
+
+end:
+ drmFreeDevice(&dev);
+}
+
+static void
+feedback_handle_tranche_done(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback
+)
+{
+}
+
+static void
+feedback_handle_tranche_target_device(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ struct wl_array *device_array
+)
+{
+}
+
+static void
+feedback_handle_tranche_formats(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ struct wl_array *indices_array
+)
+{
+}
+
+static void
+feedback_handle_tranche_flags(
+ void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ uint32_t flags
+)
+{
+}
+
+static const struct zwp_linux_dmabuf_feedback_v1_listener feedback_listener = {
+ .done = feedback_handle_done,
+ .format_table = feedback_handle_format_table,
+ .main_device = feedback_handle_main_device,
+ .tranche_done = feedback_handle_tranche_done,
+ .tranche_target_device = feedback_handle_tranche_target_device,
+ .tranche_formats = feedback_handle_tranche_formats,
+ .tranche_flags = feedback_handle_tranche_flags,
+};
+
+static void
+registry_handle_global(
+ void *data,
+ struct wl_registry *registry,
+ uint32_t name,
+ const char *interface,
+ uint32_t version
+)
+{
+ VADisplayContextP const pDisplayContext = data;
+ struct va_wayland_linux_dmabuf_context *wl_linux_dmabuf_ctx = pDisplayContext->opaque;
+ struct zwp_linux_dmabuf_v1 *linux_dmabuf;
+ struct zwp_linux_dmabuf_feedback_v1 *feedback;
+
+ if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
+ version >= 4) {
+ wl_linux_dmabuf_ctx->has_linux_dmabuf = true;
+ linux_dmabuf =
+ wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, 4);
+ feedback = zwp_linux_dmabuf_v1_get_default_feedback(linux_dmabuf);
+ zwp_linux_dmabuf_feedback_v1_add_listener(feedback, &feedback_listener, data);
+ zwp_linux_dmabuf_v1_destroy(linux_dmabuf);
+ }
+}
+
+static void
+registry_handle_global_remove(
+ void *data,
+ struct wl_registry *registry,
+ uint32_t name
+)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = registry_handle_global,
+ .global_remove = registry_handle_global_remove,
+};
+
+static VAStatus
+va_DisplayContextGetDriverNames(
+ VADisplayContextP pDisplayContext,
+ char **drivers,
+ unsigned *num_drivers
+)
+{
+ VADriverContextP const ctx = pDisplayContext->pDriverContext;
+
+ return VA_DRM_GetDriverNames(ctx, drivers, num_drivers);
+}
+
+bool
+va_wayland_linux_dmabuf_create(VADisplayContextP pDisplayContext)
+{
+ bool result = false;
+ VADriverContextP const ctx = pDisplayContext->pDriverContext;
+ struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
+ struct va_wayland_linux_dmabuf_context *wl_linux_dmabuf_ctx;
+ struct drm_state *drm_state;
+ struct wl_event_queue *queue = NULL;
+ struct wl_display *display = NULL;
+ struct wl_registry *registry = NULL;
+
+ wl_linux_dmabuf_ctx = calloc(1, sizeof(*wl_linux_dmabuf_ctx));
+ if (!wl_linux_dmabuf_ctx) {
+ va_wayland_error("could not allocate wl_linux_dmabuf_ctx");
+ goto end;
+ }
+ wl_linux_dmabuf_ctx->base.destroy = va_wayland_linux_dmabuf_destroy;
+ pDisplayContext->opaque = wl_linux_dmabuf_ctx;
+ pDisplayContext->vaGetDriverNames = va_DisplayContextGetDriverNames;
+
+ drm_state = calloc(1, sizeof(*drm_state));
+ if (!drm_state) {
+ va_wayland_error("could not allocate drm_state");
+ goto end;
+ }
+ drm_state->fd = -1;
+ drm_state->auth_type = 0;
+ ctx->drm_state = drm_state;
+
+ vtable->has_prime_sharing = 0;
+
+ /* Use wrapped wl_display with private event queue to prevent
+ * thread safety issues with applications that e.g. run an event pump
+ * parallel to libva initialization.
+ * Using the default queue, events might get lost and crashes occur
+ * because wl_display_roundtrip is not thread-safe with respect to the
+ * same queue.
+ */
+ queue = wl_display_create_queue(ctx->native_dpy);
+ if (!queue) {
+ va_wayland_error("could not create Wayland event queue");
+ goto end;
+ }
+
+ display = wl_proxy_create_wrapper(ctx->native_dpy);
+ if (!display) {
+ va_wayland_error("could not create Wayland proxy wrapper");
+ goto end;
+ }
+ wl_proxy_set_queue((struct wl_proxy *) display, queue);
+
+ registry = wl_display_get_registry(display);
+ if (!registry) {
+ va_wayland_error("could not create wl_registry");
+ goto end;
+ }
+ wl_registry_add_listener(registry, ®istry_listener, pDisplayContext);
+
+ if (wl_display_roundtrip_queue(ctx->native_dpy, queue) < 0) {
+ va_wayland_error("failed to roundtrip Wayland queue");
+ goto end;
+ }
+
+ if (!wl_linux_dmabuf_ctx->has_linux_dmabuf)
+ goto end;
+
+ while (!wl_linux_dmabuf_ctx->default_feedback_done) {
+ if (wl_display_dispatch_queue(ctx->native_dpy, queue) < 0) {
+ va_wayland_error("failed to dispatch Wayland queue");
+ goto end;
+ }
+ }
+
+ if (drm_state->fd < 0)
+ goto end;
+
+ result = true;
+ vtable->has_prime_sharing = true;
+
+end:
+ if (registry)
+ wl_registry_destroy(registry);
+ if (display)
+ wl_proxy_wrapper_destroy(display);
+ if (queue)
+ wl_event_queue_destroy(queue);
+ return result;
+}
+
+void
+va_wayland_linux_dmabuf_destroy(VADisplayContextP pDisplayContext)
+{
+ VADriverContextP const ctx = pDisplayContext->pDriverContext;
+ struct drm_state * const drm_state = ctx->drm_state;
+ struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
+
+ vtable->has_prime_sharing = 0;
+
+ if (drm_state) {
+ if (drm_state->fd >= 0) {
+ close(drm_state->fd);
+ drm_state->fd = -1;
+ }
+ free(ctx->drm_state);
+ ctx->drm_state = NULL;
+ }
+}
diff --git a/va/wayland/va_wayland_linux_dmabuf.h b/va/wayland/va_wayland_linux_dmabuf.h
new file mode 100644
index 000000000..aeea38bb1
--- /dev/null
+++ b/va/wayland/va_wayland_linux_dmabuf.h
@@ -0,0 +1,52 @@
+/*
+ * va_wayland_linux_dmabuf.h - Wayland/linux-dmabuf helpers
+ *
+ * Copyright (c) 2024 Simon Ser
+ *
+ * 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, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS 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.
+ */
+
+#ifndef VA_WAYLAND_LINUX_DMABUF_H
+#define VA_WAYLAND_LINUX_DMABUF_H
+
+#include
+#include "va_wayland.h"
+#include "va_backend.h"
+#include "va_backend_wayland.h"
+
+/**
+ * \brief Initializes Wayland/linux-dmabuf layer.
+ *
+ * This is an internal function used to initialize the VA/linux-dmabuf subsystem
+ * if the application is running on a linux-dmabuf-based server.
+ *
+ * @param[in] pDisplayContext the VA display context
+ * @return true if successful
+ */
+DLL_HIDDEN
+bool
+va_wayland_linux_dmabuf_create(VADisplayContextP pDisplayContext);
+
+DLL_HIDDEN
+void
+va_wayland_linux_dmabuf_destroy(VADisplayContextP pDisplayContext);
+
+#endif /* VA_WAYLAND_LINUX_DMABUF_H */