• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2  *
3  * Copyright (C) 2014 Red Hat Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  **************************************************************************/
24 /* create our own EGL offscreen rendering context via gbm and rendernodes */
25 
26 
27 /* if we are using EGL and rendernodes then we talk via file descriptors to the remote
28    node */
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #define EGL_EGLEXT_PROTOTYPES
34 #include <dirent.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <stdbool.h>
42 #include <epoxy/egl.h>
43 #include <gbm.h>
44 #include <xf86drm.h>
45 #include "virglrenderer.h"
46 #include "virgl_egl.h"
47 
48 #include "virgl_hw.h"
49 struct virgl_egl {
50    int fd;
51    struct gbm_device *gbm_dev;
52    EGLDisplay egl_display;
53    EGLConfig egl_conf;
54    EGLContext egl_ctx;
55    bool have_mesa_drm_image;
56    bool have_mesa_dma_buf_img_export;
57 };
58 
egl_rendernode_open(void)59 static int egl_rendernode_open(void)
60 {
61    DIR *dir;
62    struct dirent *e;
63    int r, fd;
64    char *p;
65    dir = opendir("/dev/dri");
66    if (!dir)
67       return -1;
68 
69    fd = -1;
70    while ((e = readdir(dir))) {
71       if (e->d_type != DT_CHR)
72          continue;
73 
74       if (strncmp(e->d_name, "renderD", 7))
75          continue;
76 
77       r = asprintf(&p, "/dev/dri/%s", e->d_name);
78       if (r < 0)
79          return -1;
80 
81       r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
82       if (r < 0){
83          free(p);
84          continue;
85       }
86       fd = r;
87       free(p);
88       break;
89    }
90 
91    closedir(dir);
92    if (fd < 0)
93       return -1;
94    return fd;
95 }
96 
virgl_egl_has_extension_in_string(const char * haystack,const char * needle)97 static bool virgl_egl_has_extension_in_string(const char *haystack, const char *needle)
98 {
99    const unsigned needle_len = strlen(needle);
100 
101    if (needle_len == 0)
102       return false;
103 
104    while (true) {
105       const char *const s = strstr(haystack, needle);
106 
107       if (s == NULL)
108          return false;
109 
110       if (s[needle_len] == ' ' || s[needle_len] == '\0') {
111          return true;
112       }
113 
114       /* strstr found an extension whose name begins with
115        * needle, but whose name is not equal to needle.
116        * Restart the search at s + needle_len so that we
117        * don't just find the same extension again and go
118        * into an infinite loop.
119        */
120       haystack = s + needle_len;
121    }
122 
123    return false;
124 }
125 
virgl_egl_init(int fd,bool surfaceless,bool gles)126 struct virgl_egl *virgl_egl_init(int fd, bool surfaceless, bool gles)
127 {
128    static EGLint conf_att[] = {
129       EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
130       EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
131       EGL_RED_SIZE, 1,
132       EGL_GREEN_SIZE, 1,
133       EGL_BLUE_SIZE, 1,
134       EGL_ALPHA_SIZE, 0,
135       EGL_NONE,
136    };
137    static const EGLint ctx_att[] = {
138       EGL_CONTEXT_CLIENT_VERSION, 2,
139       EGL_NONE
140    };
141    EGLBoolean b;
142    EGLenum api;
143    EGLint major, minor, n;
144    const char *extension_list;
145    struct virgl_egl *d;
146 
147    d = malloc(sizeof(struct virgl_egl));
148    if (!d)
149       return NULL;
150 
151    if (gles)
152       conf_att[3] = EGL_OPENGL_ES_BIT;
153 
154    if (surfaceless) {
155       conf_att[1] = EGL_PBUFFER_BIT;
156       d->fd = -1;
157       d->gbm_dev = NULL;
158    } else {
159       if (fd >= 0) {
160          d->fd = fd;
161       } else {
162          d->fd = egl_rendernode_open();
163       }
164       if (d->fd == -1)
165          goto fail;
166       d->gbm_dev = gbm_create_device(d->fd);
167       if (!d->gbm_dev)
168          goto fail;
169    }
170 
171    const char *client_extensions = eglQueryString (NULL, EGL_EXTENSIONS);
172 
173    if (strstr (client_extensions, "EGL_KHR_platform_base")) {
174       PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display =
175          (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress ("eglGetPlatformDisplay");
176 
177       if (!get_platform_display)
178         goto fail;
179 
180       if (surfaceless) {
181          d->egl_display = get_platform_display (EGL_PLATFORM_SURFACELESS_MESA,
182                                                 EGL_DEFAULT_DISPLAY, NULL);
183       } else
184          d->egl_display = get_platform_display (EGL_PLATFORM_GBM_KHR,
185                                                 (EGLNativeDisplayType)d->gbm_dev, NULL);
186    } else if (strstr (client_extensions, "EGL_EXT_platform_base")) {
187       PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display =
188          (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress ("eglGetPlatformDisplayEXT");
189 
190       if (!get_platform_display)
191         goto fail;
192 
193       if (surfaceless) {
194          d->egl_display = get_platform_display (EGL_PLATFORM_SURFACELESS_MESA,
195                                                 EGL_DEFAULT_DISPLAY, NULL);
196       } else
197          d->egl_display = get_platform_display (EGL_PLATFORM_GBM_KHR,
198                                                 (EGLNativeDisplayType)d->gbm_dev, NULL);
199    } else {
200       d->egl_display = eglGetDisplay((EGLNativeDisplayType)d->gbm_dev);
201    }
202 
203    if (!d->egl_display)
204       goto fail;
205 
206    b = eglInitialize(d->egl_display, &major, &minor);
207    if (!b)
208       goto fail;
209 
210    extension_list = eglQueryString(d->egl_display, EGL_EXTENSIONS);
211 #ifdef VIRGL_EGL_DEBUG
212    fprintf(stderr, "EGL major/minor: %d.%d\n", major, minor);
213    fprintf(stderr, "EGL version: %s\n",
214            eglQueryString(d->egl_display, EGL_VERSION));
215    fprintf(stderr, "EGL vendor: %s\n",
216            eglQueryString(d->egl_display, EGL_VENDOR));
217    fprintf(stderr, "EGL extensions: %s\n", extension_list);
218 #endif
219    /* require surfaceless context */
220    if (!virgl_egl_has_extension_in_string(extension_list, "EGL_KHR_surfaceless_context"))
221       goto fail;
222 
223    d->have_mesa_drm_image = false;
224    d->have_mesa_dma_buf_img_export = false;
225    if (virgl_egl_has_extension_in_string(extension_list, "EGL_MESA_drm_image"))
226       d->have_mesa_drm_image = true;
227 
228    if (virgl_egl_has_extension_in_string(extension_list, "EGL_MESA_image_dma_buf_export"))
229       d->have_mesa_dma_buf_img_export = true;
230 
231    if (d->have_mesa_drm_image == false && d->have_mesa_dma_buf_img_export == false) {
232       fprintf(stderr, "failed to find drm image extensions\n");
233       goto fail;
234    }
235 
236    if (gles)
237       api = EGL_OPENGL_ES_API;
238    else
239       api = EGL_OPENGL_API;
240    b = eglBindAPI(api);
241    if (!b)
242       goto fail;
243 
244    b = eglChooseConfig(d->egl_display, conf_att, &d->egl_conf,
245                        1, &n);
246 
247    if (!b || n != 1)
248       goto fail;
249 
250    d->egl_ctx = eglCreateContext(d->egl_display,
251                                  d->egl_conf,
252                                  EGL_NO_CONTEXT,
253                                  ctx_att);
254    if (!d->egl_ctx)
255       goto fail;
256 
257 
258    eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
259                   d->egl_ctx);
260    return d;
261  fail:
262    free(d);
263    return NULL;
264 }
265 
virgl_egl_destroy(struct virgl_egl * d)266 void virgl_egl_destroy(struct virgl_egl *d)
267 {
268    eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
269                   EGL_NO_CONTEXT);
270    eglDestroyContext(d->egl_display, d->egl_ctx);
271    eglTerminate(d->egl_display);
272    if (d->gbm_dev)
273       gbm_device_destroy(d->gbm_dev);
274    if (d->fd >= 0)
275       close(d->fd);
276    free(d);
277 }
278 
virgl_egl_create_context(struct virgl_egl * ve,struct virgl_gl_ctx_param * vparams)279 virgl_renderer_gl_context virgl_egl_create_context(struct virgl_egl *ve, struct virgl_gl_ctx_param *vparams)
280 {
281    EGLContext eglctx;
282    EGLint ctx_att[] = {
283       EGL_CONTEXT_CLIENT_VERSION, vparams->major_ver,
284       EGL_CONTEXT_MINOR_VERSION_KHR, vparams->minor_ver,
285       EGL_NONE
286    };
287    eglctx = eglCreateContext(ve->egl_display,
288                              ve->egl_conf,
289                              vparams->shared ? eglGetCurrentContext() : EGL_NO_CONTEXT,
290                              ctx_att);
291    return (virgl_renderer_gl_context)eglctx;
292 }
293 
virgl_egl_destroy_context(struct virgl_egl * ve,virgl_renderer_gl_context virglctx)294 void virgl_egl_destroy_context(struct virgl_egl *ve, virgl_renderer_gl_context virglctx)
295 {
296    EGLContext eglctx = (EGLContext)virglctx;
297    eglDestroyContext(ve->egl_display, eglctx);
298 }
299 
virgl_egl_make_context_current(struct virgl_egl * ve,virgl_renderer_gl_context virglctx)300 int virgl_egl_make_context_current(struct virgl_egl *ve, virgl_renderer_gl_context virglctx)
301 {
302    EGLContext eglctx = (EGLContext)virglctx;
303 
304    return eglMakeCurrent(ve->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
305                          eglctx);
306 }
307 
virgl_egl_get_current_context(UNUSED struct virgl_egl * ve)308 virgl_renderer_gl_context virgl_egl_get_current_context(UNUSED struct virgl_egl *ve)
309 {
310    EGLContext eglctx = eglGetCurrentContext();
311    return (virgl_renderer_gl_context)eglctx;
312 }
313 
virgl_egl_get_fourcc_for_texture(struct virgl_egl * ve,uint32_t tex_id,uint32_t format,int * fourcc)314 int virgl_egl_get_fourcc_for_texture(struct virgl_egl *ve, uint32_t tex_id, uint32_t format, int *fourcc)
315 {
316    int ret = EINVAL;
317 
318 #ifndef EGL_MESA_image_dma_buf_export
319    ret = 0;
320    goto fallback;
321 #else
322    EGLImageKHR image;
323    EGLBoolean b;
324 
325    if (!ve->have_mesa_dma_buf_img_export)
326       goto fallback;
327 
328    image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL);
329 
330    if (!image)
331       return EINVAL;
332 
333    b = eglExportDMABUFImageQueryMESA(ve->egl_display, image, fourcc, NULL, NULL);
334    if (!b)
335       goto out_destroy;
336    ret = 0;
337  out_destroy:
338    eglDestroyImageKHR(ve->egl_display, image);
339    return ret;
340 
341 #endif
342 
343  fallback:
344    *fourcc = virgl_egl_get_gbm_format(format);
345    return ret;
346 }
347 
virgl_egl_get_fd_for_texture2(struct virgl_egl * ve,uint32_t tex_id,int * fd,int * stride,int * offset)348 int virgl_egl_get_fd_for_texture2(struct virgl_egl *ve, uint32_t tex_id, int *fd,
349                                   int *stride, int *offset)
350 {
351    int ret = EINVAL;
352    EGLImageKHR image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(),
353                                          EGL_GL_TEXTURE_2D_KHR,
354                                          (EGLClientBuffer)(unsigned long)tex_id, NULL);
355    if (!image)
356       return EINVAL;
357    if (!ve->have_mesa_dma_buf_img_export)
358       goto out_destroy;
359 
360    if (!eglExportDMABUFImageMESA(ve->egl_display, image, fd,
361                                  stride, offset))
362       goto out_destroy;
363 
364    ret = 0;
365 
366 out_destroy:
367    eglDestroyImageKHR(ve->egl_display, image);
368    return ret;
369 }
370 
virgl_egl_get_fd_for_texture(struct virgl_egl * ve,uint32_t tex_id,int * fd)371 int virgl_egl_get_fd_for_texture(struct virgl_egl *ve, uint32_t tex_id, int *fd)
372 {
373    EGLImageKHR image;
374    EGLint stride;
375 #ifdef EGL_MESA_image_dma_buf_export
376    EGLint offset;
377 #endif
378    EGLBoolean b;
379    int ret;
380    image = eglCreateImageKHR(ve->egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL);
381 
382    if (!image)
383       return EINVAL;
384 
385    ret = EINVAL;
386    if (ve->have_mesa_dma_buf_img_export) {
387 #ifdef EGL_MESA_image_dma_buf_export
388       b = eglExportDMABUFImageMESA(ve->egl_display,
389                                    image,
390                                    fd,
391                                    &stride,
392                                    &offset);
393       if (!b)
394          goto out_destroy;
395 #else
396       goto out_destroy;
397 #endif
398    } else {
399 #ifdef EGL_MESA_drm_image
400       EGLint handle;
401       int r;
402       b = eglExportDRMImageMESA(ve->egl_display,
403                                 image,
404                                 NULL, &handle,
405                                 &stride);
406 
407       if (!b)
408 	 goto out_destroy;
409 
410       fprintf(stderr,"image exported %d %d\n", handle, stride);
411 
412       r = drmPrimeHandleToFD(ve->fd, handle, DRM_CLOEXEC, fd);
413       if (r < 0)
414 	 goto out_destroy;
415 #else
416       goto out_destroy;
417 #endif
418    }
419    ret = 0;
420  out_destroy:
421    eglDestroyImageKHR(ve->egl_display, image);
422    return ret;
423 }
424 
virgl_egl_get_gbm_format(uint32_t format)425 uint32_t virgl_egl_get_gbm_format(uint32_t format)
426 {
427    switch (format) {
428    case VIRGL_FORMAT_B8G8R8X8_UNORM:
429       return GBM_FORMAT_XRGB8888;
430    case VIRGL_FORMAT_B8G8R8A8_UNORM:
431       return GBM_FORMAT_ARGB8888;
432    default:
433       fprintf(stderr, "unknown format to convert to GBM %d\n", format);
434       return 0;
435    }
436 }
437