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
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

View file

@ -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,

View file

@ -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,
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,24 +866,29 @@ 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. */
@ -901,17 +904,18 @@ MakeFeedback (struct wl_client *client, struct wl_resource *resource,
for (i = 0; i < format_array_size; ++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);
/* 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. */
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");

16
egl.c
View file

@ -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,

View file

@ -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))
/* Select for various kinds of required input. */
XRRSelectInput (compositor.display,
DefaultRootWindow (compositor.display),
(RRCrtcChangeNotifyMask
| RROutputChangeNotifyMask
| RROutputPropertyNotifyMask
| RRResourceChangeNotifyMask));
else
XRRSelectInput (compositor.display,
DefaultRootWindow (compositor.display),
(RRCrtcChangeNotifyMask
| RROutputChangeNotifyMask
| RROutputPropertyNotifyMask));
all_outputs = BuildOutputTree ();
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 <xcb/dri3.h>
#include <xcb/randr.h>
#include <X11/Xmd.h>
#include <X11/extensions/dri3proto.h>
@ -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,62 +2294,119 @@ 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);
free (reply);
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);
/* 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 *
GetShmFormats (int *num_formats)
{
@ -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,

View file

@ -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 *