1 #include "qemu/osdep.h" 2 #include "qemu/error-report.h" 3 #include "qemu/module.h" 4 #include "qapi/error.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 void *d3d_tex2d) 66 { 67 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 68 69 edpy->y_0_top = backing_y_0_top; 70 71 /* source framebuffer */ 72 egl_fb_setup_for_tex(&edpy->guest_fb, 73 backing_width, backing_height, backing_id, false); 74 75 /* dest framebuffer */ 76 if (edpy->blit_fb.width != backing_width || 77 edpy->blit_fb.height != backing_height) { 78 egl_fb_destroy(&edpy->blit_fb); 79 egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height); 80 } 81 } 82 83 #ifdef CONFIG_GBM 84 85 static void egl_scanout_dmabuf(DisplayChangeListener *dcl, 86 QemuDmaBuf *dmabuf) 87 { 88 egl_dmabuf_import_texture(dmabuf); 89 if (!dmabuf->texture) { 90 return; 91 } 92 93 egl_scanout_texture(dcl, dmabuf->texture, 94 false, dmabuf->width, dmabuf->height, 95 0, 0, dmabuf->width, dmabuf->height, NULL); 96 } 97 98 static void egl_cursor_dmabuf(DisplayChangeListener *dcl, 99 QemuDmaBuf *dmabuf, bool have_hot, 100 uint32_t hot_x, uint32_t hot_y) 101 { 102 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 103 104 if (dmabuf) { 105 egl_dmabuf_import_texture(dmabuf); 106 if (!dmabuf->texture) { 107 return; 108 } 109 egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height, 110 dmabuf->texture, false); 111 } else { 112 egl_fb_destroy(&edpy->cursor_fb); 113 } 114 } 115 116 static void egl_release_dmabuf(DisplayChangeListener *dcl, 117 QemuDmaBuf *dmabuf) 118 { 119 egl_dmabuf_release_texture(dmabuf); 120 } 121 122 #endif 123 124 static void egl_cursor_position(DisplayChangeListener *dcl, 125 uint32_t pos_x, uint32_t pos_y) 126 { 127 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 128 129 edpy->pos_x = pos_x; 130 edpy->pos_y = pos_y; 131 } 132 133 static void egl_scanout_flush(DisplayChangeListener *dcl, 134 uint32_t x, uint32_t y, 135 uint32_t w, uint32_t h) 136 { 137 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); 138 139 if (!edpy->guest_fb.texture || !edpy->ds) { 140 return; 141 } 142 assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); 143 144 if (edpy->cursor_fb.texture) { 145 /* have cursor -> render using textures */ 146 egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, 147 !edpy->y_0_top); 148 egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, 149 !edpy->y_0_top, edpy->pos_x, edpy->pos_y, 150 1.0, 1.0); 151 } else { 152 /* no cursor -> use simple framebuffer blit */ 153 egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); 154 } 155 156 egl_fb_read(edpy->ds, &edpy->blit_fb); 157 dpy_gfx_update(edpy->dcl.con, x, y, w, h); 158 } 159 160 static const DisplayChangeListenerOps egl_ops = { 161 .dpy_name = "egl-headless", 162 .dpy_refresh = egl_refresh, 163 .dpy_gfx_update = egl_gfx_update, 164 .dpy_gfx_switch = egl_gfx_switch, 165 166 .dpy_gl_scanout_disable = egl_scanout_disable, 167 .dpy_gl_scanout_texture = egl_scanout_texture, 168 #ifdef CONFIG_GBM 169 .dpy_gl_scanout_dmabuf = egl_scanout_dmabuf, 170 .dpy_gl_cursor_dmabuf = egl_cursor_dmabuf, 171 .dpy_gl_release_dmabuf = egl_release_dmabuf, 172 #endif 173 .dpy_gl_cursor_position = egl_cursor_position, 174 .dpy_gl_update = egl_scanout_flush, 175 }; 176 177 static bool 178 egl_is_compatible_dcl(DisplayGLCtx *dgc, 179 DisplayChangeListener *dcl) 180 { 181 if (!dcl->ops->dpy_gl_update) { 182 /* 183 * egl-headless is compatible with all 2d listeners, as it blits the GL 184 * updates on the 2d console surface. 185 */ 186 return true; 187 } 188 189 return dcl->ops == &egl_ops; 190 } 191 192 static const DisplayGLCtxOps eglctx_ops = { 193 .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, 194 .dpy_gl_ctx_create = egl_create_context, 195 .dpy_gl_ctx_destroy = qemu_egl_destroy_context, 196 .dpy_gl_ctx_make_current = qemu_egl_make_context_current, 197 }; 198 199 static void early_egl_headless_init(DisplayOptions *opts) 200 { 201 DisplayGLMode mode = DISPLAYGL_MODE_ON; 202 203 if (opts->has_gl) { 204 mode = opts->gl; 205 } 206 207 egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal); 208 } 209 210 static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) 211 { 212 QemuConsole *con; 213 egl_dpy *edpy; 214 int idx; 215 216 for (idx = 0;; idx++) { 217 DisplayGLCtx *ctx; 218 219 con = qemu_console_lookup_by_index(idx); 220 if (!con || !qemu_console_is_graphic(con)) { 221 break; 222 } 223 224 edpy = g_new0(egl_dpy, 1); 225 edpy->dcl.con = con; 226 edpy->dcl.ops = &egl_ops; 227 edpy->gls = qemu_gl_init_shader(); 228 ctx = g_new0(DisplayGLCtx, 1); 229 ctx->ops = &eglctx_ops; 230 qemu_console_set_display_gl_ctx(con, ctx); 231 register_displaychangelistener(&edpy->dcl); 232 } 233 } 234 235 static QemuDisplay qemu_display_egl = { 236 .type = DISPLAY_TYPE_EGL_HEADLESS, 237 .early_init = early_egl_headless_init, 238 .init = egl_headless_init, 239 }; 240 241 static void register_egl(void) 242 { 243 qemu_display_register(&qemu_display_egl); 244 } 245 246 type_init(register_egl); 247 248 module_dep("ui-opengl"); 249