From 14cdb6153f4293d7e058465ab4acaebd6e1f647c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Fri, 23 Feb 2018 18:45:16 +0100 Subject: [PATCH] Add initial linux_dmabuf protocol support Tested with ./weston-simple-dmabuf-drm ./weston-simple-dmabuf-drm --import-immediate=1 ./weston-simple-dmabuf-drm --y-inverted=1 (and combinations) Supports only single plane XRGB dmabufs for now. --- include/wlr/render.h | 2 + include/wlr/render/egl.h | 28 ++ include/wlr/render/interface.h | 3 + include/wlr/types/wlr_linux_dmabuf.h | 84 +++++ protocol/meson.build | 1 + render/egl.c | 135 ++++++++ render/glapi.txt | 2 + render/gles2/texture.c | 62 +++- render/wlr_texture.c | 5 + types/meson.build | 1 + types/wlr_linux_dmabuf.c | 464 +++++++++++++++++++++++++++ types/wlr_surface.c | 4 + 12 files changed, 787 insertions(+), 4 deletions(-) create mode 100644 include/wlr/types/wlr_linux_dmabuf.h create mode 100644 types/wlr_linux_dmabuf.c diff --git a/include/wlr/render.h b/include/wlr/render.h index 77449556..9080175f 100644 --- a/include/wlr/render.h +++ b/include/wlr/render.h @@ -123,6 +123,8 @@ bool wlr_texture_upload_drm(struct wlr_texture *tex, bool wlr_texture_upload_eglimage(struct wlr_texture *tex, EGLImageKHR image, uint32_t width, uint32_t height); +bool wlr_texture_upload_dmabuf(struct wlr_texture *tex, + struct wl_resource *dmabuf_resource); /** * Copies a rectangle of pixels from a wl_shm_buffer onto the texture. The * buffer is not accessed after this function returns. Under some circumstances, diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 97a28016..f05a9837 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -6,6 +6,7 @@ #include #include #include +#include struct wlr_egl { EGLDisplay display; @@ -18,6 +19,8 @@ struct wlr_egl { struct { bool buffer_age; bool swap_buffers_with_damage; + bool dmabuf_import; + bool dmabuf_import_modifiers; } egl_exts; struct wl_display *wl_display; @@ -61,6 +64,31 @@ EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window); EGLImageKHR wlr_egl_create_image(struct wlr_egl *egl, EGLenum target, EGLClientBuffer buffer, const EGLint *attribs); +/** + * Creates an egl image from the given dmabuf attributes. Check usability + * of the dmabuf with wlr_egl_check_import_dmabuf once first. + */ +EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, + struct wlr_dmabuf_buffer_attribs *attributes); + +/** + * Try to import the given dmabuf. On success return true false otherwise. + * If this succeeds the dmabuf can be used for rendering on a texture + */ +bool wlr_egl_check_import_dmabuf(struct wlr_egl *egl, + struct wlr_dmabuf_buffer *dmabuf); + +/** + * Get the available dmabuf formats + */ +int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats); + +/** + * Get the available dmabuf modifiers for a given format + */ +int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format, + uint64_t **modifiers); + /** * Destroys an egl image created with the given wlr_egl. */ diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index eda5af1c..2ba584d9 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -8,6 +8,7 @@ #include #include #include +#include struct wlr_renderer_impl; @@ -58,6 +59,8 @@ struct wlr_texture_impl { struct wl_resource *drm_buf); bool (*upload_eglimage)(struct wlr_texture *texture, EGLImageKHR image, uint32_t width, uint32_t height); + bool (*upload_dmabuf)(struct wlr_texture *texture, + struct wl_resource *dmabuf_resource); void (*get_matrix)(struct wlr_texture *state, float (*matrix)[16], const float (*projection)[16], int x, int y); void (*get_buffer_size)(struct wlr_texture *texture, diff --git a/include/wlr/types/wlr_linux_dmabuf.h b/include/wlr/types/wlr_linux_dmabuf.h new file mode 100644 index 00000000..9d71e598 --- /dev/null +++ b/include/wlr/types/wlr_linux_dmabuf.h @@ -0,0 +1,84 @@ +#ifndef WLR_TYPES_WLR_LINUX_DMABUF_H +#define WLR_TYPES_WLR_LINUX_DMABUF_H + +#define WLR_LINUX_DMABUF_MAX_PLANES 4 + +#include +#include + +/* So we don't have to pull in linux specific drm headers */ +#ifndef DRM_FORMAT_MOD_INVALID +#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1) +#endif + +struct wlr_dmabuf_buffer_attribs { + /* set via params_add */ + int n_planes; + uint32_t offset[WLR_LINUX_DMABUF_MAX_PLANES]; + uint32_t stride[WLR_LINUX_DMABUF_MAX_PLANES]; + uint64_t modifier[WLR_LINUX_DMABUF_MAX_PLANES]; + int fd[WLR_LINUX_DMABUF_MAX_PLANES]; + /* set via params_create */ + int32_t width; + int32_t height; + uint32_t format; + uint32_t flags; /* enum zlinux_buffer_params_flags */ +}; + +struct wlr_dmabuf_buffer { + struct wlr_egl *egl; + struct wl_resource *buffer_resource; + struct wl_resource *params_resource; + struct wlr_dmabuf_buffer_attribs attributes; +}; + +/** + * Returns true if the given resource was created via the linux-dmabuf + * buffer protocol, false otherwise + */ +bool wlr_dmabuf_resource_is_buffer(struct wl_resource *buffer_resource); + +/** + * Returns the wlr_dmabuf_buffer if the given resource was created + * via the linux-dmabuf buffer protocol + */ +struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_buffer_resource( + struct wl_resource *buffer_resource); + +/** + * Returns the wlr_dmabuf_buffer if the given resource was created + * via the linux-dmabuf params protocol + */ +struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_params_resource( + struct wl_resource *params_resource); + +/** + * Returns true if the given dmabuf has y-axis inverted, false otherwise + */ +bool wlr_dmabuf_buffer_has_inverted_y(struct wlr_dmabuf_buffer *dmabuf); + +/* the protocol interface */ +struct wlr_linux_dmabuf { + struct wl_global *wl_global; + struct wl_listener display_destroy; + struct wlr_egl *egl; +}; + +/** + * Create linux-dmabuf interface + */ +struct wlr_linux_dmabuf *wlr_linux_dmabuf_create(struct wl_display *display, + struct wlr_egl *egl); +/** + * Destroy the linux-dmabuf interface + */ +void wlr_linux_dmabuf_destroy(struct wlr_linux_dmabuf *linux_dmabuf); + +/** + * Returns the wlr_linux_dmabuf if the given resource was created + * via the linux_dmabuf protocol + */ +struct wlr_linux_dmabuf *wlr_linux_dmabuf_from_resource( + struct wl_resource *resource); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 200cbd7a..638b0c46 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -23,6 +23,7 @@ wayland_scanner_client = generator( protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], + [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 'gamma-control.xml', 'gtk-primary-selection.xml', diff --git a/render/egl.c b/render/egl.c index 0a68d6e5..55809983 100644 --- a/render/egl.c +++ b/render/egl.c @@ -167,6 +167,12 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, check_egl_ext(egl->egl_exts_str, "EGL_EXT_swap_buffers_with_damage") || check_egl_ext(egl->egl_exts_str, "EGL_KHR_swap_buffers_with_damage"); + egl->egl_exts.dmabuf_import = + check_egl_ext(egl->egl_exts_str, "EGL_EXT_image_dma_buf_import"); + egl->egl_exts.dmabuf_import_modifiers = + check_egl_ext(egl->egl_exts_str, "EGL_EXT_image_dma_buf_import_modifiers") + && eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT; + return true; error: @@ -299,3 +305,132 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, } return true; } + +EGLImage wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, + struct wlr_dmabuf_buffer_attribs *attributes) { + int atti = 0; + EGLint attribs[20]; + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = attributes->width; + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = attributes->height; + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = attributes->format; + + bool has_modifier = false; + if (attributes->modifier[0] != DRM_FORMAT_MOD_INVALID) { + if (!egl->egl_exts.dmabuf_import_modifiers) { + return NULL; + } + has_modifier = true; + } + + /* TODO: YUV planes have up four planes but we only support a + single EGLImage for now */ + if (attributes->n_planes > 1) { + return NULL; + } + + attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attribs[atti++] = attributes->fd[0]; + attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attribs[atti++] = attributes->offset[0]; + attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attribs[atti++] = attributes->stride[0]; + if (has_modifier) { + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + attribs[atti++] = attributes->modifier[0] & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + attribs[atti++] = attributes->modifier[0] >> 32; + } + attribs[atti++] = EGL_NONE; + return eglCreateImageKHR(egl->display, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, NULL, attribs); +} + +#ifndef DRM_FORMAT_BIG_ENDIAN +# define DRM_FORMAT_BIG_ENDIAN 0x80000000 +#endif +bool wlr_egl_check_import_dmabuf(struct wlr_egl *egl, + struct wlr_dmabuf_buffer *dmabuf) { + switch (dmabuf->attributes.format & ~DRM_FORMAT_BIG_ENDIAN) { + /* YUV based formats not yet supported */ + case WL_SHM_FORMAT_YUYV: + case WL_SHM_FORMAT_YVYU: + case WL_SHM_FORMAT_UYVY: + case WL_SHM_FORMAT_VYUY: + case WL_SHM_FORMAT_AYUV: + return false; + default: + break; + } + + EGLImage egl_image = wlr_egl_create_image_from_dmabuf(egl, + &dmabuf->attributes); + if (egl_image) { + /* We can import the image, good. No need to keep it + since wlr_texture_upload_dmabuf will import it again */ + wlr_egl_destroy_image(egl, egl_image); + return true; + } + /* TODO: import yuv dmabufs */ + return false; +} + +int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, + int **formats) { + if (!egl->egl_exts.dmabuf_import || + !egl->egl_exts.dmabuf_import_modifiers) { + wlr_log(L_ERROR, "dmabuf extension not present"); + return -1; + } + + EGLint num; + if (!eglQueryDmaBufFormatsEXT(egl->display, 0, NULL, &num)) { + wlr_log(L_ERROR, "failed to query number of dmabuf formats"); + return -1; + } + + *formats = calloc(num, sizeof(int)); + if (*formats == NULL) { + wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); + return -1; + } + + if (!eglQueryDmaBufFormatsEXT(egl->display, num, *formats, &num)) { + wlr_log(L_ERROR, "failed to query dmabuf format"); + free(*formats); + return -1; + } + return num; +} + +int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, + int format, uint64_t **modifiers) { + if (!egl->egl_exts.dmabuf_import || + !egl->egl_exts.dmabuf_import_modifiers) { + wlr_log(L_ERROR, "dmabuf extension not present"); + return -1; + } + + EGLint num; + if (!eglQueryDmaBufModifiersEXT(egl->display, format, 0, + NULL, NULL, &num)) { + wlr_log(L_ERROR, "failed to query dmabuf number of modifiers"); + return -1; + } + + *modifiers = calloc(num, sizeof(uint64_t)); + if (*modifiers == NULL) { + wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); + return -1; + } + + if (!eglQueryDmaBufModifiersEXT(egl->display, format, num, + *modifiers, NULL, &num)) { + wlr_log(L_ERROR, "failed to query dmabuf modifiers"); + free(*modifiers); + return -1; + } + return num; +} diff --git a/render/glapi.txt b/render/glapi.txt index 0b0b452c..02ac7dd8 100644 --- a/render/glapi.txt +++ b/render/glapi.txt @@ -8,3 +8,5 @@ eglCreatePlatformWindowSurfaceEXT -glEGLImageTargetTexture2DOES -eglSwapBuffersWithDamageEXT -eglSwapBuffersWithDamageKHR +-eglQueryDmaBufFormatsEXT +-eglQueryDmaBufModifiersEXT diff --git a/render/gles2/texture.c b/render/gles2/texture.c index d25d1809..5e84890f 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -227,6 +227,42 @@ static bool gles2_texture_upload_eglimage(struct wlr_texture *wlr_tex, return true; } + +static bool gles2_texture_upload_dmabuf(struct wlr_texture *_tex, + struct wl_resource *dmabuf_resource) { + struct wlr_gles2_texture *tex = (struct wlr_gles2_texture *)_tex; + struct wlr_dmabuf_buffer *dmabuf = wlr_dmabuf_buffer_from_buffer_resource( + dmabuf_resource); + + if (!tex->egl->egl_exts.dmabuf_import) { + wlr_log(L_ERROR, "Want dmabuf but extension not present"); + return false; + } + + tex->wlr_texture.width = dmabuf->attributes.width; + tex->wlr_texture.height = dmabuf->attributes.height; + + if (tex->image) { + wlr_egl_destroy_image(tex->egl, tex->image); + } + + if (wlr_dmabuf_buffer_has_inverted_y(dmabuf)) { + _tex->inverted_y = true; + } + + GLenum target = GL_TEXTURE_2D; + const struct pixel_format *pf = + gl_format_for_wl_format(WL_SHM_FORMAT_ARGB8888); + gles2_texture_ensure_texture(tex); + GL_CALL(glBindTexture(target, tex->tex_id)); + tex->image = wlr_egl_create_image_from_dmabuf(tex->egl, &dmabuf->attributes); + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glEGLImageTargetTexture2DOES(target, tex->image)); + tex->pixel_format = pf; + tex->wlr_texture.valid = true; + return true; +} + static void gles2_texture_get_matrix(struct wlr_texture *_texture, float (*matrix)[16], const float (*projection)[16], int x, int y) { struct wlr_gles2_texture *texture = (struct wlr_gles2_texture *)_texture; @@ -240,6 +276,21 @@ static void gles2_texture_get_matrix(struct wlr_texture *_texture, wlr_matrix_mul(projection, matrix, matrix); } + +static bool gles2_texture_get_dmabuf_size(struct wlr_texture *texture, struct + wl_resource *resource, int *width, int *height) { + if (!wlr_dmabuf_resource_is_buffer(resource)) { + return false; + } + + struct wlr_dmabuf_buffer *dmabuf = wlr_dmabuf_buffer_from_buffer_resource( + resource); + *width = dmabuf->attributes.width; + *height = dmabuf->attributes.height; + return true; +} + + static void gles2_texture_get_buffer_size(struct wlr_texture *texture, struct wl_resource *resource, int *width, int *height) { struct wl_shm_buffer *buffer = wl_shm_buffer_get(resource); @@ -250,10 +301,12 @@ static void gles2_texture_get_buffer_size(struct wlr_texture *texture, struct } if (!wlr_egl_query_buffer(tex->egl, resource, EGL_WIDTH, (EGLint*)width)) { - wlr_log(L_ERROR, "could not get size of the buffer " - "(no buffer found)"); - return; - }; + if (!gles2_texture_get_dmabuf_size(texture, resource, + width, height)) { + wlr_log(L_ERROR, "could not get size of the buffer"); + return; + } + } wlr_egl_query_buffer(tex->egl, resource, EGL_HEIGHT, (EGLint*)height); @@ -292,6 +345,7 @@ static struct wlr_texture_impl wlr_texture_impl = { .upload_shm = gles2_texture_upload_shm, .update_shm = gles2_texture_update_shm, .upload_drm = gles2_texture_upload_drm, + .upload_dmabuf = gles2_texture_upload_dmabuf, .upload_eglimage = gles2_texture_upload_eglimage, .get_matrix = gles2_texture_get_matrix, .get_buffer_size = gles2_texture_get_buffer_size, diff --git a/render/wlr_texture.c b/render/wlr_texture.c index a82a16b2..e4e6b9ff 100644 --- a/render/wlr_texture.c +++ b/render/wlr_texture.c @@ -53,6 +53,11 @@ bool wlr_texture_upload_eglimage(struct wlr_texture *texture, return texture->impl->upload_eglimage(texture, image, width, height); } +bool wlr_texture_upload_dmabuf(struct wlr_texture *texture, + struct wl_resource *dmabuf_resource) { + return texture->impl->upload_dmabuf(texture, dmabuf_resource); +} + void wlr_texture_get_matrix(struct wlr_texture *texture, float (*matrix)[16], const float (*projection)[16], int x, int y) { texture->impl->get_matrix(texture, matrix, projection, x, y); diff --git a/types/meson.build b/types/meson.build index 703b06ca..11aa5e20 100644 --- a/types/meson.build +++ b/types/meson.build @@ -9,6 +9,7 @@ lib_wlr_types = static_library( 'wlr_idle.c', 'wlr_input_device.c', 'wlr_keyboard.c', + 'wlr_linux_dmabuf.c', 'wlr_list.c', 'wlr_output_damage.c', 'wlr_output_layout.c', diff --git a/types/wlr_linux_dmabuf.c b/types/wlr_linux_dmabuf.c new file mode 100644 index 00000000..407a5717 --- /dev/null +++ b/types/wlr_linux_dmabuf.c @@ -0,0 +1,464 @@ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux-dmabuf-unstable-v1-protocol.h" + +static void wl_buffer_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct wl_buffer_interface wl_buffer_impl = { + wl_buffer_destroy, +}; + +bool wlr_dmabuf_buffer_has_inverted_y(struct wlr_dmabuf_buffer *dmabuf) { + return dmabuf->attributes.flags + & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; +} + +bool wlr_dmabuf_resource_is_buffer(struct wl_resource *buffer_resource) { + if (!wl_resource_instance_of(buffer_resource, &wl_buffer_interface, + &wl_buffer_impl)) { + return false; + } + + struct wlr_dmabuf_buffer *buffer = wl_resource_get_user_data(buffer_resource); + if (buffer && buffer->buffer_resource && !buffer->params_resource && + buffer->buffer_resource == buffer_resource) { + return true; + } + + return false; +} + +struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_buffer_resource( + struct wl_resource *buffer_resource) { + assert(wl_resource_instance_of(buffer_resource, &wl_buffer_interface, + &wl_buffer_impl)); + + struct wlr_dmabuf_buffer *buffer = wl_resource_get_user_data(buffer_resource); + assert(buffer); + assert(buffer->buffer_resource); + assert(!buffer->params_resource); + assert(buffer->buffer_resource == buffer_resource); + + return buffer; +} + +static void linux_dmabuf_buffer_destroy(struct wlr_dmabuf_buffer *buffer) { + for (int i = 0; i < buffer->attributes.n_planes; i++) { + close(buffer->attributes.fd[i]); + buffer->attributes.fd[i] = -1; + } + buffer->attributes.n_planes = 0; + free(buffer); +} + +static void params_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void params_add(struct wl_client *client, + struct wl_resource *params_resource, int32_t name_fd, + uint32_t plane_idx, uint32_t offset, uint32_t stride, + uint32_t modifier_hi, uint32_t modifier_lo) { + struct wlr_dmabuf_buffer *buffer = wlr_dmabuf_buffer_from_params_resource( + params_resource); + + if (!buffer) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params was already used to create a wl_buffer"); + close(name_fd); + return; + } + + if (plane_idx >= WLR_LINUX_DMABUF_MAX_PLANES) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, + "plane index %u > %u", plane_idx, WLR_LINUX_DMABUF_MAX_PLANES); + close(name_fd); + return; + } + + if (buffer->attributes.fd[plane_idx] != -1) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, + "a dmabuf with id %d has already been added for plane %u", + buffer->attributes.fd[plane_idx], + plane_idx); + close(name_fd); + return; + } + + buffer->attributes.fd[plane_idx] = name_fd; + buffer->attributes.offset[plane_idx] = offset; + buffer->attributes.stride[plane_idx] = stride; + buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) | + modifier_lo; + buffer->attributes.n_planes++; +} + +static void handle_buffer_destroy(struct wl_resource *buffer_resource) +{ + struct wlr_dmabuf_buffer *buffer = wlr_dmabuf_buffer_from_buffer_resource( + buffer_resource); + + linux_dmabuf_buffer_destroy(buffer); +} + +static void params_create_common(struct wl_client *client, + struct wl_resource *params_resource, uint32_t buffer_id, int32_t width, + int32_t height, uint32_t format, uint32_t flags) { + if (!wl_resource_get_user_data(params_resource)) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params was already used to create a wl_buffer"); + return; + } + struct wlr_dmabuf_buffer *buffer = wlr_dmabuf_buffer_from_params_resource( + params_resource); + + /* Switch the linux_dmabuf_buffer object from params resource to + * eventually wl_buffer resource. */ + wl_resource_set_user_data(buffer->params_resource, NULL); + buffer->params_resource = NULL; + + if (!buffer->attributes.n_planes) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "no dmabuf has been added to the params"); + goto err_out; + } + + /* TODO: support more planes */ + if (buffer->attributes.n_planes != 1) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "only single plane buffers supported not %d", + buffer->attributes.n_planes); + goto err_out; + } + + if (buffer->attributes.fd[0] == -1) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "no dmabuf has been added for plane"); + goto err_out; + } + + buffer->attributes.width = width; + buffer->attributes.height = height; + buffer->attributes.format = format; + buffer->attributes.flags = flags; + + if (width < 1 || height < 1) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, + "invalid width %d or height %d", width, height); + goto err_out; + } + + if ((uint64_t)buffer->attributes.offset[0] + buffer->attributes.stride[0] > UINT32_MAX) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "size overflow for plane"); + goto err_out; + } + + if ((uint64_t)buffer->attributes.offset[0] + + (uint64_t) buffer->attributes.stride[0] * height > UINT32_MAX) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "size overflow for plane"); + goto err_out; + } + + off_t size = lseek(buffer->attributes.fd[0], 0, SEEK_END); + if (size != -1) { /* Skip checks if kernel does no support seek on buffer */ + if (buffer->attributes.offset[0] >= size) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid offset %i for plane", + buffer->attributes.offset[0]); + goto err_out; + } + + if (buffer->attributes.offset[0] + buffer->attributes.stride[0] > size) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid stride %i for plane", + buffer->attributes.stride[0]); + goto err_out; + } + + if (buffer->attributes.offset[0] + buffer->attributes.stride[0] * height > size) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid buffer stride or height for plane"); + goto err_out; + } + } + + /* reject unknown flags */ + if (buffer->attributes.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, + "Unknown dmabuf flags %"PRIu32, buffer->attributes.flags); + goto err_out; + } + + /* Check if dmabuf is usable */ + if (!wlr_egl_check_import_dmabuf(buffer->egl, buffer)) { + goto err_failed; + } + + buffer->buffer_resource = wl_resource_create(client, &wl_buffer_interface, + 1, buffer_id); + if (!buffer->buffer_resource) { + wl_resource_post_no_memory(params_resource); + goto err_failed; + } + + wl_resource_set_implementation(buffer->buffer_resource, + &wl_buffer_impl, buffer, handle_buffer_destroy); + + /* send 'created' event when the request is not for an immediate + * import, that is buffer_id is zero */ + if (buffer_id == 0) { + zwp_linux_buffer_params_v1_send_created(params_resource, + buffer->buffer_resource); + } + return; + +err_failed: + if (buffer_id == 0) { + zwp_linux_buffer_params_v1_send_failed(params_resource); + } else { + /* since the behavior is left implementation defined by the + * protocol in case of create_immed failure due to an unknown cause, + * we choose to treat it as a fatal error and immediately kill the + * client instead of creating an invalid handle and waiting for it + * to be used. + */ + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, + "importing the supplied dmabufs failed"); + } +err_out: + linux_dmabuf_buffer_destroy(buffer); + return; +} + +static void params_create(struct wl_client *client, + struct wl_resource *params_resource, + int32_t width, int32_t height,uint32_t format, uint32_t flags) { + params_create_common(client, params_resource, 0, width, height, format, flags); +} + +static void params_create_immed(struct wl_client *client, + struct wl_resource *params_resource, uint32_t buffer_id, + int32_t width, int32_t height,uint32_t format, uint32_t flags) { + params_create_common(client, params_resource, buffer_id, width, height, format, flags); +} + +static const struct zwp_linux_buffer_params_v1_interface linux_buffer_params_impl = { + params_destroy, + params_add, + params_create, + params_create_immed, +}; + +struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_params_resource( + struct wl_resource *params_resource) { + assert(wl_resource_instance_of(params_resource, + &zwp_linux_buffer_params_v1_interface, + &linux_buffer_params_impl)); + + struct wlr_dmabuf_buffer *buffer = wl_resource_get_user_data(params_resource); + assert(buffer); + assert(buffer->params_resource); + assert(!buffer->buffer_resource); + assert(buffer->params_resource == params_resource); + + return buffer; +} + +static void handle_params_destroy(struct wl_resource *params_resource) { + /* Check for NULL since wlr_dmabuf_buffer_from_params_resource will choke */ + if (!wl_resource_get_user_data(params_resource)) { + return; + } + + struct wlr_dmabuf_buffer *buffer = + wlr_dmabuf_buffer_from_params_resource(params_resource); + linux_dmabuf_buffer_destroy(buffer); +} + +static void linux_dmabuf_create_params(struct wl_client *client, + struct wl_resource *linux_dmabuf_resource, + uint32_t params_id) { + struct wlr_linux_dmabuf *linux_dmabuf = wlr_linux_dmabuf_from_resource( + linux_dmabuf_resource); + + uint32_t version = wl_resource_get_version(linux_dmabuf_resource); + struct wlr_dmabuf_buffer *buffer = calloc(1, sizeof *buffer); + if (!buffer) { + goto err; + } + + for (int i = 0; i < WLR_LINUX_DMABUF_MAX_PLANES; i++) { + buffer->attributes.fd[i] = -1; + } + + buffer->egl = linux_dmabuf->egl; + buffer->params_resource = wl_resource_create(client, + &zwp_linux_buffer_params_v1_interface, + version, params_id); + if (!buffer->params_resource) { + goto err_free; + } + + wl_resource_set_implementation(buffer->params_resource, + &linux_buffer_params_impl,buffer, handle_params_destroy); + return; + +err_free: + free(buffer); +err: + wl_resource_post_no_memory(linux_dmabuf_resource); +} + +static void linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl = { + linux_dmabuf_destroy, + linux_dmabuf_create_params +}; + +struct wlr_linux_dmabuf *wlr_linux_dmabuf_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &zwp_linux_dmabuf_v1_interface, + &linux_dmabuf_impl)); + + struct wlr_linux_dmabuf *dmabuf = wl_resource_get_user_data(resource); + assert(dmabuf); + return dmabuf; +} + +static void linux_dmabuf_send_modifiers(struct wlr_linux_dmabuf *linux_dmabuf, + struct wl_resource *resource) { + struct wlr_egl *egl = linux_dmabuf->egl; + /* + * Use EGL_EXT_image_dma_buf_import_modifiers to query and advertise + * format/modifier codes. + */ + uint64_t modifier_invalid = DRM_FORMAT_MOD_INVALID; + int *formats = NULL; + int num_formats = wlr_egl_get_dmabuf_formats(egl, &formats); + + if (num_formats < 0) { + return; + } + + for (int i = 0; i < num_formats; i++) { + int num_modifiers; + uint64_t *modifiers = NULL; + + num_modifiers = wlr_egl_get_dmabuf_modifiers(egl, formats[i], + &modifiers); + if (num_modifiers < 0) { + return; + } + /* send DRM_FORMAT_MOD_INVALID token when no modifiers are supported + * for this format */ + if (num_modifiers == 0) { + num_modifiers = 1; + modifiers = &modifier_invalid; + } + for (int j = 0; j < num_modifiers; j++) { + uint32_t modifier_lo = modifiers[j] & 0xFFFFFFFF; + uint32_t modifier_hi = modifiers[j] >> 32; + zwp_linux_dmabuf_v1_send_modifier(resource, formats[i], + modifier_hi, + modifier_lo); + } + if (modifiers != &modifier_invalid) { + free(modifiers); + } + } + free(formats); +} + +static void linux_dmabuf_bind(struct wl_client *client, + void *data, uint32_t version, uint32_t id) { + struct wlr_linux_dmabuf *linux_dmabuf = data; + struct wl_resource *resource = wl_resource_create(client, + &zwp_linux_dmabuf_v1_interface, + version, id); + + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &linux_dmabuf_impl, + linux_dmabuf, NULL); + if (version < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { + return; + } + + linux_dmabuf_send_modifiers(linux_dmabuf, resource); +} + +void wlr_linux_dmabuf_destroy(struct wlr_linux_dmabuf *linux_dmabuf) { + if (!linux_dmabuf) { + return; + } + wl_list_remove(&linux_dmabuf->display_destroy.link); + + wl_global_destroy(linux_dmabuf->wl_global); + free(linux_dmabuf); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_linux_dmabuf *linux_dmabuf = wl_container_of(listener, linux_dmabuf, display_destroy); + wlr_linux_dmabuf_destroy(linux_dmabuf); +} + +struct wlr_linux_dmabuf *wlr_linux_dmabuf_create(struct wl_display *display, + struct wlr_egl *egl) { + struct wlr_linux_dmabuf *linux_dmabuf = + calloc(1, sizeof(struct wlr_linux_dmabuf)); + if (linux_dmabuf == NULL) { + wlr_log(L_ERROR, "could not create simple dmabuf manager"); + return NULL; + } + + linux_dmabuf->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy); + + linux_dmabuf->wl_global = + wl_global_create(display, &zwp_linux_dmabuf_v1_interface, + 3, linux_dmabuf, linux_dmabuf_bind); + + linux_dmabuf->egl = egl; + if (!linux_dmabuf->wl_global) { + wlr_log(L_ERROR, "could not create linux dmabuf v1 wl global"); + free(linux_dmabuf); + return NULL; + } + + return linux_dmabuf; +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index d63fc1f1..fd5bf4f1 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -325,6 +325,10 @@ static void wlr_surface_apply_damage(struct wlr_surface *surface, surface->current->buffer)) { wlr_texture_upload_drm(surface->texture, surface->current->buffer); goto release; + } else if (wlr_dmabuf_resource_is_buffer( + surface->current->buffer)) { + wlr_texture_upload_dmabuf(surface->texture, surface->current->buffer); + goto release; } else { wlr_log(L_INFO, "Unknown buffer handle attached"); return;