diff --git a/configure.ac b/configure.ac index bb8b97a8b..5d194b731 100644 --- a/configure.ac +++ b/configure.ac @@ -75,12 +75,16 @@ m4_define([libva_lt_age], [m4_eval(libva_binary_age - libva_interface_age)]) # libdrm minimun version requirement -m4_define([libdrm_version], [2.4.60]) +m4_define([libdrm_version], [2.4.109]) # Wayland minimum version number # 1.11.0 for wl_proxy_create_wrapper m4_define([wayland_api_version], [1.11.0]) +# wayland-protocols minimum version number +# 1.24 for linux-dmabuf v4 +m4_define([wayland_protocols_version], [1.24]) + AC_PREREQ(2.57) AC_INIT([libva], [libva_version], @@ -304,10 +308,15 @@ USE_WAYLAND="no" if test "x$enable_wayland" != "xno"; then PKG_CHECK_MODULES([WAYLAND], [wayland-client >= wayland_api_version], [USE_WAYLAND="yes"], [:]) + PKG_CHECK_MODULES([WAYLAND_PROTOCOLS], [wayland-protocols >= wayland_protocols_version], + [USE_WAYLAND="yes"], [:]) if test "x$USE_WAYLAND" = "xno" -a "x$enable_wayland" = "xyes"; then AC_MSG_ERROR([wayland explicitly enabled, however $WAYLAND_PKG_ERRORS]) fi + if test "x$USE_WAYLAND_PROTOCOLS" = "xno" -a "x$enable_wayland" = "xyes"; then + AC_MSG_ERROR([wayland explicitly enabled, however $WAYLAND_PROTOCOLS_PKG_ERRORS]) + fi if test "$USE_WAYLAND" = "yes"; then @@ -322,6 +331,8 @@ if test "x$enable_wayland" != "xno"; then AC_SUBST(WAYLAND_SCANNER, `$PKG_CONFIG --variable=wayland_scanner wayland-scanner`) fi + AC_SUBST(WAYLAND_PROTOCOLS_DIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`) + AC_DEFINE([HAVE_VA_WAYLAND], [1], [Defined to 1 if VA/Wayland API is built]) fi diff --git a/meson.build b/meson.build index 6acf90676..493fb5ad1 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.109', required : (host_machine.system() != 'windows')) WITH_X11 = false if get_option('with_x11') != 'no' @@ -121,7 +121,10 @@ if get_option('with_wayland') != 'no' if wayland_scanner_dep.found() wl_scanner = find_program(wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner')) endif - WITH_WAYLAND = wayland_dep.found() and wayland_scanner_dep.found() + wayland_protocols_dep = dependency('wayland-protocols', version : '>= 1.24', + required : get_option('with_wayland') == 'yes') + WITH_WAYLAND = wayland_dep.found() and wayland_scanner_dep.found() and \ + wayland_protocols_dep.found() endif WITH_WIN32 = false diff --git a/va/meson.build b/va/meson.build index 372ae89ff..4e6916cdb 100644 --- a/va/meson.build +++ b/va/meson.build @@ -227,6 +227,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', @@ -240,24 +241,32 @@ 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', + wayland_protocols_dir = wayland_protocols_dep.get_variable(pkgconfig : 'pkgdatadir') + + protocols = { + 'wayland-drm': 'wayland/wayland-drm.xml', + 'linux-dmabuf-unstable-v1': wayland_protocols_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-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..b4e81fcdb 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-unstable-v1-client-protocol.c \ wayland-drm-client-protocol.c \ $(NULL) protocol_source_h = \ + linux-dmabuf-unstable-v1-client-protocol.h \ wayland-drm-client-protocol.h \ $(NULL) @@ -66,6 +70,12 @@ va_wayland_drm.c: $(protocol_source_h) %-client-protocol.c : %.xml $(AM_V_GEN)$(WAYLAND_SCANNER) private-code < $< > $@ +va_wayland_linux_dmabuf.c: $(protocol_source_h) +linux-dmabuf-unstable-v1-client-protocol.h: $(WAYLAND_PROTOCOLS_DIR)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml + $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@ +linux-dmabuf-unstable-v1-client-protocol.c : $(WAYLAND_PROTOCOLS_DIR)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml + $(AM_V_GEN)$(WAYLAND_SCANNER) private-code < $< > $@ + EXTRA_DIST = \ wayland-drm.xml \ $(NULL) diff --git a/va/wayland/va_wayland.c b/va/wayland/va_wayland.c index b3e1f5c8a..9397e5eb6 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..d661ae684 --- /dev/null +++ b/va/wayland/va_wayland_linux_dmabuf.c @@ -0,0 +1,312 @@ +/* + * 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-unstable-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); +} + +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)); + + if (drmGetDeviceFromDevId(dev_id, 0, &dev) < 0) { + 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 */