glamor_egl.c revision 35c4bbdf
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 <xf86drm.h>
39#define EGL_DISPLAY_NO_X_MESA
40
41#ifdef GLAMOR_HAS_GBM
42#include <gbm.h>
43#include <drm_fourcc.h>
44#endif
45
46#define MESA_EGL_NO_X11_HEADERS
47#include <epoxy/gl.h>
48#include <epoxy/egl.h>
49
50#include "glamor.h"
51#include "glamor_priv.h"
52#include "dri3.h"
53
54static const char glamor_name[] = "glamor";
55
56static void
57glamor_identify(int flags)
58{
59    xf86Msg(X_INFO, "%s: OpenGL accelerated X.org driver based.\n",
60            glamor_name);
61}
62
63struct glamor_egl_screen_private {
64    EGLDisplay display;
65    EGLContext context;
66    EGLint major, minor;
67    char *device_path;
68
69    CreateScreenResourcesProcPtr CreateScreenResources;
70    CloseScreenProcPtr CloseScreen;
71    int fd;
72    int cpp;
73#ifdef GLAMOR_HAS_GBM
74    struct gbm_device *gbm;
75#endif
76    int has_gem;
77    int gl_context_depth;
78    int dri3_capable;
79
80    CloseScreenProcPtr saved_close_screen;
81    xf86FreeScreenProc *saved_free_screen;
82};
83
84int xf86GlamorEGLPrivateIndex = -1;
85
86
87static struct glamor_egl_screen_private *
88glamor_egl_get_screen_private(ScrnInfoPtr scrn)
89{
90    return (struct glamor_egl_screen_private *)
91        scrn->privates[xf86GlamorEGLPrivateIndex].ptr;
92}
93
94static void
95glamor_egl_make_current(struct glamor_context *glamor_ctx)
96{
97    /* There's only a single global dispatch table in Mesa.  EGL, GLX,
98     * and AIGLX's direct dispatch table manipulation don't talk to
99     * each other.  We need to set the context to NULL first to avoid
100     * EGL's no-op context change fast path when switching back to
101     * EGL.
102     */
103    eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
104                   EGL_NO_SURFACE, EGL_NO_CONTEXT);
105
106    if (!eglMakeCurrent(glamor_ctx->display,
107                        EGL_NO_SURFACE, EGL_NO_SURFACE,
108                        glamor_ctx->ctx)) {
109        FatalError("Failed to make EGL context current\n");
110    }
111}
112
113static EGLImageKHR
114_glamor_egl_create_image(struct glamor_egl_screen_private *glamor_egl,
115                         int width, int height, int stride, int name, int depth)
116{
117    EGLImageKHR image;
118
119    EGLint attribs[] = {
120        EGL_WIDTH, 0,
121        EGL_HEIGHT, 0,
122        EGL_DRM_BUFFER_STRIDE_MESA, 0,
123        EGL_DRM_BUFFER_FORMAT_MESA,
124        EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
125        EGL_DRM_BUFFER_USE_MESA,
126        EGL_DRM_BUFFER_USE_SHARE_MESA | EGL_DRM_BUFFER_USE_SCANOUT_MESA,
127        EGL_NONE
128    };
129    attribs[1] = width;
130    attribs[3] = height;
131    attribs[5] = stride;
132    if (depth != 32 && depth != 24)
133        return EGL_NO_IMAGE_KHR;
134    image = eglCreateImageKHR(glamor_egl->display,
135                              glamor_egl->context,
136                              EGL_DRM_BUFFER_MESA,
137                              (void *) (uintptr_t) name,
138                              attribs);
139    if (image == EGL_NO_IMAGE_KHR)
140        return EGL_NO_IMAGE_KHR;
141
142    return image;
143}
144
145static int
146glamor_get_flink_name(int fd, int handle, int *name)
147{
148    struct drm_gem_flink flink;
149
150    flink.handle = handle;
151    if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0)
152        return FALSE;
153    *name = flink.name;
154    return TRUE;
155}
156
157static Bool
158glamor_create_texture_from_image(ScreenPtr screen,
159                                 EGLImageKHR image, GLuint * texture)
160{
161    struct glamor_screen_private *glamor_priv =
162        glamor_get_screen_private(screen);
163
164    glamor_make_current(glamor_priv);
165
166    glGenTextures(1, texture);
167    glBindTexture(GL_TEXTURE_2D, *texture);
168    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
169    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
170
171    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
172    glBindTexture(GL_TEXTURE_2D, 0);
173
174    return TRUE;
175}
176
177void *
178glamor_egl_get_gbm_device(ScreenPtr screen)
179{
180#ifdef GLAMOR_HAS_GBM
181    struct glamor_egl_screen_private *glamor_egl =
182        glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
183    return glamor_egl->gbm;
184#else
185    return NULL;
186#endif
187}
188
189unsigned int
190glamor_egl_create_argb8888_based_texture(ScreenPtr screen, int w, int h, Bool linear)
191{
192    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
193    struct glamor_egl_screen_private *glamor_egl;
194    EGLImageKHR image;
195    GLuint texture;
196
197#ifdef GLAMOR_HAS_GBM
198    struct gbm_bo *bo;
199    EGLNativePixmapType native_pixmap;
200
201    glamor_egl = glamor_egl_get_screen_private(scrn);
202    bo = gbm_bo_create(glamor_egl->gbm, w, h, GBM_FORMAT_ARGB8888,
203#ifdef GLAMOR_HAS_GBM_LINEAR
204                       (linear ? GBM_BO_USE_LINEAR : 0) |
205#endif
206                       GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
207    if (!bo)
208        return 0;
209
210    /* If the following assignment raises an error or a warning
211     * then that means EGLNativePixmapType is not struct gbm_bo *
212     * on your platform: This code won't work and you should not
213     * compile with dri3 support enabled */
214    native_pixmap = bo;
215
216    image = eglCreateImageKHR(glamor_egl->display,
217                              EGL_NO_CONTEXT,
218                              EGL_NATIVE_PIXMAP_KHR,
219                              native_pixmap, NULL);
220    gbm_bo_destroy(bo);
221    if (image == EGL_NO_IMAGE_KHR)
222        return 0;
223    glamor_create_texture_from_image(screen, image, &texture);
224    eglDestroyImageKHR(glamor_egl->display, image);
225
226    return texture;
227#else
228    return 0;                   /* this path should never happen */
229#endif
230}
231
232Bool
233glamor_egl_create_textured_screen(ScreenPtr screen, int handle, int stride)
234{
235    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
236    PixmapPtr screen_pixmap;
237
238    screen_pixmap = screen->GetScreenPixmap(screen);
239
240    if (!glamor_egl_create_textured_pixmap(screen_pixmap, handle, stride)) {
241        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
242                   "Failed to create textured screen.");
243        return FALSE;
244    }
245    glamor_set_screen_pixmap(screen_pixmap, NULL);
246    return TRUE;
247}
248
249Bool
250glamor_egl_create_textured_screen_ext(ScreenPtr screen,
251                                      int handle,
252                                      int stride, PixmapPtr *back_pixmap)
253{
254    return glamor_egl_create_textured_screen(screen, handle, stride);
255}
256
257static Bool
258glamor_egl_check_has_gem(int fd)
259{
260    struct drm_gem_flink flink;
261
262    flink.handle = 0;
263
264    ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink);
265    if (errno == ENOENT || errno == EINVAL)
266        return TRUE;
267    return FALSE;
268}
269
270static void
271glamor_egl_set_pixmap_image(PixmapPtr pixmap, EGLImageKHR image)
272{
273    struct glamor_pixmap_private *pixmap_priv =
274        glamor_get_pixmap_private(pixmap);
275    EGLImageKHR old;
276
277    old = pixmap_priv->image;
278    if (old) {
279        ScreenPtr                               screen = pixmap->drawable.pScreen;
280        ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
281        struct glamor_egl_screen_private        *glamor_egl = glamor_egl_get_screen_private(scrn);
282
283        eglDestroyImageKHR(glamor_egl->display, old);
284    }
285    pixmap_priv->image = image;
286}
287
288Bool
289glamor_egl_create_textured_pixmap(PixmapPtr pixmap, int handle, int stride)
290{
291    ScreenPtr screen = pixmap->drawable.pScreen;
292    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
293    struct glamor_screen_private *glamor_priv =
294        glamor_get_screen_private(screen);
295    struct glamor_egl_screen_private *glamor_egl;
296    EGLImageKHR image;
297    GLuint texture;
298    int name;
299    Bool ret = FALSE;
300
301    glamor_egl = glamor_egl_get_screen_private(scrn);
302
303    glamor_make_current(glamor_priv);
304    if (glamor_egl->has_gem) {
305        if (!glamor_get_flink_name(glamor_egl->fd, handle, &name)) {
306            xf86DrvMsg(scrn->scrnIndex, X_ERROR,
307                       "Couldn't flink pixmap handle\n");
308            glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY);
309            assert(0);
310            return FALSE;
311        }
312    }
313    else
314        name = handle;
315
316    image = _glamor_egl_create_image(glamor_egl,
317                                     pixmap->drawable.width,
318                                     pixmap->drawable.height,
319                                     ((stride * 8 +
320                                       7) / pixmap->drawable.bitsPerPixel),
321                                     name, pixmap->drawable.depth);
322    if (image == EGL_NO_IMAGE_KHR) {
323        glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY);
324        goto done;
325    }
326    glamor_create_texture_from_image(screen, image, &texture);
327    glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
328    glamor_set_pixmap_texture(pixmap, texture);
329    glamor_egl_set_pixmap_image(pixmap, image);
330    ret = TRUE;
331
332 done:
333    return ret;
334}
335
336Bool
337glamor_egl_create_textured_pixmap_from_gbm_bo(PixmapPtr pixmap, void *bo)
338{
339    ScreenPtr screen = pixmap->drawable.pScreen;
340    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
341    struct glamor_screen_private *glamor_priv =
342        glamor_get_screen_private(screen);
343    struct glamor_egl_screen_private *glamor_egl;
344    EGLImageKHR image;
345    GLuint texture;
346    Bool ret = FALSE;
347
348    glamor_egl = glamor_egl_get_screen_private(scrn);
349
350    glamor_make_current(glamor_priv);
351
352    image = eglCreateImageKHR(glamor_egl->display,
353                              glamor_egl->context,
354                              EGL_NATIVE_PIXMAP_KHR, bo, NULL);
355    if (image == EGL_NO_IMAGE_KHR) {
356        glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY);
357        goto done;
358    }
359    glamor_create_texture_from_image(screen, image, &texture);
360    glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
361    glamor_set_pixmap_texture(pixmap, texture);
362    glamor_egl_set_pixmap_image(pixmap, image);
363    ret = TRUE;
364
365 done:
366    return ret;
367}
368
369#ifdef GLAMOR_HAS_GBM
370int glamor_get_fd_from_bo(int gbm_fd, struct gbm_bo *bo, int *fd);
371void glamor_get_name_from_bo(int gbm_fd, struct gbm_bo *bo, int *name);
372int
373glamor_get_fd_from_bo(int gbm_fd, struct gbm_bo *bo, int *fd)
374{
375    union gbm_bo_handle handle;
376    struct drm_prime_handle args;
377
378    handle = gbm_bo_get_handle(bo);
379    args.handle = handle.u32;
380    args.flags = DRM_CLOEXEC;
381    if (ioctl(gbm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
382        return FALSE;
383    *fd = args.fd;
384    return TRUE;
385}
386
387void
388glamor_get_name_from_bo(int gbm_fd, struct gbm_bo *bo, int *name)
389{
390    union gbm_bo_handle handle;
391
392    handle = gbm_bo_get_handle(bo);
393    if (!glamor_get_flink_name(gbm_fd, handle.u32, name))
394        *name = -1;
395}
396#endif
397
398static Bool
399glamor_make_pixmap_exportable(PixmapPtr pixmap)
400{
401#ifdef GLAMOR_HAS_GBM
402    ScreenPtr screen = pixmap->drawable.pScreen;
403    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
404    struct glamor_egl_screen_private *glamor_egl =
405        glamor_egl_get_screen_private(scrn);
406    struct glamor_pixmap_private *pixmap_priv =
407        glamor_get_pixmap_private(pixmap);
408    unsigned width = pixmap->drawable.width;
409    unsigned height = pixmap->drawable.height;
410    struct gbm_bo *bo;
411    PixmapPtr exported;
412    GCPtr scratch_gc;
413
414    if (pixmap_priv->image)
415        return TRUE;
416
417    if (pixmap->drawable.bitsPerPixel != 32) {
418        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
419                   "Failed to make %dbpp pixmap exportable\n",
420                   pixmap->drawable.bitsPerPixel);
421        return FALSE;
422    }
423
424    bo = gbm_bo_create(glamor_egl->gbm, width, height,
425                       GBM_FORMAT_ARGB8888,
426#ifdef GLAMOR_HAS_GBM_LINEAR
427                       (pixmap->usage_hint == CREATE_PIXMAP_USAGE_SHARED ?
428                        GBM_BO_USE_LINEAR : 0) |
429#endif
430                       GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
431    if (!bo) {
432        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
433                   "Failed to make %dx%dx%dbpp GBM bo\n",
434                   width, height, pixmap->drawable.bitsPerPixel);
435        return FALSE;
436    }
437
438    exported = screen->CreatePixmap(screen, 0, 0, pixmap->drawable.depth, 0);
439    screen->ModifyPixmapHeader(exported, width, height, 0, 0,
440                               gbm_bo_get_stride(bo), NULL);
441    if (!glamor_egl_create_textured_pixmap_from_gbm_bo(exported, bo)) {
442        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
443                   "Failed to make %dx%dx%dbpp pixmap from GBM bo\n",
444                   width, height, pixmap->drawable.bitsPerPixel);
445        screen->DestroyPixmap(exported);
446        gbm_bo_destroy(bo);
447        return FALSE;
448    }
449    gbm_bo_destroy(bo);
450
451    scratch_gc = GetScratchGC(pixmap->drawable.depth, screen);
452    ValidateGC(&pixmap->drawable, scratch_gc);
453    scratch_gc->ops->CopyArea(&pixmap->drawable, &exported->drawable,
454                              scratch_gc,
455                              0, 0, width, height, 0, 0);
456    FreeScratchGC(scratch_gc);
457
458    /* Now, swap the tex/gbm/EGLImage/etc. of the exported pixmap into
459     * the original pixmap struct.
460     */
461    glamor_egl_exchange_buffers(pixmap, exported);
462
463    screen->DestroyPixmap(exported);
464
465    return TRUE;
466#else
467    return FALSE;
468#endif
469}
470
471void *
472glamor_gbm_bo_from_pixmap(ScreenPtr screen, PixmapPtr pixmap)
473{
474    struct glamor_egl_screen_private *glamor_egl =
475        glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
476    struct glamor_pixmap_private *pixmap_priv =
477        glamor_get_pixmap_private(pixmap);
478
479    if (!glamor_make_pixmap_exportable(pixmap))
480        return NULL;
481
482    return gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_EGL_IMAGE,
483                         pixmap_priv->image, 0);
484}
485
486int
487glamor_egl_dri3_fd_name_from_tex(ScreenPtr screen,
488                                 PixmapPtr pixmap,
489                                 unsigned int tex,
490                                 Bool want_name, CARD16 *stride, CARD32 *size)
491{
492#ifdef GLAMOR_HAS_GBM
493    struct glamor_egl_screen_private *glamor_egl;
494    struct gbm_bo *bo;
495    int fd = -1;
496
497    glamor_egl = glamor_egl_get_screen_private(xf86ScreenToScrn(screen));
498
499    bo = glamor_gbm_bo_from_pixmap(screen, pixmap);
500    if (!bo)
501        goto failure;
502
503    pixmap->devKind = gbm_bo_get_stride(bo);
504
505    if (want_name) {
506        if (glamor_egl->has_gem)
507            glamor_get_name_from_bo(glamor_egl->fd, bo, &fd);
508    }
509    else {
510        if (glamor_get_fd_from_bo(glamor_egl->fd, bo, &fd)) {
511        }
512    }
513    *stride = pixmap->devKind;
514    *size = pixmap->devKind * gbm_bo_get_height(bo);
515
516    gbm_bo_destroy(bo);
517 failure:
518    return fd;
519#else
520    return -1;
521#endif
522}
523
524_X_EXPORT Bool
525glamor_back_pixmap_from_fd(PixmapPtr pixmap,
526                           int fd,
527                           CARD16 width,
528                           CARD16 height,
529                           CARD16 stride, CARD8 depth, CARD8 bpp)
530{
531#ifdef GLAMOR_HAS_GBM
532    ScreenPtr screen = pixmap->drawable.pScreen;
533    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
534    struct glamor_egl_screen_private *glamor_egl;
535    struct gbm_bo *bo;
536    struct gbm_import_fd_data import_data = { 0 };
537    Bool ret;
538
539    glamor_egl = glamor_egl_get_screen_private(scrn);
540
541    if (!glamor_egl->dri3_capable)
542        return FALSE;
543
544    if (bpp != 32 || !(depth == 24 || depth == 32) || width == 0 || height == 0)
545        return FALSE;
546
547    import_data.fd = fd;
548    import_data.width = width;
549    import_data.height = height;
550    import_data.stride = stride;
551    import_data.format = GBM_FORMAT_ARGB8888;
552    bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_FD, &import_data, 0);
553    if (!bo)
554        return FALSE;
555
556    screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, stride, NULL);
557
558    ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo);
559    gbm_bo_destroy(bo);
560    return ret;
561#else
562    return FALSE;
563#endif
564}
565
566_X_EXPORT PixmapPtr
567glamor_pixmap_from_fd(ScreenPtr screen,
568                      int fd,
569                      CARD16 width,
570                      CARD16 height,
571                      CARD16 stride, CARD8 depth, CARD8 bpp)
572{
573#ifdef GLAMOR_HAS_GBM
574    PixmapPtr pixmap;
575    Bool ret;
576
577    pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0);
578    ret = glamor_back_pixmap_from_fd(pixmap, fd, width, height,
579                                     stride, depth, bpp);
580    if (ret == FALSE) {
581        screen->DestroyPixmap(pixmap);
582        return NULL;
583    }
584    return pixmap;
585#else
586    return NULL;
587#endif
588}
589
590void
591glamor_egl_destroy_pixmap_image(PixmapPtr pixmap)
592{
593    struct glamor_pixmap_private *pixmap_priv =
594        glamor_get_pixmap_private(pixmap);
595
596    if (pixmap_priv->image) {
597        ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen);
598        struct glamor_egl_screen_private *glamor_egl =
599            glamor_egl_get_screen_private(scrn);
600
601        eglDestroyImageKHR(glamor_egl->display, pixmap_priv->image);
602        pixmap_priv->image = NULL;
603    }
604}
605
606_X_EXPORT void
607glamor_egl_exchange_buffers(PixmapPtr front, PixmapPtr back)
608{
609    EGLImageKHR temp;
610    struct glamor_pixmap_private *front_priv =
611        glamor_get_pixmap_private(front);
612    struct glamor_pixmap_private *back_priv =
613        glamor_get_pixmap_private(back);
614
615    glamor_pixmap_exchange_fbos(front, back);
616
617    temp = back_priv->image;
618    back_priv->image = front_priv->image;
619    front_priv->image = temp;
620
621    glamor_set_pixmap_type(front, GLAMOR_TEXTURE_DRM);
622    glamor_set_pixmap_type(back, GLAMOR_TEXTURE_DRM);
623}
624
625void
626glamor_egl_destroy_textured_pixmap(PixmapPtr pixmap)
627{
628    glamor_destroy_textured_pixmap(pixmap);
629}
630
631static Bool
632glamor_egl_close_screen(ScreenPtr screen)
633{
634    ScrnInfoPtr scrn;
635    struct glamor_egl_screen_private *glamor_egl;
636    struct glamor_pixmap_private *pixmap_priv;
637    PixmapPtr screen_pixmap;
638
639    scrn = xf86ScreenToScrn(screen);
640    glamor_egl = glamor_egl_get_screen_private(scrn);
641    screen_pixmap = screen->GetScreenPixmap(screen);
642    pixmap_priv = glamor_get_pixmap_private(screen_pixmap);
643
644    eglDestroyImageKHR(glamor_egl->display, pixmap_priv->image);
645    pixmap_priv->image = NULL;
646
647    screen->CloseScreen = glamor_egl->saved_close_screen;
648
649    return screen->CloseScreen(screen);
650}
651
652#ifdef DRI3
653static int
654glamor_dri3_open_client(ClientPtr client,
655                        ScreenPtr screen,
656                        RRProviderPtr provider,
657                        int *fdp)
658{
659    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
660    struct glamor_egl_screen_private *glamor_egl =
661        glamor_egl_get_screen_private(scrn);
662    int fd;
663    drm_magic_t magic;
664
665    fd = open(glamor_egl->device_path, O_RDWR|O_CLOEXEC);
666    if (fd < 0)
667        return BadAlloc;
668
669    /* Before FD passing in the X protocol with DRI3 (and increased
670     * security of rendering with per-process address spaces on the
671     * GPU), the kernel had to come up with a way to have the server
672     * decide which clients got to access the GPU, which was done by
673     * each client getting a unique (magic) number from the kernel,
674     * passing it to the server, and the server then telling the
675     * kernel which clients were authenticated for using the device.
676     *
677     * Now that we have FD passing, the server can just set up the
678     * authentication on its own and hand the prepared FD off to the
679     * client.
680     */
681    if (drmGetMagic(fd, &magic) < 0) {
682        if (errno == EACCES) {
683            /* Assume that we're on a render node, and the fd is
684             * already as authenticated as it should be.
685             */
686            *fdp = fd;
687            return Success;
688        } else {
689            close(fd);
690            return BadMatch;
691        }
692    }
693
694    if (drmAuthMagic(glamor_egl->fd, magic) < 0) {
695        close(fd);
696        return BadMatch;
697    }
698
699    *fdp = fd;
700    return Success;
701}
702
703static dri3_screen_info_rec glamor_dri3_info = {
704    .version = 1,
705    .open_client = glamor_dri3_open_client,
706    .pixmap_from_fd = glamor_pixmap_from_fd,
707    .fd_from_pixmap = glamor_fd_from_pixmap,
708};
709#endif /* DRI3 */
710
711void
712glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
713{
714    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
715    struct glamor_egl_screen_private *glamor_egl =
716        glamor_egl_get_screen_private(scrn);
717
718    glamor_egl->saved_close_screen = screen->CloseScreen;
719    screen->CloseScreen = glamor_egl_close_screen;
720
721    glamor_ctx->ctx = glamor_egl->context;
722    glamor_ctx->display = glamor_egl->display;
723
724    glamor_ctx->make_current = glamor_egl_make_current;
725
726#ifdef DRI3
727    if (glamor_egl->dri3_capable) {
728    	glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
729        /* Tell the core that we have the interfaces for import/export
730         * of pixmaps.
731         */
732        glamor_enable_dri3(screen);
733
734        /* If the driver wants to do its own auth dance (e.g. Xwayland
735         * on pre-3.15 kernels that don't have render nodes and thus
736         * has the wayland compositor as a master), then it needs us
737         * to stay out of the way and let it init DRI3 on its own.
738         */
739        if (!(glamor_priv->flags & GLAMOR_NO_DRI3)) {
740            /* To do DRI3 device FD generation, we need to open a new fd
741             * to the same device we were handed in originally.
742             */
743            glamor_egl->device_path = drmGetDeviceNameFromFd(glamor_egl->fd);
744
745            if (!dri3_screen_init(screen, &glamor_dri3_info)) {
746                xf86DrvMsg(scrn->scrnIndex, X_ERROR,
747                           "Failed to initialize DRI3.\n");
748            }
749        }
750    }
751#endif
752}
753
754static void glamor_egl_cleanup(struct glamor_egl_screen_private *glamor_egl)
755{
756    if (glamor_egl->display != EGL_NO_DISPLAY) {
757        eglMakeCurrent(glamor_egl->display,
758                       EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
759        eglTerminate(glamor_egl->display);
760    }
761#ifdef GLAMOR_HAS_GBM
762    if (glamor_egl->gbm)
763        gbm_device_destroy(glamor_egl->gbm);
764#endif
765    free(glamor_egl->device_path);
766    free(glamor_egl);
767}
768
769static void
770glamor_egl_free_screen(ScrnInfoPtr scrn)
771{
772    struct glamor_egl_screen_private *glamor_egl;
773
774    glamor_egl = glamor_egl_get_screen_private(scrn);
775    if (glamor_egl != NULL) {
776        scrn->FreeScreen = glamor_egl->saved_free_screen;
777        glamor_egl_cleanup(glamor_egl);
778        scrn->FreeScreen(scrn);
779    }
780}
781
782Bool
783glamor_egl_init(ScrnInfoPtr scrn, int fd)
784{
785    struct glamor_egl_screen_private *glamor_egl;
786    const char *version;
787
788    EGLint config_attribs[] = {
789#ifdef GLAMOR_GLES2
790        EGL_CONTEXT_CLIENT_VERSION, 2,
791#endif
792        EGL_NONE
793    };
794    static const EGLint config_attribs_core[] = {
795        EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
796        EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
797        EGL_CONTEXT_MAJOR_VERSION_KHR,
798        GLAMOR_GL_CORE_VER_MAJOR,
799        EGL_CONTEXT_MINOR_VERSION_KHR,
800        GLAMOR_GL_CORE_VER_MINOR,
801        EGL_NONE
802    };
803
804    glamor_identify(0);
805    glamor_egl = calloc(sizeof(*glamor_egl), 1);
806    if (glamor_egl == NULL)
807        return FALSE;
808    if (xf86GlamorEGLPrivateIndex == -1)
809        xf86GlamorEGLPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
810
811    scrn->privates[xf86GlamorEGLPrivateIndex].ptr = glamor_egl;
812    glamor_egl->fd = fd;
813#ifdef GLAMOR_HAS_GBM
814    glamor_egl->gbm = gbm_create_device(glamor_egl->fd);
815    if (glamor_egl->gbm == NULL) {
816        ErrorF("couldn't get display device\n");
817        goto error;
818    }
819    glamor_egl->display = eglGetDisplay(glamor_egl->gbm);
820#else
821    glamor_egl->display = eglGetDisplay((EGLNativeDisplayType) (intptr_t) fd);
822#endif
823
824    glamor_egl->has_gem = glamor_egl_check_has_gem(fd);
825
826    if (!eglInitialize
827        (glamor_egl->display, &glamor_egl->major, &glamor_egl->minor)) {
828        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "eglInitialize() failed\n");
829        glamor_egl->display = EGL_NO_DISPLAY;
830        goto error;
831    }
832
833#ifndef GLAMOR_GLES2
834    eglBindAPI(EGL_OPENGL_API);
835#else
836    eglBindAPI(EGL_OPENGL_ES_API);
837#endif
838
839    version = eglQueryString(glamor_egl->display, EGL_VERSION);
840    xf86Msg(X_INFO, "%s: EGL version %s:\n", glamor_name, version);
841
842#define GLAMOR_CHECK_EGL_EXTENSION(EXT)  \
843	if (!epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT)) {  \
844		ErrorF("EGL_" #EXT " required.\n");  \
845		goto error;  \
846	}
847
848#define GLAMOR_CHECK_EGL_EXTENSIONS(EXT1, EXT2)	 \
849	if (!epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT1) &&  \
850	    !epoxy_has_egl_extension(glamor_egl->display, "EGL_" #EXT2)) {  \
851		ErrorF("EGL_" #EXT1 " or EGL_" #EXT2 " required.\n");  \
852		goto error;  \
853	}
854
855    GLAMOR_CHECK_EGL_EXTENSION(MESA_drm_image);
856    GLAMOR_CHECK_EGL_EXTENSION(KHR_gl_renderbuffer_image);
857#ifdef GLAMOR_GLES2
858    GLAMOR_CHECK_EGL_EXTENSIONS(KHR_surfaceless_context, KHR_surfaceless_gles2);
859#else
860    GLAMOR_CHECK_EGL_EXTENSIONS(KHR_surfaceless_context,
861                                KHR_surfaceless_opengl);
862#endif
863
864#ifndef GLAMOR_GLES2
865    glamor_egl->context = eglCreateContext(glamor_egl->display,
866                                           NULL, EGL_NO_CONTEXT,
867                                           config_attribs_core);
868#else
869    glamor_egl->context = NULL;
870#endif
871    if (!glamor_egl->context) {
872        glamor_egl->context = eglCreateContext(glamor_egl->display,
873                                               NULL, EGL_NO_CONTEXT,
874                                               config_attribs);
875        if (glamor_egl->context == EGL_NO_CONTEXT) {
876            xf86DrvMsg(scrn->scrnIndex, X_ERROR, "Failed to create EGL context\n");
877            goto error;
878        }
879    }
880
881    if (!eglMakeCurrent(glamor_egl->display,
882                        EGL_NO_SURFACE, EGL_NO_SURFACE, glamor_egl->context)) {
883        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
884                   "Failed to make EGL context current\n");
885        goto error;
886    }
887#ifdef GLAMOR_HAS_GBM
888    if (epoxy_has_egl_extension(glamor_egl->display,
889                                "EGL_KHR_gl_texture_2D_image") &&
890        epoxy_has_gl_extension("GL_OES_EGL_image"))
891        glamor_egl->dri3_capable = TRUE;
892#endif
893
894    glamor_egl->saved_free_screen = scrn->FreeScreen;
895    scrn->FreeScreen = glamor_egl_free_screen;
896#ifdef GLAMOR_GLES2
897    xf86DrvMsg(scrn->scrnIndex, X_INFO, "Using GLES2.\n");
898    xf86DrvMsg(scrn->scrnIndex, X_WARNING,
899               "Glamor is using GLES2 but GLX needs GL. "
900               "Indirect GLX may not work correctly.\n");
901#endif
902    return TRUE;
903
904error:
905    glamor_egl_cleanup(glamor_egl);
906    return FALSE;
907}
908
909/** Stub to retain compatibility with pre-server-1.16 ABI. */
910Bool
911glamor_egl_init_textured_pixmap(ScreenPtr screen)
912{
913    return TRUE;
914}
915