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