From 442f80b667f3b916c46043490f7f0732d47df600 Mon Sep 17 00:00:00 2001 From: hujianwei Date: Sun, 13 Nov 2022 02:08:10 +0000 Subject: [PATCH] Implement support for multiple providers * README: Document changes. * compositor.h (struct _BufferFuncs): Rename `get_render_device' to `get_render_devices' and make it return a list of providers. * dmabuf.c (MakeFeedback): Send 1 tranche for each device. (InitDrmDevice): Rename to InitDrmDevices. Set up for multiple devices. (WriteFormatTable): Adjust call accordingly. * egl.c (GetRenderDevice): Remove function. (GetRenderDevices): New function. (egl_buffer_funcs): Change accordingly. * output.c (XLInitRROutputs): Require RandR 1.4. * picture_renderer.c (GetRenderDevice): Remove calls to open. (GetRenderDevices): New function. (picture_buffer_funcs): Adjust accordingly. * renderer.c (RenderGetRenderDevice): Delete function. (RenderGetRenderDevices): New function. --- README | 10 +--- compositor.h | 7 +-- dmabuf.c | 101 ++++++++++++++++++------------------ egl.c | 16 ++++-- output.c | 24 +++------ picture_renderer.c | 126 ++++++++++++++++++++++++++++++++++----------- renderer.c | 6 +-- 7 files changed, 173 insertions(+), 117 deletions(-) diff --git a/README b/README index 7ba6d9f..d072bea 100644 --- a/README +++ b/README @@ -16,7 +16,7 @@ extensions: Nonrectangular Window Shape Extension, version 1.1 or later MIT Shared Memory Extension, version 1.2 or later - X Resize, Rotate and Reflect Extension, version 1.3 or later + X Resize, Rotate and Reflect Extension, version 1.4 or later X Synchronization Extension, version 1.0 or later X Rendering Extension, version 1.2 or later X Input Extension, version 2.3 or later @@ -84,14 +84,6 @@ Extension, the following Wayland protocol is also supported: 'zwp_pointer_gestures_v1', version: 3 -With the main caveat being that zwp_linux_dmabuf_v1 has no real -support for multiple-provider setups (help wanted). - -Window decorations are also not supported, even though they fit in -nicely with X window management. - -It would also be nice to have pinch gesture support. - This directory is organized as follows: Imakefile - the top level Makefile template diff --git a/compositor.h b/compositor.h index 10919dd..436beec 100644 --- a/compositor.h +++ b/compositor.h @@ -455,8 +455,9 @@ struct _BufferFuncs 0 formats if nothing is supported. */ DrmFormat *(*get_drm_formats) (int *); - /* Get the DRM device node. */ - dev_t (*get_render_device) (Bool *); + /* Get a list of DRM device nodes for each provider. Return NULL if + the provider list could not be initialized. */ + dev_t *(*get_render_devices) (int *); /* Get SHM formats supported by this renderer. */ ShmFormat *(*get_shm_formats) (int *); @@ -570,7 +571,7 @@ extern PresentCompletionKey RenderPresentToWindow (RenderTarget, RenderBuffer, extern void RenderCancelPresentationCallback (PresentCompletionKey); extern DrmFormat *RenderGetDrmFormats (int *); -extern dev_t RenderGetRenderDevice (Bool *); +extern dev_t *RenderGetRenderDevices (int *); extern ShmFormat *RenderGetShmFormats (int *); extern RenderBuffer RenderBufferFromDmaBuf (DmaBufAttributes *, Bool *); extern void RenderBufferFromDmaBufAsync (DmaBufAttributes *, DmaBufSuccessFunc, diff --git a/dmabuf.c b/dmabuf.c index 347f03d..27f9d64 100644 --- a/dmabuf.c +++ b/dmabuf.c @@ -111,9 +111,11 @@ static int format_table_fd; /* Size of the format table. */ static ssize_t format_table_size; -/* Device node of the DRM device. TODO: make this - output-specific. */ -static dev_t drm_device_node; +/* Device nodes of the DRM device. */ +static dev_t *drm_device_nodes; + +/* The number of DRM device nodes present. */ +static int num_device_nodes; /* DRM formats supported by the renderer. */ static DrmFormat *supported_formats; @@ -836,31 +838,27 @@ static struct zwp_linux_dmabuf_feedback_v1_interface zld_feedback_v1_impl = .destroy = Destroy, }; -/* TODO: dynamically switch tranche for surface feedbacks based on the - provider of the crtc the surface is in. */ - static void MakeFeedback (struct wl_client *client, struct wl_resource *resource, uint32_t id) { - struct wl_resource *feedback_resource; - struct wl_array main_device_array, format_array; - int i; + struct wl_resource *feedback; + struct wl_array main_device_array, format_array, array; + int i, provider; ptrdiff_t format_array_size; uint16_t *format_array_data; - feedback_resource = wl_resource_create (client, - &zwp_linux_dmabuf_feedback_v1_interface, - wl_resource_get_version (resource), id); + feedback = wl_resource_create (client, + &zwp_linux_dmabuf_feedback_v1_interface, + wl_resource_get_version (resource), id); - if (!resource) + if (!feedback) { wl_resource_post_no_memory (resource); return; } - wl_resource_set_implementation (feedback_resource, - &zld_feedback_v1_impl, + wl_resource_set_implementation (feedback, &zld_feedback_v1_impl, NULL, NULL); /* Now, send the relevant information. This should eventually be @@ -868,50 +866,56 @@ MakeFeedback (struct wl_client *client, struct wl_resource *resource, /* First, send the format table. */ - zwp_linux_dmabuf_feedback_v1_send_format_table (feedback_resource, + zwp_linux_dmabuf_feedback_v1_send_format_table (feedback, format_table_fd, format_table_size); - /* Next, send the main device. */ - - main_device_array.size = sizeof drm_device_node; - main_device_array.data = &drm_device_node; + /* Next, send the main device. The first provider returned by + RRGetProviders is considered to be the main device. */ + main_device_array.size = sizeof drm_device_nodes[0]; + main_device_array.data = &drm_device_nodes[0]; main_device_array.alloc = main_device_array.size; - zwp_linux_dmabuf_feedback_v1_send_main_device (feedback_resource, + + zwp_linux_dmabuf_feedback_v1_send_main_device (feedback, &main_device_array); - /* Then, send the first tranche. Right now, the only tranche - contains the formats supported by the default provider. */ + /* Then, send the one tranche for each device. */ + for (provider = 0; provider < num_device_nodes; ++provider) + { + array.size = sizeof drm_device_nodes[provider]; + array.data = &drm_device_nodes[provider]; + array.alloc = array.size; - zwp_linux_dmabuf_feedback_v1_send_tranche_target_device (feedback_resource, - &main_device_array); + zwp_linux_dmabuf_feedback_v1_send_tranche_target_device (feedback, + &array); - /* Populate the formats array with the contents of the format - table, and send it to the client. */ + /* Populate the formats array with the contents of the format + table, and send it to the client. */ - format_array_size = format_table_size / sizeof (FormatModifierPair); - format_array.size = format_array_size * sizeof (uint16_t); - format_array.data = format_array_data = alloca (format_array.size); + format_array_size = format_table_size / sizeof (FormatModifierPair); + format_array.size = format_array_size * sizeof (uint16_t); + format_array.data = format_array_data = alloca (format_array.size); - /* This must be reset too. */ - format_array.alloc = format_array.size; + /* This must be reset too. */ + format_array.alloc = format_array.size; - /* Simply announce every format to the client. */ - for (i = 0; i < format_array_size; ++i) - format_array_data[i] = i; + /* Simply announce every format to the client. */ + for (i = 0; i < format_array_size; ++i) + format_array_data[i] = i; - zwp_linux_dmabuf_feedback_v1_send_tranche_formats (feedback_resource, - &format_array); + zwp_linux_dmabuf_feedback_v1_send_tranche_formats (feedback, + &format_array); - /* Send flags. We don't currently support direct scanout, so send - nothing. */ + /* Send flags. We don't currently support direct scanout, so send + nothing. */ - zwp_linux_dmabuf_feedback_v1_send_tranche_flags (feedback_resource, 0); + zwp_linux_dmabuf_feedback_v1_send_tranche_flags (feedback, 0); - /* Mark the end of the tranche. */ + /* Mark the end of the tranche. */ - zwp_linux_dmabuf_feedback_v1_send_tranche_done (feedback_resource); + zwp_linux_dmabuf_feedback_v1_send_tranche_done (feedback); + } } static void @@ -991,15 +995,12 @@ HandleBind (struct wl_client *client, void *data, } static Bool -InitDrmDevice (void) +InitDrmDevices (void) { - Bool error; + /* These can either be master nodes or render nodes. */ + drm_device_nodes = RenderGetRenderDevices (&num_device_nodes); - error = False; - - /* This can either be a master node or a render node. */ - drm_device_node = RenderGetRenderDevice (&error); - return !error; + return num_device_nodes > 0; } static ssize_t @@ -1011,7 +1012,7 @@ WriteFormatTable (void) /* Before writing the format table, make sure the DRM device node can be obtained. */ - if (!InitDrmDevice ()) + if (!InitDrmDevices ()) { fprintf (stderr, "Failed to get direct rendering device node. " "Hardware acceleration will probably be unavailable.\n"); diff --git a/egl.c b/egl.c index 84cd53c..8621504 100644 --- a/egl.c +++ b/egl.c @@ -1720,11 +1720,17 @@ GetDrmFormats (int *num_formats) return drm_formats; } -static dev_t -GetRenderDevice (Bool *error) +static dev_t * +GetRenderDevices (int *num_devices) { - *error = !drm_device_available; - return drm_device; + if (!drm_device_available) + { + *num_devices = 0; + return NULL; + } + + *num_devices = 1; + return &drm_device; } static ShmFormat * @@ -2559,7 +2565,7 @@ IsBufferOpaque (RenderBuffer buffer) static BufferFuncs egl_buffer_funcs = { .get_drm_formats = GetDrmFormats, - .get_render_device = GetRenderDevice, + .get_render_devices = GetRenderDevices, .get_shm_formats = GetShmFormats, .buffer_from_dma_buf = BufferFromDmaBuf, .buffer_from_dma_buf_async = BufferFromDmaBufAsync, diff --git a/output.c b/output.c index 351f1ea..0e990fc 100644 --- a/output.c +++ b/output.c @@ -1192,7 +1192,7 @@ XLInitRROutputs (void) if (compositor.rr_major < 1 || (compositor.rr_major == 1 - && compositor.rr_minor < 3)) + && compositor.rr_minor < 4)) { fprintf (stderr, "Display '%s' does not support a" " sufficiently new version of the RandR extension\n", @@ -1216,21 +1216,13 @@ XLInitRROutputs (void) scale_callbacks.next = &scale_callbacks; scale_callbacks.last = &scale_callbacks; - if (compositor.rr_major > 1 - && (compositor.rr_major == 1 - && compositor.rr_minor >= 4)) - XRRSelectInput (compositor.display, - DefaultRootWindow (compositor.display), - (RRCrtcChangeNotifyMask - | RROutputChangeNotifyMask - | RROutputPropertyNotifyMask - | RRResourceChangeNotifyMask)); - else - XRRSelectInput (compositor.display, - DefaultRootWindow (compositor.display), - (RRCrtcChangeNotifyMask - | RROutputChangeNotifyMask - | RROutputPropertyNotifyMask)); + /* Select for various kinds of required input. */ + XRRSelectInput (compositor.display, + DefaultRootWindow (compositor.display), + (RRCrtcChangeNotifyMask + | RROutputChangeNotifyMask + | RROutputPropertyNotifyMask + | RRResourceChangeNotifyMask)); all_outputs = BuildOutputTree (); MakeGlobalsForOutputTree (all_outputs); diff --git a/picture_renderer.c b/picture_renderer.c index 8bacfdf..86b08f4 100644 --- a/picture_renderer.c +++ b/picture_renderer.c @@ -29,6 +29,7 @@ along with 12to11. If not, see . */ #include "drm_modifiers.h" #include +#include #include #include @@ -453,6 +454,12 @@ static PresentCompletionCallback all_completion_callbacks; /* Whether or not direct presentation should be used. */ static Bool use_direct_presentation; +/* The device nodes of each provider. */ +static dev_t *render_devices; + +/* The number of device nodes. */ +static int num_render_devices; + /* XRender, DRI3 and XPresent-based renderer. A RenderTarget is just a Picture. Here is a rough explanation of how the buffer release machinery works. @@ -2287,60 +2294,117 @@ GetDrmFormats (int *num_formats) } static dev_t -GetRenderDevice (Bool *error) +GetRenderDevice (xcb_dri3_open_reply_t *reply, Bool *error) { - xcb_dri3_open_cookie_t cookie; - xcb_dri3_open_reply_t *reply; int *fds, fd; struct stat dev_stat; - cookie = xcb_dri3_open (compositor.conn, - DefaultRootWindow (compositor.display), - None); - reply = xcb_dri3_open_reply (compositor.conn, cookie, NULL); - - if (!reply) - { - *error = True; - return (dev_t) 0; - } - fds = xcb_dri3_open_reply_fds (compositor.conn, reply); if (!fds) { - free (reply); *error = True; - return (dev_t) 0; } fd = fds[0]; - XLAddFdFlag (fd, FD_CLOEXEC, True); - if (fstat (fd, &dev_stat) != 0) { close (fd); - free (reply); *error = True; - - return (dev_t) 0; - } - - if (!dev_stat.st_rdev) - { - close (fd); - free (reply); - *error = True; - return (dev_t) 0; } close (fd); + return dev_stat.st_rdev; +} + +static dev_t * +GetRenderDevices (int *num_devices) +{ + Window root; + xcb_randr_get_providers_cookie_t cookie; + xcb_randr_get_providers_reply_t *reply; + xcb_randr_provider_t *providers; + int nproviders; + xcb_dri3_open_cookie_t *open_cookies; + xcb_dri3_open_reply_t *open_reply; + xcb_generic_error_t *error; + int ndevices, i; + dev_t *devices; + Bool error_experienced; + + if (render_devices) + { + *num_devices = num_render_devices; + return render_devices; + } + + root = DefaultRootWindow (compositor.display); + + /* Get a list of all providers on the default screen. */ + cookie = xcb_randr_get_providers (compositor.conn, + root); + reply = xcb_randr_get_providers_reply (compositor.conn, + cookie, NULL); + + if (!reply) + return NULL; + + providers = xcb_randr_get_providers_providers (reply); + nproviders = xcb_randr_get_providers_providers_length (reply); + + /* Now, open each and every provider. */ + open_cookies = alloca (nproviders * sizeof *open_cookies); + + for (i = 0; i < nproviders; ++i) + open_cookies[i] = xcb_dri3_open (compositor.conn, root, + providers[i]); + + /* Free the provider list and wait for replies from the X server. + Also, allocate an array large enough to hold each device. */ + free (reply); - return dev_stat.st_rdev; + /* Allocate 1 extra provider so that render_devices is not set to + NULL if there are no providers. */ + devices = XLCalloc (nproviders + 1, sizeof *devices); + ndevices = 0; + + for (i = 0; i < nproviders; ++i) + { + open_reply = xcb_dri3_open_reply (compositor.conn, open_cookies[i], + &error); + + if (error || !open_reply) + { + if (error) + free (error); + + continue; + } + + /* Now obtain the device node associated with the opened DRM + node. */ + error_experienced = False; + devices[ndevices] = GetRenderDevice (open_reply, &error_experienced); + free (open_reply); + + if (error_experienced) + /* An error occured. */ + continue; + + /* Otherwise, increment ndevices. */ + ndevices++; + } + + num_render_devices = ndevices; + render_devices = devices; + + /* Return the device list. */ + *num_devices = ndevices; + return devices; } static ShmFormat * @@ -3242,7 +3306,7 @@ IsBufferOpaque (RenderBuffer buffer) static BufferFuncs picture_buffer_funcs = { .get_drm_formats = GetDrmFormats, - .get_render_device = GetRenderDevice, + .get_render_devices = GetRenderDevices, .get_shm_formats = GetShmFormats, .buffer_from_dma_buf = BufferFromDmaBuf, .buffer_from_dma_buf_async = BufferFromDmaBufAsync, diff --git a/renderer.c b/renderer.c index 3ccf559..d13eb28 100644 --- a/renderer.c +++ b/renderer.c @@ -230,10 +230,10 @@ RenderGetDrmFormats (int *n_formats) return buffer_funcs.get_drm_formats (n_formats); } -dev_t -RenderGetRenderDevice (Bool *error) +dev_t * +RenderGetRenderDevices (int *num_devices) { - return buffer_funcs.get_render_device (error); + return buffer_funcs.get_render_devices (num_devices); } ShmFormat *