glamor_egl.c revision 1b5d61b8
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
358struct gbm_bo *
359glamor_gbm_bo_from_pixmap(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
373int
374glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
375                           uint32_t *strides, uint32_t *offsets,
376                           uint64_t *modifier)
377{
378#ifdef GLAMOR_HAS_GBM
379    struct gbm_bo *bo;
380    int num_fds;
381#ifdef GBM_BO_WITH_MODIFIERS
382    int i;
383#endif
384
385    if (!glamor_make_pixmap_exportable(pixmap, TRUE))
386        return 0;
387
388    bo = glamor_gbm_bo_from_pixmap(screen, pixmap);
389    if (!bo)
390        return 0;
391
392#ifdef GBM_BO_WITH_MODIFIERS
393    num_fds = gbm_bo_get_plane_count(bo);
394    for (i = 0; i < num_fds; i++) {
395        fds[i] = gbm_bo_get_fd(bo);
396        strides[i] = gbm_bo_get_stride_for_plane(bo, i);
397        offsets[i] = gbm_bo_get_offset(bo, i);
398    }
399    *modifier = gbm_bo_get_modifier(bo);
400#else
401    num_fds = 1;
402    fds[0] = gbm_bo_get_fd(bo);
403    strides[0] = gbm_bo_get_stride(bo);
404    offsets[0] = 0;
405    *modifier = DRM_FORMAT_MOD_INVALID;
406#endif
407
408    gbm_bo_destroy(bo);
409    return num_fds;
410#else
411    return 0;
412#endif
413}
414
415_X_EXPORT int
416glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
417                          CARD16 *stride, CARD32 *size)
418{
419#ifdef GLAMOR_HAS_GBM
420    struct gbm_bo *bo;
421    int fd;
422
423    if (!glamor_make_pixmap_exportable(pixmap, FALSE))
424        return -1;
425
426    bo = glamor_gbm_bo_from_pixmap(screen, pixmap);
427    if (!bo)
428        return -1;
429
430    fd = gbm_bo_get_fd(bo);
431    *stride = gbm_bo_get_stride(bo);
432    *size = *stride * gbm_bo_get_height(bo);
433    gbm_bo_destroy(bo);
434
435    return fd;
436#else
437    return -1;
438#endif
439}
440
441int
442glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
443                               PixmapPtr pixmap,
444                               CARD16 *stride, CARD32 *size)
445{
446    struct glamor_egl_screen_private *glamor_egl;
447    struct gbm_bo *bo;
448    int fd = -1;
449
450    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
451
452    if (!glamor_make_pixmap_exportable(pixmap, FALSE))
453        goto failure;
454
455    bo = glamor_gbm_bo_from_pixmap(screen, pixmap);
456    if (!bo)
457        goto failure;
458
459    pixmap->devKind = gbm_bo_get_stride(bo);
460
461    glamor_get_name_from_bo(glamor_egl->fd, bo, &fd);
462    *stride = pixmap->devKind;
463    *size = pixmap->devKind * gbm_bo_get_height(bo);
464
465    gbm_bo_destroy(bo);
466 failure:
467    return fd;
468}
469
470_X_EXPORT Bool
471glamor_back_pixmap_from_fd(PixmapPtr pixmap,
472                           int fd,
473                           CARD16 width,
474                           CARD16 height,
475                           CARD16 stride, CARD8 depth, CARD8 bpp)
476{
477    ScreenPtr screen = pixmap->drawable.pScreen;
478    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
479    struct glamor_egl_screen_private *glamor_egl;
480    struct gbm_bo *bo;
481    struct gbm_import_fd_data import_data = { 0 };
482    Bool ret;
483
484    glamor_egl = glamor_egl_get_screen_private(scrn);
485
486    if (bpp != 32 || !(depth == 24 || depth == 32 || depth == 30) || width == 0 || height == 0)
487        return FALSE;
488
489    import_data.fd = fd;
490    import_data.width = width;
491    import_data.height = height;
492    import_data.stride = stride;
493    if (depth == 30)
494        import_data.format = GBM_FORMAT_ARGB2101010;
495    else
496        import_data.format = GBM_FORMAT_ARGB8888;
497    bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_FD, &import_data, 0);
498    if (!bo)
499        return FALSE;
500
501    screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, stride, NULL);
502
503    ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo, FALSE);
504    gbm_bo_destroy(bo);
505    return ret;
506}
507
508static uint32_t
509gbm_format_for_depth(CARD8 depth)
510{
511    switch (depth) {
512    case 16:
513        return GBM_FORMAT_RGB565;
514    case 24:
515        return GBM_FORMAT_XRGB8888;
516    case 30:
517        return GBM_FORMAT_ARGB2101010;
518    default:
519        ErrorF("unexpected depth: %d\n", depth);
520    case 32:
521        return GBM_FORMAT_ARGB8888;
522    }
523}
524
525_X_EXPORT PixmapPtr
526glamor_pixmap_from_fds(ScreenPtr screen,
527                       CARD8 num_fds, const int *fds,
528                       CARD16 width, CARD16 height,
529                       const CARD32 *strides, const CARD32 *offsets,
530                       CARD8 depth, CARD8 bpp,
531                       uint64_t modifier)
532{
533    PixmapPtr pixmap;
534    struct glamor_egl_screen_private *glamor_egl;
535    Bool ret = FALSE;
536    int i;
537
538    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
539
540    pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0);
541
542#ifdef GBM_BO_WITH_MODIFIERS
543    if (glamor_egl->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) {
544        struct gbm_import_fd_modifier_data import_data = { 0 };
545        struct gbm_bo *bo;
546
547        import_data.width = width;
548        import_data.height = height;
549        import_data.num_fds = num_fds;
550        import_data.modifier = modifier;
551        for (i = 0; i < num_fds; i++) {
552            import_data.fds[i] = fds[i];
553            import_data.strides[i] = strides[i];
554            import_data.offsets[i] = offsets[i];
555        }
556        import_data.format = gbm_format_for_depth(depth);
557        bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_FD_MODIFIER, &import_data, 0);
558        if (bo) {
559            screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, strides[0], NULL);
560            ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo, TRUE);
561            gbm_bo_destroy(bo);
562        }
563    } else
564#endif
565    {
566        if (num_fds == 1) {
567            ret = glamor_back_pixmap_from_fd(pixmap, fds[0], width, height,
568                                             strides[0], depth, bpp);
569        }
570    }
571
572    if (ret == FALSE) {
573        screen->DestroyPixmap(pixmap);
574        return NULL;
575    }
576    return pixmap;
577}
578
579_X_EXPORT PixmapPtr
580glamor_pixmap_from_fd(ScreenPtr screen,
581                      int fd,
582                      CARD16 width,
583                      CARD16 height,
584                      CARD16 stride, CARD8 depth, CARD8 bpp)
585{
586    PixmapPtr pixmap;
587    Bool ret;
588
589    pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0);
590
591    ret = glamor_back_pixmap_from_fd(pixmap, fd, width, height,
592                                     stride, depth, bpp);
593
594    if (ret == FALSE) {
595        screen->DestroyPixmap(pixmap);
596        return NULL;
597    }
598    return pixmap;
599}
600
601_X_EXPORT Bool
602glamor_get_formats(ScreenPtr screen,
603                   CARD32 *num_formats, CARD32 **formats)
604{
605#ifdef GLAMOR_HAS_EGL_QUERY_DMABUF
606    struct glamor_egl_screen_private *glamor_egl;
607    EGLint num;
608
609    /* Explicitly zero the count as the caller may ignore the return value */
610    *num_formats = 0;
611
612    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
613
614    if (!glamor_egl->dmabuf_capable)
615        return TRUE;
616
617    if (!eglQueryDmaBufFormatsEXT(glamor_egl->display, 0, NULL, &num))
618        return FALSE;
619
620    if (num == 0)
621        return TRUE;
622
623    *formats = calloc(num, sizeof(CARD32));
624    if (*formats == NULL)
625        return FALSE;
626
627    if (!eglQueryDmaBufFormatsEXT(glamor_egl->display, num,
628                                  (EGLint *) *formats, &num)) {
629        free(*formats);
630        return FALSE;
631    }
632
633    *num_formats = num;
634    return TRUE;
635#else
636    *num_formats = 0;
637    return TRUE;
638#endif
639}
640
641_X_EXPORT Bool
642glamor_get_modifiers(ScreenPtr screen, uint32_t format,
643                     uint32_t *num_modifiers, uint64_t **modifiers)
644{
645#ifdef GLAMOR_HAS_EGL_QUERY_DMABUF
646    struct glamor_egl_screen_private *glamor_egl;
647    EGLint num;
648
649    /* Explicitly zero the count as the caller may ignore the return value */
650    *num_modifiers = 0;
651
652    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
653
654    if (!glamor_egl->dmabuf_capable)
655        return FALSE;
656
657    if (!eglQueryDmaBufModifiersEXT(glamor_egl->display, format, 0, NULL,
658                                    NULL, &num))
659        return FALSE;
660
661    if (num == 0)
662        return TRUE;
663
664    *modifiers = calloc(num, sizeof(uint64_t));
665    if (*modifiers == NULL)
666        return FALSE;
667
668    if (!eglQueryDmaBufModifiersEXT(glamor_egl->display, format, num,
669                                    (EGLuint64KHR *) *modifiers, NULL, &num)) {
670        free(*modifiers);
671        return FALSE;
672    }
673
674    *num_modifiers = num;
675    return TRUE;
676#else
677    *num_modifiers = 0;
678    return TRUE;
679#endif
680}
681
682static Bool
683glamor_egl_destroy_pixmap(PixmapPtr pixmap)
684{
685    ScreenPtr screen = pixmap->drawable.pScreen;
686    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
687    struct glamor_egl_screen_private *glamor_egl =
688        glamor_egl_get_screen_private(scrn);
689    Bool ret;
690
691    if (pixmap->refcnt == 1) {
692        struct glamor_pixmap_private *pixmap_priv =
693            glamor_get_pixmap_private(pixmap);
694
695        if (pixmap_priv->image)
696            eglDestroyImageKHR(glamor_egl->display, pixmap_priv->image);
697    }
698
699    screen->DestroyPixmap = glamor_egl->saved_destroy_pixmap;
700    ret = screen->DestroyPixmap(pixmap);
701    glamor_egl->saved_destroy_pixmap = screen->DestroyPixmap;
702    screen->DestroyPixmap = glamor_egl_destroy_pixmap;
703
704    return ret;
705}
706
707_X_EXPORT void
708glamor_egl_exchange_buffers(PixmapPtr front, PixmapPtr back)
709{
710    EGLImageKHR temp_img;
711    Bool temp_mod;
712    struct glamor_pixmap_private *front_priv =
713        glamor_get_pixmap_private(front);
714    struct glamor_pixmap_private *back_priv =
715        glamor_get_pixmap_private(back);
716
717    glamor_pixmap_exchange_fbos(front, back);
718
719    temp_img = back_priv->image;
720    temp_mod = back_priv->used_modifiers;
721    back_priv->image = front_priv->image;
722    back_priv->used_modifiers = front_priv->used_modifiers;
723    front_priv->image = temp_img;
724    front_priv->used_modifiers = temp_mod;
725
726    glamor_set_pixmap_type(front, GLAMOR_TEXTURE_DRM);
727    glamor_set_pixmap_type(back, GLAMOR_TEXTURE_DRM);
728}
729
730static Bool
731glamor_egl_close_screen(ScreenPtr screen)
732{
733    ScrnInfoPtr scrn;
734    struct glamor_egl_screen_private *glamor_egl;
735    struct glamor_pixmap_private *pixmap_priv;
736    PixmapPtr screen_pixmap;
737
738    scrn = xf86ScreenToScrn(screen);
739    glamor_egl = glamor_egl_get_screen_private(scrn);
740    screen_pixmap = screen->GetScreenPixmap(screen);
741    pixmap_priv = glamor_get_pixmap_private(screen_pixmap);
742
743    eglDestroyImageKHR(glamor_egl->display, pixmap_priv->image);
744    pixmap_priv->image = NULL;
745
746    screen->CloseScreen = glamor_egl->saved_close_screen;
747
748    return screen->CloseScreen(screen);
749}
750
751#ifdef DRI3
752static int
753glamor_dri3_open_client(ClientPtr client,
754                        ScreenPtr screen,
755                        RRProviderPtr provider,
756                        int *fdp)
757{
758    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
759    struct glamor_egl_screen_private *glamor_egl =
760        glamor_egl_get_screen_private(scrn);
761    int fd;
762    drm_magic_t magic;
763
764    fd = open(glamor_egl->device_path, O_RDWR|O_CLOEXEC);
765    if (fd < 0)
766        return BadAlloc;
767
768    /* Before FD passing in the X protocol with DRI3 (and increased
769     * security of rendering with per-process address spaces on the
770     * GPU), the kernel had to come up with a way to have the server
771     * decide which clients got to access the GPU, which was done by
772     * each client getting a unique (magic) number from the kernel,
773     * passing it to the server, and the server then telling the
774     * kernel which clients were authenticated for using the device.
775     *
776     * Now that we have FD passing, the server can just set up the
777     * authentication on its own and hand the prepared FD off to the
778     * client.
779     */
780    if (drmGetMagic(fd, &magic) < 0) {
781        if (errno == EACCES) {
782            /* Assume that we're on a render node, and the fd is
783             * already as authenticated as it should be.
784             */
785            *fdp = fd;
786            return Success;
787        } else {
788            close(fd);
789            return BadMatch;
790        }
791    }
792
793    if (drmAuthMagic(glamor_egl->fd, magic) < 0) {
794        close(fd);
795        return BadMatch;
796    }
797
798    *fdp = fd;
799    return Success;
800}
801
802static const dri3_screen_info_rec glamor_dri3_info = {
803    .version = 2,
804    .open_client = glamor_dri3_open_client,
805    .pixmap_from_fds = glamor_pixmap_from_fds,
806    .fd_from_pixmap = glamor_egl_fd_from_pixmap,
807    .fds_from_pixmap = glamor_egl_fds_from_pixmap,
808    .get_formats = glamor_get_formats,
809    .get_modifiers = glamor_get_modifiers,
810    .get_drawable_modifiers = glamor_get_drawable_modifiers,
811};
812#endif /* DRI3 */
813
814void
815glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
816{
817    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
818    struct glamor_egl_screen_private *glamor_egl =
819        glamor_egl_get_screen_private(scrn);
820#ifdef DRI3
821    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
822#endif
823
824    glamor_egl->saved_close_screen = screen->CloseScreen;
825    screen->CloseScreen = glamor_egl_close_screen;
826
827    glamor_egl->saved_destroy_pixmap = screen->DestroyPixmap;
828    screen->DestroyPixmap = glamor_egl_destroy_pixmap;
829
830    glamor_ctx->ctx = glamor_egl->context;
831    glamor_ctx->display = glamor_egl->display;
832
833    glamor_ctx->make_current = glamor_egl_make_current;
834
835#ifdef DRI3
836    /* Tell the core that we have the interfaces for import/export
837     * of pixmaps.
838     */
839    glamor_enable_dri3(screen);
840
841    /* If the driver wants to do its own auth dance (e.g. Xwayland
842     * on pre-3.15 kernels that don't have render nodes and thus
843     * has the wayland compositor as a master), then it needs us
844     * to stay out of the way and let it init DRI3 on its own.
845     */
846    if (!(glamor_priv->flags & GLAMOR_NO_DRI3)) {
847        /* To do DRI3 device FD generation, we need to open a new fd
848         * to the same device we were handed in originally.
849         */
850        glamor_egl->device_path = drmGetDeviceNameFromFd2(glamor_egl->fd);
851
852        if (!dri3_screen_init(screen, &glamor_dri3_info)) {
853            xf86DrvMsg(scrn->scrnIndex, X_ERROR,
854                       "Failed to initialize DRI3.\n");
855        }
856    }
857#endif
858}
859
860static void glamor_egl_cleanup(struct glamor_egl_screen_private *glamor_egl)
861{
862    if (glamor_egl->display != EGL_NO_DISPLAY) {
863        eglMakeCurrent(glamor_egl->display,
864                       EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
865        /*
866         * Force the next glamor_make_current call to update the context
867         * (on hot unplug another GPU may still be using glamor)
868         */
869        lastGLContext = NULL;
870        eglTerminate(glamor_egl->display);
871    }
872    if (glamor_egl->gbm)
873        gbm_device_destroy(glamor_egl->gbm);
874    free(glamor_egl->device_path);
875    free(glamor_egl);
876}
877
878static void
879glamor_egl_free_screen(ScrnInfoPtr scrn)
880{
881    struct glamor_egl_screen_private *glamor_egl;
882
883    glamor_egl = glamor_egl_get_screen_private(scrn);
884    if (glamor_egl != NULL) {
885        scrn->FreeScreen = glamor_egl->saved_free_screen;
886        glamor_egl_cleanup(glamor_egl);
887        scrn->FreeScreen(scrn);
888    }
889}
890
891Bool
892glamor_egl_init(ScrnInfoPtr scrn, int fd)
893{
894    struct glamor_egl_screen_private *glamor_egl;
895    const GLubyte *renderer;
896
897    glamor_egl = calloc(sizeof(*glamor_egl), 1);
898    if (glamor_egl == NULL)
899        return FALSE;
900    if (xf86GlamorEGLPrivateIndex == -1)
901        xf86GlamorEGLPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
902
903    scrn->privates[xf86GlamorEGLPrivateIndex].ptr = glamor_egl;
904    glamor_egl->fd = fd;
905    glamor_egl->gbm = gbm_create_device(glamor_egl->fd);
906    if (glamor_egl->gbm == NULL) {
907        ErrorF("couldn't get display device\n");
908        goto error;
909    }
910
911    glamor_egl->display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA,
912                                                 glamor_egl->gbm);
913    if (!glamor_egl->display) {
914        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "eglGetDisplay() failed\n");
915        goto error;
916    }
917
918    if (!eglInitialize(glamor_egl->display, NULL, NULL)) {
919        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "eglInitialize() failed\n");
920        glamor_egl->display = EGL_NO_DISPLAY;
921        goto error;
922    }
923
924#define GLAMOR_CHECK_EGL_EXTENSION(EXT)  \
925	if (!epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT)) {  \
926		ErrorF("EGL_" #EXT " required.\n");  \
927		goto error;  \
928	}
929
930#define GLAMOR_CHECK_EGL_EXTENSIONS(EXT1, EXT2)	 \
931	if (!epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT1) &&  \
932	    !epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT2)) {  \
933		ErrorF("EGL_" #EXT1 " or EGL_" #EXT2 " required.\n");  \
934		goto error;  \
935	}
936
937    GLAMOR_CHECK_EGL_EXTENSION(KHR_surfaceless_context);
938
939    if (eglBindAPI(EGL_OPENGL_API)) {
940        static const EGLint config_attribs_core[] = {
941            EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
942            EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
943            EGL_CONTEXT_MAJOR_VERSION_KHR,
944            GLAMOR_GL_CORE_VER_MAJOR,
945            EGL_CONTEXT_MINOR_VERSION_KHR,
946            GLAMOR_GL_CORE_VER_MINOR,
947            EGL_NONE
948        };
949        static const EGLint config_attribs[] = {
950            EGL_NONE
951        };
952
953        glamor_egl->context = eglCreateContext(glamor_egl->display,
954                                               NULL, EGL_NO_CONTEXT,
955                                               config_attribs_core);
956
957        if (glamor_egl->context == EGL_NO_CONTEXT)
958            glamor_egl->context = eglCreateContext(glamor_egl->display,
959                                                   NULL, EGL_NO_CONTEXT,
960                                                   config_attribs);
961    }
962
963    if (glamor_egl->context == EGL_NO_CONTEXT) {
964        static const EGLint config_attribs[] = {
965            EGL_CONTEXT_CLIENT_VERSION, 2,
966            EGL_NONE
967        };
968        if (!eglBindAPI(EGL_OPENGL_ES_API)) {
969            xf86DrvMsg(scrn->scrnIndex, X_ERROR,
970                       "glamor: Failed to bind either GL or GLES APIs.\n");
971            goto error;
972        }
973
974        glamor_egl->context = eglCreateContext(glamor_egl->display,
975                                               NULL, EGL_NO_CONTEXT,
976                                               config_attribs);
977    }
978    if (glamor_egl->context == EGL_NO_CONTEXT) {
979        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
980                   "glamor: Failed to create GL or GLES2 contexts\n");
981        goto error;
982    }
983
984    if (!eglMakeCurrent(glamor_egl->display,
985                        EGL_NO_SURFACE, EGL_NO_SURFACE, glamor_egl->context)) {
986        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
987                   "Failed to make EGL context current\n");
988        goto error;
989    }
990
991    renderer = glGetString(GL_RENDERER);
992    if (!renderer) {
993        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
994                   "glGetString() returned NULL, your GL is broken\n");
995        goto error;
996    }
997    if (strstr((const char *)renderer, "llvmpipe")) {
998        xf86DrvMsg(scrn->scrnIndex, X_INFO,
999                   "Refusing to try glamor on llvmpipe\n");
1000        goto error;
1001    }
1002
1003    /*
1004     * Force the next glamor_make_current call to set the right context
1005     * (in case of multiple GPUs using glamor)
1006     */
1007    lastGLContext = NULL;
1008
1009    if (!epoxy_has_gl_extension("GL_OES_EGL_image")) {
1010        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1011                   "glamor acceleration requires GL_OES_EGL_image\n");
1012        goto error;
1013    }
1014
1015    xf86DrvMsg(scrn->scrnIndex, X_INFO, "glamor X acceleration enabled on %s\n",
1016               renderer);
1017
1018#ifdef GBM_BO_WITH_MODIFIERS
1019    if (epoxy_has_egl_extension(glamor_egl->display,
1020                                "EGL_EXT_image_dma_buf_import") &&
1021        epoxy_has_egl_extension(glamor_egl->display,
1022                                "EGL_EXT_image_dma_buf_import_modifiers")) {
1023       if (xf86Info.debug != NULL)
1024           glamor_egl->dmabuf_capable = !!strstr(xf86Info.debug,
1025                                                "dmabuf_capable");
1026        else
1027            glamor_egl->dmabuf_capable = FALSE;
1028    }
1029#endif
1030
1031    glamor_egl->saved_free_screen = scrn->FreeScreen;
1032    scrn->FreeScreen = glamor_egl_free_screen;
1033    return TRUE;
1034
1035error:
1036    glamor_egl_cleanup(glamor_egl);
1037    return FALSE;
1038}
1039
1040/** Stub to retain compatibility with pre-server-1.16 ABI. */
1041Bool
1042glamor_egl_init_textured_pixmap(ScreenPtr screen)
1043{
1044    return TRUE;
1045}
1046