xref: /openbmc/qemu/ui/egl-helpers.c (revision 044ca4bf45b5bc232a2d699a9e63f359b1b85df6)
1 /*
2  * Copyright (C) 2015-2016 Gerd Hoffmann <kraxel@redhat.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "qemu/osdep.h"
18 #include "qemu/drm.h"
19 #include "qemu/error-report.h"
20 #include "ui/console.h"
21 #include "ui/egl-helpers.h"
22 #include "sysemu/sysemu.h"
23 #include "qapi/error.h"
24 
25 EGLDisplay *qemu_egl_display;
26 EGLConfig qemu_egl_config;
27 DisplayGLMode qemu_egl_mode;
28 
29 /* ------------------------------------------------------------------ */
30 
31 const char *qemu_egl_get_error_string(void)
32 {
33     EGLint error = eglGetError();
34 
35     switch (error) {
36     case EGL_SUCCESS:
37         return "EGL_SUCCESS";
38     case EGL_NOT_INITIALIZED:
39         return "EGL_NOT_INITIALIZED";
40     case EGL_BAD_ACCESS:
41         return "EGL_BAD_ACCESS";
42     case EGL_BAD_ALLOC:
43         return "EGL_BAD_ALLOC";
44     case EGL_BAD_ATTRIBUTE:
45         return "EGL_BAD_ATTRIBUTE";
46     case EGL_BAD_CONTEXT:
47         return "EGL_BAD_CONTEXT";
48     case EGL_BAD_CONFIG:
49         return "EGL_BAD_CONFIG";
50     case EGL_BAD_CURRENT_SURFACE:
51         return "EGL_BAD_CURRENT_SURFACE";
52     case EGL_BAD_DISPLAY:
53         return "EGL_BAD_DISPLAY";
54     case EGL_BAD_SURFACE:
55         return "EGL_BAD_SURFACE";
56     case EGL_BAD_MATCH:
57         return "EGL_BAD_MATCH";
58     case EGL_BAD_PARAMETER:
59         return "EGL_BAD_PARAMETER";
60     case EGL_BAD_NATIVE_PIXMAP:
61         return "EGL_BAD_NATIVE_PIXMAP";
62     case EGL_BAD_NATIVE_WINDOW:
63         return "EGL_BAD_NATIVE_WINDOW";
64     case EGL_CONTEXT_LOST:
65         return "EGL_CONTEXT_LOST";
66     default:
67         return "Unknown EGL error";
68     }
69 }
70 
71 static void egl_fb_delete_texture(egl_fb *fb)
72 {
73     if (!fb->delete_texture) {
74         return;
75     }
76 
77     glDeleteTextures(1, &fb->texture);
78     fb->delete_texture = false;
79 }
80 
81 void egl_fb_destroy(egl_fb *fb)
82 {
83     if (!fb->framebuffer) {
84         return;
85     }
86 
87     egl_fb_delete_texture(fb);
88     glDeleteFramebuffers(1, &fb->framebuffer);
89 
90     fb->width = 0;
91     fb->height = 0;
92     fb->texture = 0;
93     fb->framebuffer = 0;
94 }
95 
96 void egl_fb_setup_default(egl_fb *fb, int width, int height)
97 {
98     fb->width = width;
99     fb->height = height;
100     fb->framebuffer = 0; /* default framebuffer */
101 }
102 
103 void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
104                           GLuint texture, bool delete)
105 {
106     egl_fb_delete_texture(fb);
107 
108     fb->width = width;
109     fb->height = height;
110     fb->texture = texture;
111     fb->delete_texture = delete;
112     if (!fb->framebuffer) {
113         glGenFramebuffers(1, &fb->framebuffer);
114     }
115 
116     glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb->framebuffer);
117     glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
118                               GL_TEXTURE_2D, fb->texture, 0);
119 }
120 
121 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
122 {
123     GLuint texture;
124 
125     glGenTextures(1, &texture);
126     glBindTexture(GL_TEXTURE_2D, texture);
127     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
128                  0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
129 
130     egl_fb_setup_for_tex(fb, width, height, texture, true);
131 }
132 
133 void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
134 {
135     GLuint x1 = 0;
136     GLuint y1 = 0;
137     GLuint x2, y2;
138     GLuint w = src->width;
139     GLuint h = src->height;
140 
141     glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
142     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer);
143     glViewport(0, 0, dst->width, dst->height);
144 
145     if (src->dmabuf) {
146         x1 = src->dmabuf->x;
147         y1 = src->dmabuf->y;
148         w = src->dmabuf->scanout_width;
149         h = src->dmabuf->scanout_height;
150     }
151 
152     w = (x1 + w) > src->width ? src->width - x1 : w;
153     h = (y1 + h) > src->height ? src->height - y1 : h;
154 
155     y2 = flip ? y1 : h + y1;
156     y1 = flip ? h + y1 : y1;
157     x2 = x1 + w;
158 
159     glBlitFramebuffer(x1, y1, x2, y2,
160                       0, 0, dst->width, dst->height,
161                       GL_COLOR_BUFFER_BIT, GL_LINEAR);
162 }
163 
164 void egl_fb_read(DisplaySurface *dst, egl_fb *src)
165 {
166     glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
167     glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
168     glReadPixels(0, 0, surface_width(dst), surface_height(dst),
169                  GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst));
170 }
171 
172 void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip)
173 {
174     glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
175     glViewport(0, 0, dst->width, dst->height);
176     glEnable(GL_TEXTURE_2D);
177     glBindTexture(GL_TEXTURE_2D, src->texture);
178     qemu_gl_run_texture_blit(gls, flip);
179 }
180 
181 void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
182                        int x, int y, double scale_x, double scale_y)
183 {
184     glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
185     int w = scale_x * src->width;
186     int h = scale_y * src->height;
187     if (flip) {
188         glViewport(x, y, w, h);
189     } else {
190         glViewport(x, dst->height - h - y, w, h);
191     }
192     glEnable(GL_TEXTURE_2D);
193     glBindTexture(GL_TEXTURE_2D, src->texture);
194     glEnable(GL_BLEND);
195     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
196     qemu_gl_run_texture_blit(gls, flip);
197     glDisable(GL_BLEND);
198 }
199 
200 /* ---------------------------------------------------------------------- */
201 
202 #ifdef CONFIG_GBM
203 
204 int qemu_egl_rn_fd;
205 struct gbm_device *qemu_egl_rn_gbm_dev;
206 EGLContext qemu_egl_rn_ctx;
207 
208 int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
209 {
210     qemu_egl_rn_fd = -1;
211     int rc;
212 
213     qemu_egl_rn_fd = qemu_drm_rendernode_open(rendernode);
214     if (qemu_egl_rn_fd == -1) {
215         error_report("egl: no drm render node available");
216         goto err;
217     }
218 
219     qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
220     if (!qemu_egl_rn_gbm_dev) {
221         error_report("egl: gbm_create_device failed");
222         goto err;
223     }
224 
225     rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev,
226                                 mode);
227     if (rc != 0) {
228         /* qemu_egl_init_dpy_mesa reports error */
229         goto err;
230     }
231 
232     if (!epoxy_has_egl_extension(qemu_egl_display,
233                                  "EGL_KHR_surfaceless_context")) {
234         error_report("egl: EGL_KHR_surfaceless_context not supported");
235         goto err;
236     }
237     if (!epoxy_has_egl_extension(qemu_egl_display,
238                                  "EGL_MESA_image_dma_buf_export")) {
239         error_report("egl: EGL_MESA_image_dma_buf_export not supported");
240         goto err;
241     }
242 
243     qemu_egl_rn_ctx = qemu_egl_init_ctx();
244     if (!qemu_egl_rn_ctx) {
245         error_report("egl: egl_init_ctx failed");
246         goto err;
247     }
248 
249     return 0;
250 
251 err:
252     if (qemu_egl_rn_gbm_dev) {
253         gbm_device_destroy(qemu_egl_rn_gbm_dev);
254     }
255     if (qemu_egl_rn_fd != -1) {
256         close(qemu_egl_rn_fd);
257     }
258 
259     return -1;
260 }
261 
262 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
263                            EGLuint64KHR *modifier)
264 {
265     EGLImageKHR image;
266     EGLint num_planes, fd;
267 
268     image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
269                               EGL_GL_TEXTURE_2D_KHR,
270                               (EGLClientBuffer)(unsigned long)tex_id,
271                               NULL);
272     if (!image) {
273         return -1;
274     }
275 
276     eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
277                                   &num_planes, modifier);
278     if (num_planes != 1) {
279         eglDestroyImageKHR(qemu_egl_display, image);
280         return -1;
281     }
282     eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
283     eglDestroyImageKHR(qemu_egl_display, image);
284 
285     return fd;
286 }
287 
288 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
289 {
290     EGLImageKHR image = EGL_NO_IMAGE_KHR;
291     EGLint attrs[64];
292     int i = 0;
293 
294     if (dmabuf->texture != 0) {
295         return;
296     }
297 
298     attrs[i++] = EGL_WIDTH;
299     attrs[i++] = dmabuf->width;
300     attrs[i++] = EGL_HEIGHT;
301     attrs[i++] = dmabuf->height;
302     attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
303     attrs[i++] = dmabuf->fourcc;
304 
305     attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
306     attrs[i++] = dmabuf->fd;
307     attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
308     attrs[i++] = dmabuf->stride;
309     attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
310     attrs[i++] = 0;
311 #ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT
312     if (dmabuf->modifier) {
313         attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
314         attrs[i++] = (dmabuf->modifier >>  0) & 0xffffffff;
315         attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
316         attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff;
317     }
318 #endif
319     attrs[i++] = EGL_NONE;
320 
321     image = eglCreateImageKHR(qemu_egl_display,
322                               EGL_NO_CONTEXT,
323                               EGL_LINUX_DMA_BUF_EXT,
324                               NULL, attrs);
325     if (image == EGL_NO_IMAGE_KHR) {
326         error_report("eglCreateImageKHR failed");
327         return;
328     }
329 
330     glGenTextures(1, &dmabuf->texture);
331     glBindTexture(GL_TEXTURE_2D, dmabuf->texture);
332     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
333     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
334 
335     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
336     eglDestroyImageKHR(qemu_egl_display, image);
337 }
338 
339 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
340 {
341     if (dmabuf->texture == 0) {
342         return;
343     }
344 
345     glDeleteTextures(1, &dmabuf->texture);
346     dmabuf->texture = 0;
347 }
348 
349 void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
350 {
351     EGLSyncKHR sync;
352 
353     if (epoxy_has_egl_extension(qemu_egl_display,
354                                 "EGL_KHR_fence_sync") &&
355         epoxy_has_egl_extension(qemu_egl_display,
356                                 "EGL_ANDROID_native_fence_sync")) {
357         sync = eglCreateSyncKHR(qemu_egl_display,
358                                 EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
359         if (sync != EGL_NO_SYNC_KHR) {
360             dmabuf->sync = sync;
361         }
362     }
363 }
364 
365 void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
366 {
367     if (dmabuf->sync) {
368         dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
369                                                       dmabuf->sync);
370         eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
371         dmabuf->sync = NULL;
372     }
373 }
374 
375 #endif /* CONFIG_GBM */
376 
377 /* ---------------------------------------------------------------------- */
378 
379 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
380 {
381     EGLSurface esurface;
382     EGLBoolean b;
383 
384     esurface = eglCreateWindowSurface(qemu_egl_display,
385                                       qemu_egl_config,
386                                       win, NULL);
387     if (esurface == EGL_NO_SURFACE) {
388         error_report("egl: eglCreateWindowSurface failed");
389         return NULL;
390     }
391 
392     b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
393     if (b == EGL_FALSE) {
394         error_report("egl: eglMakeCurrent failed");
395         return NULL;
396     }
397 
398     return esurface;
399 }
400 
401 /* ---------------------------------------------------------------------- */
402 
403 #if defined(CONFIG_X11) || defined(CONFIG_GBM)
404 
405 /*
406  * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed
407  *
408  * Create an EGLDisplay from a native display type. This is a little quirky
409  * for a few reasons.
410  *
411  * 1: GetPlatformDisplayEXT and GetPlatformDisplay are the API you want to
412  * use, but have different function signatures in the third argument; this
413  * happens not to matter for us, at the moment, but it means epoxy won't alias
414  * them together.
415  *
416  * 2: epoxy 1.3 and earlier don't understand EGL client extensions, which
417  * means you can't call "eglGetPlatformDisplayEXT" directly, as the resolver
418  * will crash.
419  *
420  * 3: You can't tell whether you have EGL 1.5 at this point, because
421  * eglQueryString(EGL_VERSION) is a property of the display, which we don't
422  * have yet. So you have to query for extensions no matter what. Fortunately
423  * epoxy_has_egl_extension _does_ let you query for client extensions, so
424  * we don't have to write our own extension string parsing.
425  *
426  * 4. There is no EGL_KHR_platform_base to complement the EXT one, thus one
427  * needs to know EGL 1.5 is supported in order to use the eglGetPlatformDisplay
428  * function pointer.
429  * We can workaround this (circular dependency) by probing for the EGL 1.5
430  * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
431  * like mesa will be able to advertise these (even though it can do EGL 1.5).
432  */
433 static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
434                                        EGLenum platform)
435 {
436     EGLDisplay dpy = EGL_NO_DISPLAY;
437 
438     /* In practise any EGL 1.5 implementation would support the EXT extension */
439     if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
440         if (platform != 0) {
441             dpy = eglGetPlatformDisplayEXT(platform, native, NULL);
442         }
443     }
444 
445     if (dpy == EGL_NO_DISPLAY) {
446         /* fallback */
447         dpy = eglGetDisplay(native);
448     }
449     return dpy;
450 }
451 
452 static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
453                              EGLenum platform,
454                              DisplayGLMode mode)
455 {
456     static const EGLint conf_att_core[] = {
457         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
458         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
459         EGL_RED_SIZE,   5,
460         EGL_GREEN_SIZE, 5,
461         EGL_BLUE_SIZE,  5,
462         EGL_ALPHA_SIZE, 0,
463         EGL_NONE,
464     };
465     static const EGLint conf_att_gles[] = {
466         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
467         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
468         EGL_RED_SIZE,   5,
469         EGL_GREEN_SIZE, 5,
470         EGL_BLUE_SIZE,  5,
471         EGL_ALPHA_SIZE, 0,
472         EGL_NONE,
473     };
474     EGLint major, minor;
475     EGLBoolean b;
476     EGLint n;
477     bool gles = (mode == DISPLAYGL_MODE_ES);
478 
479     qemu_egl_display = qemu_egl_get_display(dpy, platform);
480     if (qemu_egl_display == EGL_NO_DISPLAY) {
481         error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string());
482         return -1;
483     }
484 
485     b = eglInitialize(qemu_egl_display, &major, &minor);
486     if (b == EGL_FALSE) {
487         error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string());
488         return -1;
489     }
490 
491     b = eglBindAPI(gles ?  EGL_OPENGL_ES_API : EGL_OPENGL_API);
492     if (b == EGL_FALSE) {
493         error_report("egl: eglBindAPI failed (%s mode): %s",
494                      gles ? "gles" : "core", qemu_egl_get_error_string());
495         return -1;
496     }
497 
498     b = eglChooseConfig(qemu_egl_display,
499                         gles ? conf_att_gles : conf_att_core,
500                         &qemu_egl_config, 1, &n);
501     if (b == EGL_FALSE || n != 1) {
502         error_report("egl: eglChooseConfig failed (%s mode): %s",
503                      gles ? "gles" : "core", qemu_egl_get_error_string());
504         return -1;
505     }
506 
507     qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
508     return 0;
509 }
510 
511 int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
512 {
513 #ifdef EGL_KHR_platform_x11
514     return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
515 #else
516     return qemu_egl_init_dpy(dpy, 0, mode);
517 #endif
518 }
519 
520 int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
521 {
522 #ifdef EGL_MESA_platform_gbm
523     return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
524 #else
525     return qemu_egl_init_dpy(dpy, 0, mode);
526 #endif
527 }
528 
529 #endif
530 
531 bool qemu_egl_has_dmabuf(void)
532 {
533     if (qemu_egl_display == EGL_NO_DISPLAY) {
534         return false;
535     }
536 
537     return epoxy_has_egl_extension(qemu_egl_display,
538                                    "EGL_EXT_image_dma_buf_import");
539 }
540 
541 EGLContext qemu_egl_init_ctx(void)
542 {
543     static const EGLint ctx_att_core[] = {
544         EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
545         EGL_NONE
546     };
547     static const EGLint ctx_att_gles[] = {
548         EGL_CONTEXT_CLIENT_VERSION, 2,
549         EGL_NONE
550     };
551     bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES);
552     EGLContext ectx;
553     EGLBoolean b;
554 
555     ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
556                             gles ? ctx_att_gles : ctx_att_core);
557     if (ectx == EGL_NO_CONTEXT) {
558         error_report("egl: eglCreateContext failed");
559         return NULL;
560     }
561 
562     b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
563     if (b == EGL_FALSE) {
564         error_report("egl: eglMakeCurrent failed");
565         return NULL;
566     }
567 
568     return ectx;
569 }
570 
571 bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp)
572 {
573     ERRP_GUARD();
574 
575     if (mode == DISPLAYGL_MODE_OFF) {
576         error_setg(errp, "egl: turning off GL doesn't make sense");
577         return false;
578     }
579 
580 #ifdef CONFIG_GBM
581     if (egl_rendernode_init(rendernode, mode) < 0) {
582         error_setg(errp, "egl: render node init failed");
583         return false;
584     }
585     display_opengl = 1;
586     return true;
587 #else
588     error_setg(errp, "egl: not available on this platform");
589     return false;
590 #endif
591 }
592