| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2023-02-10 12:19:31 +01:00
										 |  |  | #include "qemu/error-report.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-23 16:35:07 +02:00
										 |  |  | #include "qemu/module.h"
 | 
					
						
							| 
									
										
										
										
											2023-02-14 16:11:24 +04:00
										 |  |  | #include "qapi/error.h"
 | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | #include "ui/console.h"
 | 
					
						
							|  |  |  | #include "ui/egl-helpers.h"
 | 
					
						
							|  |  |  | #include "ui/egl-context.h"
 | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  | #include "ui/shader.h"
 | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | typedef struct egl_dpy { | 
					
						
							|  |  |  |     DisplayChangeListener dcl; | 
					
						
							|  |  |  |     DisplaySurface *ds; | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     QemuGLShader *gls; | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  |     egl_fb guest_fb; | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     egl_fb cursor_fb; | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  |     egl_fb blit_fb; | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |     bool y_0_top; | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     uint32_t pos_x; | 
					
						
							|  |  |  |     uint32_t pos_y; | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | } egl_dpy; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  | /* ------------------------------------------------------------------ */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | static void egl_refresh(DisplayChangeListener *dcl) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     graphic_hw_update(dcl->con); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void egl_gfx_update(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                            int x, int y, int w, int h) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void egl_gfx_switch(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                            struct DisplaySurface *new_surface) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     edpy->ds = new_surface; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 23:48:46 +04:00
										 |  |  | static QEMUGLContext egl_create_context(DisplayGLCtx *dgc, | 
					
						
							| 
									
										
										
										
											2018-11-29 13:35:02 +01:00
										 |  |  |                                         QEMUGLParams *params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, | 
					
						
							|  |  |  |                    qemu_egl_rn_ctx); | 
					
						
							| 
									
										
										
										
											2021-10-09 23:48:46 +04:00
										 |  |  |     return qemu_egl_create_context(dgc, params); | 
					
						
							| 
									
										
										
										
											2018-11-29 13:35:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | static void egl_scanout_disable(DisplayChangeListener *dcl) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  |     egl_fb_destroy(&edpy->guest_fb); | 
					
						
							|  |  |  |     egl_fb_destroy(&edpy->blit_fb); | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void egl_scanout_texture(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                                 uint32_t backing_id, | 
					
						
							|  |  |  |                                 bool backing_y_0_top, | 
					
						
							|  |  |  |                                 uint32_t backing_width, | 
					
						
							|  |  |  |                                 uint32_t backing_height, | 
					
						
							|  |  |  |                                 uint32_t x, uint32_t y, | 
					
						
							|  |  |  |                                 uint32_t w, uint32_t h) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     edpy->y_0_top = backing_y_0_top; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* source framebuffer */ | 
					
						
							| 
									
										
										
										
											2017-09-27 13:50:31 +02:00
										 |  |  |     egl_fb_setup_for_tex(&edpy->guest_fb, | 
					
						
							|  |  |  |                          backing_width, backing_height, backing_id, false); | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* dest framebuffer */ | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  |     if (edpy->blit_fb.width  != backing_width || | 
					
						
							|  |  |  |         edpy->blit_fb.height != backing_height) { | 
					
						
							|  |  |  |         egl_fb_destroy(&edpy->blit_fb); | 
					
						
							| 
									
										
										
										
											2017-09-27 13:50:31 +02:00
										 |  |  |         egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height); | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  | static void egl_scanout_dmabuf(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                                QemuDmaBuf *dmabuf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dmabuf_import_texture(dmabuf); | 
					
						
							|  |  |  |     if (!dmabuf->texture) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     egl_scanout_texture(dcl, dmabuf->texture, | 
					
						
							|  |  |  |                         false, dmabuf->width, dmabuf->height, | 
					
						
							|  |  |  |                         0, 0, dmabuf->width, dmabuf->height); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void egl_cursor_dmabuf(DisplayChangeListener *dcl, | 
					
						
							| 
									
										
										
										
											2018-02-20 12:04:31 +01:00
										 |  |  |                               QemuDmaBuf *dmabuf, bool have_hot, | 
					
						
							|  |  |  |                               uint32_t hot_x, uint32_t hot_y) | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-20 12:04:32 +01:00
										 |  |  |     if (dmabuf) { | 
					
						
							|  |  |  |         egl_dmabuf_import_texture(dmabuf); | 
					
						
							|  |  |  |         if (!dmabuf->texture) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height, | 
					
						
							|  |  |  |                              dmabuf->texture, false); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         egl_fb_destroy(&edpy->cursor_fb); | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-20 12:04:31 +01:00
										 |  |  | static void egl_cursor_position(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                                 uint32_t pos_x, uint32_t pos_y) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     edpy->pos_x = pos_x; | 
					
						
							|  |  |  |     edpy->pos_y = pos_y; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  | static void egl_release_dmabuf(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                                QemuDmaBuf *dmabuf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dmabuf_release_texture(dmabuf); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | static void egl_scanout_flush(DisplayChangeListener *dcl, | 
					
						
							|  |  |  |                               uint32_t x, uint32_t y, | 
					
						
							|  |  |  |                               uint32_t w, uint32_t h) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  |     if (!edpy->guest_fb.texture || !edpy->ds) { | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     if (edpy->cursor_fb.texture) { | 
					
						
							|  |  |  |         /* have cursor -> render using textures */ | 
					
						
							|  |  |  |         egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, | 
					
						
							|  |  |  |                          !edpy->y_0_top); | 
					
						
							|  |  |  |         egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, | 
					
						
							| 
									
										
										
										
											2019-01-25 15:47:23 +08:00
										 |  |  |                           !edpy->y_0_top, edpy->pos_x, edpy->pos_y, | 
					
						
							|  |  |  |                           1.0, 1.0); | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         /* no cursor -> use simple framebuffer blit */ | 
					
						
							|  |  |  |         egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-06-14 10:41:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-09 09:39:11 +02:00
										 |  |  |     egl_fb_read(edpy->ds, &edpy->blit_fb); | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |     dpy_gfx_update(edpy->dcl.con, x, y, w, h); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const DisplayChangeListenerOps egl_ops = { | 
					
						
							|  |  |  |     .dpy_name                = "egl-headless", | 
					
						
							|  |  |  |     .dpy_refresh             = egl_refresh, | 
					
						
							|  |  |  |     .dpy_gfx_update          = egl_gfx_update, | 
					
						
							|  |  |  |     .dpy_gfx_switch          = egl_gfx_switch, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .dpy_gl_scanout_disable  = egl_scanout_disable, | 
					
						
							|  |  |  |     .dpy_gl_scanout_texture  = egl_scanout_texture, | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     .dpy_gl_scanout_dmabuf   = egl_scanout_dmabuf, | 
					
						
							|  |  |  |     .dpy_gl_cursor_dmabuf    = egl_cursor_dmabuf, | 
					
						
							| 
									
										
										
										
											2018-02-20 12:04:31 +01:00
										 |  |  |     .dpy_gl_cursor_position  = egl_cursor_position, | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |     .dpy_gl_release_dmabuf   = egl_release_dmabuf, | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |     .dpy_gl_update           = egl_scanout_flush, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 19:33:37 +04:00
										 |  |  | static bool | 
					
						
							|  |  |  | egl_is_compatible_dcl(DisplayGLCtx *dgc, | 
					
						
							|  |  |  |                       DisplayChangeListener *dcl) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-02-16 19:42:40 +04:00
										 |  |  |     if (!dcl->ops->dpy_gl_update) { | 
					
						
							|  |  |  |         /*
 | 
					
						
							|  |  |  |          * egl-headless is compatible with all 2d listeners, as it blits the GL | 
					
						
							|  |  |  |          * updates on the 2d console surface. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 19:33:37 +04:00
										 |  |  |     return dcl->ops == &egl_ops; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 23:48:46 +04:00
										 |  |  | static const DisplayGLCtxOps eglctx_ops = { | 
					
						
							| 
									
										
										
										
											2022-02-16 19:33:37 +04:00
										 |  |  |     .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, | 
					
						
							| 
									
										
										
										
											2021-10-09 23:48:46 +04:00
										 |  |  |     .dpy_gl_ctx_create       = egl_create_context, | 
					
						
							|  |  |  |     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context, | 
					
						
							|  |  |  |     .dpy_gl_ctx_make_current = qemu_egl_make_context_current, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-01 11:05:39 +01:00
										 |  |  | static void early_egl_headless_init(DisplayOptions *opts) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-02-14 16:11:24 +04:00
										 |  |  |     DisplayGLMode mode = DISPLAYGL_MODE_ON; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (opts->has_gl) { | 
					
						
							|  |  |  |         mode = opts->gl; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal); | 
					
						
							| 
									
										
										
										
											2018-03-01 11:05:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     QemuConsole *con; | 
					
						
							|  |  |  |     egl_dpy *edpy; | 
					
						
							|  |  |  |     int idx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (idx = 0;; idx++) { | 
					
						
							| 
									
										
										
										
											2021-10-09 23:48:46 +04:00
										 |  |  |         DisplayGLCtx *ctx; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |         con = qemu_console_lookup_by_index(idx); | 
					
						
							|  |  |  |         if (!con || !qemu_console_is_graphic(con)) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         edpy = g_new0(egl_dpy, 1); | 
					
						
							|  |  |  |         edpy->dcl.con = con; | 
					
						
							|  |  |  |         edpy->dcl.ops = &egl_ops; | 
					
						
							| 
									
										
										
										
											2017-10-10 15:54:53 +02:00
										 |  |  |         edpy->gls = qemu_gl_init_shader(); | 
					
						
							| 
									
										
										
										
											2021-10-09 23:48:46 +04:00
										 |  |  |         ctx = g_new0(DisplayGLCtx, 1); | 
					
						
							|  |  |  |         ctx->ops = &eglctx_ops; | 
					
						
							|  |  |  |         qemu_console_set_display_gl_ctx(con, ctx); | 
					
						
							| 
									
										
										
										
											2017-05-05 12:41:01 +02:00
										 |  |  |         register_displaychangelistener(&edpy->dcl); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-01 11:05:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | static QemuDisplay qemu_display_egl = { | 
					
						
							|  |  |  |     .type       = DISPLAY_TYPE_EGL_HEADLESS, | 
					
						
							|  |  |  |     .early_init = early_egl_headless_init, | 
					
						
							|  |  |  |     .init       = egl_headless_init, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void register_egl(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_display_register(&qemu_display_egl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type_init(register_egl); | 
					
						
							| 
									
										
										
										
											2021-06-24 12:38:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | module_dep("ui-opengl"); |