| 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 | DisplayGLCtx *ctx; |
| 13 | DisplaySurface *ds; |
| 14 | QemuGLShader *gls; |
| 15 | egl_fb guest_fb; |
| 16 | egl_fb cursor_fb; |
| 17 | egl_fb blit_fb; |
| 18 | bool y_0_top; |
| 19 | uint32_t pos_x; |
| 20 | uint32_t pos_y; |
| 21 | } egl_dpy; |
| 22 | |
| 23 | static GPtrArray *egl_dpys; |
| 24 | |
| 25 | /* ------------------------------------------------------------------ */ |
| 26 | |
| 27 | static void egl_refresh(DisplayChangeListener *dcl) |
| 28 | { |
| 29 | qemu_console_hw_update(dcl->con); |
| 30 | } |
| 31 | |
| 32 | static void egl_gfx_update(DisplayChangeListener *dcl, |
| 33 | int x, int y, int w, int h) |
| 34 | { |
| 35 | } |
| 36 | |
| 37 | static void egl_gfx_switch(DisplayChangeListener *dcl, |
| 38 | struct DisplaySurface *new_surface) |
| 39 | { |
| 40 | egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); |
| 41 | |
| 42 | edpy->ds = new_surface; |
| 43 | } |
| 44 | |
| 45 | static QEMUGLContext egl_create_context(DisplayGLCtx *dgc, |
| 46 | QEMUGLParams *params) |
| 47 | { |
| 48 | return qemu_egl_create_context(dgc, params, qemu_egl_rn_ctx); |
| 49 | } |
| 50 | |
| 51 | static void egl_scanout_disable(DisplayChangeListener *dcl) |
| 52 | { |
| 53 | egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); |
| 54 | |
| 55 | egl_fb_destroy(&edpy->guest_fb); |
| 56 | egl_fb_destroy(&edpy->blit_fb); |
| 57 | } |
| 58 | |
| 59 | static void egl_scanout_texture(DisplayChangeListener *dcl, |
| 60 | uint32_t backing_id, |
| 61 | bool backing_y_0_top, |
| 62 | uint32_t backing_width, |
| 63 | uint32_t backing_height, |
| 64 | uint32_t x, uint32_t y, |
| 65 | uint32_t w, uint32_t h, |
| 66 | void *d3d_tex2d) |
| 67 | { |
| 68 | egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); |
| 69 | |
| 70 | edpy->y_0_top = backing_y_0_top; |
| 71 | |
| 72 | /* source framebuffer */ |
| 73 | egl_fb_setup_for_tex(&edpy->guest_fb, |
| 74 | backing_width, backing_height, backing_id, false); |
| 75 | |
| 76 | /* dest framebuffer */ |
| 77 | if (edpy->blit_fb.width != backing_width || |
| 78 | edpy->blit_fb.height != backing_height) { |
| 79 | egl_fb_destroy(&edpy->blit_fb); |
| 80 | egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | #ifdef CONFIG_GBM |
| 85 | |
| 86 | static void egl_scanout_dmabuf(DisplayChangeListener *dcl, |
| 87 | QemuDmaBuf *dmabuf) |
| 88 | { |
| 89 | uint32_t width, height, texture; |
| 90 | |
| 91 | egl_dmabuf_import_texture(dmabuf); |
| 92 | texture = qemu_dmabuf_get_texture(dmabuf); |
| 93 | if (!texture) { |
| 94 | return; |
| 95 | } |
| 96 | |
| 97 | width = qemu_dmabuf_get_width(dmabuf); |
| 98 | height = qemu_dmabuf_get_height(dmabuf); |
| 99 | |
| 100 | egl_scanout_texture(dcl, texture, false, width, height, 0, 0, |
| 101 | width, height, NULL); |
| 102 | } |
| 103 | |
| 104 | static void egl_cursor_dmabuf(DisplayChangeListener *dcl, |
| 105 | QemuDmaBuf *dmabuf, bool have_hot, |
| 106 | uint32_t hot_x, uint32_t hot_y) |
| 107 | { |
| 108 | uint32_t width, height, texture; |
| 109 | egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); |
| 110 | |
| 111 | if (dmabuf) { |
| 112 | egl_dmabuf_import_texture(dmabuf); |
| 113 | texture = qemu_dmabuf_get_texture(dmabuf); |
| 114 | if (!texture) { |
| 115 | return; |
| 116 | } |
| 117 | |
| 118 | width = qemu_dmabuf_get_width(dmabuf); |
| 119 | height = qemu_dmabuf_get_height(dmabuf); |
| 120 | egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false); |
| 121 | } else { |
| 122 | egl_fb_destroy(&edpy->cursor_fb); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | static void egl_release_dmabuf(DisplayChangeListener *dcl, |
| 127 | QemuDmaBuf *dmabuf) |
| 128 | { |
| 129 | egl_dmabuf_release_texture(dmabuf); |
| 130 | } |
| 131 | |
| 132 | #endif |
| 133 | |
| 134 | static void egl_cursor_position(DisplayChangeListener *dcl, |
| 135 | uint32_t pos_x, uint32_t pos_y) |
| 136 | { |
| 137 | egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); |
| 138 | |
| 139 | edpy->pos_x = pos_x; |
| 140 | edpy->pos_y = pos_y; |
| 141 | } |
| 142 | |
| 143 | static void egl_scanout_flush(DisplayChangeListener *dcl, |
| 144 | uint32_t x, uint32_t y, |
| 145 | uint32_t w, uint32_t h) |
| 146 | { |
| 147 | egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); |
| 148 | |
| 149 | if (!edpy->guest_fb.texture || !edpy->ds) { |
| 150 | return; |
| 151 | } |
| 152 | assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8); |
| 153 | |
| 154 | if (edpy->cursor_fb.texture) { |
| 155 | /* have cursor -> render using textures */ |
| 156 | egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, |
| 157 | !edpy->y_0_top); |
| 158 | egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, |
| 159 | !edpy->y_0_top, edpy->pos_x, edpy->pos_y, |
| 160 | 1.0, 1.0); |
| 161 | } else { |
| 162 | /* no cursor -> use simple framebuffer blit */ |
| 163 | egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); |
| 164 | } |
| 165 | |
| 166 | egl_fb_read(edpy->ds, &edpy->blit_fb); |
| 167 | qemu_console_update(edpy->dcl.con, x, y, w, h); |
| 168 | } |
| 169 | |
| 170 | static const DisplayChangeListenerOps egl_ops = { |
| 171 | .dpy_name = "egl-headless", |
| 172 | .dpy_refresh = egl_refresh, |
| 173 | .dpy_gfx_update = egl_gfx_update, |
| 174 | .dpy_gfx_switch = egl_gfx_switch, |
| 175 | |
| 176 | .dpy_gl_scanout_disable = egl_scanout_disable, |
| 177 | .dpy_gl_scanout_texture = egl_scanout_texture, |
| 178 | #ifdef CONFIG_GBM |
| 179 | .dpy_gl_scanout_dmabuf = egl_scanout_dmabuf, |
| 180 | .dpy_gl_cursor_dmabuf = egl_cursor_dmabuf, |
| 181 | .dpy_gl_release_dmabuf = egl_release_dmabuf, |
| 182 | #endif |
| 183 | .dpy_gl_cursor_position = egl_cursor_position, |
| 184 | .dpy_gl_update = egl_scanout_flush, |
| 185 | }; |
| 186 | |
| 187 | static bool |
| 188 | egl_is_compatible_dcl(DisplayGLCtx *dgc, |
| 189 | DisplayChangeListener *dcl) |
| 190 | { |
| 191 | if (!dcl->ops->dpy_gl_update) { |
| 192 | /* |
| 193 | * egl-headless is compatible with all 2d listeners, as it blits the GL |
| 194 | * updates on the 2d console surface. |
| 195 | */ |
| 196 | return true; |
| 197 | } |
| 198 | |
| 199 | return dcl->ops == &egl_ops; |
| 200 | } |
| 201 | |
| 202 | static const DisplayGLCtxOps eglctx_ops = { |
| 203 | .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, |
| 204 | .dpy_gl_ctx_create = egl_create_context, |
| 205 | .dpy_gl_ctx_destroy = qemu_egl_destroy_context, |
| 206 | .dpy_gl_ctx_make_current = qemu_egl_make_context_current, |
| 207 | }; |
| 208 | |
| 209 | static void early_egl_headless_init(DisplayOptions *opts) |
| 210 | { |
| 211 | DisplayGLMode mode = DISPLAY_GL_MODE_ON; |
| 212 | |
| 213 | if (opts->has_gl) { |
| 214 | mode = opts->gl; |
| 215 | } |
| 216 | |
| 217 | egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal); |
| 218 | } |
| 219 | |
| 220 | static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) |
| 221 | { |
| 222 | QemuConsole *con; |
| 223 | egl_dpy *edpy; |
| 224 | int idx; |
| 225 | |
| 226 | egl_dpys = g_ptr_array_new(); |
| 227 | |
| 228 | for (idx = 0;; idx++) { |
| 229 | DisplayGLCtx *ctx; |
| 230 | |
| 231 | con = qemu_console_lookup_by_index(idx); |
| 232 | if (!con || !qemu_console_is_graphic(con)) { |
| 233 | break; |
| 234 | } |
| 235 | |
| 236 | edpy = g_new0(egl_dpy, 1); |
| 237 | edpy->gls = qemu_gl_init_shader(); |
| 238 | ctx = g_new0(DisplayGLCtx, 1); |
| 239 | ctx->ops = &eglctx_ops; |
| 240 | edpy->ctx = ctx; |
| 241 | qemu_console_set_display_gl_ctx(con, ctx); |
| 242 | qemu_console_register_listener(con, &edpy->dcl, &egl_ops); |
| 243 | g_ptr_array_add(egl_dpys, edpy); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | static void egl_headless_cleanup(void) |
| 248 | { |
| 249 | if (!egl_dpys) { |
| 250 | return; |
| 251 | } |
| 252 | |
| 253 | for (guint i = 0; i < egl_dpys->len; i++) { |
| 254 | egl_dpy *edpy = g_ptr_array_index(egl_dpys, i); |
| 255 | |
| 256 | qemu_console_unregister_listener(&edpy->dcl); |
| 257 | qemu_console_set_display_gl_ctx(edpy->dcl.con, NULL); |
| 258 | egl_fb_destroy(&edpy->guest_fb); |
| 259 | egl_fb_destroy(&edpy->cursor_fb); |
| 260 | egl_fb_destroy(&edpy->blit_fb); |
| 261 | qemu_gl_fini_shader(edpy->gls); |
| 262 | g_free(edpy->ctx); |
| 263 | g_free(edpy); |
| 264 | } |
| 265 | g_clear_pointer(&egl_dpys, g_ptr_array_unref); |
| 266 | |
| 267 | egl_cleanup(); |
| 268 | } |
| 269 | |
| 270 | static QemuDisplay qemu_display_egl = { |
| 271 | .type = DISPLAY_TYPE_EGL_HEADLESS, |
| 272 | .early_init = early_egl_headless_init, |
| 273 | .init = egl_headless_init, |
| 274 | .cleanup = egl_headless_cleanup, |
| 275 | }; |
| 276 | |
| 277 | static void register_egl(void) |
| 278 | { |
| 279 | qemu_display_register(&qemu_display_egl); |
| 280 | } |
| 281 | |
| 282 | type_init(register_egl); |
| 283 | |
| 284 | module_dep("ui-opengl"); |