1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include "qemu/osdep.h"
18#include <glob.h>
19#include <dirent.h>
20
21#include "qemu/error-report.h"
22#include "ui/console.h"
23#include "ui/egl-helpers.h"
24
25EGLDisplay *qemu_egl_display;
26EGLConfig qemu_egl_config;
27DisplayGLMode qemu_egl_mode;
28
29
30
31static void egl_fb_delete_texture(egl_fb *fb)
32{
33 if (!fb->delete_texture) {
34 return;
35 }
36
37 glDeleteTextures(1, &fb->texture);
38 fb->delete_texture = false;
39}
40
41void egl_fb_destroy(egl_fb *fb)
42{
43 if (!fb->framebuffer) {
44 return;
45 }
46
47 egl_fb_delete_texture(fb);
48 glDeleteFramebuffers(1, &fb->framebuffer);
49
50 fb->width = 0;
51 fb->height = 0;
52 fb->texture = 0;
53 fb->framebuffer = 0;
54}
55
56void egl_fb_setup_default(egl_fb *fb, int width, int height)
57{
58 fb->width = width;
59 fb->height = height;
60 fb->framebuffer = 0;
61}
62
63void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
64 GLuint texture, bool delete)
65{
66 egl_fb_delete_texture(fb);
67
68 fb->width = width;
69 fb->height = height;
70 fb->texture = texture;
71 fb->delete_texture = delete;
72 if (!fb->framebuffer) {
73 glGenFramebuffers(1, &fb->framebuffer);
74 }
75
76 glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb->framebuffer);
77 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
78 GL_TEXTURE_2D, fb->texture, 0);
79}
80
81void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
82{
83 GLuint texture;
84
85 glGenTextures(1, &texture);
86 glBindTexture(GL_TEXTURE_2D, texture);
87 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
88 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
89
90 egl_fb_setup_for_tex(fb, width, height, texture, true);
91}
92
93void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
94{
95 GLuint y1, y2;
96
97 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
98 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer);
99 glViewport(0, 0, dst->width, dst->height);
100 y1 = flip ? src->height : 0;
101 y2 = flip ? 0 : src->height;
102 glBlitFramebuffer(0, y1, src->width, y2,
103 0, 0, dst->width, dst->height,
104 GL_COLOR_BUFFER_BIT, GL_LINEAR);
105}
106
107void egl_fb_read(void *dst, egl_fb *src)
108{
109 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
110 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
111 glReadPixels(0, 0, src->width, src->height,
112 GL_BGRA, GL_UNSIGNED_BYTE, dst);
113}
114
115void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip)
116{
117 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
118 glViewport(0, 0, dst->width, dst->height);
119 glEnable(GL_TEXTURE_2D);
120 glBindTexture(GL_TEXTURE_2D, src->texture);
121 qemu_gl_run_texture_blit(gls, flip);
122}
123
124void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
125 int x, int y)
126{
127 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
128 if (flip) {
129 glViewport(x, y, src->width, src->height);
130 } else {
131 glViewport(x, dst->height - src->height - y,
132 src->width, src->height);
133 }
134 glEnable(GL_TEXTURE_2D);
135 glBindTexture(GL_TEXTURE_2D, src->texture);
136 glEnable(GL_BLEND);
137 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
138 qemu_gl_run_texture_blit(gls, flip);
139 glDisable(GL_BLEND);
140}
141
142
143
144#ifdef CONFIG_OPENGL_DMABUF
145
146int qemu_egl_rn_fd;
147struct gbm_device *qemu_egl_rn_gbm_dev;
148EGLContext qemu_egl_rn_ctx;
149
150static int qemu_egl_rendernode_open(const char *rendernode)
151{
152 DIR *dir;
153 struct dirent *e;
154 int r, fd;
155 char *p;
156
157 if (rendernode) {
158 return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
159 }
160
161 dir = opendir("/dev/dri");
162 if (!dir) {
163 return -1;
164 }
165
166 fd = -1;
167 while ((e = readdir(dir))) {
168 if (e->d_type != DT_CHR) {
169 continue;
170 }
171
172 if (strncmp(e->d_name, "renderD", 7)) {
173 continue;
174 }
175
176 p = g_strdup_printf("/dev/dri/%s", e->d_name);
177
178 r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
179 if (r < 0) {
180 g_free(p);
181 continue;
182 }
183 fd = r;
184 g_free(p);
185 break;
186 }
187
188 closedir(dir);
189 if (fd < 0) {
190 return -1;
191 }
192 return fd;
193}
194
195int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
196{
197 qemu_egl_rn_fd = -1;
198 int rc;
199
200 qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode);
201 if (qemu_egl_rn_fd == -1) {
202 error_report("egl: no drm render node available");
203 goto err;
204 }
205
206 qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
207 if (!qemu_egl_rn_gbm_dev) {
208 error_report("egl: gbm_create_device failed");
209 goto err;
210 }
211
212 rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev,
213 mode);
214 if (rc != 0) {
215
216 goto err;
217 }
218
219 if (!epoxy_has_egl_extension(qemu_egl_display,
220 "EGL_KHR_surfaceless_context")) {
221 error_report("egl: EGL_KHR_surfaceless_context not supported");
222 goto err;
223 }
224 if (!epoxy_has_egl_extension(qemu_egl_display,
225 "EGL_MESA_image_dma_buf_export")) {
226 error_report("egl: EGL_MESA_image_dma_buf_export not supported");
227 goto err;
228 }
229
230 qemu_egl_rn_ctx = qemu_egl_init_ctx();
231 if (!qemu_egl_rn_ctx) {
232 error_report("egl: egl_init_ctx failed");
233 goto err;
234 }
235
236 return 0;
237
238err:
239 if (qemu_egl_rn_gbm_dev) {
240 gbm_device_destroy(qemu_egl_rn_gbm_dev);
241 }
242 if (qemu_egl_rn_fd != -1) {
243 close(qemu_egl_rn_fd);
244 }
245
246 return -1;
247}
248
249int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
250{
251 EGLImageKHR image;
252 EGLint num_planes, fd;
253
254 image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
255 EGL_GL_TEXTURE_2D_KHR,
256 (EGLClientBuffer)(unsigned long)tex_id,
257 NULL);
258 if (!image) {
259 return -1;
260 }
261
262 eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
263 &num_planes, NULL);
264 if (num_planes != 1) {
265 eglDestroyImageKHR(qemu_egl_display, image);
266 return -1;
267 }
268 eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
269 eglDestroyImageKHR(qemu_egl_display, image);
270
271 return fd;
272}
273
274void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
275{
276 EGLImageKHR image = EGL_NO_IMAGE_KHR;
277 EGLint attrs[] = {
278 EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf->fd,
279 EGL_DMA_BUF_PLANE0_PITCH_EXT, dmabuf->stride,
280 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
281 EGL_WIDTH, dmabuf->width,
282 EGL_HEIGHT, dmabuf->height,
283 EGL_LINUX_DRM_FOURCC_EXT, dmabuf->fourcc,
284 EGL_NONE,
285 };
286
287 if (dmabuf->texture != 0) {
288 return;
289 }
290
291 image = eglCreateImageKHR(qemu_egl_display,
292 EGL_NO_CONTEXT,
293 EGL_LINUX_DMA_BUF_EXT,
294 NULL, attrs);
295 if (image == EGL_NO_IMAGE_KHR) {
296 error_report("eglCreateImageKHR failed");
297 return;
298 }
299
300 glGenTextures(1, &dmabuf->texture);
301 glBindTexture(GL_TEXTURE_2D, dmabuf->texture);
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
303 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
304
305 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
306 eglDestroyImageKHR(qemu_egl_display, image);
307}
308
309void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
310{
311 if (dmabuf->texture == 0) {
312 return;
313 }
314
315 glDeleteTextures(1, &dmabuf->texture);
316 dmabuf->texture = 0;
317}
318
319#endif
320
321
322
323EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
324{
325 EGLSurface esurface;
326 EGLBoolean b;
327
328 esurface = eglCreateWindowSurface(qemu_egl_display,
329 qemu_egl_config,
330 (EGLNativeWindowType)win, NULL);
331 if (esurface == EGL_NO_SURFACE) {
332 error_report("egl: eglCreateWindowSurface failed");
333 return NULL;
334 }
335
336 b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
337 if (b == EGL_FALSE) {
338 error_report("egl: eglMakeCurrent failed");
339 return NULL;
340 }
341
342 return esurface;
343}
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
376 EGLenum platform)
377{
378 EGLDisplay dpy = EGL_NO_DISPLAY;
379
380
381 if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
382 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
383 (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
384 if (getPlatformDisplayEXT && platform != 0) {
385 dpy = getPlatformDisplayEXT(platform, native, NULL);
386 }
387 }
388
389 if (dpy == EGL_NO_DISPLAY) {
390
391 dpy = eglGetDisplay(native);
392 }
393 return dpy;
394}
395
396static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
397 EGLenum platform,
398 DisplayGLMode mode)
399{
400 static const EGLint conf_att_core[] = {
401 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
402 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
403 EGL_RED_SIZE, 5,
404 EGL_GREEN_SIZE, 5,
405 EGL_BLUE_SIZE, 5,
406 EGL_ALPHA_SIZE, 0,
407 EGL_NONE,
408 };
409 static const EGLint conf_att_gles[] = {
410 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
411 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
412 EGL_RED_SIZE, 5,
413 EGL_GREEN_SIZE, 5,
414 EGL_BLUE_SIZE, 5,
415 EGL_ALPHA_SIZE, 0,
416 EGL_NONE,
417 };
418 EGLint major, minor;
419 EGLBoolean b;
420 EGLint n;
421 bool gles = (mode == DISPLAYGL_MODE_ES);
422
423 qemu_egl_display = qemu_egl_get_display(dpy, platform);
424 if (qemu_egl_display == EGL_NO_DISPLAY) {
425 error_report("egl: eglGetDisplay failed");
426 return -1;
427 }
428
429 b = eglInitialize(qemu_egl_display, &major, &minor);
430 if (b == EGL_FALSE) {
431 error_report("egl: eglInitialize failed");
432 return -1;
433 }
434
435 b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
436 if (b == EGL_FALSE) {
437 error_report("egl: eglBindAPI failed (%s mode)",
438 gles ? "gles" : "core");
439 return -1;
440 }
441
442 b = eglChooseConfig(qemu_egl_display,
443 gles ? conf_att_gles : conf_att_core,
444 &qemu_egl_config, 1, &n);
445 if (b == EGL_FALSE || n != 1) {
446 error_report("egl: eglChooseConfig failed (%s mode)",
447 gles ? "gles" : "core");
448 return -1;
449 }
450
451 qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
452 return 0;
453}
454
455int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
456{
457#ifdef EGL_KHR_platform_x11
458 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
459#else
460 return qemu_egl_init_dpy(dpy, 0, mode);
461#endif
462}
463
464int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
465{
466#ifdef EGL_MESA_platform_gbm
467 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
468#else
469 return qemu_egl_init_dpy(dpy, 0, mode);
470#endif
471}
472
473EGLContext qemu_egl_init_ctx(void)
474{
475 static const EGLint ctx_att_core[] = {
476 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
477 EGL_NONE
478 };
479 static const EGLint ctx_att_gles[] = {
480 EGL_CONTEXT_CLIENT_VERSION, 2,
481 EGL_NONE
482 };
483 bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES);
484 EGLContext ectx;
485 EGLBoolean b;
486
487 ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
488 gles ? ctx_att_gles : ctx_att_core);
489 if (ectx == EGL_NO_CONTEXT) {
490 error_report("egl: eglCreateContext failed");
491 return NULL;
492 }
493
494 b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
495 if (b == EGL_FALSE) {
496 error_report("egl: eglMakeCurrent failed");
497 return NULL;
498 }
499
500 return ectx;
501}
502