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)
124{
125 glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
126 if (flip) {
127 glViewport(x, y, src->width, src->height);
128 } else {
129 glViewport(x, dst->height - src->height - y,
130 src->width, src->height);
131 }
132 glEnable(GL_TEXTURE_2D);
133 glBindTexture(GL_TEXTURE_2D, src->texture);
134 glEnable(GL_BLEND);
135 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
136 qemu_gl_run_texture_blit(gls, flip);
137 glDisable(GL_BLEND);
138}
139
140
141
142#ifdef CONFIG_OPENGL_DMABUF
143
144int qemu_egl_rn_fd;
145struct gbm_device *qemu_egl_rn_gbm_dev;
146EGLContext qemu_egl_rn_ctx;
147
148int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
149{
150 qemu_egl_rn_fd = -1;
151 int rc;
152
153 qemu_egl_rn_fd = qemu_drm_rendernode_open(rendernode);
154 if (qemu_egl_rn_fd == -1) {
155 error_report("egl: no drm render node available");
156 goto err;
157 }
158
159 qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
160 if (!qemu_egl_rn_gbm_dev) {
161 error_report("egl: gbm_create_device failed");
162 goto err;
163 }
164
165 rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev,
166 mode);
167 if (rc != 0) {
168
169 goto err;
170 }
171
172 if (!epoxy_has_egl_extension(qemu_egl_display,
173 "EGL_KHR_surfaceless_context")) {
174 error_report("egl: EGL_KHR_surfaceless_context not supported");
175 goto err;
176 }
177 if (!epoxy_has_egl_extension(qemu_egl_display,
178 "EGL_MESA_image_dma_buf_export")) {
179 error_report("egl: EGL_MESA_image_dma_buf_export not supported");
180 goto err;
181 }
182
183 qemu_egl_rn_ctx = qemu_egl_init_ctx();
184 if (!qemu_egl_rn_ctx) {
185 error_report("egl: egl_init_ctx failed");
186 goto err;
187 }
188
189 return 0;
190
191err:
192 if (qemu_egl_rn_gbm_dev) {
193 gbm_device_destroy(qemu_egl_rn_gbm_dev);
194 }
195 if (qemu_egl_rn_fd != -1) {
196 close(qemu_egl_rn_fd);
197 }
198
199 return -1;
200}
201
202int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
203{
204 EGLImageKHR image;
205 EGLint num_planes, fd;
206
207 image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
208 EGL_GL_TEXTURE_2D_KHR,
209 (EGLClientBuffer)(unsigned long)tex_id,
210 NULL);
211 if (!image) {
212 return -1;
213 }
214
215 eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
216 &num_planes, NULL);
217 if (num_planes != 1) {
218 eglDestroyImageKHR(qemu_egl_display, image);
219 return -1;
220 }
221 eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
222 eglDestroyImageKHR(qemu_egl_display, image);
223
224 return fd;
225}
226
227void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
228{
229 EGLImageKHR image = EGL_NO_IMAGE_KHR;
230 EGLint attrs[] = {
231 EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf->fd,
232 EGL_DMA_BUF_PLANE0_PITCH_EXT, dmabuf->stride,
233 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
234 EGL_WIDTH, dmabuf->width,
235 EGL_HEIGHT, dmabuf->height,
236 EGL_LINUX_DRM_FOURCC_EXT, dmabuf->fourcc,
237 EGL_NONE,
238 };
239
240 if (dmabuf->texture != 0) {
241 return;
242 }
243
244 image = eglCreateImageKHR(qemu_egl_display,
245 EGL_NO_CONTEXT,
246 EGL_LINUX_DMA_BUF_EXT,
247 NULL, attrs);
248 if (image == EGL_NO_IMAGE_KHR) {
249 error_report("eglCreateImageKHR failed");
250 return;
251 }
252
253 glGenTextures(1, &dmabuf->texture);
254 glBindTexture(GL_TEXTURE_2D, dmabuf->texture);
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
257
258 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
259 eglDestroyImageKHR(qemu_egl_display, image);
260}
261
262void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
263{
264 if (dmabuf->texture == 0) {
265 return;
266 }
267
268 glDeleteTextures(1, &dmabuf->texture);
269 dmabuf->texture = 0;
270}
271
272#endif
273
274
275
276EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
277{
278 EGLSurface esurface;
279 EGLBoolean b;
280
281 esurface = eglCreateWindowSurface(qemu_egl_display,
282 qemu_egl_config,
283 (EGLNativeWindowType)win, NULL);
284 if (esurface == EGL_NO_SURFACE) {
285 error_report("egl: eglCreateWindowSurface failed");
286 return NULL;
287 }
288
289 b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
290 if (b == EGL_FALSE) {
291 error_report("egl: eglMakeCurrent failed");
292 return NULL;
293 }
294
295 return esurface;
296}
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
329 EGLenum platform)
330{
331 EGLDisplay dpy = EGL_NO_DISPLAY;
332
333
334 if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
335 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
336 (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
337 if (getPlatformDisplayEXT && platform != 0) {
338 dpy = getPlatformDisplayEXT(platform, native, NULL);
339 }
340 }
341
342 if (dpy == EGL_NO_DISPLAY) {
343
344 dpy = eglGetDisplay(native);
345 }
346 return dpy;
347}
348
349static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
350 EGLenum platform,
351 DisplayGLMode mode)
352{
353 static const EGLint conf_att_core[] = {
354 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
355 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
356 EGL_RED_SIZE, 5,
357 EGL_GREEN_SIZE, 5,
358 EGL_BLUE_SIZE, 5,
359 EGL_ALPHA_SIZE, 0,
360 EGL_NONE,
361 };
362 static const EGLint conf_att_gles[] = {
363 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
364 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
365 EGL_RED_SIZE, 5,
366 EGL_GREEN_SIZE, 5,
367 EGL_BLUE_SIZE, 5,
368 EGL_ALPHA_SIZE, 0,
369 EGL_NONE,
370 };
371 EGLint major, minor;
372 EGLBoolean b;
373 EGLint n;
374 bool gles = (mode == DISPLAYGL_MODE_ES);
375
376 qemu_egl_display = qemu_egl_get_display(dpy, platform);
377 if (qemu_egl_display == EGL_NO_DISPLAY) {
378 error_report("egl: eglGetDisplay failed");
379 return -1;
380 }
381
382 b = eglInitialize(qemu_egl_display, &major, &minor);
383 if (b == EGL_FALSE) {
384 error_report("egl: eglInitialize failed");
385 return -1;
386 }
387
388 b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
389 if (b == EGL_FALSE) {
390 error_report("egl: eglBindAPI failed (%s mode)",
391 gles ? "gles" : "core");
392 return -1;
393 }
394
395 b = eglChooseConfig(qemu_egl_display,
396 gles ? conf_att_gles : conf_att_core,
397 &qemu_egl_config, 1, &n);
398 if (b == EGL_FALSE || n != 1) {
399 error_report("egl: eglChooseConfig failed (%s mode)",
400 gles ? "gles" : "core");
401 return -1;
402 }
403
404 qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
405 return 0;
406}
407
408int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
409{
410#ifdef EGL_KHR_platform_x11
411 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
412#else
413 return qemu_egl_init_dpy(dpy, 0, mode);
414#endif
415}
416
417int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
418{
419#ifdef EGL_MESA_platform_gbm
420 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
421#else
422 return qemu_egl_init_dpy(dpy, 0, mode);
423#endif
424}
425
426EGLContext qemu_egl_init_ctx(void)
427{
428 static const EGLint ctx_att_core[] = {
429 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
430 EGL_NONE
431 };
432 static const EGLint ctx_att_gles[] = {
433 EGL_CONTEXT_CLIENT_VERSION, 2,
434 EGL_NONE
435 };
436 bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES);
437 EGLContext ectx;
438 EGLBoolean b;
439
440 ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
441 gles ? ctx_att_gles : ctx_att_core);
442 if (ectx == EGL_NO_CONTEXT) {
443 error_report("egl: eglCreateContext failed");
444 return NULL;
445 }
446
447 b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
448 if (b == EGL_FALSE) {
449 error_report("egl: eglMakeCurrent failed");
450 return NULL;
451 }
452
453 return ectx;
454}
455