1 #include "qemu/osdep.h" 2 #include "qemu/error-report.h" 3 #include "qemu/module.h" 4 #include "sysemu/sysemu.h" 5 #include "ui/console.h" 6 #include "ui/egl-helpers.h" 7 #include "ui/egl-context.h" 8 #include "ui/shader.h" 9 10 typedef struct egl_dpy { 11 DisplayChangeListener dcl; 12 DisplaySurface *ds; 13 QemuGLShader *gls; 14 egl_fb guest_fb; 15 egl_fb cursor_fb; 16 egl_fb blit_fb; 17 bool y_0_top; 18 uint32_t pos_x; 19 uint32_t pos_y; 20 } egl_dpy; 21 22 /* ------------------------------------------------------------------ */ 23 24 static void egl_refresh(DisplayChangeListener *dcl) 25 { 26 graphic_hw_update(dcl->con); 27 } 28 29 static void egl_gfx_update(DisplayChangeListener *dcl, 30 int x, int y, int w, int h) 31 { 32 } 33 34 static void egl_gfx_switch(DisplayChangeListener *dcl, 35 struct DisplaySurface *new_surface) 36 { 37 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 38 39 edpy->ds = new_surface; 40 } 41 42 static QEMUGLContext egl_create_context(DisplayGLCtx *dgc, 43 QEMUGLParams *params) 44 { 45 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 46 qemu_egl_rn_ctx); 47 return qemu_egl_create_context(dgc, params); 48 } 49 50 static void egl_scanout_disable(DisplayChangeListener *dcl) 51 { 52 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 53 54 egl_fb_destroy(&edpy->guest_fb); 55 egl_fb_destroy(&edpy->blit_fb); 56 } 57 58 static void egl_scanout_texture(DisplayChangeListener *dcl, 59 uint32_t backing_id, 60 bool backing_y_0_top, 61 uint32_t backing_width, 62 uint32_t backing_height, 63 uint32_t x, uint32_t y, 64 uint32_t w, uint32_t h) 65 { 66 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 67 68 edpy->y_0_top = backing_y_0_top; 69 70 /* source framebuffer */ 71 egl_fb_setup_for_tex(&edpy->guest_fb, 72 backing_width, backing_height, backing_id, false); 73 74 /* dest framebuffer */ 75 if (edpy->blit_fb.width != backing_width || 76 edpy->blit_fb.height != backing_height) { 77 egl_fb_destroy(&edpy->blit_fb); 78 egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height); 79 } 80 } 81 82 static void egl_scanout_dmabuf(DisplayChangeListener *dcl, 83 QemuDmaBuf *dmabuf) 84 { 85 egl_dmabuf_import_texture(dmabuf); 86 if (!dmabuf->texture) { 87 return; 88 } 89 90 egl_scanout_texture(dcl, dmabuf->texture, 91 false, dmabuf->width, dmabuf->height, 92 0, 0, dmabuf->width, dmabuf->height); 93 } 94 95 static void egl_cursor_dmabuf(DisplayChangeListener *dcl, 96 QemuDmaBuf *dmabuf, bool have_hot, 97 uint32_t hot_x, uint32_t hot_y) 98 { 99 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 100 101 if (dmabuf) { 102 egl_dmabuf_import_texture(dmabuf); 103 if (!dmabuf->texture) { 104 return; 105 } 106 egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height, 107 dmabuf->texture, false); 108 } else { 109 egl_fb_destroy(&edpy->cursor_fb); 110 } 111 } 112 113 static void egl_cursor_position(DisplayChangeListener *dcl, 114 uint32_t pos_x, uint32_t pos_y) 115 { 116 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 117 118 edpy->pos_x = pos_x; 119 edpy->pos_y = pos_y; 120 } 121 122 static void egl_release_dmabuf(DisplayChangeListener *dcl, 123 QemuDmaBuf *dmabuf) 124 { 125 egl_dmabuf_release_texture(dmabuf); 126 } 127 128 static void egl_scanout_flush(DisplayChangeListener *dcl, 129 uint32_t x, uint32_t y, 130 uint32_t w, uint32_t h) 131 { 132 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 133 134 if (!edpy->guest_fb.texture || !edpy->ds) { 135 return; 136 } 137 assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); 138 139 if (edpy->cursor_fb.texture) { 140 /* have cursor -> render using textures */ 141 egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, 142 !edpy->y_0_top); 143 egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, 144 !edpy->y_0_top, edpy->pos_x, edpy->pos_y, 145 1.0, 1.0); 146 } else { 147 /* no cursor -> use simple framebuffer blit */ 148 egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); 149 } 150 151 egl_fb_read(edpy->ds, &edpy->blit_fb); 152 dpy_gfx_update(edpy->dcl.con, x, y, w, h); 153 } 154 155 static const DisplayChangeListenerOps egl_ops = { 156 .dpy_name = "egl-headless", 157 .dpy_refresh = egl_refresh, 158 .dpy_gfx_update = egl_gfx_update, 159 .dpy_gfx_switch = egl_gfx_switch, 160 161 .dpy_gl_scanout_disable = egl_scanout_disable, 162 .dpy_gl_scanout_texture = egl_scanout_texture, 163 .dpy_gl_scanout_dmabuf = egl_scanout_dmabuf, 164 .dpy_gl_cursor_dmabuf = egl_cursor_dmabuf, 165 .dpy_gl_cursor_position = egl_cursor_position, 166 .dpy_gl_release_dmabuf = egl_release_dmabuf, 167 .dpy_gl_update = egl_scanout_flush, 168 }; 169 170 static bool 171 egl_is_compatible_dcl(DisplayGLCtx *dgc, 172 DisplayChangeListener *dcl) 173 { 174 if (!dcl->ops->dpy_gl_update) { 175 /* 176 * egl-headless is compatible with all 2d listeners, as it blits the GL 177 * updates on the 2d console surface. 178 */ 179 return true; 180 } 181 182 return dcl->ops == &egl_ops; 183 } 184 185 static const DisplayGLCtxOps eglctx_ops = { 186 .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, 187 .dpy_gl_ctx_create = egl_create_context, 188 .dpy_gl_ctx_destroy = qemu_egl_destroy_context, 189 .dpy_gl_ctx_make_current = qemu_egl_make_context_current, 190 }; 191 192 static void early_egl_headless_init(DisplayOptions *opts) 193 { 194 display_opengl = 1; 195 } 196 197 static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) 198 { 199 DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; 200 QemuConsole *con; 201 egl_dpy *edpy; 202 int idx; 203 204 if (egl_rendernode_init(opts->u.egl_headless.rendernode, mode) < 0) { 205 error_report("egl: render node init failed"); 206 exit(1); 207 } 208 209 for (idx = 0;; idx++) { 210 DisplayGLCtx *ctx; 211 212 con = qemu_console_lookup_by_index(idx); 213 if (!con || !qemu_console_is_graphic(con)) { 214 break; 215 } 216 217 edpy = g_new0(egl_dpy, 1); 218 edpy->dcl.con = con; 219 edpy->dcl.ops = &egl_ops; 220 edpy->gls = qemu_gl_init_shader(); 221 ctx = g_new0(DisplayGLCtx, 1); 222 ctx->ops = &eglctx_ops; 223 qemu_console_set_display_gl_ctx(con, ctx); 224 register_displaychangelistener(&edpy->dcl); 225 } 226 } 227 228 static QemuDisplay qemu_display_egl = { 229 .type = DISPLAY_TYPE_EGL_HEADLESS, 230 .early_init = early_egl_headless_init, 231 .init = egl_headless_init, 232 }; 233 234 static void register_egl(void) 235 { 236 qemu_display_register(&qemu_display_egl); 237 } 238 239 type_init(register_egl); 240 241 module_dep("ui-opengl"); 242