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(void *dst, egl_fb *src)
106{
107 glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
108 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
109 glReadPixels(0, 0, src->width, src->height,
110 GL_BGRA, GL_UNSIGNED_BYTE, 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_OPENGL_DMABUF
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
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
346static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
347 EGLenum platform)
348{
349 EGLDisplay dpy = EGL_NO_DISPLAY;
350
351
352 if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
353 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
354 (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
355 if (getPlatformDisplayEXT && platform != 0) {
356 dpy = getPlatformDisplayEXT(platform, native, NULL);
357 }
358 }
359
360 if (dpy == EGL_NO_DISPLAY) {
361
362 dpy = eglGetDisplay(native);
363 }
364 return dpy;
365}
366
367static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
368 EGLenum platform,
369 DisplayGLMode mode)
370{
371 static const EGLint conf_att_core[] = {
372 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
373 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
374 EGL_RED_SIZE, 5,
375 EGL_GREEN_SIZE, 5,
376 EGL_BLUE_SIZE, 5,
377 EGL_ALPHA_SIZE, 0,
378 EGL_NONE,
379 };
380 static const EGLint conf_att_gles[] = {
381 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
382 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
383 EGL_RED_SIZE, 5,
384 EGL_GREEN_SIZE, 5,
385 EGL_BLUE_SIZE, 5,
386 EGL_ALPHA_SIZE, 0,
387 EGL_NONE,
388 };
389 EGLint major, minor;
390 EGLBoolean b;
391 EGLint n;
392 bool gles = (mode == DISPLAYGL_MODE_ES);
393
394 qemu_egl_display = qemu_egl_get_display(dpy, platform);
395 if (qemu_egl_display == EGL_NO_DISPLAY) {
396 error_report("egl: eglGetDisplay failed");
397 return -1;
398 }
399
400 b = eglInitialize(qemu_egl_display, &major, &minor);
401 if (b == EGL_FALSE) {
402 error_report("egl: eglInitialize failed");
403 return -1;
404 }
405
406 b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
407 if (b == EGL_FALSE) {
408 error_report("egl: eglBindAPI failed (%s mode)",
409 gles ? "gles" : "core");
410 return -1;
411 }
412
413 b = eglChooseConfig(qemu_egl_display,
414 gles ? conf_att_gles : conf_att_core,
415 &qemu_egl_config, 1, &n);
416 if (b == EGL_FALSE || n != 1) {
417 error_report("egl: eglChooseConfig failed (%s mode)",
418 gles ? "gles" : "core");
419 return -1;
420 }
421
422 qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
423 return 0;
424}
425
426int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
427{
428#ifdef EGL_KHR_platform_x11
429 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
430#else
431 return qemu_egl_init_dpy(dpy, 0, mode);
432#endif
433}
434
435int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
436{
437#ifdef EGL_MESA_platform_gbm
438 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
439#else
440 return qemu_egl_init_dpy(dpy, 0, mode);
441#endif
442}
443
444EGLContext qemu_egl_init_ctx(void)
445{
446 static const EGLint ctx_att_core[] = {
447 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
448 EGL_NONE
449 };
450 static const EGLint ctx_att_gles[] = {
451 EGL_CONTEXT_CLIENT_VERSION, 2,
452 EGL_NONE
453 };
454 bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES);
455 EGLContext ectx;
456 EGLBoolean b;
457
458 ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
459 gles ? ctx_att_gles : ctx_att_core);
460 if (ectx == EGL_NO_CONTEXT) {
461 error_report("egl: eglCreateContext failed");
462 return NULL;
463 }
464
465 b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
466 if (b == EGL_FALSE) {
467 error_report("egl: eglMakeCurrent failed");
468 return NULL;
469 }
470
471 return ectx;
472}
473