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.
This commit is contained in:
hujianwei 2022-11-13 02:08:10 +00:00
parent b022f0dbd9
commit 442f80b667
7 changed files with 173 additions and 117 deletions

10
README
View file

@ -16,7 +16,7 @@ extensions:
Nonrectangular Window Shape Extension, version 1.1 or later Nonrectangular Window Shape Extension, version 1.1 or later
MIT Shared Memory Extension, version 1.2 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 Synchronization Extension, version 1.0 or later
X Rendering Extension, version 1.2 or later X Rendering Extension, version 1.2 or later
X Input Extension, version 2.3 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 '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: This directory is organized as follows:
Imakefile - the top level Makefile template Imakefile - the top level Makefile template

View file

@ -455,8 +455,9 @@ struct _BufferFuncs
0 formats if nothing is supported. */ 0 formats if nothing is supported. */
DrmFormat *(*get_drm_formats) (int *); DrmFormat *(*get_drm_formats) (int *);
/* Get the DRM device node. */ /* Get a list of DRM device nodes for each provider. Return NULL if
dev_t (*get_render_device) (Bool *); the provider list could not be initialized. */
dev_t *(*get_render_devices) (int *);
/* Get SHM formats supported by this renderer. */ /* Get SHM formats supported by this renderer. */
ShmFormat *(*get_shm_formats) (int *); ShmFormat *(*get_shm_formats) (int *);
@ -570,7 +571,7 @@ extern PresentCompletionKey RenderPresentToWindow (RenderTarget, RenderBuffer,
extern void RenderCancelPresentationCallback (PresentCompletionKey); extern void RenderCancelPresentationCallback (PresentCompletionKey);
extern DrmFormat *RenderGetDrmFormats (int *); extern DrmFormat *RenderGetDrmFormats (int *);
extern dev_t RenderGetRenderDevice (Bool *); extern dev_t *RenderGetRenderDevices (int *);
extern ShmFormat *RenderGetShmFormats (int *); extern ShmFormat *RenderGetShmFormats (int *);
extern RenderBuffer RenderBufferFromDmaBuf (DmaBufAttributes *, Bool *); extern RenderBuffer RenderBufferFromDmaBuf (DmaBufAttributes *, Bool *);
extern void RenderBufferFromDmaBufAsync (DmaBufAttributes *, DmaBufSuccessFunc, extern void RenderBufferFromDmaBufAsync (DmaBufAttributes *, DmaBufSuccessFunc,

101
dmabuf.c
View file

@ -111,9 +111,11 @@ static int format_table_fd;
/* Size of the format table. */ /* Size of the format table. */
static ssize_t format_table_size; static ssize_t format_table_size;
/* Device node of the DRM device. TODO: make this /* Device nodes of the DRM device. */
output-specific. */ static dev_t *drm_device_nodes;
static dev_t drm_device_node;
/* The number of DRM device nodes present. */
static int num_device_nodes;
/* DRM formats supported by the renderer. */ /* DRM formats supported by the renderer. */
static DrmFormat *supported_formats; static DrmFormat *supported_formats;
@ -836,31 +838,27 @@ static struct zwp_linux_dmabuf_feedback_v1_interface zld_feedback_v1_impl =
.destroy = Destroy, .destroy = Destroy,
}; };
/* TODO: dynamically switch tranche for surface feedbacks based on the
provider of the crtc the surface is in. */
static void static void
MakeFeedback (struct wl_client *client, struct wl_resource *resource, MakeFeedback (struct wl_client *client, struct wl_resource *resource,
uint32_t id) uint32_t id)
{ {
struct wl_resource *feedback_resource; struct wl_resource *feedback;
struct wl_array main_device_array, format_array; struct wl_array main_device_array, format_array, array;
int i; int i, provider;
ptrdiff_t format_array_size; ptrdiff_t format_array_size;
uint16_t *format_array_data; uint16_t *format_array_data;
feedback_resource = wl_resource_create (client, feedback = wl_resource_create (client,
&zwp_linux_dmabuf_feedback_v1_interface, &zwp_linux_dmabuf_feedback_v1_interface,
wl_resource_get_version (resource), id); wl_resource_get_version (resource), id);
if (!resource) if (!feedback)
{ {
wl_resource_post_no_memory (resource); wl_resource_post_no_memory (resource);
return; return;
} }
wl_resource_set_implementation (feedback_resource, wl_resource_set_implementation (feedback, &zld_feedback_v1_impl,
&zld_feedback_v1_impl,
NULL, NULL); NULL, NULL);
/* Now, send the relevant information. This should eventually be /* 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. */ /* 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_fd,
format_table_size); format_table_size);
/* Next, send the main device. */ /* 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_node;
main_device_array.data = &drm_device_node;
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; 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); &main_device_array);
/* Then, send the first tranche. Right now, the only tranche /* Then, send the one tranche for each device. */
contains the formats supported by the default provider. */ 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, zwp_linux_dmabuf_feedback_v1_send_tranche_target_device (feedback,
&main_device_array); &array);
/* Populate the formats array with the contents of the format /* Populate the formats array with the contents of the format
table, and send it to the client. */ table, and send it to the client. */
format_array_size = format_table_size / sizeof (FormatModifierPair); format_array_size = format_table_size / sizeof (FormatModifierPair);
format_array.size = format_array_size * sizeof (uint16_t); format_array.size = format_array_size * sizeof (uint16_t);
format_array.data = format_array_data = alloca (format_array.size); format_array.data = format_array_data = alloca (format_array.size);
/* This must be reset too. */ /* This must be reset too. */
format_array.alloc = format_array.size; format_array.alloc = format_array.size;
/* Simply announce every format to the client. */ /* Simply announce every format to the client. */
for (i = 0; i < format_array_size; ++i) for (i = 0; i < format_array_size; ++i)
format_array_data[i] = i; format_array_data[i] = i;
zwp_linux_dmabuf_feedback_v1_send_tranche_formats (feedback_resource, zwp_linux_dmabuf_feedback_v1_send_tranche_formats (feedback,
&format_array); &format_array);
/* Send flags. We don't currently support direct scanout, so send /* Send flags. We don't currently support direct scanout, so send
nothing. */ 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 static void
@ -991,15 +995,12 @@ HandleBind (struct wl_client *client, void *data,
} }
static Bool 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; return num_device_nodes > 0;
/* This can either be a master node or a render node. */
drm_device_node = RenderGetRenderDevice (&error);
return !error;
} }
static ssize_t static ssize_t
@ -1011,7 +1012,7 @@ WriteFormatTable (void)
/* Before writing the format table, make sure the DRM device node /* Before writing the format table, make sure the DRM device node
can be obtained. */ can be obtained. */
if (!InitDrmDevice ()) if (!InitDrmDevices ())
{ {
fprintf (stderr, "Failed to get direct rendering device node. " fprintf (stderr, "Failed to get direct rendering device node. "
"Hardware acceleration will probably be unavailable.\n"); "Hardware acceleration will probably be unavailable.\n");

16
egl.c
View file

@ -1720,11 +1720,17 @@ GetDrmFormats (int *num_formats)
return drm_formats; return drm_formats;
} }
static dev_t static dev_t *
GetRenderDevice (Bool *error) GetRenderDevices (int *num_devices)
{ {
*error = !drm_device_available; if (!drm_device_available)
return drm_device; {
*num_devices = 0;
return NULL;
}
*num_devices = 1;
return &drm_device;
} }
static ShmFormat * static ShmFormat *
@ -2559,7 +2565,7 @@ IsBufferOpaque (RenderBuffer buffer)
static BufferFuncs egl_buffer_funcs = static BufferFuncs egl_buffer_funcs =
{ {
.get_drm_formats = GetDrmFormats, .get_drm_formats = GetDrmFormats,
.get_render_device = GetRenderDevice, .get_render_devices = GetRenderDevices,
.get_shm_formats = GetShmFormats, .get_shm_formats = GetShmFormats,
.buffer_from_dma_buf = BufferFromDmaBuf, .buffer_from_dma_buf = BufferFromDmaBuf,
.buffer_from_dma_buf_async = BufferFromDmaBufAsync, .buffer_from_dma_buf_async = BufferFromDmaBufAsync,

View file

@ -1192,7 +1192,7 @@ XLInitRROutputs (void)
if (compositor.rr_major < 1 if (compositor.rr_major < 1
|| (compositor.rr_major == 1 || (compositor.rr_major == 1
&& compositor.rr_minor < 3)) && compositor.rr_minor < 4))
{ {
fprintf (stderr, "Display '%s' does not support a" fprintf (stderr, "Display '%s' does not support a"
" sufficiently new version of the RandR extension\n", " sufficiently new version of the RandR extension\n",
@ -1216,21 +1216,13 @@ XLInitRROutputs (void)
scale_callbacks.next = &scale_callbacks; scale_callbacks.next = &scale_callbacks;
scale_callbacks.last = &scale_callbacks; scale_callbacks.last = &scale_callbacks;
if (compositor.rr_major > 1 /* Select for various kinds of required input. */
&& (compositor.rr_major == 1 XRRSelectInput (compositor.display,
&& compositor.rr_minor >= 4)) DefaultRootWindow (compositor.display),
XRRSelectInput (compositor.display, (RRCrtcChangeNotifyMask
DefaultRootWindow (compositor.display), | RROutputChangeNotifyMask
(RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask
| RROutputChangeNotifyMask | RRResourceChangeNotifyMask));
| RROutputPropertyNotifyMask
| RRResourceChangeNotifyMask));
else
XRRSelectInput (compositor.display,
DefaultRootWindow (compositor.display),
(RRCrtcChangeNotifyMask
| RROutputChangeNotifyMask
| RROutputPropertyNotifyMask));
all_outputs = BuildOutputTree (); all_outputs = BuildOutputTree ();
MakeGlobalsForOutputTree (all_outputs); MakeGlobalsForOutputTree (all_outputs);

View file

@ -29,6 +29,7 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include "drm_modifiers.h" #include "drm_modifiers.h"
#include <xcb/dri3.h> #include <xcb/dri3.h>
#include <xcb/randr.h>
#include <X11/Xmd.h> #include <X11/Xmd.h>
#include <X11/extensions/dri3proto.h> #include <X11/extensions/dri3proto.h>
@ -453,6 +454,12 @@ static PresentCompletionCallback all_completion_callbacks;
/* Whether or not direct presentation should be used. */ /* Whether or not direct presentation should be used. */
static Bool use_direct_presentation; 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 /* XRender, DRI3 and XPresent-based renderer. A RenderTarget is just
a Picture. Here is a rough explanation of how the buffer release a Picture. Here is a rough explanation of how the buffer release
machinery works. machinery works.
@ -2287,60 +2294,117 @@ GetDrmFormats (int *num_formats)
} }
static dev_t 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; int *fds, fd;
struct stat dev_stat; 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); fds = xcb_dri3_open_reply_fds (compositor.conn, reply);
if (!fds) if (!fds)
{ {
free (reply);
*error = True; *error = True;
return (dev_t) 0; return (dev_t) 0;
} }
fd = fds[0]; fd = fds[0];
XLAddFdFlag (fd, FD_CLOEXEC, True);
if (fstat (fd, &dev_stat) != 0) if (fstat (fd, &dev_stat) != 0)
{ {
close (fd); close (fd);
free (reply);
*error = True; *error = True;
return (dev_t) 0;
}
if (!dev_stat.st_rdev)
{
close (fd);
free (reply);
*error = True;
return (dev_t) 0; return (dev_t) 0;
} }
close (fd); 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); 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 * static ShmFormat *
@ -3242,7 +3306,7 @@ IsBufferOpaque (RenderBuffer buffer)
static BufferFuncs picture_buffer_funcs = static BufferFuncs picture_buffer_funcs =
{ {
.get_drm_formats = GetDrmFormats, .get_drm_formats = GetDrmFormats,
.get_render_device = GetRenderDevice, .get_render_devices = GetRenderDevices,
.get_shm_formats = GetShmFormats, .get_shm_formats = GetShmFormats,
.buffer_from_dma_buf = BufferFromDmaBuf, .buffer_from_dma_buf = BufferFromDmaBuf,
.buffer_from_dma_buf_async = BufferFromDmaBufAsync, .buffer_from_dma_buf_async = BufferFromDmaBufAsync,

View file

@ -230,10 +230,10 @@ RenderGetDrmFormats (int *n_formats)
return buffer_funcs.get_drm_formats (n_formats); return buffer_funcs.get_drm_formats (n_formats);
} }
dev_t dev_t *
RenderGetRenderDevice (Bool *error) RenderGetRenderDevices (int *num_devices)
{ {
return buffer_funcs.get_render_device (error); return buffer_funcs.get_render_devices (num_devices);
} }
ShmFormat * ShmFormat *