xref: /qemu/ui/egl-helpers.c (revision ffdc5a2b)
1 #include "qemu/osdep.h"
2 #include <glob.h>
3 #include <dirent.h>
4 
5 #include "qemu/error-report.h"
6 #include "ui/egl-helpers.h"
7 
8 EGLDisplay *qemu_egl_display;
9 EGLConfig qemu_egl_config;
10 
11 /* ---------------------------------------------------------------------- */
12 
13 static bool egl_gles;
14 static int egl_debug;
15 
16 #define egl_dbg(_x ...)                          \
17     do {                                         \
18         if (egl_debug) {                         \
19             fprintf(stderr, "egl: " _x);         \
20         }                                        \
21     } while (0);
22 
23 /* ---------------------------------------------------------------------- */
24 
25 #ifdef CONFIG_OPENGL_DMABUF
26 
27 int qemu_egl_rn_fd;
28 struct gbm_device *qemu_egl_rn_gbm_dev;
29 EGLContext qemu_egl_rn_ctx;
30 
31 int qemu_egl_rendernode_open(void)
32 {
33     DIR *dir;
34     struct dirent *e;
35     int r, fd;
36     char *p;
37 
38     dir = opendir("/dev/dri");
39     if (!dir) {
40         return -1;
41     }
42 
43     fd = -1;
44     while ((e = readdir(dir))) {
45         if (e->d_type != DT_CHR) {
46             continue;
47         }
48 
49         if (strncmp(e->d_name, "renderD", 7)) {
50             continue;
51         }
52 
53         p = g_strdup_printf("/dev/dri/%s", e->d_name);
54 
55         r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
56         if (r < 0) {
57             g_free(p);
58             continue;
59         }
60         fd = r;
61         g_free(p);
62         break;
63     }
64 
65     closedir(dir);
66     if (fd < 0) {
67         return -1;
68     }
69     return fd;
70 }
71 
72 int egl_rendernode_init(void)
73 {
74     qemu_egl_rn_fd = -1;
75 
76     qemu_egl_rn_fd = qemu_egl_rendernode_open();
77     if (qemu_egl_rn_fd == -1) {
78         error_report("egl: no drm render node available");
79         goto err;
80     }
81 
82     qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
83     if (!qemu_egl_rn_gbm_dev) {
84         error_report("egl: gbm_create_device failed");
85         goto err;
86     }
87 
88     qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false);
89 
90     if (!epoxy_has_egl_extension(qemu_egl_display,
91                                  "EGL_KHR_surfaceless_context")) {
92         error_report("egl: EGL_KHR_surfaceless_context not supported");
93         goto err;
94     }
95     if (!epoxy_has_egl_extension(qemu_egl_display,
96                                  "EGL_MESA_image_dma_buf_export")) {
97         error_report("egl: EGL_MESA_image_dma_buf_export not supported");
98         goto err;
99     }
100 
101     qemu_egl_rn_ctx = qemu_egl_init_ctx();
102     if (!qemu_egl_rn_ctx) {
103         error_report("egl: egl_init_ctx failed");
104         goto err;
105     }
106 
107     return 0;
108 
109 err:
110     if (qemu_egl_rn_gbm_dev) {
111         gbm_device_destroy(qemu_egl_rn_gbm_dev);
112     }
113     if (qemu_egl_rn_fd != -1) {
114         close(qemu_egl_rn_fd);
115     }
116 
117     return -1;
118 }
119 
120 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
121 {
122     EGLImageKHR image;
123     EGLint num_planes, fd;
124 
125     image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
126                               EGL_GL_TEXTURE_2D_KHR,
127                               (EGLClientBuffer)(unsigned long)tex_id,
128                               NULL);
129     if (!image) {
130         return -1;
131     }
132 
133     eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
134                                   &num_planes, NULL);
135     if (num_planes != 1) {
136         eglDestroyImageKHR(qemu_egl_display, image);
137         return -1;
138     }
139     eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
140     eglDestroyImageKHR(qemu_egl_display, image);
141 
142     return fd;
143 }
144 
145 #endif /* CONFIG_OPENGL_DMABUF */
146 
147 /* ---------------------------------------------------------------------- */
148 
149 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
150 {
151     EGLSurface esurface;
152     EGLBoolean b;
153 
154     egl_dbg("eglCreateWindowSurface (x11 win id 0x%lx) ...\n",
155             (unsigned long) win);
156     esurface = eglCreateWindowSurface(qemu_egl_display,
157                                       qemu_egl_config,
158                                       (EGLNativeWindowType)win, NULL);
159     if (esurface == EGL_NO_SURFACE) {
160         error_report("egl: eglCreateWindowSurface failed");
161         return NULL;
162     }
163 
164     b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
165     if (b == EGL_FALSE) {
166         error_report("egl: eglMakeCurrent failed");
167         return NULL;
168     }
169 
170     return esurface;
171 }
172 
173 /* ---------------------------------------------------------------------- */
174 
175 int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug)
176 {
177     static const EGLint conf_att_gl[] = {
178         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
179         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
180         EGL_RED_SIZE,   5,
181         EGL_GREEN_SIZE, 5,
182         EGL_BLUE_SIZE,  5,
183         EGL_ALPHA_SIZE, 0,
184         EGL_NONE,
185     };
186     static const EGLint conf_att_gles[] = {
187         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
188         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
189         EGL_RED_SIZE,   5,
190         EGL_GREEN_SIZE, 5,
191         EGL_BLUE_SIZE,  5,
192         EGL_ALPHA_SIZE, 0,
193         EGL_NONE,
194     };
195     EGLint major, minor;
196     EGLBoolean b;
197     EGLint n;
198 
199     if (debug) {
200         egl_debug = 1;
201         setenv("EGL_LOG_LEVEL", "debug", true);
202         setenv("LIBGL_DEBUG", "verbose", true);
203     }
204 
205     egl_dbg("eglGetDisplay (dpy %p) ...\n", dpy);
206     qemu_egl_display = eglGetDisplay(dpy);
207     if (qemu_egl_display == EGL_NO_DISPLAY) {
208         error_report("egl: eglGetDisplay failed");
209         return -1;
210     }
211 
212     egl_dbg("eglInitialize ...\n");
213     b = eglInitialize(qemu_egl_display, &major, &minor);
214     if (b == EGL_FALSE) {
215         error_report("egl: eglInitialize failed");
216         return -1;
217     }
218 
219     egl_dbg("eglBindAPI ...\n");
220     b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
221     if (b == EGL_FALSE) {
222         error_report("egl: eglBindAPI failed");
223         return -1;
224     }
225 
226     egl_dbg("eglChooseConfig ...\n");
227     b = eglChooseConfig(qemu_egl_display,
228                         gles ? conf_att_gles : conf_att_gl,
229                         &qemu_egl_config, 1, &n);
230     if (b == EGL_FALSE || n != 1) {
231         error_report("egl: eglChooseConfig failed");
232         return -1;
233     }
234 
235     egl_gles = gles;
236     return 0;
237 }
238 
239 EGLContext qemu_egl_init_ctx(void)
240 {
241     static const EGLint ctx_att_gl[] = {
242         EGL_NONE
243     };
244     static const EGLint ctx_att_gles[] = {
245         EGL_CONTEXT_CLIENT_VERSION, 2,
246         EGL_NONE
247     };
248 
249     EGLContext ectx;
250     EGLBoolean b;
251 
252     egl_dbg("eglCreateContext ...\n");
253     ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
254                             egl_gles ? ctx_att_gles : ctx_att_gl);
255     if (ectx == EGL_NO_CONTEXT) {
256         error_report("egl: eglCreateContext failed");
257         return NULL;
258     }
259 
260     b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
261     if (b == EGL_FALSE) {
262         error_report("egl: eglMakeCurrent failed");
263         return NULL;
264     }
265 
266     return ectx;
267 }
268