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