glamor_egl.c revision 25da500f
1/*
2 * Copyright © 2010 Intel Corporation.
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including
13 * the next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 *    Zhigang Gong <zhigang.gong@linux.intel.com>
27 *
28 */
29
30#include "dix-config.h"
31
32#define GLAMOR_FOR_XORG
33#include <unistd.h>
34#include <fcntl.h>
35#include <sys/ioctl.h>
36#include <errno.h>
37#include <xf86.h>
38#include <xf86Priv.h>
39#include <xf86drm.h>
40#define EGL_DISPLAY_NO_X_MESA
41
42#include <gbm.h>
43#include <drm_fourcc.h>
44
45#include "glamor_egl.h"
46
47#include "glamor.h"
48#include "glamor_priv.h"
49#include "dri3.h"
50
51struct glamor_egl_screen_private {
52    EGLDisplay display;
53    EGLContext context;
54    char *device_path;
55
56    CreateScreenResourcesProcPtr CreateScreenResources;
57    CloseScreenProcPtr CloseScreen;
58    int fd;
59    struct gbm_device *gbm;
60    int dmabuf_capable;
61
62    CloseScreenProcPtr saved_close_screen;
63    DestroyPixmapProcPtr saved_destroy_pixmap;
64    xf86FreeScreenProc *saved_free_screen;
65};
66
67int xf86GlamorEGLPrivateIndex = -1;
68
69
70static struct glamor_egl_screen_private *
71glamor_egl_get_screen_private(ScrnInfoPtr scrn)
72{
73    return (struct glamor_egl_screen_private *)
74        scrn->privates[xf86GlamorEGLPrivateIndex].ptr;
75}
76
77static void
78glamor_egl_make_current(struct glamor_context *glamor_ctx)
79{
80    /* There's only a single global dispatch table in Mesa.  EGL, GLX,
81     * and AIGLX's direct dispatch table manipulation don't talk to
82     * each other.  We need to set the context to NULL first to avoid
83     * EGL's no-op context change fast path when switching back to
84     * EGL.
85     */
86    eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
87                   EGL_NO_SURFACE, EGL_NO_CONTEXT);
88
89    if (!eglMakeCurrent(glamor_ctx->display,
90                        EGL_NO_SURFACE, EGL_NO_SURFACE,
91                        glamor_ctx->ctx)) {
92        FatalError("Failed to make EGL context current\n");
93    }
94}
95
96static int
97glamor_get_flink_name(int fd, int handle, int *name)
98{
99    struct drm_gem_flink flink;
100
101    flink.handle = handle;
102    if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) {
103
104	/*
105	 * Assume non-GEM kernels have names identical to the handle
106	 */
107	if (errno == ENODEV) {
108	    *name = handle;
109	    return TRUE;
110	} else {
111	    return FALSE;
112	}
113    }
114    *name = flink.name;
115    return TRUE;
116}
117
118static Bool
119glamor_create_texture_from_image(ScreenPtr screen,
120                                 EGLImageKHR image, GLuint * texture)
121{
122    struct glamor_screen_private *glamor_priv =
123        glamor_get_screen_private(screen);
124
125    glamor_make_current(glamor_priv);
126
127    glGenTextures(1, texture);
128    glBindTexture(GL_TEXTURE_2D, *texture);
129    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
130    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
131
132    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
133    glBindTexture(GL_TEXTURE_2D, 0);
134
135    return TRUE;
136}
137
138struct gbm_device *
139glamor_egl_get_gbm_device(ScreenPtr screen)
140{
141    struct glamor_egl_screen_private *glamor_egl =
142        glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
143    return glamor_egl->gbm;
144}
145
146Bool
147glamor_egl_create_textured_screen(ScreenPtr screen, int handle, int stride)
148{
149    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
150    PixmapPtr screen_pixmap;
151
152    screen_pixmap = screen->GetScreenPixmap(screen);
153
154    if (!glamor_egl_create_textured_pixmap(screen_pixmap, handle, stride)) {
155        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
156                   "Failed to create textured screen.");
157        return FALSE;
158    }
159    return TRUE;
160}
161
162static void
163glamor_egl_set_pixmap_image(PixmapPtr pixmap, EGLImageKHR image,
164                            Bool used_modifiers)
165{
166    struct glamor_pixmap_private *pixmap_priv =
167        glamor_get_pixmap_private(pixmap);
168    EGLImageKHR old;
169
170    old = pixmap_priv->image;
171    if (old) {
172        ScreenPtr                               screen = pixmap->drawable.pScreen;
173        ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
174        struct glamor_egl_screen_private        *glamor_egl = glamor_egl_get_screen_private(scrn);
175
176        eglDestroyImageKHR(glamor_egl->display, old);
177    }
178    pixmap_priv->image = image;
179    pixmap_priv->used_modifiers = used_modifiers;
180}
181
182Bool
183glamor_egl_create_textured_pixmap(PixmapPtr pixmap, int handle, int stride)
184{
185    ScreenPtr screen = pixmap->drawable.pScreen;
186    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
187    struct glamor_egl_screen_private *glamor_egl =
188        glamor_egl_get_screen_private(scrn);
189    int ret, fd;
190
191    /* GBM doesn't have an import path from handles, so we make a
192     * dma-buf fd from it and then go through that.
193     */
194    ret = drmPrimeHandleToFD(glamor_egl->fd, handle, O_CLOEXEC, &fd);
195    if (ret) {
196        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
197                   "Failed to make prime FD for handle: %d\n", errno);
198        return FALSE;
199    }
200
201    if (!glamor_back_pixmap_from_fd(pixmap, fd,
202                                    pixmap->drawable.width,
203                                    pixmap->drawable.height,
204                                    stride,
205                                    pixmap->drawable.depth,
206                                    pixmap->drawable.bitsPerPixel)) {
207        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
208                   "Failed to make import prime FD as pixmap: %d\n", errno);
209        close(fd);
210        return FALSE;
211    }
212
213    close(fd);
214    return TRUE;
215}
216
217Bool
218glamor_egl_create_textured_pixmap_from_gbm_bo(PixmapPtr pixmap,
219                                              struct gbm_bo *bo,
220                                              Bool used_modifiers)
221{
222    ScreenPtr screen = pixmap->drawable.pScreen;
223    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
224    struct glamor_screen_private *glamor_priv =
225        glamor_get_screen_private(screen);
226    struct glamor_egl_screen_private *glamor_egl;
227    EGLImageKHR image;
228    GLuint texture;
229    Bool ret = FALSE;
230
231    glamor_egl = glamor_egl_get_screen_private(scrn);
232
233    glamor_make_current(glamor_priv);
234
235    image = eglCreateImageKHR(glamor_egl->display,
236                              glamor_egl->context,
237                              EGL_NATIVE_PIXMAP_KHR, bo, NULL);
238    if (image == EGL_NO_IMAGE_KHR) {
239        glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY);
240        goto done;
241    }
242    glamor_create_texture_from_image(screen, image, &texture);
243    glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
244    glamor_set_pixmap_texture(pixmap, texture);
245    glamor_egl_set_pixmap_image(pixmap, image, used_modifiers);
246    ret = TRUE;
247
248 done:
249    return ret;
250}
251
252static void
253glamor_get_name_from_bo(int gbm_fd, struct gbm_bo *bo, int *name)
254{
255    union gbm_bo_handle handle;
256
257    handle = gbm_bo_get_handle(bo);
258    if (!glamor_get_flink_name(gbm_fd, handle.u32, name))
259        *name = -1;
260}
261
262static Bool
263glamor_make_pixmap_exportable(PixmapPtr pixmap, Bool modifiers_ok)
264{
265    ScreenPtr screen = pixmap->drawable.pScreen;
266    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
267    struct glamor_egl_screen_private *glamor_egl =
268        glamor_egl_get_screen_private(scrn);
269    struct glamor_pixmap_private *pixmap_priv =
270        glamor_get_pixmap_private(pixmap);
271    unsigned width = pixmap->drawable.width;
272    unsigned height = pixmap->drawable.height;
273    uint32_t format;
274    struct gbm_bo *bo = NULL;
275    Bool used_modifiers = FALSE;
276    PixmapPtr exported;
277    GCPtr scratch_gc;
278
279    if (pixmap_priv->image &&
280        (modifiers_ok || !pixmap_priv->used_modifiers))
281        return TRUE;
282
283    if (pixmap->drawable.bitsPerPixel != 32) {
284        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
285                   "Failed to make %dbpp pixmap exportable\n",
286                   pixmap->drawable.bitsPerPixel);
287        return FALSE;
288    }
289
290    if (pixmap->drawable.depth == 30)
291	format = GBM_FORMAT_ARGB2101010;
292    else
293        format = GBM_FORMAT_ARGB8888;
294
295#ifdef GBM_BO_WITH_MODIFIERS
296    if (modifiers_ok && glamor_egl->dmabuf_capable) {
297        uint32_t num_modifiers;
298        uint64_t *modifiers = NULL;
299
300        glamor_get_modifiers(screen, format, &num_modifiers, &modifiers);
301
302        bo = gbm_bo_create_with_modifiers(glamor_egl->gbm, width, height,
303                                          format, modifiers, num_modifiers);
304        if (bo)
305            used_modifiers = TRUE;
306        free(modifiers);
307    }
308#endif
309
310    if (!bo)
311    {
312        bo = gbm_bo_create(glamor_egl->gbm, width, height, format,
313#ifdef GLAMOR_HAS_GBM_LINEAR
314                (pixmap->usage_hint == CREATE_PIXMAP_USAGE_SHARED ?
315                 GBM_BO_USE_LINEAR : 0) |
316#endif
317                GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
318    }
319
320    if (!bo) {
321        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
322                   "Failed to make %dx%dx%dbpp GBM bo\n",
323                   width, height, pixmap->drawable.bitsPerPixel);
324        return FALSE;
325    }
326
327    exported = screen->CreatePixmap(screen, 0, 0, pixmap->drawable.depth, 0);
328    screen->ModifyPixmapHeader(exported, width, height, 0, 0,
329                               gbm_bo_get_stride(bo), NULL);
330    if (!glamor_egl_create_textured_pixmap_from_gbm_bo(exported, bo,
331                                                       used_modifiers)) {
332        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
333                   "Failed to make %dx%dx%dbpp pixmap from GBM bo\n",
334                   width, height, pixmap->drawable.bitsPerPixel);
335        screen->DestroyPixmap(exported);
336        gbm_bo_destroy(bo);
337        return FALSE;
338    }
339    gbm_bo_destroy(bo);
340
341    scratch_gc = GetScratchGC(pixmap->drawable.depth, screen);
342    ValidateGC(&pixmap->drawable, scratch_gc);
343    scratch_gc->ops->CopyArea(&pixmap->drawable, &exported->drawable,
344                              scratch_gc,
345                              0, 0, width, height, 0, 0);
346    FreeScratchGC(scratch_gc);
347
348    /* Now, swap the tex/gbm/EGLImage/etc. of the exported pixmap into
349     * the original pixmap struct.
350     */
351    glamor_egl_exchange_buffers(pixmap, exported);
352
353    screen->DestroyPixmap(exported);
354
355    return TRUE;
356}
357
358static struct gbm_bo *
359glamor_gbm_bo_from_pixmap_internal(ScreenPtr screen, PixmapPtr pixmap)
360{
361    struct glamor_egl_screen_private *glamor_egl =
362        glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
363    struct glamor_pixmap_private *pixmap_priv =
364        glamor_get_pixmap_private(pixmap);
365
366    if (!pixmap_priv->image)
367        return NULL;
368
369    return gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_EGL_IMAGE,
370                         pixmap_priv->image, 0);
371}
372
373struct gbm_bo *
374glamor_gbm_bo_from_pixmap(ScreenPtr screen, PixmapPtr pixmap)
375{
376    if (!glamor_make_pixmap_exportable(pixmap, TRUE))
377        return NULL;
378
379    return glamor_gbm_bo_from_pixmap_internal(screen, pixmap);
380}
381
382int
383glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
384                           uint32_t *strides, uint32_t *offsets,
385                           uint64_t *modifier)
386{
387#ifdef GLAMOR_HAS_GBM
388    struct gbm_bo *bo;
389    int num_fds;
390#ifdef GBM_BO_WITH_MODIFIERS
391    int i;
392#endif
393
394    if (!glamor_make_pixmap_exportable(pixmap, TRUE))
395        return 0;
396
397    bo = glamor_gbm_bo_from_pixmap_internal(screen, pixmap);
398    if (!bo)
399        return 0;
400
401#ifdef GBM_BO_WITH_MODIFIERS
402    num_fds = gbm_bo_get_plane_count(bo);
403    for (i = 0; i < num_fds; i++) {
404        fds[i] = gbm_bo_get_fd(bo);
405        strides[i] = gbm_bo_get_stride_for_plane(bo, i);
406        offsets[i] = gbm_bo_get_offset(bo, i);
407    }
408    *modifier = gbm_bo_get_modifier(bo);
409#else
410    num_fds = 1;
411    fds[0] = gbm_bo_get_fd(bo);
412    strides[0] = gbm_bo_get_stride(bo);
413    offsets[0] = 0;
414    *modifier = DRM_FORMAT_MOD_INVALID;
415#endif
416
417    gbm_bo_destroy(bo);
418    return num_fds;
419#else
420    return 0;
421#endif
422}
423
424_X_EXPORT int
425glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
426                          CARD16 *stride, CARD32 *size)
427{
428#ifdef GLAMOR_HAS_GBM
429    struct gbm_bo *bo;
430    int fd;
431
432    if (!glamor_make_pixmap_exportable(pixmap, FALSE))
433        return -1;
434
435    bo = glamor_gbm_bo_from_pixmap_internal(screen, pixmap);
436    if (!bo)
437        return -1;
438
439    fd = gbm_bo_get_fd(bo);
440    *stride = gbm_bo_get_stride(bo);
441    *size = *stride * gbm_bo_get_height(bo);
442    gbm_bo_destroy(bo);
443
444    return fd;
445#else
446    return -1;
447#endif
448}
449
450int
451glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
452                               PixmapPtr pixmap,
453                               CARD16 *stride, CARD32 *size)
454{
455    struct glamor_egl_screen_private *glamor_egl;
456    struct gbm_bo *bo;
457    int fd = -1;
458
459    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
460
461    if (!glamor_make_pixmap_exportable(pixmap, FALSE))
462        goto failure;
463
464    bo = glamor_gbm_bo_from_pixmap_internal(screen, pixmap);
465    if (!bo)
466        goto failure;
467
468    pixmap->devKind = gbm_bo_get_stride(bo);
469
470    glamor_get_name_from_bo(glamor_egl->fd, bo, &fd);
471    *stride = pixmap->devKind;
472    *size = pixmap->devKind * gbm_bo_get_height(bo);
473
474    gbm_bo_destroy(bo);
475 failure:
476    return fd;
477}
478
479_X_EXPORT Bool
480glamor_back_pixmap_from_fd(PixmapPtr pixmap,
481                           int fd,
482                           CARD16 width,
483                           CARD16 height,
484                           CARD16 stride, CARD8 depth, CARD8 bpp)
485{
486    ScreenPtr screen = pixmap->drawable.pScreen;
487    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
488    struct glamor_egl_screen_private *glamor_egl;
489    struct gbm_bo *bo;
490    struct gbm_import_fd_data import_data = { 0 };
491    Bool ret;
492
493    glamor_egl = glamor_egl_get_screen_private(scrn);
494
495    if (bpp != 32 || !(depth == 24 || depth == 32 || depth == 30) || width == 0 || height == 0)
496        return FALSE;
497
498    import_data.fd = fd;
499    import_data.width = width;
500    import_data.height = height;
501    import_data.stride = stride;
502    if (depth == 30)
503        import_data.format = GBM_FORMAT_ARGB2101010;
504    else
505        import_data.format = GBM_FORMAT_ARGB8888;
506    bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_FD, &import_data, 0);
507    if (!bo)
508        return FALSE;
509
510    screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, stride, NULL);
511
512    ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo, FALSE);
513    gbm_bo_destroy(bo);
514    return ret;
515}
516
517static uint32_t
518gbm_format_for_depth(CARD8 depth)
519{
520    switch (depth) {
521    case 16:
522        return GBM_FORMAT_RGB565;
523    case 24:
524        return GBM_FORMAT_XRGB8888;
525    case 30:
526        return GBM_FORMAT_ARGB2101010;
527    default:
528        ErrorF("unexpected depth: %d\n", depth);
529    case 32:
530        return GBM_FORMAT_ARGB8888;
531    }
532}
533
534_X_EXPORT PixmapPtr
535glamor_pixmap_from_fds(ScreenPtr screen,
536                       CARD8 num_fds, const int *fds,
537                       CARD16 width, CARD16 height,
538                       const CARD32 *strides, const CARD32 *offsets,
539                       CARD8 depth, CARD8 bpp,
540                       uint64_t modifier)
541{
542    PixmapPtr pixmap;
543    struct glamor_egl_screen_private *glamor_egl;
544    Bool ret = FALSE;
545    int i;
546
547    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
548
549    pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0);
550
551#ifdef GBM_BO_WITH_MODIFIERS
552    if (glamor_egl->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) {
553        struct gbm_import_fd_modifier_data import_data = { 0 };
554        struct gbm_bo *bo;
555
556        import_data.width = width;
557        import_data.height = height;
558        import_data.num_fds = num_fds;
559        import_data.modifier = modifier;
560        for (i = 0; i < num_fds; i++) {
561            import_data.fds[i] = fds[i];
562            import_data.strides[i] = strides[i];
563            import_data.offsets[i] = offsets[i];
564        }
565        import_data.format = gbm_format_for_depth(depth);
566        bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_FD_MODIFIER, &import_data, 0);
567        if (bo) {
568            screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, strides[0], NULL);
569            ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo, TRUE);
570            gbm_bo_destroy(bo);
571        }
572    } else
573#endif
574    {
575        if (num_fds == 1) {
576            ret = glamor_back_pixmap_from_fd(pixmap, fds[0], width, height,
577                                             strides[0], depth, bpp);
578        }
579    }
580
581    if (ret == FALSE) {
582        screen->DestroyPixmap(pixmap);
583        return NULL;
584    }
585    return pixmap;
586}
587
588_X_EXPORT PixmapPtr
589glamor_pixmap_from_fd(ScreenPtr screen,
590                      int fd,
591                      CARD16 width,
592                      CARD16 height,
593                      CARD16 stride, CARD8 depth, CARD8 bpp)
594{
595    PixmapPtr pixmap;
596    Bool ret;
597
598    pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0);
599
600    ret = glamor_back_pixmap_from_fd(pixmap, fd, width, height,
601                                     stride, depth, bpp);
602
603    if (ret == FALSE) {
604        screen->DestroyPixmap(pixmap);
605        return NULL;
606    }
607    return pixmap;
608}
609
610_X_EXPORT Bool
611glamor_get_formats(ScreenPtr screen,
612                   CARD32 *num_formats, CARD32 **formats)
613{
614#ifdef GLAMOR_HAS_EGL_QUERY_DMABUF
615    struct glamor_egl_screen_private *glamor_egl;
616    EGLint num;
617
618    /* Explicitly zero the count as the caller may ignore the return value */
619    *num_formats = 0;
620
621    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
622
623    if (!glamor_egl->dmabuf_capable)
624        return TRUE;
625
626    if (!eglQueryDmaBufFormatsEXT(glamor_egl->display, 0, NULL, &num))
627        return FALSE;
628
629    if (num == 0)
630        return TRUE;
631
632    *formats = calloc(num, sizeof(CARD32));
633    if (*formats == NULL)
634        return FALSE;
635
636    if (!eglQueryDmaBufFormatsEXT(glamor_egl->display, num,
637                                  (EGLint *) *formats, &num)) {
638        free(*formats);
639        return FALSE;
640    }
641
642    *num_formats = num;
643    return TRUE;
644#else
645    *num_formats = 0;
646    return TRUE;
647#endif
648}
649
650_X_EXPORT Bool
651glamor_get_modifiers(ScreenPtr screen, uint32_t format,
652                     uint32_t *num_modifiers, uint64_t **modifiers)
653{
654#ifdef GLAMOR_HAS_EGL_QUERY_DMABUF
655    struct glamor_egl_screen_private *glamor_egl;
656    EGLint num;
657
658    /* Explicitly zero the count as the caller may ignore the return value */
659    *num_modifiers = 0;
660
661    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
662
663    if (!glamor_egl->dmabuf_capable)
664        return FALSE;
665
666    if (!eglQueryDmaBufModifiersEXT(glamor_egl->display, format, 0, NULL,
667                                    NULL, &num))
668        return FALSE;
669
670    if (num == 0)
671        return TRUE;
672
673    *modifiers = calloc(num, sizeof(uint64_t));
674    if (*modifiers == NULL)
675        return FALSE;
676
677    if (!eglQueryDmaBufModifiersEXT(glamor_egl->display, format, num,
678                                    (EGLuint64KHR *) *modifiers, NULL, &num)) {
679        free(*modifiers);
680        return FALSE;
681    }
682
683    *num_modifiers = num;
684    return TRUE;
685#else
686    *num_modifiers = 0;
687    return TRUE;
688#endif
689}
690
691static Bool
692glamor_egl_destroy_pixmap(PixmapPtr pixmap)
693{
694    ScreenPtr screen = pixmap->drawable.pScreen;
695    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
696    struct glamor_egl_screen_private *glamor_egl =
697        glamor_egl_get_screen_private(scrn);
698    Bool ret;
699
700    if (pixmap->refcnt == 1) {
701        struct glamor_pixmap_private *pixmap_priv =
702            glamor_get_pixmap_private(pixmap);
703
704        if (pixmap_priv->image)
705            eglDestroyImageKHR(glamor_egl->display, pixmap_priv->image);
706    }
707
708    screen->DestroyPixmap = glamor_egl->saved_destroy_pixmap;
709    ret = screen->DestroyPixmap(pixmap);
710    glamor_egl->saved_destroy_pixmap = screen->DestroyPixmap;
711    screen->DestroyPixmap = glamor_egl_destroy_pixmap;
712
713    return ret;
714}
715
716_X_EXPORT void
717glamor_egl_exchange_buffers(PixmapPtr front, PixmapPtr back)
718{
719    EGLImageKHR temp_img;
720    Bool temp_mod;
721    struct glamor_pixmap_private *front_priv =
722        glamor_get_pixmap_private(front);
723    struct glamor_pixmap_private *back_priv =
724        glamor_get_pixmap_private(back);
725
726    glamor_pixmap_exchange_fbos(front, back);
727
728    temp_img = back_priv->image;
729    temp_mod = back_priv->used_modifiers;
730    back_priv->image = front_priv->image;
731    back_priv->used_modifiers = front_priv->used_modifiers;
732    front_priv->image = temp_img;
733    front_priv->used_modifiers = temp_mod;
734
735    glamor_set_pixmap_type(front, GLAMOR_TEXTURE_DRM);
736    glamor_set_pixmap_type(back, GLAMOR_TEXTURE_DRM);
737}
738
739static Bool
740glamor_egl_close_screen(ScreenPtr screen)
741{
742    ScrnInfoPtr scrn;
743    struct glamor_egl_screen_private *glamor_egl;
744    struct glamor_pixmap_private *pixmap_priv;
745    PixmapPtr screen_pixmap;
746
747    scrn = xf86ScreenToScrn(screen);
748    glamor_egl = glamor_egl_get_screen_private(scrn);
749    screen_pixmap = screen->GetScreenPixmap(screen);
750    pixmap_priv = glamor_get_pixmap_private(screen_pixmap);
751
752    eglDestroyImageKHR(glamor_egl->display, pixmap_priv->image);
753    pixmap_priv->image = NULL;
754
755    screen->CloseScreen = glamor_egl->saved_close_screen;
756
757    return screen->CloseScreen(screen);
758}
759
760#ifdef DRI3
761static int
762glamor_dri3_open_client(ClientPtr client,
763                        ScreenPtr screen,
764                        RRProviderPtr provider,
765                        int *fdp)
766{
767    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
768    struct glamor_egl_screen_private *glamor_egl =
769        glamor_egl_get_screen_private(scrn);
770    int fd;
771    drm_magic_t magic;
772
773    fd = open(glamor_egl->device_path, O_RDWR|O_CLOEXEC);
774    if (fd < 0)
775        return BadAlloc;
776
777    /* Before FD passing in the X protocol with DRI3 (and increased
778     * security of rendering with per-process address spaces on the
779     * GPU), the kernel had to come up with a way to have the server
780     * decide which clients got to access the GPU, which was done by
781     * each client getting a unique (magic) number from the kernel,
782     * passing it to the server, and the server then telling the
783     * kernel which clients were authenticated for using the device.
784     *
785     * Now that we have FD passing, the server can just set up the
786     * authentication on its own and hand the prepared FD off to the
787     * client.
788     */
789    if (drmGetMagic(fd, &magic) < 0) {
790        if (errno == EACCES) {
791            /* Assume that we're on a render node, and the fd is
792             * already as authenticated as it should be.
793             */
794            *fdp = fd;
795            return Success;
796        } else {
797            close(fd);
798            return BadMatch;
799        }
800    }
801
802    if (drmAuthMagic(glamor_egl->fd, magic) < 0) {
803        close(fd);
804        return BadMatch;
805    }
806
807    *fdp = fd;
808    return Success;
809}
810
811static const dri3_screen_info_rec glamor_dri3_info = {
812    .version = 2,
813    .open_client = glamor_dri3_open_client,
814    .pixmap_from_fds = glamor_pixmap_from_fds,
815    .fd_from_pixmap = glamor_egl_fd_from_pixmap,
816    .fds_from_pixmap = glamor_egl_fds_from_pixmap,
817    .get_formats = glamor_get_formats,
818    .get_modifiers = glamor_get_modifiers,
819    .get_drawable_modifiers = glamor_get_drawable_modifiers,
820};
821#endif /* DRI3 */
822
823void
824glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
825{
826    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
827    struct glamor_egl_screen_private *glamor_egl =
828        glamor_egl_get_screen_private(scrn);
829#ifdef DRI3
830    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
831#endif
832
833    glamor_egl->saved_close_screen = screen->CloseScreen;
834    screen->CloseScreen = glamor_egl_close_screen;
835
836    glamor_egl->saved_destroy_pixmap = screen->DestroyPixmap;
837    screen->DestroyPixmap = glamor_egl_destroy_pixmap;
838
839    glamor_ctx->ctx = glamor_egl->context;
840    glamor_ctx->display = glamor_egl->display;
841
842    glamor_ctx->make_current = glamor_egl_make_current;
843
844#ifdef DRI3
845    /* Tell the core that we have the interfaces for import/export
846     * of pixmaps.
847     */
848    glamor_enable_dri3(screen);
849
850    /* If the driver wants to do its own auth dance (e.g. Xwayland
851     * on pre-3.15 kernels that don't have render nodes and thus
852     * has the wayland compositor as a master), then it needs us
853     * to stay out of the way and let it init DRI3 on its own.
854     */
855    if (!(glamor_priv->flags & GLAMOR_NO_DRI3)) {
856        /* To do DRI3 device FD generation, we need to open a new fd
857         * to the same device we were handed in originally.
858         */
859        glamor_egl->device_path = drmGetDeviceNameFromFd2(glamor_egl->fd);
860
861        if (!dri3_screen_init(screen, &glamor_dri3_info)) {
862            xf86DrvMsg(scrn->scrnIndex, X_ERROR,
863                       "Failed to initialize DRI3.\n");
864        }
865    }
866#endif
867}
868
869static void glamor_egl_cleanup(struct glamor_egl_screen_private *glamor_egl)
870{
871    if (glamor_egl->display != EGL_NO_DISPLAY) {
872        eglMakeCurrent(glamor_egl->display,
873                       EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
874        /*
875         * Force the next glamor_make_current call to update the context
876         * (on hot unplug another GPU may still be using glamor)
877         */
878        lastGLContext = NULL;
879        eglTerminate(glamor_egl->display);
880    }
881    if (glamor_egl->gbm)
882        gbm_device_destroy(glamor_egl->gbm);
883    free(glamor_egl->device_path);
884    free(glamor_egl);
885}
886
887static void
888glamor_egl_free_screen(ScrnInfoPtr scrn)
889{
890    struct glamor_egl_screen_private *glamor_egl;
891
892    glamor_egl = glamor_egl_get_screen_private(scrn);
893    if (glamor_egl != NULL) {
894        scrn->FreeScreen = glamor_egl->saved_free_screen;
895        glamor_egl_cleanup(glamor_egl);
896        scrn->FreeScreen(scrn);
897    }
898}
899
900Bool
901glamor_egl_init(ScrnInfoPtr scrn, int fd)
902{
903    struct glamor_egl_screen_private *glamor_egl;
904    const GLubyte *renderer;
905
906    glamor_egl = calloc(sizeof(*glamor_egl), 1);
907    if (glamor_egl == NULL)
908        return FALSE;
909    if (xf86GlamorEGLPrivateIndex == -1)
910        xf86GlamorEGLPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
911
912    scrn->privates[xf86GlamorEGLPrivateIndex].ptr = glamor_egl;
913    glamor_egl->fd = fd;
914    glamor_egl->gbm = gbm_create_device(glamor_egl->fd);
915    if (glamor_egl->gbm == NULL) {
916        ErrorF("couldn't get display device\n");
917        goto error;
918    }
919
920    glamor_egl->display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA,
921                                                 glamor_egl->gbm);
922    if (!glamor_egl->display) {
923        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "eglGetDisplay() failed\n");
924        goto error;
925    }
926
927    if (!eglInitialize(glamor_egl->display, NULL, NULL)) {
928        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "eglInitialize() failed\n");
929        glamor_egl->display = EGL_NO_DISPLAY;
930        goto error;
931    }
932
933#define GLAMOR_CHECK_EGL_EXTENSION(EXT)  \
934	if (!epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT)) {  \
935		ErrorF("EGL_" #EXT " required.\n");  \
936		goto error;  \
937	}
938
939#define GLAMOR_CHECK_EGL_EXTENSIONS(EXT1, EXT2)	 \
940	if (!epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT1) &&  \
941	    !epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT2)) {  \
942		ErrorF("EGL_" #EXT1 " or EGL_" #EXT2 " required.\n");  \
943		goto error;  \
944	}
945
946    GLAMOR_CHECK_EGL_EXTENSION(KHR_surfaceless_context);
947
948    if (eglBindAPI(EGL_OPENGL_API)) {
949        static const EGLint config_attribs_core[] = {
950            EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
951            EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
952            EGL_CONTEXT_MAJOR_VERSION_KHR,
953            GLAMOR_GL_CORE_VER_MAJOR,
954            EGL_CONTEXT_MINOR_VERSION_KHR,
955            GLAMOR_GL_CORE_VER_MINOR,
956            EGL_NONE
957        };
958        static const EGLint config_attribs[] = {
959            EGL_NONE
960        };
961
962        glamor_egl->context = eglCreateContext(glamor_egl->display,
963                                               NULL, EGL_NO_CONTEXT,
964                                               config_attribs_core);
965
966        if (glamor_egl->context == EGL_NO_CONTEXT)
967            glamor_egl->context = eglCreateContext(glamor_egl->display,
968                                                   NULL, EGL_NO_CONTEXT,
969                                                   config_attribs);
970    }
971
972    if (glamor_egl->context == EGL_NO_CONTEXT) {
973        static const EGLint config_attribs[] = {
974            EGL_CONTEXT_CLIENT_VERSION, 2,
975            EGL_NONE
976        };
977        if (!eglBindAPI(EGL_OPENGL_ES_API)) {
978            xf86DrvMsg(scrn->scrnIndex, X_ERROR,
979                       "glamor: Failed to bind either GL or GLES APIs.\n");
980            goto error;
981        }
982
983        glamor_egl->context = eglCreateContext(glamor_egl->display,
984                                               NULL, EGL_NO_CONTEXT,
985                                               config_attribs);
986    }
987    if (glamor_egl->context == EGL_NO_CONTEXT) {
988        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
989                   "glamor: Failed to create GL or GLES2 contexts\n");
990        goto error;
991    }
992
993    if (!eglMakeCurrent(glamor_egl->display,
994                        EGL_NO_SURFACE, EGL_NO_SURFACE, glamor_egl->context)) {
995        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
996                   "Failed to make EGL context current\n");
997        goto error;
998    }
999
1000    renderer = glGetString(GL_RENDERER);
1001    if (!renderer) {
1002        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1003                   "glGetString() returned NULL, your GL is broken\n");
1004        goto error;
1005    }
1006    if (strstr((const char *)renderer, "llvmpipe")) {
1007        xf86DrvMsg(scrn->scrnIndex, X_INFO,
1008                   "Refusing to try glamor on llvmpipe\n");
1009        goto error;
1010    }
1011
1012    /*
1013     * Force the next glamor_make_current call to set the right context
1014     * (in case of multiple GPUs using glamor)
1015     */
1016    lastGLContext = NULL;
1017
1018    if (!epoxy_has_gl_extension("GL_OES_EGL_image")) {
1019        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1020                   "glamor acceleration requires GL_OES_EGL_image\n");
1021        goto error;
1022    }
1023
1024    xf86DrvMsg(scrn->scrnIndex, X_INFO, "glamor X acceleration enabled on %s\n",
1025               renderer);
1026
1027#ifdef GBM_BO_WITH_MODIFIERS
1028    if (epoxy_has_egl_extension(glamor_egl->display,
1029                                "EGL_EXT_image_dma_buf_import") &&
1030        epoxy_has_egl_extension(glamor_egl->display,
1031                                "EGL_EXT_image_dma_buf_import_modifiers")) {
1032       if (xf86Info.debug != NULL)
1033           glamor_egl->dmabuf_capable = !!strstr(xf86Info.debug,
1034                                                "dmabuf_capable");
1035        else
1036            glamor_egl->dmabuf_capable = FALSE;
1037    }
1038#endif
1039
1040    glamor_egl->saved_free_screen = scrn->FreeScreen;
1041    scrn->FreeScreen = glamor_egl_free_screen;
1042    return TRUE;
1043
1044error:
1045    glamor_egl_cleanup(glamor_egl);
1046    return FALSE;
1047}
1048
1049/** Stub to retain compatibility with pre-server-1.16 ABI. */
1050Bool
1051glamor_egl_init_textured_pixmap(ScreenPtr screen)
1052{
1053    return TRUE;
1054}
1055