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