xref: /openbmc/qemu/ui/gtk-gl-area.c (revision 265aad58e9cab31d0e69c374ec2efcede7fa8881)
1  /*
2   * GTK UI -- glarea opengl code.
3   *
4   * Requires 3.16+ (GtkGLArea widget).
5   *
6   * This work is licensed under the terms of the GNU GPL, version 2 or later.
7   * See the COPYING file in the top-level directory.
8   */
9  
10  #include "qemu/osdep.h"
11  #include "qemu/main-loop.h"
12  
13  #include "trace.h"
14  
15  #include "ui/console.h"
16  #include "ui/gtk.h"
17  #include "ui/egl-helpers.h"
18  
19  #include "sysemu/sysemu.h"
20  
gtk_gl_area_set_scanout_mode(VirtualConsole * vc,bool scanout)21  static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
22  {
23      if (vc->gfx.scanout_mode == scanout) {
24          return;
25      }
26  
27      vc->gfx.scanout_mode = scanout;
28      if (!vc->gfx.scanout_mode) {
29          gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
30          egl_fb_destroy(&vc->gfx.guest_fb);
31          if (vc->gfx.surface) {
32              surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
33              surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
34          }
35      }
36  }
37  
38  /** DisplayState Callbacks (opengl version) **/
39  
gd_gl_area_draw(VirtualConsole * vc)40  void gd_gl_area_draw(VirtualConsole *vc)
41  {
42  #ifdef CONFIG_GBM
43      QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
44  #endif
45      int ww, wh, ws, y1, y2;
46  
47      if (!vc->gfx.gls) {
48          return;
49      }
50  
51      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
52      ws = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area));
53      ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * ws;
54      wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * ws;
55  
56      if (vc->gfx.scanout_mode) {
57          if (!vc->gfx.guest_fb.framebuffer) {
58              return;
59          }
60  
61  #ifdef CONFIG_GBM
62          if (dmabuf) {
63              if (!qemu_dmabuf_get_draw_submitted(dmabuf)) {
64                  return;
65              } else {
66                  qemu_dmabuf_set_draw_submitted(dmabuf, false);
67              }
68          }
69  #endif
70  
71          glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer);
72          /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
73  
74          glViewport(0, 0, ww, wh);
75          y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
76          y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
77          glBlitFramebuffer(0, y1, vc->gfx.w, y2,
78                            0, 0, ww, wh,
79                            GL_COLOR_BUFFER_BIT, GL_NEAREST);
80  #ifdef CONFIG_GBM
81          if (dmabuf) {
82              egl_dmabuf_create_sync(dmabuf);
83          }
84  #endif
85          glFlush();
86  #ifdef CONFIG_GBM
87          if (dmabuf) {
88              int fence_fd;
89              egl_dmabuf_create_fence(dmabuf);
90              fence_fd = qemu_dmabuf_get_fence_fd(dmabuf);
91              if (fence_fd >= 0) {
92                  qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc);
93                  return;
94              }
95              graphic_hw_gl_block(vc->gfx.dcl.con, false);
96          }
97  #endif
98      } else {
99          if (!vc->gfx.ds) {
100              return;
101          }
102          gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
103  
104          surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
105          surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
106      }
107  }
108  
gd_gl_area_update(DisplayChangeListener * dcl,int x,int y,int w,int h)109  void gd_gl_area_update(DisplayChangeListener *dcl,
110                     int x, int y, int w, int h)
111  {
112      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
113  
114      if (!vc->gfx.gls || !vc->gfx.ds) {
115          return;
116      }
117  
118      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
119      surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
120      vc->gfx.glupdates++;
121      gdk_gl_context_clear_current();
122  }
123  
gd_gl_area_refresh(DisplayChangeListener * dcl)124  void gd_gl_area_refresh(DisplayChangeListener *dcl)
125  {
126      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
127  
128      gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area);
129  
130      if (vc->gfx.guest_fb.dmabuf &&
131          qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
132          gd_gl_area_draw(vc);
133          return;
134      }
135  
136      if (!vc->gfx.gls) {
137          if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
138              return;
139          }
140          gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
141          vc->gfx.gls = qemu_gl_init_shader();
142          if (vc->gfx.ds) {
143              surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
144          }
145      }
146  
147      graphic_hw_update(dcl->con);
148  
149      if (vc->gfx.glupdates) {
150          vc->gfx.glupdates = 0;
151          gtk_gl_area_set_scanout_mode(vc, false);
152          gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
153      }
154  }
155  
gd_gl_area_switch(DisplayChangeListener * dcl,DisplaySurface * surface)156  void gd_gl_area_switch(DisplayChangeListener *dcl,
157                         DisplaySurface *surface)
158  {
159      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
160      bool resized = true;
161  
162      trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
163  
164      if (vc->gfx.ds &&
165          surface_width(vc->gfx.ds) == surface_width(surface) &&
166          surface_height(vc->gfx.ds) == surface_height(surface)) {
167          resized = false;
168      }
169  
170      if (vc->gfx.gls) {
171          gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
172          surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
173          surface_gl_create_texture(vc->gfx.gls, surface);
174      }
175      vc->gfx.ds = surface;
176  
177      if (resized) {
178          gd_update_windowsize(vc);
179      }
180  }
181  
gd_cmp_gl_context_version(int major,int minor,QEMUGLParams * params)182  static int gd_cmp_gl_context_version(int major, int minor, QEMUGLParams *params)
183  {
184      if (major > params->major_ver) {
185          return 1;
186      }
187      if (major < params->major_ver) {
188          return -1;
189      }
190      if (minor > params->minor_ver) {
191          return 1;
192      }
193      if (minor < params->minor_ver) {
194          return -1;
195      }
196      return 0;
197  }
198  
gd_gl_area_create_context(DisplayGLCtx * dgc,QEMUGLParams * params)199  QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
200                                          QEMUGLParams *params)
201  {
202      VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
203      GdkWindow *window;
204      GdkGLContext *ctx;
205      GError *err = NULL;
206      int major, minor;
207  
208      window = gtk_widget_get_window(vc->gfx.drawing_area);
209      ctx = gdk_window_create_gl_context(window, &err);
210      if (err) {
211          g_printerr("Create gdk gl context failed: %s\n", err->message);
212          g_error_free(err);
213          return NULL;
214      }
215      gdk_gl_context_set_required_version(ctx,
216                                          params->major_ver,
217                                          params->minor_ver);
218      gdk_gl_context_realize(ctx, &err);
219      if (err) {
220          g_printerr("Realize gdk gl context failed: %s\n", err->message);
221          g_error_free(err);
222          g_clear_object(&ctx);
223          return NULL;
224      }
225  
226      gdk_gl_context_make_current(ctx);
227      gdk_gl_context_get_version(ctx, &major, &minor);
228      gdk_gl_context_clear_current();
229      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
230  
231      if (gd_cmp_gl_context_version(major, minor, params) == -1) {
232          /* created ctx version < requested version */
233          g_clear_object(&ctx);
234      }
235  
236      trace_gd_gl_area_create_context(ctx, params->major_ver, params->minor_ver);
237      return ctx;
238  }
239  
gd_gl_area_destroy_context(DisplayGLCtx * dgc,QEMUGLContext ctx)240  void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
241  {
242      GdkGLContext *current_ctx = gdk_gl_context_get_current();
243  
244      trace_gd_gl_area_destroy_context(ctx, current_ctx);
245      if (ctx == current_ctx) {
246          gdk_gl_context_clear_current();
247      }
248      g_clear_object(&ctx);
249  }
250  
gd_gl_area_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)251  void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
252                                  uint32_t backing_id,
253                                  bool backing_y_0_top,
254                                  uint32_t backing_width,
255                                  uint32_t backing_height,
256                                  uint32_t x, uint32_t y,
257                                  uint32_t w, uint32_t h,
258                                  void *d3d_tex2d)
259  {
260      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
261  
262      vc->gfx.x = x;
263      vc->gfx.y = y;
264      vc->gfx.w = w;
265      vc->gfx.h = h;
266      vc->gfx.y0_top = backing_y_0_top;
267  
268      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
269  
270      if (backing_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
271          gtk_gl_area_set_scanout_mode(vc, false);
272          return;
273      }
274  
275      gtk_gl_area_set_scanout_mode(vc, true);
276      egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
277                           backing_id, false);
278  }
279  
gd_gl_area_scanout_disable(DisplayChangeListener * dcl)280  void gd_gl_area_scanout_disable(DisplayChangeListener *dcl)
281  {
282      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
283  
284      gtk_gl_area_set_scanout_mode(vc, false);
285  }
286  
gd_gl_area_scanout_flush(DisplayChangeListener * dcl,uint32_t x,uint32_t y,uint32_t w,uint32_t h)287  void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
288                            uint32_t x, uint32_t y, uint32_t w, uint32_t h)
289  {
290      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
291  
292      if (vc->gfx.guest_fb.dmabuf &&
293          !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
294          graphic_hw_gl_block(vc->gfx.dcl.con, true);
295          qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
296          gtk_gl_area_set_scanout_mode(vc, true);
297      }
298      gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
299  }
300  
gd_gl_area_scanout_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)301  void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
302                                 QemuDmaBuf *dmabuf)
303  {
304  #ifdef CONFIG_GBM
305      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
306      uint32_t x, y, width, height, backing_width, backing_height, texture;
307      bool y0_top;
308  
309      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
310      egl_dmabuf_import_texture(dmabuf);
311      texture = qemu_dmabuf_get_texture(dmabuf);
312      if (!texture) {
313          return;
314      }
315  
316      x = qemu_dmabuf_get_x(dmabuf);
317      y = qemu_dmabuf_get_y(dmabuf);
318      width = qemu_dmabuf_get_width(dmabuf);
319      height = qemu_dmabuf_get_height(dmabuf);
320      backing_width = qemu_dmabuf_get_backing_width(dmabuf);
321      backing_height = qemu_dmabuf_get_backing_height(dmabuf);
322      y0_top = qemu_dmabuf_get_y0_top(dmabuf);
323  
324      gd_gl_area_scanout_texture(dcl, texture, y0_top,
325                                 backing_width, backing_height,
326                                 x, y, width, height, NULL);
327  
328      if (qemu_dmabuf_get_allow_fences(dmabuf)) {
329          vc->gfx.guest_fb.dmabuf = dmabuf;
330      }
331  #endif
332  }
333  
gtk_gl_area_init(void)334  void gtk_gl_area_init(void)
335  {
336      display_opengl = 1;
337  }
338  
gd_gl_area_make_current(DisplayGLCtx * dgc,QEMUGLContext ctx)339  int gd_gl_area_make_current(DisplayGLCtx *dgc,
340                              QEMUGLContext ctx)
341  {
342      gdk_gl_context_make_current(ctx);
343      return 0;
344  }
345