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