xref: /openbmc/qemu/ui/egl-headless.c (revision b8eada54b2ad8a7d98d93d5ab4d3e888c5880097)
1bb1599b6SGerd Hoffmann #include "qemu/osdep.h"
25feed38cSThomas Huth #include "qemu/error-report.h"
30b8fa32fSMarkus Armbruster #include "qemu/module.h"
40e1be59eSMarc-André Lureau #include "qapi/error.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 
egl_refresh(DisplayChangeListener * dcl)24bb1599b6SGerd Hoffmann static void egl_refresh(DisplayChangeListener *dcl)
25bb1599b6SGerd Hoffmann {
26bb1599b6SGerd Hoffmann     graphic_hw_update(dcl->con);
27bb1599b6SGerd Hoffmann }
28bb1599b6SGerd Hoffmann 
egl_gfx_update(DisplayChangeListener * dcl,int x,int y,int w,int h)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 
egl_gfx_switch(DisplayChangeListener * dcl,struct DisplaySurface * new_surface)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 
egl_create_context(DisplayGLCtx * dgc,QEMUGLParams * params)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 
egl_scanout_disable(DisplayChangeListener * dcl)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 
egl_scanout_texture(DisplayChangeListener * dcl,uint32_t backing_id,bool backing_y_0_top,uint32_t backing_width,uint32_t backing_height,uint32_t x,uint32_t y,uint32_t w,uint32_t h,void * d3d_tex2d)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,
64bf41ab61SMarc-André Lureau                                 uint32_t w, uint32_t h,
65bf41ab61SMarc-André Lureau                                 void *d3d_tex2d)
66bb1599b6SGerd Hoffmann {
67bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
68bb1599b6SGerd Hoffmann 
69bb1599b6SGerd Hoffmann     edpy->y_0_top = backing_y_0_top;
70bb1599b6SGerd Hoffmann 
71bb1599b6SGerd Hoffmann     /* source framebuffer */
7274083f9cSGerd Hoffmann     egl_fb_setup_for_tex(&edpy->guest_fb,
7374083f9cSGerd Hoffmann                          backing_width, backing_height, backing_id, false);
74bb1599b6SGerd Hoffmann 
75bb1599b6SGerd Hoffmann     /* dest framebuffer */
76d8dc67e1SGerd Hoffmann     if (edpy->blit_fb.width  != backing_width ||
77d8dc67e1SGerd Hoffmann         edpy->blit_fb.height != backing_height) {
78d8dc67e1SGerd Hoffmann         egl_fb_destroy(&edpy->blit_fb);
7974083f9cSGerd Hoffmann         egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height);
80bb1599b6SGerd Hoffmann     }
81bb1599b6SGerd Hoffmann }
82bb1599b6SGerd Hoffmann 
8339324b49SMarc-André Lureau #ifdef CONFIG_GBM
8439324b49SMarc-André Lureau 
egl_scanout_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)85a3517917SGerd Hoffmann static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
86a3517917SGerd Hoffmann                                QemuDmaBuf *dmabuf)
87a3517917SGerd Hoffmann {
886779a307SDongwon Kim     uint32_t width, height, texture;
896779a307SDongwon Kim 
90a3517917SGerd Hoffmann     egl_dmabuf_import_texture(dmabuf);
916779a307SDongwon Kim     texture = qemu_dmabuf_get_texture(dmabuf);
926779a307SDongwon Kim     if (!texture) {
93a3517917SGerd Hoffmann         return;
94a3517917SGerd Hoffmann     }
95a3517917SGerd Hoffmann 
966779a307SDongwon Kim     width = qemu_dmabuf_get_width(dmabuf);
976779a307SDongwon Kim     height = qemu_dmabuf_get_height(dmabuf);
986779a307SDongwon Kim 
996779a307SDongwon Kim     egl_scanout_texture(dcl, texture, false, width, height, 0, 0,
1006779a307SDongwon Kim                         width, height, NULL);
101a3517917SGerd Hoffmann }
102a3517917SGerd Hoffmann 
egl_cursor_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf,bool have_hot,uint32_t hot_x,uint32_t hot_y)103a3517917SGerd Hoffmann static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
1046e1f2cb5SGerd Hoffmann                               QemuDmaBuf *dmabuf, bool have_hot,
1056e1f2cb5SGerd Hoffmann                               uint32_t hot_x, uint32_t hot_y)
106a3517917SGerd Hoffmann {
1076779a307SDongwon Kim     uint32_t width, height, texture;
108a3517917SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
109a3517917SGerd Hoffmann 
110b0916928SGerd Hoffmann     if (dmabuf) {
111a3517917SGerd Hoffmann         egl_dmabuf_import_texture(dmabuf);
1126779a307SDongwon Kim         texture = qemu_dmabuf_get_texture(dmabuf);
1136779a307SDongwon Kim         if (!texture) {
114a3517917SGerd Hoffmann             return;
115a3517917SGerd Hoffmann         }
1166779a307SDongwon Kim 
1176779a307SDongwon Kim         width = qemu_dmabuf_get_width(dmabuf);
1186779a307SDongwon Kim         height = qemu_dmabuf_get_height(dmabuf);
1196779a307SDongwon Kim         egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false);
120b0916928SGerd Hoffmann     } else {
121b0916928SGerd Hoffmann         egl_fb_destroy(&edpy->cursor_fb);
122b0916928SGerd Hoffmann     }
123a3517917SGerd Hoffmann }
124a3517917SGerd Hoffmann 
egl_release_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)12539324b49SMarc-André Lureau static void egl_release_dmabuf(DisplayChangeListener *dcl,
12639324b49SMarc-André Lureau                                QemuDmaBuf *dmabuf)
12739324b49SMarc-André Lureau {
12839324b49SMarc-André Lureau     egl_dmabuf_release_texture(dmabuf);
12939324b49SMarc-André Lureau }
13039324b49SMarc-André Lureau 
13139324b49SMarc-André Lureau #endif
13239324b49SMarc-André Lureau 
egl_cursor_position(DisplayChangeListener * dcl,uint32_t pos_x,uint32_t pos_y)1336e1f2cb5SGerd Hoffmann static void egl_cursor_position(DisplayChangeListener *dcl,
1346e1f2cb5SGerd Hoffmann                                 uint32_t pos_x, uint32_t pos_y)
1356e1f2cb5SGerd Hoffmann {
1366e1f2cb5SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
1376e1f2cb5SGerd Hoffmann 
1386e1f2cb5SGerd Hoffmann     edpy->pos_x = pos_x;
1396e1f2cb5SGerd Hoffmann     edpy->pos_y = pos_y;
1406e1f2cb5SGerd Hoffmann }
1416e1f2cb5SGerd Hoffmann 
egl_scanout_flush(DisplayChangeListener * dcl,uint32_t x,uint32_t y,uint32_t w,uint32_t h)142bb1599b6SGerd Hoffmann static void egl_scanout_flush(DisplayChangeListener *dcl,
143bb1599b6SGerd Hoffmann                               uint32_t x, uint32_t y,
144bb1599b6SGerd Hoffmann                               uint32_t w, uint32_t h)
145bb1599b6SGerd Hoffmann {
146bb1599b6SGerd Hoffmann     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
147bb1599b6SGerd Hoffmann 
148d8dc67e1SGerd Hoffmann     if (!edpy->guest_fb.texture || !edpy->ds) {
149bb1599b6SGerd Hoffmann         return;
150bb1599b6SGerd Hoffmann     }
151bb1599b6SGerd Hoffmann     assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
152bb1599b6SGerd Hoffmann 
153a3517917SGerd Hoffmann     if (edpy->cursor_fb.texture) {
154a3517917SGerd Hoffmann         /* have cursor -> render using textures */
155a3517917SGerd Hoffmann         egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
156a3517917SGerd Hoffmann                          !edpy->y_0_top);
157a3517917SGerd Hoffmann         egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
158051a0cdeSChen Zhang                           !edpy->y_0_top, edpy->pos_x, edpy->pos_y,
159051a0cdeSChen Zhang                           1.0, 1.0);
160a3517917SGerd Hoffmann     } else {
161a3517917SGerd Hoffmann         /* no cursor -> use simple framebuffer blit */
162d8dc67e1SGerd Hoffmann         egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
163a3517917SGerd Hoffmann     }
164bb1599b6SGerd Hoffmann 
165d2329237SGerd Hoffmann     egl_fb_read(edpy->ds, &edpy->blit_fb);
166bb1599b6SGerd Hoffmann     dpy_gfx_update(edpy->dcl.con, x, y, w, h);
167bb1599b6SGerd Hoffmann }
168bb1599b6SGerd Hoffmann 
169bb1599b6SGerd Hoffmann static const DisplayChangeListenerOps egl_ops = {
170bb1599b6SGerd Hoffmann     .dpy_name                = "egl-headless",
171bb1599b6SGerd Hoffmann     .dpy_refresh             = egl_refresh,
172bb1599b6SGerd Hoffmann     .dpy_gfx_update          = egl_gfx_update,
173bb1599b6SGerd Hoffmann     .dpy_gfx_switch          = egl_gfx_switch,
174bb1599b6SGerd Hoffmann 
175bb1599b6SGerd Hoffmann     .dpy_gl_scanout_disable  = egl_scanout_disable,
176bb1599b6SGerd Hoffmann     .dpy_gl_scanout_texture  = egl_scanout_texture,
17739324b49SMarc-André Lureau #ifdef CONFIG_GBM
178a3517917SGerd Hoffmann     .dpy_gl_scanout_dmabuf   = egl_scanout_dmabuf,
179a3517917SGerd Hoffmann     .dpy_gl_cursor_dmabuf    = egl_cursor_dmabuf,
180a3517917SGerd Hoffmann     .dpy_gl_release_dmabuf   = egl_release_dmabuf,
18139324b49SMarc-André Lureau #endif
18239324b49SMarc-André Lureau     .dpy_gl_cursor_position  = egl_cursor_position,
183bb1599b6SGerd Hoffmann     .dpy_gl_update           = egl_scanout_flush,
184bb1599b6SGerd Hoffmann };
185bb1599b6SGerd Hoffmann 
186a62c4a17SMarc-André Lureau static bool
egl_is_compatible_dcl(DisplayGLCtx * dgc,DisplayChangeListener * dcl)187a62c4a17SMarc-André Lureau egl_is_compatible_dcl(DisplayGLCtx *dgc,
188a62c4a17SMarc-André Lureau                       DisplayChangeListener *dcl)
189a62c4a17SMarc-André Lureau {
190cd19c25fSMarc-André Lureau     if (!dcl->ops->dpy_gl_update) {
191cd19c25fSMarc-André Lureau         /*
192cd19c25fSMarc-André Lureau          * egl-headless is compatible with all 2d listeners, as it blits the GL
193cd19c25fSMarc-André Lureau          * updates on the 2d console surface.
194cd19c25fSMarc-André Lureau          */
195cd19c25fSMarc-André Lureau         return true;
196cd19c25fSMarc-André Lureau     }
197cd19c25fSMarc-André Lureau 
198a62c4a17SMarc-André Lureau     return dcl->ops == &egl_ops;
199a62c4a17SMarc-André Lureau }
200a62c4a17SMarc-André Lureau 
2015e79d516SMarc-André Lureau static const DisplayGLCtxOps eglctx_ops = {
202a62c4a17SMarc-André Lureau     .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl,
2035e79d516SMarc-André Lureau     .dpy_gl_ctx_create       = egl_create_context,
2045e79d516SMarc-André Lureau     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
2055e79d516SMarc-André Lureau     .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
2065e79d516SMarc-André Lureau };
2075e79d516SMarc-André Lureau 
early_egl_headless_init(DisplayOptions * opts)20816ab0a74SGerd Hoffmann static void early_egl_headless_init(DisplayOptions *opts)
20916ab0a74SGerd Hoffmann {
210*154fd4d1SMarkus Armbruster     DisplayGLMode mode = DISPLAY_GL_MODE_ON;
2110e1be59eSMarc-André Lureau 
2120e1be59eSMarc-André Lureau     if (opts->has_gl) {
2130e1be59eSMarc-André Lureau         mode = opts->gl;
2140e1be59eSMarc-André Lureau     }
2150e1be59eSMarc-André Lureau 
2160e1be59eSMarc-André Lureau     egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal);
21716ab0a74SGerd Hoffmann }
21816ab0a74SGerd Hoffmann 
egl_headless_init(DisplayState * ds,DisplayOptions * opts)21916ab0a74SGerd Hoffmann static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
220bb1599b6SGerd Hoffmann {
221bb1599b6SGerd Hoffmann     QemuConsole *con;
222bb1599b6SGerd Hoffmann     egl_dpy *edpy;
223bb1599b6SGerd Hoffmann     int idx;
224bb1599b6SGerd Hoffmann 
225bb1599b6SGerd Hoffmann     for (idx = 0;; idx++) {
2265e79d516SMarc-André Lureau         DisplayGLCtx *ctx;
2275e79d516SMarc-André Lureau 
228bb1599b6SGerd Hoffmann         con = qemu_console_lookup_by_index(idx);
229bb1599b6SGerd Hoffmann         if (!con || !qemu_console_is_graphic(con)) {
230bb1599b6SGerd Hoffmann             break;
231bb1599b6SGerd Hoffmann         }
232bb1599b6SGerd Hoffmann 
233bb1599b6SGerd Hoffmann         edpy = g_new0(egl_dpy, 1);
234bb1599b6SGerd Hoffmann         edpy->dcl.con = con;
235bb1599b6SGerd Hoffmann         edpy->dcl.ops = &egl_ops;
236a3517917SGerd Hoffmann         edpy->gls = qemu_gl_init_shader();
2375e79d516SMarc-André Lureau         ctx = g_new0(DisplayGLCtx, 1);
2385e79d516SMarc-André Lureau         ctx->ops = &eglctx_ops;
2395e79d516SMarc-André Lureau         qemu_console_set_display_gl_ctx(con, ctx);
240bb1599b6SGerd Hoffmann         register_displaychangelistener(&edpy->dcl);
241bb1599b6SGerd Hoffmann     }
242bb1599b6SGerd Hoffmann }
24316ab0a74SGerd Hoffmann 
24416ab0a74SGerd Hoffmann static QemuDisplay qemu_display_egl = {
24516ab0a74SGerd Hoffmann     .type       = DISPLAY_TYPE_EGL_HEADLESS,
24616ab0a74SGerd Hoffmann     .early_init = early_egl_headless_init,
24716ab0a74SGerd Hoffmann     .init       = egl_headless_init,
24816ab0a74SGerd Hoffmann };
24916ab0a74SGerd Hoffmann 
register_egl(void)25016ab0a74SGerd Hoffmann static void register_egl(void)
25116ab0a74SGerd Hoffmann {
25216ab0a74SGerd Hoffmann     qemu_display_register(&qemu_display_egl);
25316ab0a74SGerd Hoffmann }
25416ab0a74SGerd Hoffmann 
25516ab0a74SGerd Hoffmann type_init(register_egl);
256b36ae1c1SGerd Hoffmann 
257b36ae1c1SGerd Hoffmann module_dep("ui-opengl");
258