xref: /qemu/ui/egl-headless.c (revision 5feed38c2139a2cea46b4b540303ef255d4cafc7)
1bb1599b6SGerd Hoffmann #include "qemu/osdep.h"
2*5feed38cSThomas Huth #include "qemu/error-report.h"
30b8fa32fSMarkus Armbruster #include "qemu/module.h"
4bb1599b6SGerd Hoffmann #include "sysemu/sysemu.h"
5bb1599b6SGerd Hoffmann #include "ui/console.h"
6bb1599b6SGerd Hoffmann #include "ui/egl-helpers.h"
7bb1599b6SGerd Hoffmann #include "ui/egl-context.h"
8a3517917SGerd Hoffmann #include "ui/shader.h"
9bb1599b6SGerd Hoffmann 
10bb1599b6SGerd Hoffmann typedef struct egl_dpy {
11bb1599b6SGerd Hoffmann     DisplayChangeListener dcl;
12bb1599b6SGerd Hoffmann     DisplaySurface *ds;
13a3517917SGerd Hoffmann     QemuGLShader *gls;
14d8dc67e1SGerd Hoffmann     egl_fb guest_fb;
15a3517917SGerd Hoffmann     egl_fb cursor_fb;
16d8dc67e1SGerd Hoffmann     egl_fb blit_fb;
17bb1599b6SGerd Hoffmann     bool y_0_top;
18a3517917SGerd Hoffmann     uint32_t pos_x;
19a3517917SGerd Hoffmann     uint32_t pos_y;
20bb1599b6SGerd Hoffmann } egl_dpy;
21bb1599b6SGerd Hoffmann 
22d8dc67e1SGerd Hoffmann /* ------------------------------------------------------------------ */
23d8dc67e1SGerd Hoffmann 
24bb1599b6SGerd Hoffmann static void egl_refresh(DisplayChangeListener *dcl)
25bb1599b6SGerd Hoffmann {
26bb1599b6SGerd Hoffmann     graphic_hw_update(dcl->con);
27bb1599b6SGerd Hoffmann }
28bb1599b6SGerd Hoffmann 
29bb1599b6SGerd Hoffmann static void egl_gfx_update(DisplayChangeListener *dcl,
30bb1599b6SGerd Hoffmann                            int x, int y, int w, int h)
31bb1599b6SGerd Hoffmann {
32bb1599b6SGerd Hoffmann }
33bb1599b6SGerd Hoffmann 
34bb1599b6SGerd Hoffmann static void egl_gfx_switch(DisplayChangeListener *dcl,
35bb1599b6SGerd Hoffmann                            struct DisplaySurface *new_surface)
36bb1599b6SGerd Hoffmann {
37bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
38bb1599b6SGerd Hoffmann 
39bb1599b6SGerd Hoffmann     edpy->ds = new_surface;
40bb1599b6SGerd Hoffmann }
41bb1599b6SGerd Hoffmann 
425e79d516SMarc-André Lureau static QEMUGLContext egl_create_context(DisplayGLCtx *dgc,
43952e5d58SGerd Hoffmann                                         QEMUGLParams *params)
44952e5d58SGerd Hoffmann {
45952e5d58SGerd Hoffmann     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
46952e5d58SGerd Hoffmann                    qemu_egl_rn_ctx);
475e79d516SMarc-André Lureau     return qemu_egl_create_context(dgc, params);
48952e5d58SGerd Hoffmann }
49952e5d58SGerd Hoffmann 
50bb1599b6SGerd Hoffmann static void egl_scanout_disable(DisplayChangeListener *dcl)
51bb1599b6SGerd Hoffmann {
52bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
53bb1599b6SGerd Hoffmann 
54d8dc67e1SGerd Hoffmann     egl_fb_destroy(&edpy->guest_fb);
55d8dc67e1SGerd Hoffmann     egl_fb_destroy(&edpy->blit_fb);
56bb1599b6SGerd Hoffmann }
57bb1599b6SGerd Hoffmann 
58bb1599b6SGerd Hoffmann static void egl_scanout_texture(DisplayChangeListener *dcl,
59bb1599b6SGerd Hoffmann                                 uint32_t backing_id,
60bb1599b6SGerd Hoffmann                                 bool backing_y_0_top,
61bb1599b6SGerd Hoffmann                                 uint32_t backing_width,
62bb1599b6SGerd Hoffmann                                 uint32_t backing_height,
63bb1599b6SGerd Hoffmann                                 uint32_t x, uint32_t y,
64bb1599b6SGerd Hoffmann                                 uint32_t w, uint32_t h)
65bb1599b6SGerd Hoffmann {
66bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
67bb1599b6SGerd Hoffmann 
68bb1599b6SGerd Hoffmann     edpy->y_0_top = backing_y_0_top;
69bb1599b6SGerd Hoffmann 
70bb1599b6SGerd Hoffmann     /* source framebuffer */
7174083f9cSGerd Hoffmann     egl_fb_setup_for_tex(&edpy->guest_fb,
7274083f9cSGerd Hoffmann                          backing_width, backing_height, backing_id, false);
73bb1599b6SGerd Hoffmann 
74bb1599b6SGerd Hoffmann     /* dest framebuffer */
75d8dc67e1SGerd Hoffmann     if (edpy->blit_fb.width  != backing_width ||
76d8dc67e1SGerd Hoffmann         edpy->blit_fb.height != backing_height) {
77d8dc67e1SGerd Hoffmann         egl_fb_destroy(&edpy->blit_fb);
7874083f9cSGerd Hoffmann         egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height);
79bb1599b6SGerd Hoffmann     }
80bb1599b6SGerd Hoffmann }
81bb1599b6SGerd Hoffmann 
82a3517917SGerd Hoffmann static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
83a3517917SGerd Hoffmann                                QemuDmaBuf *dmabuf)
84a3517917SGerd Hoffmann {
85a3517917SGerd Hoffmann     egl_dmabuf_import_texture(dmabuf);
86a3517917SGerd Hoffmann     if (!dmabuf->texture) {
87a3517917SGerd Hoffmann         return;
88a3517917SGerd Hoffmann     }
89a3517917SGerd Hoffmann 
90a3517917SGerd Hoffmann     egl_scanout_texture(dcl, dmabuf->texture,
91a3517917SGerd Hoffmann                         false, dmabuf->width, dmabuf->height,
92a3517917SGerd Hoffmann                         0, 0, dmabuf->width, dmabuf->height);
93a3517917SGerd Hoffmann }
94a3517917SGerd Hoffmann 
95a3517917SGerd Hoffmann static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
966e1f2cb5SGerd Hoffmann                               QemuDmaBuf *dmabuf, bool have_hot,
976e1f2cb5SGerd Hoffmann                               uint32_t hot_x, uint32_t hot_y)
98a3517917SGerd Hoffmann {
99a3517917SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
100a3517917SGerd Hoffmann 
101b0916928SGerd Hoffmann     if (dmabuf) {
102a3517917SGerd Hoffmann         egl_dmabuf_import_texture(dmabuf);
103a3517917SGerd Hoffmann         if (!dmabuf->texture) {
104a3517917SGerd Hoffmann             return;
105a3517917SGerd Hoffmann         }
106a3517917SGerd Hoffmann         egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height,
107a3517917SGerd Hoffmann                              dmabuf->texture, false);
108b0916928SGerd Hoffmann     } else {
109b0916928SGerd Hoffmann         egl_fb_destroy(&edpy->cursor_fb);
110b0916928SGerd Hoffmann     }
111a3517917SGerd Hoffmann }
112a3517917SGerd Hoffmann 
1136e1f2cb5SGerd Hoffmann static void egl_cursor_position(DisplayChangeListener *dcl,
1146e1f2cb5SGerd Hoffmann                                 uint32_t pos_x, uint32_t pos_y)
1156e1f2cb5SGerd Hoffmann {
1166e1f2cb5SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
1176e1f2cb5SGerd Hoffmann 
1186e1f2cb5SGerd Hoffmann     edpy->pos_x = pos_x;
1196e1f2cb5SGerd Hoffmann     edpy->pos_y = pos_y;
1206e1f2cb5SGerd Hoffmann }
1216e1f2cb5SGerd Hoffmann 
122a3517917SGerd Hoffmann static void egl_release_dmabuf(DisplayChangeListener *dcl,
123a3517917SGerd Hoffmann                                QemuDmaBuf *dmabuf)
124a3517917SGerd Hoffmann {
125a3517917SGerd Hoffmann     egl_dmabuf_release_texture(dmabuf);
126a3517917SGerd Hoffmann }
127a3517917SGerd Hoffmann 
128bb1599b6SGerd Hoffmann static void egl_scanout_flush(DisplayChangeListener *dcl,
129bb1599b6SGerd Hoffmann                               uint32_t x, uint32_t y,
130bb1599b6SGerd Hoffmann                               uint32_t w, uint32_t h)
131bb1599b6SGerd Hoffmann {
132bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
133bb1599b6SGerd Hoffmann 
134d8dc67e1SGerd Hoffmann     if (!edpy->guest_fb.texture || !edpy->ds) {
135bb1599b6SGerd Hoffmann         return;
136bb1599b6SGerd Hoffmann     }
137bb1599b6SGerd Hoffmann     assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
138bb1599b6SGerd Hoffmann 
139a3517917SGerd Hoffmann     if (edpy->cursor_fb.texture) {
140a3517917SGerd Hoffmann         /* have cursor -> render using textures */
141a3517917SGerd Hoffmann         egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
142a3517917SGerd Hoffmann                          !edpy->y_0_top);
143a3517917SGerd Hoffmann         egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
144051a0cdeSChen Zhang                           !edpy->y_0_top, edpy->pos_x, edpy->pos_y,
145051a0cdeSChen Zhang                           1.0, 1.0);
146a3517917SGerd Hoffmann     } else {
147a3517917SGerd Hoffmann         /* no cursor -> use simple framebuffer blit */
148d8dc67e1SGerd Hoffmann         egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
149a3517917SGerd Hoffmann     }
150bb1599b6SGerd Hoffmann 
151d2329237SGerd Hoffmann     egl_fb_read(edpy->ds, &edpy->blit_fb);
152bb1599b6SGerd Hoffmann     dpy_gfx_update(edpy->dcl.con, x, y, w, h);
153bb1599b6SGerd Hoffmann }
154bb1599b6SGerd Hoffmann 
155bb1599b6SGerd Hoffmann static const DisplayChangeListenerOps egl_ops = {
156bb1599b6SGerd Hoffmann     .dpy_name                = "egl-headless",
157bb1599b6SGerd Hoffmann     .dpy_refresh             = egl_refresh,
158bb1599b6SGerd Hoffmann     .dpy_gfx_update          = egl_gfx_update,
159bb1599b6SGerd Hoffmann     .dpy_gfx_switch          = egl_gfx_switch,
160bb1599b6SGerd Hoffmann 
161bb1599b6SGerd Hoffmann     .dpy_gl_scanout_disable  = egl_scanout_disable,
162bb1599b6SGerd Hoffmann     .dpy_gl_scanout_texture  = egl_scanout_texture,
163a3517917SGerd Hoffmann     .dpy_gl_scanout_dmabuf   = egl_scanout_dmabuf,
164a3517917SGerd Hoffmann     .dpy_gl_cursor_dmabuf    = egl_cursor_dmabuf,
1656e1f2cb5SGerd Hoffmann     .dpy_gl_cursor_position  = egl_cursor_position,
166a3517917SGerd Hoffmann     .dpy_gl_release_dmabuf   = egl_release_dmabuf,
167bb1599b6SGerd Hoffmann     .dpy_gl_update           = egl_scanout_flush,
168bb1599b6SGerd Hoffmann };
169bb1599b6SGerd Hoffmann 
170a62c4a17SMarc-André Lureau static bool
171a62c4a17SMarc-André Lureau egl_is_compatible_dcl(DisplayGLCtx *dgc,
172a62c4a17SMarc-André Lureau                       DisplayChangeListener *dcl)
173a62c4a17SMarc-André Lureau {
174cd19c25fSMarc-André Lureau     if (!dcl->ops->dpy_gl_update) {
175cd19c25fSMarc-André Lureau         /*
176cd19c25fSMarc-André Lureau          * egl-headless is compatible with all 2d listeners, as it blits the GL
177cd19c25fSMarc-André Lureau          * updates on the 2d console surface.
178cd19c25fSMarc-André Lureau          */
179cd19c25fSMarc-André Lureau         return true;
180cd19c25fSMarc-André Lureau     }
181cd19c25fSMarc-André Lureau 
182a62c4a17SMarc-André Lureau     return dcl->ops == &egl_ops;
183a62c4a17SMarc-André Lureau }
184a62c4a17SMarc-André Lureau 
1855e79d516SMarc-André Lureau static const DisplayGLCtxOps eglctx_ops = {
186a62c4a17SMarc-André Lureau     .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl,
1875e79d516SMarc-André Lureau     .dpy_gl_ctx_create       = egl_create_context,
1885e79d516SMarc-André Lureau     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
1895e79d516SMarc-André Lureau     .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
1905e79d516SMarc-André Lureau };
1915e79d516SMarc-André Lureau 
19216ab0a74SGerd Hoffmann static void early_egl_headless_init(DisplayOptions *opts)
19316ab0a74SGerd Hoffmann {
19416ab0a74SGerd Hoffmann     display_opengl = 1;
19516ab0a74SGerd Hoffmann }
19616ab0a74SGerd Hoffmann 
19716ab0a74SGerd Hoffmann static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
198bb1599b6SGerd Hoffmann {
19954d208ffSGerd Hoffmann     DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
200bb1599b6SGerd Hoffmann     QemuConsole *con;
201bb1599b6SGerd Hoffmann     egl_dpy *edpy;
202bb1599b6SGerd Hoffmann     int idx;
203bb1599b6SGerd Hoffmann 
20491e61947SErik Skultety     if (egl_rendernode_init(opts->u.egl_headless.rendernode, mode) < 0) {
205bb1599b6SGerd Hoffmann         error_report("egl: render node init failed");
206bb1599b6SGerd Hoffmann         exit(1);
207bb1599b6SGerd Hoffmann     }
208bb1599b6SGerd Hoffmann 
209bb1599b6SGerd Hoffmann     for (idx = 0;; idx++) {
2105e79d516SMarc-André Lureau         DisplayGLCtx *ctx;
2115e79d516SMarc-André Lureau 
212bb1599b6SGerd Hoffmann         con = qemu_console_lookup_by_index(idx);
213bb1599b6SGerd Hoffmann         if (!con || !qemu_console_is_graphic(con)) {
214bb1599b6SGerd Hoffmann             break;
215bb1599b6SGerd Hoffmann         }
216bb1599b6SGerd Hoffmann 
217bb1599b6SGerd Hoffmann         edpy = g_new0(egl_dpy, 1);
218bb1599b6SGerd Hoffmann         edpy->dcl.con = con;
219bb1599b6SGerd Hoffmann         edpy->dcl.ops = &egl_ops;
220a3517917SGerd Hoffmann         edpy->gls = qemu_gl_init_shader();
2215e79d516SMarc-André Lureau         ctx = g_new0(DisplayGLCtx, 1);
2225e79d516SMarc-André Lureau         ctx->ops = &eglctx_ops;
2235e79d516SMarc-André Lureau         qemu_console_set_display_gl_ctx(con, ctx);
224bb1599b6SGerd Hoffmann         register_displaychangelistener(&edpy->dcl);
225bb1599b6SGerd Hoffmann     }
226bb1599b6SGerd Hoffmann }
22716ab0a74SGerd Hoffmann 
22816ab0a74SGerd Hoffmann static QemuDisplay qemu_display_egl = {
22916ab0a74SGerd Hoffmann     .type       = DISPLAY_TYPE_EGL_HEADLESS,
23016ab0a74SGerd Hoffmann     .early_init = early_egl_headless_init,
23116ab0a74SGerd Hoffmann     .init       = egl_headless_init,
23216ab0a74SGerd Hoffmann };
23316ab0a74SGerd Hoffmann 
23416ab0a74SGerd Hoffmann static void register_egl(void)
23516ab0a74SGerd Hoffmann {
23616ab0a74SGerd Hoffmann     qemu_display_register(&qemu_display_egl);
23716ab0a74SGerd Hoffmann }
23816ab0a74SGerd Hoffmann 
23916ab0a74SGerd Hoffmann type_init(register_egl);
240b36ae1c1SGerd Hoffmann 
241b36ae1c1SGerd Hoffmann module_dep("ui-opengl");
242