diff --git a/src/client/protocols/linux-dmabuf-unstable-v1.xml b/src/client/protocols/linux-dmabuf-unstable-v1.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/linux-dmabuf-unstable-v1.xml @@ -0,0 +1,348 @@ + + + + + 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 + and the Linux DRM sub-system's AddFb2 ioctl. + + This interface offers ways to create generic dmabuf-based + wl_buffers. Immediately after a client binds to this interface, + the set of supported formats and format modifiers is sent with + 'format' and 'modifier' events. + + 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. + + 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. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + 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. + + Warning: the 'format' event is likely to be deprecated and replaced + with the 'modifier' event introduced in zwp_linux_dmabuf_v1 + version 3, described below. Please refrain from using the information + received from this event. + + + + + + + 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 the definition of the format and modifier codes, see the + zwp_linux_buffer_params_v1::create request. + + + + + + + + + + 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. + + 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 + zlinux_dmabuf_params 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 + zlinux_buffer_params 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. + + + + + + + + + + + diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -53,6 +53,7 @@ xdgforeign_v2_interface.cpp xdgforeign_interface.cpp xdgshell_v6_interface.cpp + linuxdmabuf_v1_interface.cpp ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS @@ -174,6 +175,11 @@ BASENAME server_decoration_palette ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/linux-dmabuf-unstable-v1.xml + BASENAME linux-dmabuf-unstable-v1 +) + set(SERVER_GENERATED_SRCS ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-server-protocol.h @@ -221,6 +227,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-linux-dmabuf-unstable-v1-server-protocol.h ) set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON) @@ -299,6 +306,7 @@ touch_interface.h xdgshell_interface.h xdgforeign_interface.h + linuxdmabuf_v1_interface.h ) install(FILES diff --git a/src/server/buffer_interface.h b/src/server/buffer_interface.h --- a/src/server/buffer_interface.h +++ b/src/server/buffer_interface.h @@ -34,6 +34,10 @@ { class SurfaceInterface; +namespace LinuxDmabuf +{ + class Buffer; +} /** * @brief Reference counted representation of a Wayland buffer on Server side. @@ -104,6 +108,10 @@ * @returns The native wl_shm_buffer if the BufferInterface represents a shared memory buffer, otherwise @c nullptr. **/ wl_shm_buffer *shmBuffer(); + /** + * Returns a pointer to the LinuxDmabuf::Buffer when the buffer is a dmabuf buffer, and nullptr otherwise. + */ + LinuxDmabuf::Buffer *linuxDmabufBuffer(); /** * @returns the native wl_resource wrapped by this BufferInterface. **/ diff --git a/src/server/buffer_interface.cpp b/src/server/buffer_interface.cpp --- a/src/server/buffer_interface.cpp +++ b/src/server/buffer_interface.cpp @@ -21,12 +21,15 @@ #include "display.h" #include "logging_p.h" #include "surface_interface.h" +#include "linuxdmabuf_v1_interface.h" // Wayland #include // EGL #include #include +#include "drm_fourcc.h" + namespace KWayland { namespace Server @@ -47,6 +50,7 @@ QImage createImage(); wl_resource *buffer; wl_shm_buffer *shmBuffer; + LinuxDmabuf::Buffer *dmabufBuffer; SurfaceInterface *surface; int refCount; QSize size; @@ -103,11 +107,15 @@ BufferInterface::Private::Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent) : buffer(resource) , shmBuffer(wl_shm_buffer_get(resource)) + , dmabufBuffer(nullptr) , surface(parent) , refCount(0) , alpha(false) , q(q) { + if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) { + dmabufBuffer = static_cast(wl_resource_get_user_data(resource)); + } s_buffers << this; listener.notify = destroyListenerCallback; listener.link.prev = nullptr; @@ -125,6 +133,43 @@ alpha = false; break; } + } else if (dmabufBuffer) { + switch (dmabufBuffer->format()) { + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + + case DRM_FORMAT_XRGB8888_A8: + case DRM_FORMAT_XBGR8888_A8: + case DRM_FORMAT_RGBX8888_A8: + case DRM_FORMAT_BGRX8888_A8: + case DRM_FORMAT_RGB888_A8: + case DRM_FORMAT_BGR888_A8: + case DRM_FORMAT_RGB565_A8: + case DRM_FORMAT_BGR565_A8: + alpha = true; + break; + default: + alpha = false; + break; + } + size = dmabufBuffer->size(); } else if (parent) { EGLDisplay eglDisplay = parent->global()->display()->eglDisplay(); static bool resolved = false; @@ -275,6 +320,11 @@ return d->shmBuffer; } +LinuxDmabuf::Buffer *BufferInterface::linuxDmabufBuffer() +{ + return d->dmabufBuffer; +} + wl_resource *BufferInterface::resource() const { return d->buffer; diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -85,6 +85,7 @@ class XdgForeignInterface; class AppMenuManagerInterface; class ServerSideDecorationPaletteManagerInterface; +class LinuxDmabufUnstableV1Interface; /** * @brief Class holding the Wayland server display loop. @@ -256,6 +257,12 @@ **/ ServerSideDecorationPaletteManagerInterface *createServerSideDecorationPaletteManager(QObject *parent = nullptr); + /** + * Creates the LinuxDmabufUnstableV1Interface in interface @p version. + * + * @returns A pointer to the created interface + **/ + LinuxDmabufUnstableV1Interface *createLinuxDmabufInterface(QObject *parent = nullptr); /** * Gets the ClientConnection for the given @p client. diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -49,6 +49,7 @@ #include "xdgshell_v6_interface_p.h" #include "appmenu_interface.h" #include "server_decoration_palette_interface.h" +#include "linuxdmabuf_v1_interface.h" #include #include @@ -449,6 +450,13 @@ return b; } +LinuxDmabufUnstableV1Interface *Display::createLinuxDmabufInterface(QObject *parent) +{ + auto b = new LinuxDmabufUnstableV1Interface(this, parent); + connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + return b; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/server/drm_fourcc.h b/src/server/drm_fourcc.h new file mode 100644 --- /dev/null +++ b/src/server/drm_fourcc.h @@ -0,0 +1,414 @@ +/* + * Copyright 2011 Intel Corporation + * + * 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 + * VA LINUX SYSTEMS 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 DRM_FOURCC_H +#define DRM_FOURCC_H + +//#include "drm.h" + +// These typedefs are copied from drm.h +typedef uint32_t __u32; +typedef uint64_t __u64; + +#if defined(__cplusplus) +extern "C" { +#endif + +#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ + ((__u32)(c) << 16) | ((__u32)(d) << 24)) + +#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ + +/* color index */ +#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ + +/* 8 bpp Red */ +#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ + +/* 16 bpp Red */ +#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ + +/* 16 bpp RG */ +#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ +#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ + +/* 32 bpp RG */ +#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ +#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ + +/* 8 bpp RGB */ +#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ +#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ + +/* 16 bpp RGB */ +#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ + +#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ + +#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ + +#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ + +#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ +#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ + +/* 24 bpp RGB */ +#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ +#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ + +/* 32 bpp RGB */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ + +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ + +#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ + +#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ + +/* packed YCbCr */ +#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ +#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ + +#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ + +/* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ +#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') +#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') +#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') +#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') +#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') +#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') +#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') +#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') + +/* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ +#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ + +/* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ +#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ + + +/* + * Format Modifiers: + * + * Format modifiers describe, typically, a re-ordering or modification + * of the data in a plane of an FB. This can be used to express tiled/ + * swizzled formats, or compression, or a combination of the two. + * + * The upper 8 bits of the format modifier are a vendor-id as assigned + * below. The lower 56 bits are assigned as vendor sees fit. + */ + +/* Vendor Ids: */ +#define DRM_FORMAT_MOD_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 +#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 +#define DRM_FORMAT_MOD_VENDOR_NV 0x03 +#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 +#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 +#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 +#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 +/* add more to the end as needed */ + +#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) + +#define fourcc_mod_code(vendor, val) \ + ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffULL)) + +/* + * Format Modifier tokens: + * + * When adding a new token please document the layout with a code comment, + * similar to the fourcc codes above. drm_fourcc.h is considered the + * authoritative source for all of these. + */ + +/* + * Invalid Modifier + * + * This modifier can be used as a sentinel to terminate the format modifiers + * list, or to initialize a variable with an invalid modifier. It might also be + * used to report an error back to userspace for certain APIs. + */ +#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) + +/* + * Linear Layout + * + * Just plain linear layout. Note that this is different from no specifying any + * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), + * which tells the driver to also take driver-internal information into account + * and so might actually result in a tiled framebuffer. + */ +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) + +/* Intel framebuffer modifiers */ + +/* + * Intel X-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out row-major, with + * a platform-dependent stride. On top of that the memory can apply + * platform-depending swizzling of some higher address bits into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) + +/* + * Intel Y-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) + * chunks column-major, with a platform-dependent height. On top of that the + * memory can apply platform-depending swizzling of some higher address bits + * into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) + +/* + * Intel Yf-tiling layout + * + * This is a tiled layout using 4Kb tiles in row-major layout. + * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which + * are arranged in four groups (two wide, two high) with column-major layout. + * Each group therefore consits out of four 256 byte units, which are also laid + * out as 2x2 column-major. + * 256 byte units are made out of four 64 byte blocks of pixels, producing + * either a square block or a 2:1 unit. + * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width + * in pixel depends on the pixel depth. + */ +#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) + +/* + * Intel color control surface (CCS) for render compression + * + * The framebuffer format must be one of the 8:8:8:8 RGB formats. + * The main surface will be plane index 0 and must be Y/Yf-tiled, + * the CCS will be plane index 1. + * + * Each CCS tile matches a 1024x512 pixel area of the main surface. + * To match certain aspects of the 3D hardware the CCS is + * considered to be made up of normal 128Bx32 Y tiles, Thus + * the CCS pitch must be specified in multiples of 128 bytes. + * + * In reality the CCS tile appears to be a 64Bx64 Y tile, composed + * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. + * But that fact is not relevant unless the memory is accessed + * directly. + */ +#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) +#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) + +/* + * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks + * + * Macroblocks are laid in a Z-shape, and each pixel data is following the + * standard NV12 style. + * As for NV12, an image is the result of two frame buffers: one for Y, + * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). + * Alignment requirements are (for each buffer): + * - multiple of 128 pixels for the width + * - multiple of 32 pixels for the height + * + * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html + */ +#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) + +/* Vivante framebuffer modifiers */ + +/* + * Vivante 4x4 tiling layout + * + * This is a simple tiled layout using tiles of 4x4 pixels in a row-major + * layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) + +/* + * Vivante 64x64 super-tiling layout + * + * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile + * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- + * major layout. + * + * For more information: see + * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling + */ +#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) + +/* + * Vivante 4x4 tiling layout for dual-pipe + * + * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a + * different base address. Offsets from the base addresses are therefore halved + * compared to the non-split tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) + +/* + * Vivante 64x64 super-tiling layout for dual-pipe + * + * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile + * starts at a different base address. Offsets from the base addresses are + * therefore halved compared to the non-split super-tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) + +/* NVIDIA Tegra frame buffer modifiers */ + +/* + * Some modifiers take parameters, for example the number of vertical GOBs in + * a block. Reserve the lower 32 bits for parameters + */ +#define __fourcc_mod_tegra_mode_shift 32 +#define fourcc_mod_tegra_code(val, params) \ + fourcc_mod_code(NV, ((((__u64)val) << __fourcc_mod_tegra_mode_shift) | params)) +#define fourcc_mod_tegra_mod(m) \ + (m & ~((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) +#define fourcc_mod_tegra_param(m) \ + (m & ((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) + +/* + * Tegra Tiled Layout, used by Tegra 2, 3 and 4. + * + * Pixels are arranged in simple tiles of 16 x 16 bytes. + */ +#define NV_FORMAT_MOD_TEGRA_TILED fourcc_mod_tegra_code(1, 0) + +/* + * Tegra 16Bx2 Block Linear layout, used by TK1/TX1 + * + * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked + * vertically by a power of 2 (1 to 32 GOBs) to form a block. + * + * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. + * + * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. + * Valid values are: + * + * 0 == ONE_GOB + * 1 == TWO_GOBS + * 2 == FOUR_GOBS + * 3 == EIGHT_GOBS + * 4 == SIXTEEN_GOBS + * 5 == THIRTYTWO_GOBS + * + * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format + * in full detail. + */ +#define NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(v) fourcc_mod_tegra_code(2, v) + +/* + * Broadcom VC4 "T" format + * + * This is the primary layout that the V3D GPU can texture from (it + * can't do linear). The T format has: + * + * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 + * pixels at 32 bit depth. + * + * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually + * 16x16 pixels). + * + * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On + * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows + * they're (TR, BR, BL, TL), where bottom left is start of memory. + * + * - an image made of 4k tiles in rows either left-to-right (even rows of 4k + * tiles) or right-to-left (odd rows of 4k tiles). + */ +#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) + +#if defined(__cplusplus) +} +#endif + +#endif /* DRM_FOURCC_H */ diff --git a/src/server/linuxdmabuf_v1_interface.h b/src/server/linuxdmabuf_v1_interface.h new file mode 100644 --- /dev/null +++ b/src/server/linuxdmabuf_v1_interface.h @@ -0,0 +1,181 @@ +/******************************************************************** +Copyright © 2018 Fredrik Höglund + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ + +#ifndef WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H +#define WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H + +#include +#include "global.h" +#include "resource.h" + +#include + + +struct wl_buffer_interface; + +namespace KWayland +{ + +namespace Server +{ + +namespace LinuxDmabuf +{ + enum Flag { + YInverted = (1 << 0), /// Contents are y-inverted + Interlaced = (1 << 1), /// Content is interlaced + BottomFieldFirst = (1 << 2) /// Bottom field first + }; + + Q_DECLARE_FLAGS(Flags, Flag) + + /** + * Represents a plane in a buffer + */ + struct Plane { + int fd; /// The dmabuf file descriptor + uint32_t offset; /// The offset from the start of buffer + uint32_t stride; /// The distance from the start of a row to the next row in bytes + uint64_t modifier; /// The layout modifier + }; + + /** + * The base class for linux-dmabuf buffers + * + * Compositors should reimplement this class to store objects specific + * to the underlying graphics stack. + */ + class Buffer { + public: + /** + * Creates a new Buffer. + */ + Buffer(uint32_t format, const QSize &size) : m_format(format), m_size(size) {} + + /** + * Destroys the Buffer. + */ + virtual ~Buffer() = default; + + /** + * Returns the DRM format code for the buffer. + */ + uint32_t format() const { return m_format; } + + /** + * Returns the size of the buffer. + */ + QSize size() const { return m_size; } + + private: + uint32_t m_format; + QSize m_size; + }; +} + + +/** + * Represents the global zpw_linux_dmabuf_v1 interface. + * + * This interface provides a way for clients to create generic dmabuf based wl_buffers. + */ +class KWAYLANDSERVER_EXPORT LinuxDmabufUnstableV1Interface : public Global +{ + Q_OBJECT + +public: + /** + * The Bridge class provides an interface from the LinuxDmabufInterface to the compositor + */ + class Bridge { + public: + Bridge() = default; + virtual ~Bridge() = default; + + /** + * Returns the DRM format codes supported by the compositor. + */ + virtual QVector supportedFormats() const = 0; + + /** + * Returns the layout-modifiers supported for the given DRM format code. + */ + virtual QVector supportedModifiers(uint32_t format) const = 0; + + /** + * Imports a linux-dmabuf buffer into the compositor. + * + * The returned buffer takes ownership of the file descriptor for each plane. + * Note that it is the responsibility of the caller to close the file descriptors + * when the import fails. + * + * @return The imported buffer on success, and nullptr otherwise. + */ + virtual LinuxDmabuf::Buffer *importBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + LinuxDmabuf::Flags flags) = 0; + }; + + /** + * Destroys the LinuxDmabufUnstableV1Interface. + */ + virtual ~LinuxDmabufUnstableV1Interface(); + + /** + * Sets the compositor bridge for the dmabuf interface. + */ + void setBridge(Bridge *bridge); + + /** + * Returns the compositor bridge for the dmabuf interface. + */ + Bridge *bridge() const; + + /** + * Returns the LinuxDmabufInterface for the given resource. + **/ + static LinuxDmabufUnstableV1Interface *get(wl_resource *native); + + /** + * Returns a pointer to the wl_buffer implementation for imported dmabufs. + */ + static const struct wl_buffer_interface *bufferImplementation(); + +private: + /** + * @internal + */ + explicit LinuxDmabufUnstableV1Interface(Display *display, QObject *parent = nullptr); + friend class Display; + +private: + class Private; + Private *d_func() const; +}; + + +} // namespace Server +} // namespace KWayland + +Q_DECLARE_METATYPE(KWayland::Server::LinuxDmabufUnstableV1Interface*) +Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::LinuxDmabuf::Flags) + +#endif // WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H diff --git a/src/server/linuxdmabuf_v1_interface.cpp b/src/server/linuxdmabuf_v1_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/linuxdmabuf_v1_interface.cpp @@ -0,0 +1,490 @@ +/******************************************************************** +Copyright © 2018 Fredrik Höglund + +Based on the libweston implementation, +Copyright © 2014, 2015 Collabora, Ltd. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ + +#include +#include "linuxdmabuf_v1_interface.h" +#include "wayland-linux-dmabuf-unstable-v1-server-protocol.h" +#include "wayland-server-protocol.h" +#include "global_p.h" + +#include "drm_fourcc.h" + +#include + +#include +#include + +#include + + +namespace KWayland +{ + +namespace Server +{ + + +class LinuxDmabufParams +{ +public: + LinuxDmabufParams(LinuxDmabufUnstableV1Interface *dmabufInterface, wl_client *client, uint32_t version, uint32_t id); + ~LinuxDmabufParams(); + + void postNoMemory() { wl_resource_post_no_memory(m_resource); } + + wl_resource *resource() const { return m_resource; } + + void add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier); + void create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags); + + static void destroy(wl_client *client, wl_resource *resource); + static void add(wl_client *client, wl_resource *resource, int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo); + static void create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags); + static void createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags); + +private: + static const struct zwp_linux_buffer_params_v1_interface s_interface; + + wl_resource *m_resource; + LinuxDmabufUnstableV1Interface *m_dmabufInterface; + std::array m_planes; + size_t m_planeCount = 0; + bool m_bufferCreated = false; +}; + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_linux_buffer_params_v1_interface LinuxDmabufParams::s_interface = { + destroy, + add, + create, + createImmed +}; +#endif + + +LinuxDmabufParams::LinuxDmabufParams(LinuxDmabufUnstableV1Interface *dmabufInterface, wl_client *client, uint32_t version, uint32_t id) + : m_dmabufInterface(dmabufInterface) +{ + m_resource = wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, version, id); + if (!m_resource) { + return; + } + + wl_resource_set_implementation(m_resource, &s_interface, this, + [](wl_resource *resource) { + delete static_cast(wl_resource_get_user_data(resource)); + }); + + for (auto &plane : m_planes) { + plane.fd = -1; + plane.offset = 0; + plane.stride = 0; + plane.modifier = 0; + } +} + + +LinuxDmabufParams::~LinuxDmabufParams() +{ + // Close the file descriptors + for (auto &plane : m_planes) { + if (plane.fd != -1) { + ::close(plane.fd); + } + } +} + + +void LinuxDmabufParams::create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags) +{ + // Validate the parameters + // ----------------------- + const uint32_t width = size.width(); + const uint32_t height = size.height(); + + if (m_bufferCreated) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params was already used to create a wl_buffer"); + return; + } + + if (m_planeCount == 0) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "no dmabuf has been added to the params"); + return; + } + + // Check for holes in the dmabufs set (e.g. [0, 1, 3]) + for (uint32_t i = 0; i < m_planeCount; i++) { + if (m_planes[i].fd != -1) + continue; + + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "no dmabuf has been added for plane %i", i); + return; + } + + if (width < 1 || height < 1) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, + "invalid width %d or height %d", width, height); + return; + } + + for (uint32_t i = 0; i < m_planeCount; i++) { + auto &plane = m_planes[i]; + + if (uint64_t(plane.offset) + plane.stride > UINT32_MAX) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "size overflow for plane %i", i); + return; + } + + if (i == 0 && uint64_t(plane.offset) + plane.stride * height > UINT32_MAX) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "size overflow for plane %i", i); + return; + } + + // Don't report an error as it might be caused by the kernel not supporting seeking on dmabuf + off_t size = ::lseek(plane.fd, 0, SEEK_END); + if (size == -1) + continue; + + if (plane.offset >= size) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid offset %i for plane %i", + plane.offset, i); + return; + } + + if (plane.offset + plane.stride > size) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid stride %i for plane %i", + plane.stride, i); + return; + } + + // Only valid for first plane as other planes might be + // sub-sampled according to fourcc format + if (i == 0 && plane.offset + plane.stride * height > size) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "invalid buffer stride or height for plane %i", i); + return; + } + } + + + // Import the buffer + // ----------------- + QVector planes; + planes.reserve(m_planeCount); + for (uint32_t i = 0; i < m_planeCount; i++) + planes << m_planes[i]; + + LinuxDmabuf::Buffer *buffer = m_dmabufInterface->bridge()->importBuffer(planes, format, size, (LinuxDmabuf::Flags) flags); + if (buffer) { + // The buffer has ownership of the file descriptors now + for (auto &plane : m_planes) { + plane.fd = -1; + } + + wl_resource *resource = wl_resource_create(client, &wl_buffer_interface, 1, bufferId); + if (!resource ) { + postNoMemory(); + delete buffer; + return; + } + + wl_resource_set_implementation(resource, m_dmabufInterface->bufferImplementation(), buffer, + [](wl_resource *resource) { // Destructor + delete static_cast(wl_resource_get_user_data(resource)); + }); + + // XXX Do we need this? + //buffer->setResource(resource); + + // Send a 'created' event when the request is not for an immediate import, i.e. bufferId is zero + if (bufferId == 0) { + zwp_linux_buffer_params_v1_send_created(m_resource, resource); + } + + m_bufferCreated = true; + } else { + if (bufferId == 0) { + zwp_linux_buffer_params_v1_send_failed(m_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(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, + "importing the supplied dmabufs failed"); + } + } +} + + +void LinuxDmabufParams::add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier) +{ + if (m_bufferCreated) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params was already used to create a wl_buffer"); + ::close(fd); + return; + } + + if (plane_idx >= m_planes.size()) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, + "plane index %u is too high", plane_idx); + ::close(fd); + return; + } + + auto &plane = m_planes[plane_idx]; + + if (plane.fd != -1) { + wl_resource_post_error(m_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, + "a dmabuf has already been added for plane %u", + plane_idx); + ::close(fd); + return; + } + + plane.fd = fd; + plane.offset = offset; + plane.stride = stride; + plane.modifier = modifier; + + m_planeCount++; +} + + + +// -------------------------------------------------------------------- + + + +void LinuxDmabufParams::destroy(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + wl_resource_destroy(resource); +} + + +void LinuxDmabufParams::add(wl_client *client, wl_resource *resource, + int fd, uint32_t plane_idx, + uint32_t offset, uint32_t stride, + uint32_t modifier_hi, uint32_t modifier_lo) +{ + Q_UNUSED(client) + + LinuxDmabufParams *params = static_cast(wl_resource_get_user_data(resource)); + assert(params->m_resource == resource); + params->add(fd, plane_idx, offset, stride, (uint64_t(modifier_hi) << 32) | modifier_lo); +} + + +void LinuxDmabufParams::create(wl_client *client, wl_resource *resource, + int width, int height, uint32_t format, uint32_t flags) +{ + Q_UNUSED(client) + + LinuxDmabufParams *params = static_cast(wl_resource_get_user_data(resource)); + assert(params->m_resource == resource); + params->create(client, 0, QSize(width, height), format, flags); +} + + +void LinuxDmabufParams::createImmed(wl_client *client, wl_resource *resource, + uint32_t new_id, int width, int height, + uint32_t format, uint32_t flags) +{ + Q_UNUSED(client) + + LinuxDmabufParams *params = static_cast(wl_resource_get_user_data(resource)); + assert(params->m_resource == resource); + params->create(client, new_id, QSize(width, height), format, flags); +} + + + +// -------------------------------------------------------------------- + + + +class LinuxDmabufUnstableV1Interface::Private : public Global::Private +{ +public: + Private(LinuxDmabufUnstableV1Interface *q, Display *display); + ~Private(); + + static const struct wl_buffer_interface *bufferImplementation() { return &s_bufferImplementation; } + LinuxDmabufUnstableV1Interface::Bridge *bridge; + LinuxDmabufUnstableV1Interface * const q; + static const uint32_t s_version; + + void bind(wl_client *client, uint32_t version, uint32_t id) override final; + void createParams(wl_client *client, wl_resource *resource, uint32_t id); + + static void unbind(wl_client *client, wl_resource *resource); + static void createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id); + +private: + static const struct zwp_linux_dmabuf_v1_interface s_implementation; + static const struct wl_buffer_interface s_bufferImplementation; +}; + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_linux_dmabuf_v1_interface LinuxDmabufUnstableV1Interface::Private::s_implementation = { + [](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); }, // unbind + createParamsCallback +}; + + +const struct wl_buffer_interface LinuxDmabufUnstableV1Interface::Private::s_bufferImplementation = { + [](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); } // destroy +}; + +const uint32_t LinuxDmabufUnstableV1Interface::Private::s_version = 3; +#endif + + +LinuxDmabufUnstableV1Interface::Private::Private(LinuxDmabufUnstableV1Interface *q, Display *display) + : Global::Private(display, &zwp_linux_dmabuf_v1_interface, s_version), + q(q) +{ +} + + +LinuxDmabufUnstableV1Interface::Private::~Private() +{ +} + + +void LinuxDmabufUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + wl_resource *resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, std::min(s_version, version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &s_implementation, this, nullptr); + + // Send formats & modifiers + // ------------------------ + const QVector formats = bridge->supportedFormats(); + for (uint32_t format : formats) { + QVector modifiers = bridge->supportedModifiers(format); + if (modifiers.isEmpty()) { + modifiers << DRM_FORMAT_MOD_INVALID; + } + + for (uint64_t modifier : qAsConst(modifiers)) { + if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { + const uint32_t modifier_lo = modifier & 0xFFFFFFFF; + const uint32_t modifier_hi = modifier >> 32; + zwp_linux_dmabuf_v1_send_modifier(resource, format, modifier_hi, modifier_lo); + } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { + zwp_linux_dmabuf_v1_send_format(resource, format); + } + } + } +} + + +void LinuxDmabufUnstableV1Interface::Private::createParams(wl_client *client, wl_resource *resource, uint32_t id) +{ + LinuxDmabufParams *params = new LinuxDmabufParams(q, client, wl_resource_get_version(resource), id); + if (!params->resource()) { + wl_resource_post_no_memory(resource); + delete params; + } +} + + +void LinuxDmabufUnstableV1Interface::Private::createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + LinuxDmabufUnstableV1Interface::Private *global = static_cast(wl_resource_get_user_data(resource)); + global->createParams(client, resource, id); +} + + + +// -------------------------------------------------------------------- + + + +LinuxDmabufUnstableV1Interface::LinuxDmabufUnstableV1Interface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + + +LinuxDmabufUnstableV1Interface::~LinuxDmabufUnstableV1Interface() +{ +} + + +void LinuxDmabufUnstableV1Interface::setBridge(LinuxDmabufUnstableV1Interface::Bridge *bridge) +{ + d_func()->bridge = bridge; +} + + +LinuxDmabufUnstableV1Interface::Bridge *LinuxDmabufUnstableV1Interface::bridge() const +{ + return d_func()->bridge; +} + + +const struct wl_buffer_interface *LinuxDmabufUnstableV1Interface::bufferImplementation() +{ + return LinuxDmabufUnstableV1Interface::Private::bufferImplementation(); +} + + +LinuxDmabufUnstableV1Interface::Private *LinuxDmabufUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + + +} // namespace Server +} // namespace Wayland