1 /* 2 * GTK UI -- egl opengl code. 3 * 4 * Note that gtk 3.16+ (released 2015-03-23) has a GtkGLArea widget, 5 * which is GtkDrawingArea like widget with opengl rendering support. 6 * 7 * This code handles opengl support on older gtk versions, using egl 8 * to get a opengl context for the X11 window. 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2 or later. 11 * See the COPYING file in the top-level directory. 12 */ 13 14 #include "qemu/osdep.h" 15 #include "qemu/main-loop.h" 16 #include "qemu/error-report.h" 17 18 #include "trace.h" 19 20 #include "ui/console.h" 21 #include "ui/gtk.h" 22 #include "ui/egl-helpers.h" 23 #include "ui/shader.h" 24 25 #include "sysemu/sysemu.h" 26 27 static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) 28 { 29 if (vc->gfx.scanout_mode == scanout) { 30 return; 31 } 32 33 vc->gfx.scanout_mode = scanout; 34 if (!vc->gfx.scanout_mode) { 35 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 36 vc->gfx.esurface, vc->gfx.ectx); 37 egl_fb_destroy(&vc->gfx.guest_fb); 38 if (vc->gfx.surface) { 39 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 40 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 41 } 42 } 43 } 44 45 /** DisplayState Callbacks (opengl version) **/ 46 47 void gd_egl_init(VirtualConsole *vc) 48 { 49 GdkWindow *gdk_window = gtk_widget_get_window(vc->gfx.drawing_area); 50 if (!gdk_window) { 51 return; 52 } 53 54 Window x11_window = gdk_x11_window_get_xid(gdk_window); 55 if (!x11_window) { 56 return; 57 } 58 59 vc->gfx.ectx = qemu_egl_init_ctx(); 60 vc->gfx.esurface = qemu_egl_init_surface_x11 61 (vc->gfx.ectx, (EGLNativeWindowType)x11_window); 62 63 assert(vc->gfx.esurface); 64 } 65 66 void gd_egl_draw(VirtualConsole *vc) 67 { 68 GdkWindow *window; 69 #ifdef CONFIG_GBM 70 QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; 71 #endif 72 int ww, wh, ws; 73 int fence_fd; 74 75 if (!vc->gfx.gls) { 76 return; 77 } 78 79 window = gtk_widget_get_window(vc->gfx.drawing_area); 80 ws = gdk_window_get_scale_factor(window); 81 ww = gdk_window_get_width(window) * ws; 82 wh = gdk_window_get_height(window) * ws; 83 84 if (vc->gfx.scanout_mode) { 85 #ifdef CONFIG_GBM 86 if (dmabuf) { 87 if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { 88 return; 89 } else { 90 qemu_dmabuf_set_draw_submitted(dmabuf, false); 91 } 92 } 93 #endif 94 gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); 95 96 vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); 97 vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); 98 99 glFlush(); 100 #ifdef CONFIG_GBM 101 if (dmabuf) { 102 egl_dmabuf_create_fence(dmabuf); 103 fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); 104 if (fence_fd >= 0) { 105 qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc); 106 return; 107 } 108 graphic_hw_gl_block(vc->gfx.dcl.con, false); 109 } 110 #endif 111 } else { 112 if (!vc->gfx.ds) { 113 return; 114 } 115 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 116 vc->gfx.esurface, vc->gfx.ectx); 117 118 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); 119 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 120 121 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); 122 123 vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); 124 vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); 125 126 glFlush(); 127 } 128 } 129 130 void gd_egl_update(DisplayChangeListener *dcl, 131 int x, int y, int w, int h) 132 { 133 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 134 135 if (!vc->gfx.gls || !vc->gfx.ds) { 136 return; 137 } 138 139 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 140 vc->gfx.esurface, vc->gfx.ectx); 141 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); 142 vc->gfx.glupdates++; 143 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, 144 EGL_NO_SURFACE, EGL_NO_CONTEXT); 145 } 146 147 void gd_egl_refresh(DisplayChangeListener *dcl) 148 { 149 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 150 151 gd_update_monitor_refresh_rate( 152 vc, vc->window ? vc->window : vc->gfx.drawing_area); 153 154 if (vc->gfx.guest_fb.dmabuf && 155 qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { 156 gd_egl_draw(vc); 157 return; 158 } 159 160 if (!vc->gfx.esurface) { 161 gd_egl_init(vc); 162 if (!vc->gfx.esurface) { 163 return; 164 } 165 vc->gfx.gls = qemu_gl_init_shader(); 166 if (vc->gfx.ds) { 167 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 168 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 169 } 170 #ifdef CONFIG_GBM 171 if (vc->gfx.guest_fb.dmabuf) { 172 egl_dmabuf_release_texture(vc->gfx.guest_fb.dmabuf); 173 gd_egl_scanout_dmabuf(dcl, vc->gfx.guest_fb.dmabuf); 174 } 175 #endif 176 } 177 178 graphic_hw_update(dcl->con); 179 180 if (vc->gfx.glupdates) { 181 vc->gfx.glupdates = 0; 182 gtk_egl_set_scanout_mode(vc, false); 183 gd_egl_draw(vc); 184 } 185 } 186 187 void gd_egl_switch(DisplayChangeListener *dcl, 188 DisplaySurface *surface) 189 { 190 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 191 bool resized = true; 192 193 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 194 195 if (vc->gfx.ds && 196 surface_width(vc->gfx.ds) == surface_width(surface) && 197 surface_height(vc->gfx.ds) == surface_height(surface)) { 198 resized = false; 199 } 200 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 201 vc->gfx.esurface, vc->gfx.ectx); 202 203 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 204 vc->gfx.ds = surface; 205 if (vc->gfx.gls) { 206 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 207 } 208 209 if (resized) { 210 gd_update_windowsize(vc); 211 } 212 213 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 214 EGL_NO_CONTEXT); 215 } 216 217 QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc, 218 QEMUGLParams *params) 219 { 220 VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc); 221 222 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 223 vc->gfx.esurface, vc->gfx.ectx); 224 return qemu_egl_create_context(dgc, params); 225 } 226 227 void gd_egl_scanout_disable(DisplayChangeListener *dcl) 228 { 229 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 230 231 vc->gfx.w = 0; 232 vc->gfx.h = 0; 233 gtk_egl_set_scanout_mode(vc, false); 234 } 235 236 void gd_egl_scanout_texture(DisplayChangeListener *dcl, 237 uint32_t backing_id, bool backing_y_0_top, 238 uint32_t backing_width, uint32_t backing_height, 239 uint32_t x, uint32_t y, 240 uint32_t w, uint32_t h, 241 void *d3d_tex2d) 242 { 243 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 244 245 vc->gfx.x = x; 246 vc->gfx.y = y; 247 vc->gfx.w = w; 248 vc->gfx.h = h; 249 vc->gfx.y0_top = backing_y_0_top; 250 251 if (!vc->gfx.esurface) { 252 gd_egl_init(vc); 253 if (!vc->gfx.esurface) { 254 return; 255 } 256 } 257 258 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 259 vc->gfx.esurface, vc->gfx.ectx); 260 261 gtk_egl_set_scanout_mode(vc, true); 262 egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, 263 backing_id, false); 264 } 265 266 void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, 267 QemuDmaBuf *dmabuf) 268 { 269 #ifdef CONFIG_GBM 270 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 271 uint32_t x, y, width, height, backing_width, backing_height, texture; 272 bool y0_top; 273 274 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 275 vc->gfx.esurface, vc->gfx.ectx); 276 277 egl_dmabuf_import_texture(dmabuf); 278 texture = qemu_dmabuf_get_texture(dmabuf); 279 if (!texture) { 280 return; 281 } 282 283 x = qemu_dmabuf_get_x(dmabuf); 284 y = qemu_dmabuf_get_y(dmabuf); 285 width = qemu_dmabuf_get_width(dmabuf); 286 height = qemu_dmabuf_get_height(dmabuf); 287 backing_width = qemu_dmabuf_get_backing_width(dmabuf); 288 backing_height = qemu_dmabuf_get_backing_height(dmabuf); 289 y0_top = qemu_dmabuf_get_y0_top(dmabuf); 290 291 gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height, 292 x, y, width, height, NULL); 293 294 if (qemu_dmabuf_get_allow_fences(dmabuf)) { 295 vc->gfx.guest_fb.dmabuf = dmabuf; 296 } 297 #endif 298 } 299 300 void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, 301 QemuDmaBuf *dmabuf, bool have_hot, 302 uint32_t hot_x, uint32_t hot_y) 303 { 304 #ifdef CONFIG_GBM 305 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 306 uint32_t backing_width, backing_height, texture; 307 308 if (dmabuf) { 309 egl_dmabuf_import_texture(dmabuf); 310 texture = qemu_dmabuf_get_texture(dmabuf); 311 if (!texture) { 312 return; 313 } 314 315 backing_width = qemu_dmabuf_get_backing_width(dmabuf); 316 backing_height = qemu_dmabuf_get_backing_height(dmabuf); 317 egl_fb_setup_for_tex(&vc->gfx.cursor_fb, backing_width, backing_height, 318 texture, false); 319 } else { 320 egl_fb_destroy(&vc->gfx.cursor_fb); 321 } 322 #endif 323 } 324 325 void gd_egl_cursor_position(DisplayChangeListener *dcl, 326 uint32_t pos_x, uint32_t pos_y) 327 { 328 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 329 330 vc->gfx.cursor_x = pos_x * vc->gfx.scale_x; 331 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y; 332 } 333 334 void gd_egl_scanout_flush(DisplayChangeListener *dcl, 335 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 336 { 337 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 338 GdkWindow *window; 339 int ww, wh, ws; 340 341 if (!vc->gfx.scanout_mode) { 342 return; 343 } 344 if (!vc->gfx.guest_fb.framebuffer) { 345 return; 346 } 347 348 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 349 vc->gfx.esurface, vc->gfx.ectx); 350 351 window = gtk_widget_get_window(vc->gfx.drawing_area); 352 ws = gdk_window_get_scale_factor(window); 353 ww = gdk_window_get_width(window) * ws; 354 wh = gdk_window_get_height(window) * ws; 355 egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); 356 if (vc->gfx.cursor_fb.texture) { 357 egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, 358 vc->gfx.y0_top); 359 egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb, 360 vc->gfx.y0_top, 361 vc->gfx.cursor_x, vc->gfx.cursor_y, 362 vc->gfx.scale_x, vc->gfx.scale_y); 363 } else { 364 egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top); 365 } 366 367 #ifdef CONFIG_GBM 368 if (vc->gfx.guest_fb.dmabuf) { 369 egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf); 370 } 371 #endif 372 373 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); 374 } 375 376 void gd_egl_flush(DisplayChangeListener *dcl, 377 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 378 { 379 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 380 GtkWidget *area = vc->gfx.drawing_area; 381 382 if (vc->gfx.guest_fb.dmabuf && 383 !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { 384 graphic_hw_gl_block(vc->gfx.dcl.con, true); 385 qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); 386 gtk_egl_set_scanout_mode(vc, true); 387 gtk_widget_queue_draw_area(area, x, y, w, h); 388 return; 389 } 390 391 gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h); 392 } 393 394 void gtk_egl_init(DisplayGLMode mode) 395 { 396 GdkDisplay *gdk_display = gdk_display_get_default(); 397 Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display); 398 399 if (qemu_egl_init_dpy_x11(x11_display, mode) < 0) { 400 return; 401 } 402 403 display_opengl = 1; 404 } 405 406 int gd_egl_make_current(DisplayGLCtx *dgc, 407 QEMUGLContext ctx) 408 { 409 VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc); 410 411 if (!eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 412 vc->gfx.esurface, ctx)) { 413 error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string()); 414 return -1; 415 } 416 417 return 0; 418 } 419