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{
205 EGLImageKHR image;
206 EGLint num_planes, fd;
207
208 image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
209 EGL_GL_TEXTURE_2D_KHR,
210 (EGLClientBuffer)(unsigned long)tex_id,
211 NULL);
212 if (!image) {
213 return -1;
214 }
215
216 eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
217 &num_planes, NULL);
218 if (num_planes != 1) {
219 eglDestroyImageKHR(qemu_egl_display, image);
220 return -1;
221 }
222 eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
223 eglDestroyImageKHR(qemu_egl_display, image);
224
225 return fd;
226}
227
228void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
229{
230 EGLImageKHR image = EGL_NO_IMAGE_KHR;
231 EGLint attrs[] = {
232 EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf->fd,
233 EGL_DMA_BUF_PLANE0_PITCH_EXT, dmabuf->stride,
234 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
235 EGL_WIDTH, dmabuf->width,
236 EGL_HEIGHT, dmabuf->height,
237 EGL_LINUX_DRM_FOURCC_EXT, dmabuf->fourcc,
238 EGL_NONE,
239 };
240
241 if (dmabuf->texture != 0) {
242 return;
243 }
244
245 image = eglCreateImageKHR(qemu_egl_display,
246 EGL_NO_CONTEXT,
247 EGL_LINUX_DMA_BUF_EXT,
248 NULL, attrs);
249 if (image == EGL_NO_IMAGE_KHR) {
250 error_report("eglCreateImageKHR failed");
251 return;
252 }
253
254 glGenTextures(1, &dmabuf->texture);
255 glBindTexture(GL_TEXTURE_2D, dmabuf->texture);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
257 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
258
259 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
260 eglDestroyImageKHR(qemu_egl_display, image);
261}
262
263void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
264{
265 if (dmabuf->texture == 0) {
266 return;
267 }
268
269 glDeleteTextures(1, &dmabuf->texture);
270 dmabuf->texture = 0;
271}
272
273#endif
274
275
276
277EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
278{
279 EGLSurface esurface;
280 EGLBoolean b;
281
282 esurface = eglCreateWindowSurface(qemu_egl_display,
283 qemu_egl_config,
284 win, NULL);
285 if (esurface == EGL_NO_SURFACE) {
286 error_report("egl: eglCreateWindowSurface failed");
287 return NULL;
288 }
289
290 b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
291 if (b == EGL_FALSE) {
292 error_report("egl: eglMakeCurrent failed");
293 return NULL;
294 }
295
296 return esurface;
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
328
329static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
330 EGLenum platform)
331{
332 EGLDisplay dpy = EGL_NO_DISPLAY;
333
334
335 if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
336 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
337 (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
338 if (getPlatformDisplayEXT && platform != 0) {
339 dpy = getPlatformDisplayEXT(platform, native, NULL);
340 }
341 }
342
343 if (dpy == EGL_NO_DISPLAY) {
344
345 dpy = eglGetDisplay(native);
346 }
347 return dpy;
348}
349
350static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
351 EGLenum platform,
352 DisplayGLMode mode)
353{
354 static const EGLint conf_att_core[] = {
355 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
356 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
357 EGL_RED_SIZE, 5,
358 EGL_GREEN_SIZE, 5,
359 EGL_BLUE_SIZE, 5,
360 EGL_ALPHA_SIZE, 0,
361 EGL_NONE,
362 };
363 static const EGLint conf_att_gles[] = {
364 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
365 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
366 EGL_RED_SIZE, 5,
367 EGL_GREEN_SIZE, 5,
368 EGL_BLUE_SIZE, 5,
369 EGL_ALPHA_SIZE, 0,
370 EGL_NONE,
371 };
372 EGLint major, minor;
373 EGLBoolean b;
374 EGLint n;
375 bool gles = (mode == DISPLAYGL_MODE_ES);
376
377 qemu_egl_display = qemu_egl_get_display(dpy, platform);
378 if (qemu_egl_display == EGL_NO_DISPLAY) {
379 error_report("egl: eglGetDisplay failed");
380 return -1;
381 }
382
383 b = eglInitialize(qemu_egl_display, &major, &minor);
384 if (b == EGL_FALSE) {
385 error_report("egl: eglInitialize failed");
386 return -1;
387 }
388
389 b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
390 if (b == EGL_FALSE) {
391 error_report("egl: eglBindAPI failed (%s mode)",
392 gles ? "gles" : "core");
393 return -1;
394 }
395
396 b = eglChooseConfig(qemu_egl_display,
397 gles ? conf_att_gles : conf_att_core,
398 &qemu_egl_config, 1, &n);
399 if (b == EGL_FALSE || n != 1) {
400 error_report("egl: eglChooseConfig failed (%s mode)",
401 gles ? "gles" : "core");
402 return -1;
403 }
404
405 qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
406 return 0;
407}
408
409int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
410{
411#ifdef EGL_KHR_platform_x11
412 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
413#else
414 return qemu_egl_init_dpy(dpy, 0, mode);
415#endif
416}
417
418int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
419{
420#ifdef EGL_MESA_platform_gbm
421 return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
422#else
423 return qemu_egl_init_dpy(dpy, 0, mode);
424#endif
425}
426
427EGLContext qemu_egl_init_ctx(void)
428{
429 static const EGLint ctx_att_core[] = {
430 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
431 EGL_NONE
432 };
433 static const EGLint ctx_att_gles[] = {
434 EGL_CONTEXT_CLIENT_VERSION, 2,
435 EGL_NONE
436 };
437 bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES);
438 EGLContext ectx;
439 EGLBoolean b;
440
441 ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
442 gles ? ctx_att_gles : ctx_att_core);
443 if (ectx == EGL_NO_CONTEXT) {
444 error_report("egl: eglCreateContext failed");
445 return NULL;
446 }
447
448 b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
449 if (b == EGL_FALSE) {
450 error_report("egl: eglMakeCurrent failed");
451 return NULL;
452 }
453
454 return ectx;
455}
456