xref: /openbmc/qemu/ui/egl-helpers.c (revision 39324b49)
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 EGLContext qemu_egl_rn_ctx;
203 
204 #ifdef CONFIG_GBM
205 
206 int qemu_egl_rn_fd;
207 struct gbm_device *qemu_egl_rn_gbm_dev;
208 
209 int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
210 {
211     qemu_egl_rn_fd = -1;
212     int rc;
213 
214     qemu_egl_rn_fd = qemu_drm_rendernode_open(rendernode);
215     if (qemu_egl_rn_fd == -1) {
216         error_report("egl: no drm render node available");
217         goto err;
218     }
219 
220     qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
221     if (!qemu_egl_rn_gbm_dev) {
222         error_report("egl: gbm_create_device failed");
223         goto err;
224     }
225 
226     rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev,
227                                 mode);
228     if (rc != 0) {
229         /* qemu_egl_init_dpy_mesa reports error */
230         goto err;
231     }
232 
233     if (!epoxy_has_egl_extension(qemu_egl_display,
234                                  "EGL_KHR_surfaceless_context")) {
235         error_report("egl: EGL_KHR_surfaceless_context not supported");
236         goto err;
237     }
238     if (!epoxy_has_egl_extension(qemu_egl_display,
239                                  "EGL_MESA_image_dma_buf_export")) {
240         error_report("egl: EGL_MESA_image_dma_buf_export not supported");
241         goto err;
242     }
243 
244     qemu_egl_rn_ctx = qemu_egl_init_ctx();
245     if (!qemu_egl_rn_ctx) {
246         error_report("egl: egl_init_ctx failed");
247         goto err;
248     }
249 
250     return 0;
251 
252 err:
253     if (qemu_egl_rn_gbm_dev) {
254         gbm_device_destroy(qemu_egl_rn_gbm_dev);
255     }
256     if (qemu_egl_rn_fd != -1) {
257         close(qemu_egl_rn_fd);
258     }
259 
260     return -1;
261 }
262 
263 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
264                            EGLuint64KHR *modifier)
265 {
266     EGLImageKHR image;
267     EGLint num_planes, fd;
268 
269     image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
270                               EGL_GL_TEXTURE_2D_KHR,
271                               (EGLClientBuffer)(unsigned long)tex_id,
272                               NULL);
273     if (!image) {
274         return -1;
275     }
276 
277     eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
278                                   &num_planes, modifier);
279     if (num_planes != 1) {
280         eglDestroyImageKHR(qemu_egl_display, image);
281         return -1;
282     }
283     eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
284     eglDestroyImageKHR(qemu_egl_display, image);
285 
286     return fd;
287 }
288 
289 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
290 {
291     EGLImageKHR image = EGL_NO_IMAGE_KHR;
292     EGLint attrs[64];
293     int i = 0;
294 
295     if (dmabuf->texture != 0) {
296         return;
297     }
298 
299     attrs[i++] = EGL_WIDTH;
300     attrs[i++] = dmabuf->width;
301     attrs[i++] = EGL_HEIGHT;
302     attrs[i++] = dmabuf->height;
303     attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
304     attrs[i++] = dmabuf->fourcc;
305 
306     attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
307     attrs[i++] = dmabuf->fd;
308     attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
309     attrs[i++] = dmabuf->stride;
310     attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
311     attrs[i++] = 0;
312 #ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT
313     if (dmabuf->modifier) {
314         attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
315         attrs[i++] = (dmabuf->modifier >>  0) & 0xffffffff;
316         attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
317         attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff;
318     }
319 #endif
320     attrs[i++] = EGL_NONE;
321 
322     image = eglCreateImageKHR(qemu_egl_display,
323                               EGL_NO_CONTEXT,
324                               EGL_LINUX_DMA_BUF_EXT,
325                               NULL, attrs);
326     if (image == EGL_NO_IMAGE_KHR) {
327         error_report("eglCreateImageKHR failed");
328         return;
329     }
330 
331     glGenTextures(1, &dmabuf->texture);
332     glBindTexture(GL_TEXTURE_2D, dmabuf->texture);
333     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
334     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
335 
336     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
337     eglDestroyImageKHR(qemu_egl_display, image);
338 }
339 
340 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
341 {
342     if (dmabuf->texture == 0) {
343         return;
344     }
345 
346     glDeleteTextures(1, &dmabuf->texture);
347     dmabuf->texture = 0;
348 }
349 
350 void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
351 {
352     EGLSyncKHR sync;
353 
354     if (epoxy_has_egl_extension(qemu_egl_display,
355                                 "EGL_KHR_fence_sync") &&
356         epoxy_has_egl_extension(qemu_egl_display,
357                                 "EGL_ANDROID_native_fence_sync")) {
358         sync = eglCreateSyncKHR(qemu_egl_display,
359                                 EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
360         if (sync != EGL_NO_SYNC_KHR) {
361             dmabuf->sync = sync;
362         }
363     }
364 }
365 
366 void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
367 {
368     if (dmabuf->sync) {
369         dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
370                                                       dmabuf->sync);
371         eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
372         dmabuf->sync = NULL;
373     }
374 }
375 
376 #endif /* CONFIG_GBM */
377 
378 /* ---------------------------------------------------------------------- */
379 
380 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
381 {
382     EGLSurface esurface;
383     EGLBoolean b;
384 
385     esurface = eglCreateWindowSurface(qemu_egl_display,
386                                       qemu_egl_config,
387                                       win, NULL);
388     if (esurface == EGL_NO_SURFACE) {
389         error_report("egl: eglCreateWindowSurface failed");
390         return NULL;
391     }
392 
393     b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
394     if (b == EGL_FALSE) {
395         error_report("egl: eglMakeCurrent failed");
396         return NULL;
397     }
398 
399     return esurface;
400 }
401 
402 /* ---------------------------------------------------------------------- */
403 
404 #if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(WIN32)
405 
406 /*
407  * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed
408  *
409  * Create an EGLDisplay from a native display type. This is a little quirky
410  * for a few reasons.
411  *
412  * 1: GetPlatformDisplayEXT and GetPlatformDisplay are the API you want to
413  * use, but have different function signatures in the third argument; this
414  * happens not to matter for us, at the moment, but it means epoxy won't alias
415  * them together.
416  *
417  * 2: epoxy 1.3 and earlier don't understand EGL client extensions, which
418  * means you can't call "eglGetPlatformDisplayEXT" directly, as the resolver
419  * will crash.
420  *
421  * 3: You can't tell whether you have EGL 1.5 at this point, because
422  * eglQueryString(EGL_VERSION) is a property of the display, which we don't
423  * have yet. So you have to query for extensions no matter what. Fortunately
424  * epoxy_has_egl_extension _does_ let you query for client extensions, so
425  * we don't have to write our own extension string parsing.
426  *
427  * 4. There is no EGL_KHR_platform_base to complement the EXT one, thus one
428  * needs to know EGL 1.5 is supported in order to use the eglGetPlatformDisplay
429  * function pointer.
430  * We can workaround this (circular dependency) by probing for the EGL 1.5
431  * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
432  * like mesa will be able to advertise these (even though it can do EGL 1.5).
433  */
434 static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
435                                        EGLenum platform)
436 {
437     EGLDisplay dpy = EGL_NO_DISPLAY;
438 
439     /* In practise any EGL 1.5 implementation would support the EXT extension */
440     if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
441         if (platform != 0) {
442             dpy = eglGetPlatformDisplayEXT(platform, native, NULL);
443         }
444     }
445 
446     if (dpy == EGL_NO_DISPLAY) {
447         /* fallback */
448         dpy = eglGetDisplay(native);
449     }
450     return dpy;
451 }
452 
453 static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
454                              EGLenum platform,
455                              DisplayGLMode mode)
456 {
457     static const EGLint conf_att_core[] = {
458         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
459         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
460         EGL_RED_SIZE,   5,
461         EGL_GREEN_SIZE, 5,
462         EGL_BLUE_SIZE,  5,
463         EGL_ALPHA_SIZE, 0,
464         EGL_NONE,
465     };
466     static const EGLint conf_att_gles[] = {
467         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
468         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
469         EGL_RED_SIZE,   5,
470         EGL_GREEN_SIZE, 5,
471         EGL_BLUE_SIZE,  5,
472         EGL_ALPHA_SIZE, 0,
473         EGL_NONE,
474     };
475     EGLint major, minor;
476     EGLBoolean b;
477     EGLint n;
478     bool gles = (mode == DISPLAYGL_MODE_ES);
479 
480     qemu_egl_display = qemu_egl_get_display(dpy, platform);
481     if (qemu_egl_display == EGL_NO_DISPLAY) {
482         error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string());
483         return -1;
484     }
485 
486     b = eglInitialize(qemu_egl_display, &major, &minor);
487     if (b == EGL_FALSE) {
488         error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string());
489         return -1;
490     }
491 
492     b = eglBindAPI(gles ?  EGL_OPENGL_ES_API : EGL_OPENGL_API);
493     if (b == EGL_FALSE) {
494         error_report("egl: eglBindAPI failed (%s mode): %s",
495                      gles ? "gles" : "core", qemu_egl_get_error_string());
496         return -1;
497     }
498 
499     b = eglChooseConfig(qemu_egl_display,
500                         gles ? conf_att_gles : conf_att_core,
501                         &qemu_egl_config, 1, &n);
502     if (b == EGL_FALSE || n != 1) {
503         error_report("egl: eglChooseConfig failed (%s mode): %s",
504                      gles ? "gles" : "core", qemu_egl_get_error_string());
505         return -1;
506     }
507 
508     qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
509     return 0;
510 }
511 
512 #endif
513 
514 #if defined(CONFIG_X11) || defined(CONFIG_GBM)
515 int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
516 {
517 #ifdef EGL_KHR_platform_x11
518     return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
519 #else
520     return qemu_egl_init_dpy(dpy, 0, mode);
521 #endif
522 }
523 
524 int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
525 {
526 #ifdef EGL_MESA_platform_gbm
527     return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
528 #else
529     return qemu_egl_init_dpy(dpy, 0, mode);
530 #endif
531 }
532 #endif
533 
534 
535 #ifdef WIN32
536 int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode)
537 {
538     return qemu_egl_init_dpy(dpy, 0, mode);
539 }
540 #endif
541 
542 bool qemu_egl_has_dmabuf(void)
543 {
544     if (qemu_egl_display == EGL_NO_DISPLAY) {
545         return false;
546     }
547 
548     return epoxy_has_egl_extension(qemu_egl_display,
549                                    "EGL_EXT_image_dma_buf_import");
550 }
551 
552 EGLContext qemu_egl_init_ctx(void)
553 {
554     static const EGLint ctx_att_core[] = {
555         EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
556         EGL_NONE
557     };
558     static const EGLint ctx_att_gles[] = {
559         EGL_CONTEXT_CLIENT_VERSION, 2,
560         EGL_NONE
561     };
562     bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES);
563     EGLContext ectx;
564     EGLBoolean b;
565 
566     ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
567                             gles ? ctx_att_gles : ctx_att_core);
568     if (ectx == EGL_NO_CONTEXT) {
569         error_report("egl: eglCreateContext failed");
570         return NULL;
571     }
572 
573     b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
574     if (b == EGL_FALSE) {
575         error_report("egl: eglMakeCurrent failed");
576         return NULL;
577     }
578 
579     return ectx;
580 }
581 
582 bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp)
583 {
584     ERRP_GUARD();
585 
586     if (mode == DISPLAYGL_MODE_OFF) {
587         error_setg(errp, "egl: turning off GL doesn't make sense");
588         return false;
589     }
590 
591 #ifdef WIN32
592     if (qemu_egl_init_dpy_win32(EGL_DEFAULT_DISPLAY, mode) < 0) {
593         error_setg(errp, "egl: init failed");
594         return false;
595     }
596     qemu_egl_rn_ctx = qemu_egl_init_ctx();
597     if (!qemu_egl_rn_ctx) {
598         error_setg(errp, "egl: egl_init_ctx failed");
599         return false;
600     }
601 #elif defined(CONFIG_GBM)
602     if (egl_rendernode_init(rendernode, mode) < 0) {
603         error_setg(errp, "egl: render node init failed");
604         return false;
605     }
606 #endif
607 
608     if (!qemu_egl_rn_ctx) {
609         error_setg(errp, "egl: not available on this platform");
610         return false;
611     }
612 
613     display_opengl = 1;
614     return true;
615 }
616