xref: /qemu/ui/egl-headless.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
1bb1599b6SGerd Hoffmann #include "qemu/osdep.h"
2*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
3bb1599b6SGerd Hoffmann #include "sysemu/sysemu.h"
4bb1599b6SGerd Hoffmann #include "ui/console.h"
5bb1599b6SGerd Hoffmann #include "ui/egl-helpers.h"
6bb1599b6SGerd Hoffmann #include "ui/egl-context.h"
7a3517917SGerd Hoffmann #include "ui/shader.h"
8bb1599b6SGerd Hoffmann 
9bb1599b6SGerd Hoffmann typedef struct egl_dpy {
10bb1599b6SGerd Hoffmann     DisplayChangeListener dcl;
11bb1599b6SGerd Hoffmann     DisplaySurface *ds;
12a3517917SGerd Hoffmann     QemuGLShader *gls;
13d8dc67e1SGerd Hoffmann     egl_fb guest_fb;
14a3517917SGerd Hoffmann     egl_fb cursor_fb;
15d8dc67e1SGerd Hoffmann     egl_fb blit_fb;
16bb1599b6SGerd Hoffmann     bool y_0_top;
17a3517917SGerd Hoffmann     uint32_t pos_x;
18a3517917SGerd Hoffmann     uint32_t pos_y;
19bb1599b6SGerd Hoffmann } egl_dpy;
20bb1599b6SGerd Hoffmann 
21d8dc67e1SGerd Hoffmann /* ------------------------------------------------------------------ */
22d8dc67e1SGerd Hoffmann 
23bb1599b6SGerd Hoffmann static void egl_refresh(DisplayChangeListener *dcl)
24bb1599b6SGerd Hoffmann {
25bb1599b6SGerd Hoffmann     graphic_hw_update(dcl->con);
26bb1599b6SGerd Hoffmann }
27bb1599b6SGerd Hoffmann 
28bb1599b6SGerd Hoffmann static void egl_gfx_update(DisplayChangeListener *dcl,
29bb1599b6SGerd Hoffmann                            int x, int y, int w, int h)
30bb1599b6SGerd Hoffmann {
31bb1599b6SGerd Hoffmann }
32bb1599b6SGerd Hoffmann 
33bb1599b6SGerd Hoffmann static void egl_gfx_switch(DisplayChangeListener *dcl,
34bb1599b6SGerd Hoffmann                            struct DisplaySurface *new_surface)
35bb1599b6SGerd Hoffmann {
36bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
37bb1599b6SGerd Hoffmann 
38bb1599b6SGerd Hoffmann     edpy->ds = new_surface;
39bb1599b6SGerd Hoffmann }
40bb1599b6SGerd Hoffmann 
41952e5d58SGerd Hoffmann static QEMUGLContext egl_create_context(DisplayChangeListener *dcl,
42952e5d58SGerd Hoffmann                                         QEMUGLParams *params)
43952e5d58SGerd Hoffmann {
44952e5d58SGerd Hoffmann     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
45952e5d58SGerd Hoffmann                    qemu_egl_rn_ctx);
46952e5d58SGerd Hoffmann     return qemu_egl_create_context(dcl, params);
47952e5d58SGerd Hoffmann }
48952e5d58SGerd Hoffmann 
49bb1599b6SGerd Hoffmann static void egl_scanout_disable(DisplayChangeListener *dcl)
50bb1599b6SGerd Hoffmann {
51bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
52bb1599b6SGerd Hoffmann 
53d8dc67e1SGerd Hoffmann     egl_fb_destroy(&edpy->guest_fb);
54d8dc67e1SGerd Hoffmann     egl_fb_destroy(&edpy->blit_fb);
55bb1599b6SGerd Hoffmann }
56bb1599b6SGerd Hoffmann 
57bb1599b6SGerd Hoffmann static void egl_scanout_texture(DisplayChangeListener *dcl,
58bb1599b6SGerd Hoffmann                                 uint32_t backing_id,
59bb1599b6SGerd Hoffmann                                 bool backing_y_0_top,
60bb1599b6SGerd Hoffmann                                 uint32_t backing_width,
61bb1599b6SGerd Hoffmann                                 uint32_t backing_height,
62bb1599b6SGerd Hoffmann                                 uint32_t x, uint32_t y,
63bb1599b6SGerd Hoffmann                                 uint32_t w, uint32_t h)
64bb1599b6SGerd Hoffmann {
65bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
66bb1599b6SGerd Hoffmann 
67bb1599b6SGerd Hoffmann     edpy->y_0_top = backing_y_0_top;
68bb1599b6SGerd Hoffmann 
69bb1599b6SGerd Hoffmann     /* source framebuffer */
7074083f9cSGerd Hoffmann     egl_fb_setup_for_tex(&edpy->guest_fb,
7174083f9cSGerd Hoffmann                          backing_width, backing_height, backing_id, false);
72bb1599b6SGerd Hoffmann 
73bb1599b6SGerd Hoffmann     /* dest framebuffer */
74d8dc67e1SGerd Hoffmann     if (edpy->blit_fb.width  != backing_width ||
75d8dc67e1SGerd Hoffmann         edpy->blit_fb.height != backing_height) {
76d8dc67e1SGerd Hoffmann         egl_fb_destroy(&edpy->blit_fb);
7774083f9cSGerd Hoffmann         egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height);
78bb1599b6SGerd Hoffmann     }
79bb1599b6SGerd Hoffmann }
80bb1599b6SGerd Hoffmann 
81a3517917SGerd Hoffmann static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
82a3517917SGerd Hoffmann                                QemuDmaBuf *dmabuf)
83a3517917SGerd Hoffmann {
84a3517917SGerd Hoffmann     egl_dmabuf_import_texture(dmabuf);
85a3517917SGerd Hoffmann     if (!dmabuf->texture) {
86a3517917SGerd Hoffmann         return;
87a3517917SGerd Hoffmann     }
88a3517917SGerd Hoffmann 
89a3517917SGerd Hoffmann     egl_scanout_texture(dcl, dmabuf->texture,
90a3517917SGerd Hoffmann                         false, dmabuf->width, dmabuf->height,
91a3517917SGerd Hoffmann                         0, 0, dmabuf->width, dmabuf->height);
92a3517917SGerd Hoffmann }
93a3517917SGerd Hoffmann 
94a3517917SGerd Hoffmann static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
956e1f2cb5SGerd Hoffmann                               QemuDmaBuf *dmabuf, bool have_hot,
966e1f2cb5SGerd Hoffmann                               uint32_t hot_x, uint32_t hot_y)
97a3517917SGerd Hoffmann {
98a3517917SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
99a3517917SGerd Hoffmann 
100b0916928SGerd Hoffmann     if (dmabuf) {
101a3517917SGerd Hoffmann         egl_dmabuf_import_texture(dmabuf);
102a3517917SGerd Hoffmann         if (!dmabuf->texture) {
103a3517917SGerd Hoffmann             return;
104a3517917SGerd Hoffmann         }
105a3517917SGerd Hoffmann         egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height,
106a3517917SGerd Hoffmann                              dmabuf->texture, false);
107b0916928SGerd Hoffmann     } else {
108b0916928SGerd Hoffmann         egl_fb_destroy(&edpy->cursor_fb);
109b0916928SGerd Hoffmann     }
110a3517917SGerd Hoffmann }
111a3517917SGerd Hoffmann 
1126e1f2cb5SGerd Hoffmann static void egl_cursor_position(DisplayChangeListener *dcl,
1136e1f2cb5SGerd Hoffmann                                 uint32_t pos_x, uint32_t pos_y)
1146e1f2cb5SGerd Hoffmann {
1156e1f2cb5SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
1166e1f2cb5SGerd Hoffmann 
1176e1f2cb5SGerd Hoffmann     edpy->pos_x = pos_x;
1186e1f2cb5SGerd Hoffmann     edpy->pos_y = pos_y;
1196e1f2cb5SGerd Hoffmann }
1206e1f2cb5SGerd Hoffmann 
121a3517917SGerd Hoffmann static void egl_release_dmabuf(DisplayChangeListener *dcl,
122a3517917SGerd Hoffmann                                QemuDmaBuf *dmabuf)
123a3517917SGerd Hoffmann {
124a3517917SGerd Hoffmann     egl_dmabuf_release_texture(dmabuf);
125a3517917SGerd Hoffmann }
126a3517917SGerd Hoffmann 
127bb1599b6SGerd Hoffmann static void egl_scanout_flush(DisplayChangeListener *dcl,
128bb1599b6SGerd Hoffmann                               uint32_t x, uint32_t y,
129bb1599b6SGerd Hoffmann                               uint32_t w, uint32_t h)
130bb1599b6SGerd Hoffmann {
131bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
132bb1599b6SGerd Hoffmann 
133d8dc67e1SGerd Hoffmann     if (!edpy->guest_fb.texture || !edpy->ds) {
134bb1599b6SGerd Hoffmann         return;
135bb1599b6SGerd Hoffmann     }
136d8dc67e1SGerd Hoffmann     assert(surface_width(edpy->ds)  == edpy->guest_fb.width);
137d8dc67e1SGerd Hoffmann     assert(surface_height(edpy->ds) == edpy->guest_fb.height);
138bb1599b6SGerd Hoffmann     assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
139bb1599b6SGerd Hoffmann 
140a3517917SGerd Hoffmann     if (edpy->cursor_fb.texture) {
141a3517917SGerd Hoffmann         /* have cursor -> render using textures */
142a3517917SGerd Hoffmann         egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
143a3517917SGerd Hoffmann                          !edpy->y_0_top);
144a3517917SGerd Hoffmann         egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
145051a0cdeSChen Zhang                           !edpy->y_0_top, edpy->pos_x, edpy->pos_y,
146051a0cdeSChen Zhang                           1.0, 1.0);
147a3517917SGerd Hoffmann     } else {
148a3517917SGerd Hoffmann         /* no cursor -> use simple framebuffer blit */
149d8dc67e1SGerd Hoffmann         egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
150a3517917SGerd Hoffmann     }
151bb1599b6SGerd Hoffmann 
152a3517917SGerd Hoffmann     egl_fb_read(surface_data(edpy->ds), &edpy->blit_fb);
153bb1599b6SGerd Hoffmann     dpy_gfx_update(edpy->dcl.con, x, y, w, h);
154bb1599b6SGerd Hoffmann }
155bb1599b6SGerd Hoffmann 
156bb1599b6SGerd Hoffmann static const DisplayChangeListenerOps egl_ops = {
157bb1599b6SGerd Hoffmann     .dpy_name                = "egl-headless",
158bb1599b6SGerd Hoffmann     .dpy_refresh             = egl_refresh,
159bb1599b6SGerd Hoffmann     .dpy_gfx_update          = egl_gfx_update,
160bb1599b6SGerd Hoffmann     .dpy_gfx_switch          = egl_gfx_switch,
161bb1599b6SGerd Hoffmann 
162952e5d58SGerd Hoffmann     .dpy_gl_ctx_create       = egl_create_context,
163bb1599b6SGerd Hoffmann     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
164bb1599b6SGerd Hoffmann     .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
165bb1599b6SGerd Hoffmann     .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
166bb1599b6SGerd Hoffmann 
167bb1599b6SGerd Hoffmann     .dpy_gl_scanout_disable  = egl_scanout_disable,
168bb1599b6SGerd Hoffmann     .dpy_gl_scanout_texture  = egl_scanout_texture,
169a3517917SGerd Hoffmann     .dpy_gl_scanout_dmabuf   = egl_scanout_dmabuf,
170a3517917SGerd Hoffmann     .dpy_gl_cursor_dmabuf    = egl_cursor_dmabuf,
1716e1f2cb5SGerd Hoffmann     .dpy_gl_cursor_position  = egl_cursor_position,
172a3517917SGerd Hoffmann     .dpy_gl_release_dmabuf   = egl_release_dmabuf,
173bb1599b6SGerd Hoffmann     .dpy_gl_update           = egl_scanout_flush,
174bb1599b6SGerd Hoffmann };
175bb1599b6SGerd Hoffmann 
17616ab0a74SGerd Hoffmann static void early_egl_headless_init(DisplayOptions *opts)
17716ab0a74SGerd Hoffmann {
17816ab0a74SGerd Hoffmann     display_opengl = 1;
17916ab0a74SGerd Hoffmann }
18016ab0a74SGerd Hoffmann 
18116ab0a74SGerd Hoffmann static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
182bb1599b6SGerd Hoffmann {
18354d208ffSGerd Hoffmann     DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
184bb1599b6SGerd Hoffmann     QemuConsole *con;
185bb1599b6SGerd Hoffmann     egl_dpy *edpy;
186bb1599b6SGerd Hoffmann     int idx;
187bb1599b6SGerd Hoffmann 
18891e61947SErik Skultety     if (egl_rendernode_init(opts->u.egl_headless.rendernode, mode) < 0) {
189bb1599b6SGerd Hoffmann         error_report("egl: render node init failed");
190bb1599b6SGerd Hoffmann         exit(1);
191bb1599b6SGerd Hoffmann     }
192bb1599b6SGerd Hoffmann 
193bb1599b6SGerd Hoffmann     for (idx = 0;; idx++) {
194bb1599b6SGerd Hoffmann         con = qemu_console_lookup_by_index(idx);
195bb1599b6SGerd Hoffmann         if (!con || !qemu_console_is_graphic(con)) {
196bb1599b6SGerd Hoffmann             break;
197bb1599b6SGerd Hoffmann         }
198bb1599b6SGerd Hoffmann 
199bb1599b6SGerd Hoffmann         edpy = g_new0(egl_dpy, 1);
200bb1599b6SGerd Hoffmann         edpy->dcl.con = con;
201bb1599b6SGerd Hoffmann         edpy->dcl.ops = &egl_ops;
202a3517917SGerd Hoffmann         edpy->gls = qemu_gl_init_shader();
203bb1599b6SGerd Hoffmann         register_displaychangelistener(&edpy->dcl);
204bb1599b6SGerd Hoffmann     }
205bb1599b6SGerd Hoffmann }
20616ab0a74SGerd Hoffmann 
20716ab0a74SGerd Hoffmann static QemuDisplay qemu_display_egl = {
20816ab0a74SGerd Hoffmann     .type       = DISPLAY_TYPE_EGL_HEADLESS,
20916ab0a74SGerd Hoffmann     .early_init = early_egl_headless_init,
21016ab0a74SGerd Hoffmann     .init       = egl_headless_init,
21116ab0a74SGerd Hoffmann };
21216ab0a74SGerd Hoffmann 
21316ab0a74SGerd Hoffmann static void register_egl(void)
21416ab0a74SGerd Hoffmann {
21516ab0a74SGerd Hoffmann     qemu_display_register(&qemu_display_egl);
21616ab0a74SGerd Hoffmann }
21716ab0a74SGerd Hoffmann 
21816ab0a74SGerd Hoffmann type_init(register_egl);
219