summaryrefslogtreecommitdiff
path: root/d4937adc5cd04ac7df98fc5616e40319fb52fdee.patch
blob: 48845f94036a3681f524f491dc108715971df632 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
From d4937adc5cd04ac7df98fc5616e40319fb52fdee Mon Sep 17 00:00:00 2001
From: Austin Shafer <ashafer@nvidia.com>
Date: Wed, 27 Oct 2021 06:37:07 -0400
Subject: [PATCH] wayland: Fail eglGetDisplay if wl_drm is not available

This patch does two things:
- checks if wl_drm is in use on the server, and uses it to get the name
  of the drm device the compositor is driving.
- Find an EGLDevice that matches the path returned by wl_drm.

If wl_drm and the needed extensions are not present, or if a matching
EGLDevice is not found, then we fail. Right now we only support
running on the same GPU as the compositor, so any of these being
missing means that is not the case.
---
 src/wayland-egldisplay.c | 153 +++++++++++++++++++++++++++++++++++----
 1 file changed, 138 insertions(+), 15 deletions(-)

diff --git a/src/wayland-egldisplay.c b/src/wayland-egldisplay.c
index a0370a5..8b7394a 100644
--- a/src/wayland-egldisplay.c
+++ b/src/wayland-egldisplay.c
@@ -29,13 +29,19 @@
 #include "wayland-eglsurface.h"
 #include "wayland-eglhandle.h"
 #include "wayland-eglutils.h"
+#include "wayland-drm-client-protocol.h"
 #include <string.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 typedef struct WlServerProtocolsRec {
     EGLBoolean hasEglStream;
     EGLBoolean hasDmaBuf;
+    EGLBoolean hasDrm;
+    struct wl_drm *wldrm;
+    char *drm_name;
 } WlServerProtocols;
 
 /* TODO: Make global display lists hang off platform data */
@@ -241,6 +247,40 @@ static const struct wl_registry_listener registry_listener = {
     registry_handle_global_remove
 };
 
+static void wl_drm_device(void *data, struct wl_drm *wl_drm, const char *name)
+{
+    WlServerProtocols *protocols = (WlServerProtocols *)data;
+    (void) wl_drm;
+
+    protocols->drm_name = strdup(name);
+}
+
+static void wl_drm_authenticated(void *data, struct wl_drm *wl_drm)
+{
+    (void) data;
+    (void) wl_drm;
+}
+static void wl_drm_format(void *data, struct wl_drm *wl_drm, uint32_t format)
+{
+    (void) data;
+    (void) wl_drm;
+    (void) format;
+}
+static void wl_drm_capabilities(void *data, struct wl_drm *wl_drm, uint32_t value)
+{
+    (void) data;
+    (void) wl_drm;
+    (void) value;
+}
+
+static const struct wl_drm_listener drmListener = {
+    .device = wl_drm_device,
+    .authenticated = wl_drm_authenticated,
+    .format = wl_drm_format,
+    .capabilities = wl_drm_capabilities,
+};
+
+
 static void
 registry_handle_global_check_protocols(
                        void *data,
@@ -262,6 +302,12 @@ registry_handle_global_check_protocols(
         (version >= 3)) {
         protocols->hasDmaBuf = EGL_TRUE;
     }
+
+    if ((strcmp(interface, "wl_drm") == 0) && (version >= 2)) {
+        protocols->hasDrm = EGL_TRUE;
+        protocols->wldrm = wl_registry_bind(registry, name, &wl_drm_interface, 2);
+        wl_drm_add_listener(protocols->wldrm, &drmListener, protocols);
+    }
 }
 
 static void
@@ -389,8 +435,8 @@ EGLBoolean wlEglTerminateHook(EGLDisplay dpy)
     return res;
 }
 
-static void checkServerProtocols(struct wl_display *nativeDpy,
-                                 WlServerProtocols *protocols)
+static void getServerProtocolsInfo(struct wl_display *nativeDpy,
+                                   WlServerProtocols *protocols)
 {
     struct wl_display     *wrapper      = NULL;
     struct wl_registry    *wlRegistry   = NULL;
@@ -418,6 +464,11 @@ static void checkServerProtocols(struct wl_display *nativeDpy,
                                    protocols);
     if (ret == 0) {
         wl_display_roundtrip_queue(nativeDpy, queue);
+        if (protocols->hasDrm) {
+            wl_display_roundtrip_queue(nativeDpy, queue);
+            /* destroy our wl_drm object */
+            wl_drm_destroy(protocols->wldrm);
+        }
     }
 
     if (queue) {
@@ -438,9 +489,13 @@ EGLDisplay wlEglGetPlatformDisplayExport(void *data,
     WlServerProtocols      protocols;
     EGLint                 numDevices      = 0;
     int                    i               = 0;
+    EGLDeviceEXT          *eglDeviceList   = NULL;
     EGLDeviceEXT           eglDevice       = NULL;
+    EGLDeviceEXT           tmpDev          = NULL;
     EGLint                 err             = EGL_SUCCESS;
     EGLBoolean             useInitRefCount = EGL_FALSE;
+    const char *dev_exts;
+    const char *dev_name;
 
     if (platform != EGL_PLATFORM_WAYLAND_EXT) {
         wlEglSetError(data, EGL_BAD_PARAMETER);
@@ -480,7 +535,6 @@ EGLDisplay wlEglGetPlatformDisplayExport(void *data,
 
     display = calloc(1, sizeof(*display));
     if (!display) {
-        wlExternalApiUnlock();
         err = EGL_BAD_ALLOC;
         goto fail;
     }
@@ -498,7 +552,6 @@ EGLDisplay wlEglGetPlatformDisplayExport(void *data,
     if (!display->nativeDpy) {
         display->nativeDpy = wl_display_connect(NULL);
         if (!display->nativeDpy) {
-            wlExternalApiUnlock();
             err = EGL_BAD_ALLOC;
             goto fail;
         }
@@ -508,26 +561,85 @@ EGLDisplay wlEglGetPlatformDisplayExport(void *data,
     }
 
     memset(&protocols, 0, sizeof(protocols));
-    checkServerProtocols(display->nativeDpy, &protocols);
+    /*
+     * This is where we check the supported protocols on the compositor,
+     * and bind to wl_drm to get the device name.
+     * protocols.drm_name will be allocated here if using wl_drm
+     */
+    getServerProtocolsInfo(display->nativeDpy, &protocols);
 
-    if (!protocols.hasEglStream && !protocols.hasDmaBuf) {
-        wlExternalApiUnlock();
-        goto fail;
+    if (!protocols.hasDrm || (!protocols.hasEglStream && !protocols.hasDmaBuf)) {
+        goto fail_cleanup_protocols;
     }
 
-    if (!pData->egl.queryDevices(1, &eglDevice, &numDevices) || numDevices == 0) {
-        wlExternalApiUnlock();
-        goto fail;
+    /* Get the number of devices available */
+    if (!pData->egl.queryDevices(-1, NULL, &numDevices) || numDevices == 0) {
+        goto fail_cleanup_protocols;
+    }
+
+    eglDeviceList = calloc(numDevices, sizeof(*eglDeviceList));
+    if (!eglDeviceList) {
+        goto fail_cleanup_protocols;
+    }
+
+    /*
+     * Now we need to find an EGLDevice. If wl_drm is in use we will try to find one that
+     * matches the device the compositor is using. We know that device is an nvidia device
+     * since we just checked that above.
+     */
+    if (!pData->egl.queryDevices(numDevices, eglDeviceList, &numDevices) || numDevices == 0) {
+        goto fail_cleanup_devices;
     }
+
+    if (protocols.drm_name) {
+        for (int i = 0; i < numDevices; i++) {
+            tmpDev = eglDeviceList[i];
+
+            /*
+             * To check against the wl_drm name, we need to check if we can use
+             * the drm extension
+             */
+            dev_exts = display->data->egl.queryDeviceString(tmpDev,
+                    EGL_EXTENSIONS);
+            if (dev_exts) {
+                if (wlEglFindExtension("EGL_EXT_device_drm_render_node", dev_exts)) {
+                    dev_name =
+                        display->data->egl.queryDeviceString(tmpDev,
+                                EGL_DRM_RENDER_NODE_FILE_EXT);
+
+                    if (dev_name) {
+                        /*
+                         * At this point we have gotten the name from wl_drm, gotten
+                         * the drm node from the EGLDevice. If they match, then
+                         * this is the final device to use, since it is the compositor's
+                         * device.
+                         */
+                        if (strcmp(dev_name, protocols.drm_name) == 0) {
+                            eglDevice = eglDeviceList[0];
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * Right now we are pretty much limited to running on the same GPU as the
+     * compositor. If we couldn't find an EGLDevice that has EGL_EXT_device_drm_render_node
+     * and the same DRM device path, then fail.
+     */
+    if (!eglDevice) {
+        goto fail_cleanup_devices;
+    }
+
     display->devDpy = wlGetInternalDisplay(pData, eglDevice);
     if (display->devDpy == NULL) {
-        wlExternalApiUnlock();
-        goto fail;
+        goto fail_cleanup_devices;
     }
 
     if (!wlEglInitializeMutex(&display->mutex)) {
-        wlExternalApiUnlock();
-        goto fail;
+        goto fail_cleanup_devices;
     }
     display->refCount = 1;
     WL_LIST_INIT(&display->wlEglSurfaceList);
@@ -537,10 +649,21 @@ EGLDisplay wlEglGetPlatformDisplayExport(void *data,
     // in wlEglDisplayList.
     wl_list_insert(&wlEglDisplayList, &display->link);
 
+    free(eglDeviceList);
+    if (protocols.drm_name) {
+        free(protocols.drm_name);
+    }
     wlExternalApiUnlock();
     return display;
 
+fail_cleanup_devices:
+    free(eglDeviceList);
+fail_cleanup_protocols:
+    if (protocols.drm_name) {
+        free(protocols.drm_name);
+    }
 fail:
+    wlExternalApiUnlock();
 
     if (display->ownNativeDpy) {
         wl_display_disconnect(display->nativeDpy);