xref: /qemu/ui/egl-helpers.c (revision ac06724a)
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 <glob.h>
19 #include <dirent.h>
20 
21 #include "qemu/error-report.h"
22 #include "ui/egl-helpers.h"
23 
24 EGLDisplay *qemu_egl_display;
25 EGLConfig qemu_egl_config;
26 
27 /* ---------------------------------------------------------------------- */
28 
29 #ifdef CONFIG_OPENGL_DMABUF
30 
31 int qemu_egl_rn_fd;
32 struct gbm_device *qemu_egl_rn_gbm_dev;
33 EGLContext qemu_egl_rn_ctx;
34 
35 static int qemu_egl_rendernode_open(const char *rendernode)
36 {
37     DIR *dir;
38     struct dirent *e;
39     int r, fd;
40     char *p;
41 
42     if (rendernode) {
43         return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
44     }
45 
46     dir = opendir("/dev/dri");
47     if (!dir) {
48         return -1;
49     }
50 
51     fd = -1;
52     while ((e = readdir(dir))) {
53         if (e->d_type != DT_CHR) {
54             continue;
55         }
56 
57         if (strncmp(e->d_name, "renderD", 7)) {
58             continue;
59         }
60 
61         p = g_strdup_printf("/dev/dri/%s", e->d_name);
62 
63         r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
64         if (r < 0) {
65             g_free(p);
66             continue;
67         }
68         fd = r;
69         g_free(p);
70         break;
71     }
72 
73     closedir(dir);
74     if (fd < 0) {
75         return -1;
76     }
77     return fd;
78 }
79 
80 int egl_rendernode_init(const char *rendernode)
81 {
82     qemu_egl_rn_fd = -1;
83     int rc;
84 
85     qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode);
86     if (qemu_egl_rn_fd == -1) {
87         error_report("egl: no drm render node available");
88         goto err;
89     }
90 
91     qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
92     if (!qemu_egl_rn_gbm_dev) {
93         error_report("egl: gbm_create_device failed");
94         goto err;
95     }
96 
97     rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev);
98     if (rc != 0) {
99         /* qemu_egl_init_dpy_mesa reports error */
100         goto err;
101     }
102 
103     if (!epoxy_has_egl_extension(qemu_egl_display,
104                                  "EGL_KHR_surfaceless_context")) {
105         error_report("egl: EGL_KHR_surfaceless_context not supported");
106         goto err;
107     }
108     if (!epoxy_has_egl_extension(qemu_egl_display,
109                                  "EGL_MESA_image_dma_buf_export")) {
110         error_report("egl: EGL_MESA_image_dma_buf_export not supported");
111         goto err;
112     }
113 
114     qemu_egl_rn_ctx = qemu_egl_init_ctx();
115     if (!qemu_egl_rn_ctx) {
116         error_report("egl: egl_init_ctx failed");
117         goto err;
118     }
119 
120     return 0;
121 
122 err:
123     if (qemu_egl_rn_gbm_dev) {
124         gbm_device_destroy(qemu_egl_rn_gbm_dev);
125     }
126     if (qemu_egl_rn_fd != -1) {
127         close(qemu_egl_rn_fd);
128     }
129 
130     return -1;
131 }
132 
133 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
134 {
135     EGLImageKHR image;
136     EGLint num_planes, fd;
137 
138     image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
139                               EGL_GL_TEXTURE_2D_KHR,
140                               (EGLClientBuffer)(unsigned long)tex_id,
141                               NULL);
142     if (!image) {
143         return -1;
144     }
145 
146     eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
147                                   &num_planes, NULL);
148     if (num_planes != 1) {
149         eglDestroyImageKHR(qemu_egl_display, image);
150         return -1;
151     }
152     eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
153     eglDestroyImageKHR(qemu_egl_display, image);
154 
155     return fd;
156 }
157 
158 #endif /* CONFIG_OPENGL_DMABUF */
159 
160 /* ---------------------------------------------------------------------- */
161 
162 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
163 {
164     EGLSurface esurface;
165     EGLBoolean b;
166 
167     esurface = eglCreateWindowSurface(qemu_egl_display,
168                                       qemu_egl_config,
169                                       (EGLNativeWindowType)win, NULL);
170     if (esurface == EGL_NO_SURFACE) {
171         error_report("egl: eglCreateWindowSurface failed");
172         return NULL;
173     }
174 
175     b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
176     if (b == EGL_FALSE) {
177         error_report("egl: eglMakeCurrent failed");
178         return NULL;
179     }
180 
181     return esurface;
182 }
183 
184 /* ---------------------------------------------------------------------- */
185 
186 /*
187  * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed
188  *
189  * Create an EGLDisplay from a native display type. This is a little quirky
190  * for a few reasons.
191  *
192  * 1: GetPlatformDisplayEXT and GetPlatformDisplay are the API you want to
193  * use, but have different function signatures in the third argument; this
194  * happens not to matter for us, at the moment, but it means epoxy won't alias
195  * them together.
196  *
197  * 2: epoxy 1.3 and earlier don't understand EGL client extensions, which
198  * means you can't call "eglGetPlatformDisplayEXT" directly, as the resolver
199  * will crash.
200  *
201  * 3: You can't tell whether you have EGL 1.5 at this point, because
202  * eglQueryString(EGL_VERSION) is a property of the display, which we don't
203  * have yet. So you have to query for extensions no matter what. Fortunately
204  * epoxy_has_egl_extension _does_ let you query for client extensions, so
205  * we don't have to write our own extension string parsing.
206  *
207  * 4. There is no EGL_KHR_platform_base to complement the EXT one, thus one
208  * needs to know EGL 1.5 is supported in order to use the eglGetPlatformDisplay
209  * function pointer.
210  * We can workaround this (circular dependency) by probing for the EGL 1.5
211  * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
212  * like mesa will be able to advertise these (even though it can do EGL 1.5).
213  */
214 static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
215                                        EGLenum platform)
216 {
217     EGLDisplay dpy = EGL_NO_DISPLAY;
218 
219     /* In practise any EGL 1.5 implementation would support the EXT extension */
220     if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
221         PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
222             (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
223         if (getPlatformDisplayEXT && platform != 0) {
224             dpy = getPlatformDisplayEXT(platform, native, NULL);
225         }
226     }
227 
228     if (dpy == EGL_NO_DISPLAY) {
229         /* fallback */
230         dpy = eglGetDisplay(native);
231     }
232     return dpy;
233 }
234 
235 static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
236                              EGLenum platform)
237 {
238     static const EGLint conf_att_gl[] = {
239         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
240         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
241         EGL_RED_SIZE,   5,
242         EGL_GREEN_SIZE, 5,
243         EGL_BLUE_SIZE,  5,
244         EGL_ALPHA_SIZE, 0,
245         EGL_NONE,
246     };
247     EGLint major, minor;
248     EGLBoolean b;
249     EGLint n;
250 
251     qemu_egl_display = qemu_egl_get_display(dpy, platform);
252     if (qemu_egl_display == EGL_NO_DISPLAY) {
253         error_report("egl: eglGetDisplay failed");
254         return -1;
255     }
256 
257     b = eglInitialize(qemu_egl_display, &major, &minor);
258     if (b == EGL_FALSE) {
259         error_report("egl: eglInitialize failed");
260         return -1;
261     }
262 
263     b = eglBindAPI(EGL_OPENGL_API);
264     if (b == EGL_FALSE) {
265         error_report("egl: eglBindAPI failed");
266         return -1;
267     }
268 
269     b = eglChooseConfig(qemu_egl_display, conf_att_gl,
270                         &qemu_egl_config, 1, &n);
271     if (b == EGL_FALSE || n != 1) {
272         error_report("egl: eglChooseConfig failed");
273         return -1;
274     }
275     return 0;
276 }
277 
278 int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy)
279 {
280 #ifdef EGL_KHR_platform_x11
281     return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR);
282 #else
283     return qemu_egl_init_dpy(dpy, 0);
284 #endif
285 }
286 
287 int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy)
288 {
289 #ifdef EGL_MESA_platform_gbm
290     return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA);
291 #else
292     return qemu_egl_init_dpy(dpy, 0);
293 #endif
294 }
295 
296 EGLContext qemu_egl_init_ctx(void)
297 {
298     static const EGLint ctx_att_gl[] = {
299         EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
300         EGL_NONE
301     };
302     EGLContext ectx;
303     EGLBoolean b;
304 
305     ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
306                             ctx_att_gl);
307     if (ectx == EGL_NO_CONTEXT) {
308         error_report("egl: eglCreateContext failed");
309         return NULL;
310     }
311 
312     b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
313     if (b == EGL_FALSE) {
314         error_report("egl: eglMakeCurrent failed");
315         return NULL;
316     }
317 
318     return ectx;
319 }
320