xref: /openbmc/qemu/ui/egl-headless.c (revision e3d0814368d00e7985c31edf5d0cfce45972d4be)
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