1 /*
2  * Copyright © 2011-2014 Intel Corporation
3  * Copyright © 2017 Red Hat Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use, copy,
9  * modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including
14  * the next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  * Authors:
27  *    Lyude Paul <lyude@redhat.com>
28  *
29  */
30 
31 #include <xwayland-config.h>
32 
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <xf86drm.h>
37 #include <drm_fourcc.h>
38 
39 #define MESA_EGL_NO_X11_HEADERS
40 #define EGL_NO_X11
41 #include <gbm.h>
42 #include <glamor_egl.h>
43 
44 #include <glamor.h>
45 #include <glamor_context.h>
46 #include <dri3.h>
47 #include "drm-client-protocol.h"
48 
49 #include "xwayland-glamor.h"
50 #include "xwayland-pixmap.h"
51 #include "xwayland-screen.h"
52 
53 #include "linux-dmabuf-unstable-v1-client-protocol.h"
54 
55 struct xwl_gbm_private {
56     char *device_name;
57     struct gbm_device *gbm;
58     struct wl_drm *drm;
59     int drm_fd;
60     int fd_render_node;
61     Bool drm_authenticated;
62     uint32_t capabilities;
63     int dmabuf_capable;
64 };
65 
66 struct xwl_pixmap {
67     struct wl_buffer *buffer;
68     EGLImage image;
69     unsigned int texture;
70     struct gbm_bo *bo;
71 };
72 
73 static DevPrivateKeyRec xwl_gbm_private_key;
74 static DevPrivateKeyRec xwl_auth_state_private_key;
75 
76 static inline struct xwl_gbm_private *
xwl_gbm_get(struct xwl_screen * xwl_screen)77 xwl_gbm_get(struct xwl_screen *xwl_screen)
78 {
79     return dixLookupPrivate(&xwl_screen->screen->devPrivates,
80                             &xwl_gbm_private_key);
81 }
82 
83 static uint32_t
gbm_format_for_depth(int depth)84 gbm_format_for_depth(int depth)
85 {
86     switch (depth) {
87     case 16:
88         return GBM_FORMAT_RGB565;
89     case 24:
90         return GBM_FORMAT_XRGB8888;
91     case 30:
92         return GBM_FORMAT_ARGB2101010;
93     default:
94         ErrorF("unexpected depth: %d\n", depth);
95     case 32:
96         return GBM_FORMAT_ARGB8888;
97     }
98 }
99 
100 static char
is_device_path_render_node(const char * device_path)101 is_device_path_render_node (const char *device_path)
102 {
103     char is_render_node;
104     int fd;
105 
106     fd = open(device_path, O_RDWR | O_CLOEXEC);
107     if (fd < 0)
108         return 0;
109 
110     is_render_node = (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER);
111     close(fd);
112 
113     return is_render_node;
114 }
115 
116 static PixmapPtr
xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen,struct gbm_bo * bo,int depth)117 xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo,
118                                     int depth)
119 {
120     PixmapPtr pixmap;
121     struct xwl_pixmap *xwl_pixmap;
122     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
123 #ifdef GBM_BO_FD_FOR_PLANE
124     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
125     uint64_t modifier = gbm_bo_get_modifier(bo);
126     const int num_planes = gbm_bo_get_plane_count(bo);
127     int fds[GBM_MAX_PLANES];
128     int plane;
129     int attr_num = 0;
130     EGLint img_attrs[64] = {0};
131     enum PlaneAttrs {
132         PLANE_FD,
133         PLANE_OFFSET,
134         PLANE_PITCH,
135         PLANE_MODIFIER_LO,
136         PLANE_MODIFIER_HI,
137         NUM_PLANE_ATTRS
138     };
139     static const EGLint planeAttrs[][NUM_PLANE_ATTRS] = {
140         {
141             EGL_DMA_BUF_PLANE0_FD_EXT,
142             EGL_DMA_BUF_PLANE0_OFFSET_EXT,
143             EGL_DMA_BUF_PLANE0_PITCH_EXT,
144             EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
145             EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
146         },
147         {
148             EGL_DMA_BUF_PLANE1_FD_EXT,
149             EGL_DMA_BUF_PLANE1_OFFSET_EXT,
150             EGL_DMA_BUF_PLANE1_PITCH_EXT,
151             EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
152             EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
153         },
154         {
155             EGL_DMA_BUF_PLANE2_FD_EXT,
156             EGL_DMA_BUF_PLANE2_OFFSET_EXT,
157             EGL_DMA_BUF_PLANE2_PITCH_EXT,
158             EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
159             EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
160         },
161         {
162             EGL_DMA_BUF_PLANE3_FD_EXT,
163             EGL_DMA_BUF_PLANE3_OFFSET_EXT,
164             EGL_DMA_BUF_PLANE3_PITCH_EXT,
165             EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
166             EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
167         },
168     };
169 
170     for (plane = 0; plane < num_planes; plane++) fds[plane] = -1;
171 #endif
172 
173     xwl_pixmap = calloc(1, sizeof(*xwl_pixmap));
174     if (xwl_pixmap == NULL)
175         return NULL;
176 
177     pixmap = glamor_create_pixmap(screen,
178                                   gbm_bo_get_width(bo),
179                                   gbm_bo_get_height(bo),
180                                   depth,
181                                   GLAMOR_CREATE_PIXMAP_NO_TEXTURE);
182     if (!pixmap) {
183         free(xwl_pixmap);
184         return NULL;
185     }
186 
187     xwl_glamor_egl_make_current(xwl_screen);
188     xwl_pixmap->bo = bo;
189     xwl_pixmap->buffer = NULL;
190 
191 #ifdef GBM_BO_FD_FOR_PLANE
192     if (xwl_gbm->dmabuf_capable) {
193 #define ADD_ATTR(attrs, num, attr)                                      \
194         do {                                                            \
195             assert(((num) + 1) < (sizeof(attrs) / sizeof((attrs)[0]))); \
196             (attrs)[(num)++] = (attr);                                  \
197         } while (0)
198         ADD_ATTR(img_attrs, attr_num, EGL_WIDTH);
199         ADD_ATTR(img_attrs, attr_num, gbm_bo_get_width(bo));
200         ADD_ATTR(img_attrs, attr_num, EGL_HEIGHT);
201         ADD_ATTR(img_attrs, attr_num, gbm_bo_get_height(bo));
202         ADD_ATTR(img_attrs, attr_num, EGL_LINUX_DRM_FOURCC_EXT);
203         ADD_ATTR(img_attrs, attr_num, gbm_bo_get_format(bo));
204 
205         for (plane = 0; plane < num_planes; plane++) {
206             fds[plane] = gbm_bo_get_fd_for_plane(bo, plane);
207             ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_FD]);
208             ADD_ATTR(img_attrs, attr_num, fds[plane]);
209             ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_OFFSET]);
210             ADD_ATTR(img_attrs, attr_num, gbm_bo_get_offset(bo, plane));
211             ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_PITCH]);
212             ADD_ATTR(img_attrs, attr_num, gbm_bo_get_stride_for_plane(bo, plane));
213             ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_MODIFIER_LO]);
214             ADD_ATTR(img_attrs, attr_num, (uint32_t)(modifier & 0xFFFFFFFFULL));
215             ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_MODIFIER_HI]);
216             ADD_ATTR(img_attrs, attr_num, (uint32_t)(modifier >> 32ULL));
217         }
218         ADD_ATTR(img_attrs, attr_num, EGL_NONE);
219 #undef ADD_ATTR
220 
221         xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
222                                               EGL_NO_CONTEXT,
223                                               EGL_LINUX_DMA_BUF_EXT,
224                                               NULL,
225                                               img_attrs);
226 
227         for (plane = 0; plane < num_planes; plane++) {
228             close(fds[plane]);
229             fds[plane] = -1;
230         }
231     }
232     else
233 #endif
234     {
235         xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
236                                               xwl_screen->egl_context,
237                                               EGL_NATIVE_PIXMAP_KHR,
238                                               xwl_pixmap->bo, NULL);
239     }
240 
241     if (xwl_pixmap->image == EGL_NO_IMAGE_KHR)
242       goto error;
243 
244     glGenTextures(1, &xwl_pixmap->texture);
245     glBindTexture(GL_TEXTURE_2D, xwl_pixmap->texture);
246     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
247     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
248 
249     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, xwl_pixmap->image);
250     if (eglGetError() != EGL_SUCCESS)
251       goto error;
252 
253     glBindTexture(GL_TEXTURE_2D, 0);
254 
255     if (!glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture))
256       goto error;
257 
258     glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
259     xwl_pixmap_set_private(pixmap, xwl_pixmap);
260 
261     return pixmap;
262 
263 error:
264     if (xwl_pixmap->image != EGL_NO_IMAGE_KHR)
265       eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image);
266     if (pixmap)
267       glamor_destroy_pixmap(pixmap);
268     free(xwl_pixmap);
269 
270     return NULL;
271 }
272 
273 static PixmapPtr
xwl_glamor_gbm_create_pixmap(ScreenPtr screen,int width,int height,int depth,unsigned int hint)274 xwl_glamor_gbm_create_pixmap(ScreenPtr screen,
275                              int width, int height, int depth,
276                              unsigned int hint)
277 {
278     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
279     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
280     struct gbm_bo *bo;
281     PixmapPtr pixmap = NULL;
282 
283     if (width > 0 && height > 0 && depth >= 15 &&
284         (hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP ||
285          hint == CREATE_PIXMAP_USAGE_SHARED ||
286          (xwl_screen->rootless && hint == 0))) {
287         uint32_t format = gbm_format_for_depth(depth);
288 
289 #ifdef GBM_BO_WITH_MODIFIERS
290         if (xwl_gbm->dmabuf_capable) {
291             uint32_t num_modifiers;
292             uint64_t *modifiers = NULL;
293 
294             xwl_glamor_get_modifiers(screen, format, &num_modifiers, &modifiers);
295             bo = gbm_bo_create_with_modifiers(xwl_gbm->gbm, width, height,
296                                               format, modifiers, num_modifiers);
297             free(modifiers);
298         }
299         else
300 #endif
301         {
302             bo = gbm_bo_create(xwl_gbm->gbm, width, height, format,
303                                GBM_BO_USE_RENDERING);
304         }
305 
306         if (bo) {
307             pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth);
308 
309             if (!pixmap) {
310                 gbm_bo_destroy(bo);
311             }
312             else if (xwl_screen->rootless && hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP) {
313                 glamor_clear_pixmap(pixmap);
314             }
315         }
316     }
317 
318     if (!pixmap)
319         pixmap = glamor_create_pixmap(screen, width, height, depth, hint);
320 
321     return pixmap;
322 }
323 
324 static Bool
xwl_glamor_gbm_destroy_pixmap(PixmapPtr pixmap)325 xwl_glamor_gbm_destroy_pixmap(PixmapPtr pixmap)
326 {
327     struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
328     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
329 
330     if (xwl_pixmap && pixmap->refcnt == 1) {
331         xwl_pixmap_del_buffer_release_cb(pixmap);
332         if (xwl_pixmap->buffer)
333             wl_buffer_destroy(xwl_pixmap->buffer);
334 
335         eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image);
336         if (xwl_pixmap->bo)
337            gbm_bo_destroy(xwl_pixmap->bo);
338         free(xwl_pixmap);
339     }
340 
341     return glamor_destroy_pixmap(pixmap);
342 }
343 
344 static const struct wl_buffer_listener xwl_glamor_gbm_buffer_listener = {
345     xwl_pixmap_buffer_release_cb,
346 };
347 
348 static struct wl_buffer *
xwl_glamor_gbm_get_wl_buffer_for_pixmap(PixmapPtr pixmap)349 xwl_glamor_gbm_get_wl_buffer_for_pixmap(PixmapPtr pixmap)
350 {
351     struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
352     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
353     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
354     unsigned short width = pixmap->drawable.width;
355     unsigned short height = pixmap->drawable.height;
356     uint32_t format;
357     int prime_fd;
358     int num_planes;
359     uint32_t strides[4];
360     uint32_t offsets[4];
361     uint64_t modifier;
362     int i;
363 
364     if (xwl_pixmap == NULL)
365        return NULL;
366 
367     if (xwl_pixmap->buffer) {
368         /* Buffer already exists. */
369         return xwl_pixmap->buffer;
370     }
371 
372     if (!xwl_pixmap->bo)
373        return NULL;
374 
375     format = wl_drm_format_for_depth(pixmap->drawable.depth);
376 
377     prime_fd = gbm_bo_get_fd(xwl_pixmap->bo);
378     if (prime_fd == -1)
379         return NULL;
380 
381 #ifdef GBM_BO_WITH_MODIFIERS
382     num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
383     modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
384     for (i = 0; i < num_planes; i++) {
385         strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
386         offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
387     }
388 #else
389     num_planes = 1;
390     modifier = DRM_FORMAT_MOD_INVALID;
391     strides[0] = gbm_bo_get_stride(xwl_pixmap->bo);
392     offsets[0] = 0;
393 #endif
394 
395     if (xwl_screen->dmabuf &&
396         xwl_glamor_is_modifier_supported(xwl_screen, format, modifier)) {
397         struct zwp_linux_buffer_params_v1 *params;
398 
399         params = zwp_linux_dmabuf_v1_create_params(xwl_screen->dmabuf);
400         for (i = 0; i < num_planes; i++) {
401             zwp_linux_buffer_params_v1_add(params, prime_fd, i,
402                                            offsets[i], strides[i],
403                                            modifier >> 32, modifier & 0xffffffff);
404         }
405 
406         xwl_pixmap->buffer =
407            zwp_linux_buffer_params_v1_create_immed(params, width, height,
408                                                    format, 0);
409         zwp_linux_buffer_params_v1_destroy(params);
410     } else if (num_planes == 1) {
411         xwl_pixmap->buffer =
412             wl_drm_create_prime_buffer(xwl_gbm->drm, prime_fd, width, height,
413                                        format,
414                                        0, gbm_bo_get_stride(xwl_pixmap->bo),
415                                        0, 0,
416                                        0, 0);
417     }
418 
419     close(prime_fd);
420 
421     /* Add our listener now */
422     wl_buffer_add_listener(xwl_pixmap->buffer,
423                            &xwl_glamor_gbm_buffer_listener, pixmap);
424 
425     return xwl_pixmap->buffer;
426 }
427 
428 static void
xwl_glamor_gbm_cleanup(struct xwl_screen * xwl_screen)429 xwl_glamor_gbm_cleanup(struct xwl_screen *xwl_screen)
430 {
431     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
432 
433     if (xwl_gbm->device_name)
434         free(xwl_gbm->device_name);
435     if (xwl_gbm->drm_fd)
436         close(xwl_gbm->drm_fd);
437     if (xwl_gbm->drm)
438         wl_drm_destroy(xwl_gbm->drm);
439     if (xwl_gbm->gbm)
440         gbm_device_destroy(xwl_gbm->gbm);
441 
442     free(xwl_gbm);
443 }
444 
445 struct xwl_auth_state {
446     int fd;
447     ClientPtr client;
448     struct wl_callback *callback;
449 };
450 
451 static void
free_xwl_auth_state(ClientPtr pClient,struct xwl_auth_state * state)452 free_xwl_auth_state(ClientPtr pClient, struct xwl_auth_state *state)
453 {
454     dixSetPrivate(&pClient->devPrivates, &xwl_auth_state_private_key, NULL);
455     if (state) {
456         wl_callback_destroy(state->callback);
457         free(state);
458     }
459 }
460 
461 static void
xwl_auth_state_client_callback(CallbackListPtr * pcbl,void * unused,void * data)462 xwl_auth_state_client_callback(CallbackListPtr *pcbl, void *unused, void *data)
463 {
464     NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
465     ClientPtr pClient = clientinfo->client;
466     struct xwl_auth_state *state;
467 
468     switch (pClient->clientState) {
469     case ClientStateGone:
470     case ClientStateRetained:
471         state = dixLookupPrivate(&pClient->devPrivates,
472                                  &xwl_auth_state_private_key);
473         free_xwl_auth_state(pClient, state);
474         break;
475     default:
476         break;
477     }
478 }
479 
480 static void
sync_callback(void * data,struct wl_callback * callback,uint32_t serial)481 sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
482 {
483     struct xwl_auth_state *state = data;
484     ClientPtr client = state->client;
485 
486     /* if the client is gone, the callback is cancelled so it's safe to
487      * assume the client is still in ClientStateRunning at this point...
488      */
489     dri3_send_open_reply(client, state->fd);
490     AttendClient(client);
491     free_xwl_auth_state(client, state);
492 }
493 
494 static const struct wl_callback_listener sync_listener = {
495    sync_callback
496 };
497 
498 static int
xwl_dri3_open_client(ClientPtr client,ScreenPtr screen,RRProviderPtr provider,int * pfd)499 xwl_dri3_open_client(ClientPtr client,
500                      ScreenPtr screen,
501                      RRProviderPtr provider,
502                      int *pfd)
503 {
504     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
505     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
506     struct xwl_auth_state *state;
507     drm_magic_t magic;
508     int fd;
509 
510     fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
511     if (fd < 0)
512         return BadAlloc;
513     if (xwl_gbm->fd_render_node) {
514         *pfd = fd;
515         return Success;
516     }
517 
518     state = malloc(sizeof *state);
519     if (state == NULL) {
520         close(fd);
521         return BadAlloc;
522     }
523 
524     state->client = client;
525     state->fd = fd;
526 
527     if (drmGetMagic(state->fd, &magic) < 0) {
528         close(state->fd);
529         free(state);
530         return BadMatch;
531     }
532 
533     wl_drm_authenticate(xwl_gbm->drm, magic);
534     state->callback = wl_display_sync(xwl_screen->display);
535     wl_callback_add_listener(state->callback, &sync_listener, state);
536     dixSetPrivate(&client->devPrivates, &xwl_auth_state_private_key, state);
537 
538     IgnoreClient(client);
539 
540     return Success;
541 }
542 
543 _X_EXPORT PixmapPtr
glamor_pixmap_from_fds(ScreenPtr screen,CARD8 num_fds,const int * fds,CARD16 width,CARD16 height,const CARD32 * strides,const CARD32 * offsets,CARD8 depth,CARD8 bpp,uint64_t modifier)544 glamor_pixmap_from_fds(ScreenPtr screen, CARD8 num_fds, const int *fds,
545                        CARD16 width, CARD16 height,
546                        const CARD32 *strides, const CARD32 *offsets,
547                        CARD8 depth, CARD8 bpp, uint64_t modifier)
548 {
549     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
550     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
551     struct gbm_bo *bo = NULL;
552     PixmapPtr pixmap;
553     int i;
554 
555     if (width == 0 || height == 0 || num_fds == 0 ||
556         depth < 15 || bpp != BitsPerPixel(depth) ||
557         strides[0] < width * bpp / 8)
558        goto error;
559 
560     if (xwl_gbm->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) {
561 #ifdef GBM_BO_WITH_MODIFIERS
562        struct gbm_import_fd_modifier_data data;
563 
564        data.width = width;
565        data.height = height;
566        data.num_fds = num_fds;
567        data.format = gbm_format_for_depth(depth);
568        data.modifier = modifier;
569        for (i = 0; i < num_fds; i++) {
570           data.fds[i] = fds[i];
571           data.strides[i] = strides[i];
572           data.offsets[i] = offsets[i];
573        }
574        bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data,
575                           GBM_BO_USE_RENDERING);
576 #endif
577     } else if (num_fds == 1) {
578        struct gbm_import_fd_data data;
579 
580        data.fd = fds[0];
581        data.width = width;
582        data.height = height;
583        data.stride = strides[0];
584        data.format = gbm_format_for_depth(depth);
585        bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD, &data,
586                           GBM_BO_USE_RENDERING);
587     } else {
588        goto error;
589     }
590 
591     if (bo == NULL)
592        goto error;
593 
594     pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth);
595     if (pixmap == NULL) {
596        gbm_bo_destroy(bo);
597        goto error;
598     }
599 
600     return pixmap;
601 
602 error:
603     return NULL;
604 }
605 
606 _X_EXPORT int
glamor_egl_fds_from_pixmap(ScreenPtr screen,PixmapPtr pixmap,int * fds,uint32_t * strides,uint32_t * offsets,uint64_t * modifier)607 glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
608                            uint32_t *strides, uint32_t *offsets,
609                            uint64_t *modifier)
610 {
611     struct xwl_pixmap *xwl_pixmap;
612 #ifdef GBM_BO_WITH_MODIFIERS
613     uint32_t num_fds;
614     int i;
615 #endif
616 
617     xwl_pixmap = xwl_pixmap_get(pixmap);
618 
619     if (xwl_pixmap == NULL)
620        return 0;
621 
622     if (!xwl_pixmap->bo)
623        return 0;
624 
625 #ifdef GBM_BO_WITH_MODIFIERS
626     num_fds = gbm_bo_get_plane_count(xwl_pixmap->bo);
627     *modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
628 
629     for (i = 0; i < num_fds; i++) {
630         fds[i] = gbm_bo_get_fd(xwl_pixmap->bo);
631         strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
632         offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
633     }
634 
635     return num_fds;
636 #else
637     *modifier = DRM_FORMAT_MOD_INVALID;
638     fds[0] = gbm_bo_get_fd(xwl_pixmap->bo);
639     strides[0] = gbm_bo_get_stride(xwl_pixmap->bo);
640     offsets[0] = 0;
641     return 1;
642 #endif
643 }
644 
645 /* Not actually used, just defined here so there's something for
646  * _glamor_egl_fds_from_pixmap() to link against
647  */
648 _X_EXPORT int
glamor_egl_fd_from_pixmap(ScreenPtr screen,PixmapPtr pixmap,CARD16 * stride,CARD32 * size)649 glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
650                           CARD16 *stride, CARD32 *size)
651 {
652     return -1;
653 }
654 
655 static const dri3_screen_info_rec xwl_dri3_info = {
656     .version = 2,
657     .open = NULL,
658     .pixmap_from_fds = glamor_pixmap_from_fds,
659     .fds_from_pixmap = glamor_fds_from_pixmap,
660     .open_client = xwl_dri3_open_client,
661     .get_formats = xwl_glamor_get_formats,
662     .get_modifiers = xwl_glamor_get_modifiers,
663     .get_drawable_modifiers = glamor_get_drawable_modifiers,
664 };
665 
666 static const char *
get_render_node_path_for_device(const drmDevicePtr drm_device,const char * device_path)667 get_render_node_path_for_device(const drmDevicePtr drm_device,
668                                 const char *device_path)
669 {
670     char *render_node_path = NULL;
671     char device_found = 0;
672     int i;
673 
674     for (i = 0; i < DRM_NODE_MAX; i++) {
675         if ((drm_device->available_nodes & (1 << i)) == 0)
676            continue;
677 
678         if (!strcmp (device_path, drm_device->nodes[i]))
679             device_found = 1;
680 
681         if (is_device_path_render_node(drm_device->nodes[i]))
682             render_node_path = drm_device->nodes[i];
683 
684         if (device_found && render_node_path)
685             return render_node_path;
686     }
687 
688     return NULL;
689 }
690 
691 static char *
get_render_node_path(const char * device_path)692 get_render_node_path(const char *device_path)
693 {
694     drmDevicePtr *devices = NULL;
695     char *render_node_path = NULL;
696     int i, n_devices, max_devices;
697 
698     max_devices = drmGetDevices2(0, NULL, 0);
699     if (max_devices <= 0)
700         goto out;
701 
702     devices = calloc(max_devices, sizeof(drmDevicePtr));
703     if (!devices)
704         goto out;
705 
706     n_devices = drmGetDevices2(0, devices, max_devices);
707     if (n_devices < 0)
708         goto out;
709 
710     for (i = 0; i < n_devices; i++) {
711        const char *node_path = get_render_node_path_for_device(devices[i],
712                                                                device_path);
713        if (node_path) {
714            render_node_path = strdup(node_path);
715            break;
716        }
717     }
718 
719 out:
720     free(devices);
721     return render_node_path;
722 }
723 
724 static void
xwl_drm_handle_device(void * data,struct wl_drm * drm,const char * device)725 xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device)
726 {
727    struct xwl_screen *xwl_screen = data;
728    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
729    drm_magic_t magic;
730    char *render_node_path = NULL;
731 
732    if (!is_device_path_render_node(device))
733        render_node_path = get_render_node_path(device);
734 
735    if (render_node_path)
736        xwl_gbm->device_name = render_node_path;
737    else
738        xwl_gbm->device_name = strdup(device);
739 
740    if (!xwl_gbm->device_name) {
741        xwl_glamor_gbm_cleanup(xwl_screen);
742        return;
743    }
744 
745    xwl_gbm->drm_fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
746    if (xwl_gbm->drm_fd == -1) {
747        ErrorF("wayland-egl: could not open %s (%s)\n",
748               xwl_gbm->device_name, strerror(errno));
749        xwl_glamor_gbm_cleanup(xwl_screen);
750        return;
751    }
752 
753    if (drmGetNodeTypeFromFd(xwl_gbm->drm_fd) == DRM_NODE_RENDER) {
754        xwl_gbm->fd_render_node = 1;
755        xwl_screen->expecting_event--;
756    } else {
757        drmGetMagic(xwl_gbm->drm_fd, &magic);
758        wl_drm_authenticate(xwl_gbm->drm, magic);
759    }
760 }
761 
762 static void
xwl_drm_handle_format(void * data,struct wl_drm * drm,uint32_t format)763 xwl_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
764 {
765 }
766 
767 static void
xwl_drm_handle_authenticated(void * data,struct wl_drm * drm)768 xwl_drm_handle_authenticated(void *data, struct wl_drm *drm)
769 {
770     struct xwl_screen *xwl_screen = data;
771     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
772 
773     xwl_gbm->drm_authenticated = TRUE;
774     xwl_screen->expecting_event--;
775 }
776 
777 static void
xwl_drm_handle_capabilities(void * data,struct wl_drm * drm,uint32_t value)778 xwl_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value)
779 {
780     xwl_gbm_get(data)->capabilities = value;
781 }
782 
783 static const struct wl_drm_listener xwl_drm_listener = {
784     xwl_drm_handle_device,
785     xwl_drm_handle_format,
786     xwl_drm_handle_authenticated,
787     xwl_drm_handle_capabilities
788 };
789 
790 Bool
xwl_screen_set_drm_interface(struct xwl_screen * xwl_screen,uint32_t id,uint32_t version)791 xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
792                              uint32_t id, uint32_t version)
793 {
794     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
795 
796     if (version < 2)
797         return FALSE;
798 
799     xwl_gbm->drm =
800         wl_registry_bind(xwl_screen->registry, id, &wl_drm_interface, 2);
801     wl_drm_add_listener(xwl_gbm->drm, &xwl_drm_listener, xwl_screen);
802     xwl_screen->expecting_event++;
803 
804     return TRUE;
805 }
806 
807 static Bool
xwl_glamor_gbm_init_wl_registry(struct xwl_screen * xwl_screen,struct wl_registry * wl_registry,uint32_t id,const char * name,uint32_t version)808 xwl_glamor_gbm_init_wl_registry(struct xwl_screen *xwl_screen,
809                                 struct wl_registry *wl_registry,
810                                 uint32_t id, const char *name,
811                                 uint32_t version)
812 {
813     if (strcmp(name, "wl_drm") == 0) {
814         xwl_screen_set_drm_interface(xwl_screen, id, version);
815         return TRUE;
816     } else if (strcmp(name, "zwp_linux_dmabuf_v1") == 0) {
817         xwl_screen_set_dmabuf_interface(xwl_screen, id, version);
818         return TRUE;
819     }
820 
821     /* no match */
822     return FALSE;
823 }
824 
825 static Bool
xwl_glamor_gbm_has_egl_extension(void)826 xwl_glamor_gbm_has_egl_extension(void)
827 {
828     return (epoxy_has_egl_extension(NULL, "EGL_MESA_platform_gbm") ||
829             epoxy_has_egl_extension(NULL, "EGL_KHR_platform_gbm"));
830 }
831 
832 static Bool
xwl_glamor_gbm_has_wl_interfaces(struct xwl_screen * xwl_screen)833 xwl_glamor_gbm_has_wl_interfaces(struct xwl_screen *xwl_screen)
834 {
835     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
836 
837     if (xwl_gbm->drm == NULL) {
838         LogMessageVerb(X_INFO, 3, "glamor: 'wl_drm' not supported\n");
839         return FALSE;
840     }
841 
842     return TRUE;
843 }
844 
845 static Bool
xwl_glamor_try_to_make_context_current(struct xwl_screen * xwl_screen)846 xwl_glamor_try_to_make_context_current(struct xwl_screen *xwl_screen)
847 {
848     if (xwl_screen->egl_context == EGL_NO_CONTEXT)
849         return FALSE;
850 
851     return eglMakeCurrent(xwl_screen->egl_display, EGL_NO_SURFACE,
852                           EGL_NO_SURFACE, xwl_screen->egl_context);
853 }
854 
855 static void
xwl_glamor_maybe_destroy_context(struct xwl_screen * xwl_screen)856 xwl_glamor_maybe_destroy_context(struct xwl_screen *xwl_screen)
857 {
858     if (xwl_screen->egl_context == EGL_NO_CONTEXT)
859         return;
860 
861    eglMakeCurrent(xwl_screen->egl_display, EGL_NO_SURFACE,
862                   EGL_NO_SURFACE, EGL_NO_CONTEXT);
863    eglDestroyContext(xwl_screen->egl_display, xwl_screen->egl_context);
864    xwl_screen->egl_context = EGL_NO_CONTEXT;
865 }
866 
867 static Bool
xwl_glamor_try_big_gl_api(struct xwl_screen * xwl_screen)868 xwl_glamor_try_big_gl_api(struct xwl_screen *xwl_screen)
869 {
870     static const EGLint config_attribs_core[] = {
871         EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
872         EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
873         EGL_CONTEXT_MAJOR_VERSION_KHR,
874         GLAMOR_GL_CORE_VER_MAJOR,
875         EGL_CONTEXT_MINOR_VERSION_KHR,
876         GLAMOR_GL_CORE_VER_MINOR,
877         EGL_NONE
878     };
879     int gl_version;
880 
881     eglBindAPI(EGL_OPENGL_API);
882 
883     xwl_screen->egl_context =
884         eglCreateContext(xwl_screen->egl_display, EGL_NO_CONFIG_KHR,
885                          EGL_NO_CONTEXT, config_attribs_core);
886 
887     if (xwl_screen->egl_context == EGL_NO_CONTEXT)
888         xwl_screen->egl_context =
889             eglCreateContext(xwl_screen->egl_display, EGL_NO_CONFIG_KHR,
890                              EGL_NO_CONTEXT, NULL);
891 
892     if (!xwl_glamor_try_to_make_context_current(xwl_screen)) {
893         ErrorF("Failed to make EGL context current with GL\n");
894         xwl_glamor_maybe_destroy_context(xwl_screen);
895         return FALSE;
896     }
897 
898     /* glamor needs at least GL 2.1, if the GL version is less than 2.1,
899      * drop the context we created, it's useless.
900      */
901     gl_version = epoxy_gl_version();
902     if (gl_version < 21) {
903         ErrorF("Supported GL version is not sufficient (required 21, found %i)\n",
904                gl_version);
905         xwl_glamor_maybe_destroy_context(xwl_screen);
906         return FALSE;
907     }
908 
909     return TRUE;
910 }
911 
912 static Bool
xwl_glamor_try_gles_api(struct xwl_screen * xwl_screen)913 xwl_glamor_try_gles_api(struct xwl_screen *xwl_screen)
914 {
915     const EGLint gles_attribs[] = {
916         EGL_CONTEXT_CLIENT_VERSION,
917         2,
918         EGL_NONE,
919     };
920 
921     eglBindAPI(EGL_OPENGL_ES_API);
922 
923     xwl_screen->egl_context = eglCreateContext(xwl_screen->egl_display,
924                                                EGL_NO_CONFIG_KHR,
925                                                EGL_NO_CONTEXT, gles_attribs);
926 
927     if (!xwl_glamor_try_to_make_context_current(xwl_screen)) {
928         ErrorF("Failed to make EGL context current with GLES2\n");
929         xwl_glamor_maybe_destroy_context(xwl_screen);
930         return FALSE;
931     }
932 
933     return TRUE;
934 }
935 
936 static Bool
xwl_glamor_gbm_init_egl(struct xwl_screen * xwl_screen)937 xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
938 {
939     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
940     EGLint major, minor;
941     const GLubyte *renderer;
942     const char *gbm_backend_name;
943 
944     if (!xwl_gbm->fd_render_node && !xwl_gbm->drm_authenticated) {
945         ErrorF("Failed to get wl_drm, disabling Glamor and DRI3\n");
946 	return FALSE;
947     }
948 
949     xwl_gbm->gbm = gbm_create_device(xwl_gbm->drm_fd);
950     if (!xwl_gbm->gbm) {
951         ErrorF("couldn't create gbm device\n");
952         goto error;
953     }
954 
955     xwl_screen->egl_display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA,
956                                                      xwl_gbm->gbm);
957     if (xwl_screen->egl_display == EGL_NO_DISPLAY) {
958         ErrorF("glamor_egl_get_display() failed\n");
959         goto error;
960     }
961 
962     if (!eglInitialize(xwl_screen->egl_display, &major, &minor)) {
963         ErrorF("eglInitialize() failed\n");
964         goto error;
965     }
966 
967     if (!xwl_glamor_try_big_gl_api(xwl_screen) &&
968         !xwl_glamor_try_gles_api(xwl_screen)) {
969         ErrorF("Cannot use neither GL nor GLES2\n");
970         goto error;
971     }
972 
973     renderer = glGetString(GL_RENDERER);
974     if (!renderer) {
975         ErrorF("glGetString() returned NULL, your GL is broken\n");
976         goto error;
977     }
978     if (strstr((const char *)renderer, "llvmpipe")) {
979         ErrorF("Refusing to try glamor on llvmpipe\n");
980         goto error;
981     }
982 
983     if (!epoxy_has_gl_extension("GL_OES_EGL_image")) {
984         ErrorF("GL_OES_EGL_image not available\n");
985         goto error;
986     }
987 
988     if (epoxy_has_egl_extension(xwl_screen->egl_display,
989                                 "EXT_image_dma_buf_import") &&
990         epoxy_has_egl_extension(xwl_screen->egl_display,
991                                 "EXT_image_dma_buf_import_modifiers"))
992        xwl_gbm->dmabuf_capable = TRUE;
993 
994     gbm_backend_name = gbm_device_get_backend_name(xwl_gbm->gbm);
995     /* Mesa uses "drm" as backend name, in that case, just do nothing */
996     if (gbm_backend_name && strcmp(gbm_backend_name, "drm") != 0)
997         xwl_screen->glvnd_vendor = gbm_backend_name;
998 
999     return TRUE;
1000 error:
1001     if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
1002         xwl_glamor_maybe_destroy_context(xwl_screen);
1003         eglTerminate(xwl_screen->egl_display);
1004         xwl_screen->egl_display = EGL_NO_DISPLAY;
1005     }
1006 
1007     xwl_glamor_gbm_cleanup(xwl_screen);
1008     return FALSE;
1009 }
1010 
1011 static Bool
xwl_glamor_gbm_init_screen(struct xwl_screen * xwl_screen)1012 xwl_glamor_gbm_init_screen(struct xwl_screen *xwl_screen)
1013 {
1014     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
1015 
1016     if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
1017         ErrorF("Failed to initialize dri3\n");
1018         goto error;
1019     }
1020 
1021     if (xwl_gbm->fd_render_node)
1022         goto skip_drm_auth;
1023 
1024     if (!dixRegisterPrivateKey(&xwl_auth_state_private_key, PRIVATE_CLIENT,
1025                                0)) {
1026         ErrorF("Failed to register private key\n");
1027         goto error;
1028     }
1029 
1030     if (!AddCallback(&ClientStateCallback, xwl_auth_state_client_callback,
1031                      NULL)) {
1032         ErrorF("Failed to add client state callback\n");
1033         goto error;
1034     }
1035 
1036 skip_drm_auth:
1037     xwl_screen->screen->CreatePixmap = xwl_glamor_gbm_create_pixmap;
1038     xwl_screen->screen->DestroyPixmap = xwl_glamor_gbm_destroy_pixmap;
1039 
1040     return TRUE;
1041 error:
1042     xwl_glamor_gbm_cleanup(xwl_screen);
1043     return FALSE;
1044 }
1045 
1046 void
xwl_glamor_init_gbm(struct xwl_screen * xwl_screen)1047 xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
1048 {
1049     struct xwl_gbm_private *xwl_gbm;
1050 
1051     xwl_screen->gbm_backend.is_available = FALSE;
1052 
1053     if (!xwl_glamor_gbm_has_egl_extension())
1054         return;
1055 
1056     if (!dixRegisterPrivateKey(&xwl_gbm_private_key, PRIVATE_SCREEN, 0))
1057         return;
1058 
1059     xwl_gbm = calloc(sizeof(*xwl_gbm), 1);
1060     if (!xwl_gbm) {
1061         ErrorF("glamor: Not enough memory to setup GBM, disabling\n");
1062         return;
1063     }
1064 
1065     dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key,
1066                   xwl_gbm);
1067 
1068     xwl_screen->gbm_backend.init_wl_registry = xwl_glamor_gbm_init_wl_registry;
1069     xwl_screen->gbm_backend.has_wl_interfaces = xwl_glamor_gbm_has_wl_interfaces;
1070     xwl_screen->gbm_backend.init_egl = xwl_glamor_gbm_init_egl;
1071     xwl_screen->gbm_backend.init_screen = xwl_glamor_gbm_init_screen;
1072     xwl_screen->gbm_backend.get_wl_buffer_for_pixmap = xwl_glamor_gbm_get_wl_buffer_for_pixmap;
1073     xwl_screen->gbm_backend.check_flip = NULL;
1074     xwl_screen->gbm_backend.is_available = TRUE;
1075     xwl_screen->gbm_backend.backend_flags = XWL_EGL_BACKEND_NEEDS_BUFFER_FLUSH |
1076                                             XWL_EGL_BACKEND_NEEDS_N_BUFFERING;
1077 }
1078