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 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 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 (!dmabuf->draw_submitted) { 64 return; 65 } else { 66 dmabuf->draw_submitted = 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 egl_dmabuf_create_fence(dmabuf); 89 if (dmabuf->fence_fd > 0) { 90 qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); 91 return; 92 } 93 graphic_hw_gl_block(vc->gfx.dcl.con, false); 94 } 95 #endif 96 } else { 97 if (!vc->gfx.ds) { 98 return; 99 } 100 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 101 102 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); 103 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 104 } 105 } 106 107 void gd_gl_area_update(DisplayChangeListener *dcl, 108 int x, int y, int w, int h) 109 { 110 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 111 112 if (!vc->gfx.gls || !vc->gfx.ds) { 113 return; 114 } 115 116 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 117 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); 118 vc->gfx.glupdates++; 119 gdk_gl_context_clear_current(); 120 } 121 122 void gd_gl_area_refresh(DisplayChangeListener *dcl) 123 { 124 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 125 126 gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); 127 128 if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { 129 return; 130 } 131 132 if (!vc->gfx.gls) { 133 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 134 return; 135 } 136 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 137 vc->gfx.gls = qemu_gl_init_shader(); 138 if (vc->gfx.ds) { 139 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 140 } 141 } 142 143 graphic_hw_update(dcl->con); 144 145 if (vc->gfx.glupdates) { 146 vc->gfx.glupdates = 0; 147 gtk_gl_area_set_scanout_mode(vc, false); 148 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 149 } 150 } 151 152 void gd_gl_area_switch(DisplayChangeListener *dcl, 153 DisplaySurface *surface) 154 { 155 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 156 bool resized = true; 157 158 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 159 160 if (vc->gfx.ds && 161 surface_width(vc->gfx.ds) == surface_width(surface) && 162 surface_height(vc->gfx.ds) == surface_height(surface)) { 163 resized = false; 164 } 165 166 if (vc->gfx.gls) { 167 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 168 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 169 surface_gl_create_texture(vc->gfx.gls, surface); 170 } 171 vc->gfx.ds = surface; 172 173 if (resized) { 174 gd_update_windowsize(vc); 175 } 176 } 177 178 static int gd_cmp_gl_context_version(int major, int minor, QEMUGLParams *params) 179 { 180 if (major > params->major_ver) { 181 return 1; 182 } 183 if (major < params->major_ver) { 184 return -1; 185 } 186 if (minor > params->minor_ver) { 187 return 1; 188 } 189 if (minor < params->minor_ver) { 190 return -1; 191 } 192 return 0; 193 } 194 195 QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc, 196 QEMUGLParams *params) 197 { 198 VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc); 199 GdkWindow *window; 200 GdkGLContext *ctx; 201 GError *err = NULL; 202 int major, minor; 203 204 window = gtk_widget_get_window(vc->gfx.drawing_area); 205 ctx = gdk_window_create_gl_context(window, &err); 206 if (err) { 207 g_printerr("Create gdk gl context failed: %s\n", err->message); 208 g_error_free(err); 209 return NULL; 210 } 211 gdk_gl_context_set_required_version(ctx, 212 params->major_ver, 213 params->minor_ver); 214 gdk_gl_context_realize(ctx, &err); 215 if (err) { 216 g_printerr("Realize gdk gl context failed: %s\n", err->message); 217 g_error_free(err); 218 g_clear_object(&ctx); 219 return NULL; 220 } 221 222 gdk_gl_context_make_current(ctx); 223 gdk_gl_context_get_version(ctx, &major, &minor); 224 gdk_gl_context_clear_current(); 225 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 226 227 if (gd_cmp_gl_context_version(major, minor, params) == -1) { 228 /* created ctx version < requested version */ 229 g_clear_object(&ctx); 230 } 231 232 trace_gd_gl_area_create_context(ctx, params->major_ver, params->minor_ver); 233 return ctx; 234 } 235 236 void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx) 237 { 238 GdkGLContext *current_ctx = gdk_gl_context_get_current(); 239 240 trace_gd_gl_area_destroy_context(ctx, current_ctx); 241 if (ctx == current_ctx) { 242 gdk_gl_context_clear_current(); 243 } 244 g_clear_object(&ctx); 245 } 246 247 void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, 248 uint32_t backing_id, 249 bool backing_y_0_top, 250 uint32_t backing_width, 251 uint32_t backing_height, 252 uint32_t x, uint32_t y, 253 uint32_t w, uint32_t h, 254 void *d3d_tex2d) 255 { 256 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 257 258 vc->gfx.x = x; 259 vc->gfx.y = y; 260 vc->gfx.w = w; 261 vc->gfx.h = h; 262 vc->gfx.y0_top = backing_y_0_top; 263 264 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 265 266 if (backing_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { 267 gtk_gl_area_set_scanout_mode(vc, false); 268 return; 269 } 270 271 egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, 272 backing_id, false); 273 } 274 275 void gd_gl_area_scanout_disable(DisplayChangeListener *dcl) 276 { 277 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 278 279 gtk_gl_area_set_scanout_mode(vc, false); 280 } 281 282 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, 283 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 284 { 285 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 286 287 if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { 288 graphic_hw_gl_block(vc->gfx.dcl.con, true); 289 vc->gfx.guest_fb.dmabuf->draw_submitted = true; 290 gtk_gl_area_set_scanout_mode(vc, true); 291 } 292 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 293 } 294 295 void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, 296 QemuDmaBuf *dmabuf) 297 { 298 #ifdef CONFIG_GBM 299 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 300 301 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 302 egl_dmabuf_import_texture(dmabuf); 303 if (!dmabuf->texture) { 304 return; 305 } 306 307 gd_gl_area_scanout_texture(dcl, dmabuf->texture, 308 dmabuf->y0_top, 309 dmabuf->backing_width, dmabuf->backing_height, 310 dmabuf->x, dmabuf->y, dmabuf->width, 311 dmabuf->height, NULL); 312 313 if (dmabuf->allow_fences) { 314 vc->gfx.guest_fb.dmabuf = dmabuf; 315 } 316 #endif 317 } 318 319 void gtk_gl_area_init(void) 320 { 321 display_opengl = 1; 322 } 323 324 int gd_gl_area_make_current(DisplayGLCtx *dgc, 325 QEMUGLContext ctx) 326 { 327 gdk_gl_context_make_current(ctx); 328 return 0; 329 } 330