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 "system/system.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 pw, ph, gs, y1, y2; 46 int ww, wh; 47 int ww_surface, wh_surface; 48 int fbw, fbh; 49 int wx_offset, wy_offset; 50 51 if (!vc->gfx.gls) { 52 return; 53 } 54 55 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 56 gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); 57 fbw = surface_width(vc->gfx.ds); 58 fbh = surface_height(vc->gfx.ds); 59 ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); 60 wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); 61 pw = ww * gs; 62 ph = wh * gs; 63 64 gd_update_scale(vc, ww, wh, fbw, fbh); 65 66 ww_surface = fbw * vc->gfx.scale_x; 67 wh_surface = fbh * vc->gfx.scale_y; 68 69 wx_offset = wy_offset = 0; 70 if (ww > ww_surface) { 71 wx_offset = (ww - ww_surface) / 2; 72 } 73 if (wh > wh_surface) { 74 wy_offset = (wh - wh_surface) / 2; 75 } 76 77 if (vc->gfx.scanout_mode) { 78 if (!vc->gfx.guest_fb.framebuffer) { 79 return; 80 } 81 82 #ifdef CONFIG_GBM 83 if (dmabuf) { 84 if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { 85 return; 86 } else { 87 qemu_dmabuf_set_draw_submitted(dmabuf, false); 88 } 89 } 90 #endif 91 92 glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); 93 /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ 94 95 if (wx_offset > 0) { 96 glEnable(GL_SCISSOR_TEST); 97 glScissor(0, 0, wx_offset * gs, wh * gs); 98 glClear(GL_COLOR_BUFFER_BIT); 99 glScissor((ww - wx_offset) * gs, 0, wx_offset * gs, wh * gs); 100 glClear(GL_COLOR_BUFFER_BIT); 101 glDisable(GL_SCISSOR_TEST); 102 } 103 if (wy_offset > 0) { 104 glEnable(GL_SCISSOR_TEST); 105 glScissor(0, 0, ww * gs, wy_offset * gs); 106 glClear(GL_COLOR_BUFFER_BIT); 107 glScissor(0, (wh - wy_offset) * gs, ww * gs, wy_offset * gs); 108 glClear(GL_COLOR_BUFFER_BIT); 109 glDisable(GL_SCISSOR_TEST); 110 } 111 112 glViewport(0, 0, pw, ph); 113 y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; 114 y2 = vc->gfx.y0_top ? vc->gfx.h : 0; 115 glBlitFramebuffer(0, y1, vc->gfx.w, y2, 116 wx_offset * gs, wy_offset * gs, 117 (ww - wx_offset) * gs, (wh - wy_offset) * gs, 118 GL_COLOR_BUFFER_BIT, GL_NEAREST); 119 #ifdef CONFIG_GBM 120 if (dmabuf) { 121 egl_dmabuf_create_sync(dmabuf); 122 } 123 #endif 124 glFlush(); 125 #ifdef CONFIG_GBM 126 if (dmabuf) { 127 int fence_fd; 128 egl_dmabuf_create_fence(dmabuf); 129 fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); 130 if (fence_fd >= 0) { 131 qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc); 132 return; 133 } 134 graphic_hw_gl_block(vc->gfx.dcl.con, false); 135 } 136 #endif 137 } else { 138 if (!vc->gfx.ds) { 139 return; 140 } 141 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 142 143 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); 144 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 145 } 146 } 147 148 void gd_gl_area_update(DisplayChangeListener *dcl, 149 int x, int y, int w, int h) 150 { 151 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 152 153 if (!vc->gfx.gls || !vc->gfx.ds) { 154 return; 155 } 156 157 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 158 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); 159 vc->gfx.glupdates++; 160 gdk_gl_context_clear_current(); 161 } 162 163 void gd_gl_area_refresh(DisplayChangeListener *dcl) 164 { 165 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 166 167 gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); 168 169 if (vc->gfx.guest_fb.dmabuf && 170 qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { 171 gd_gl_area_draw(vc); 172 return; 173 } 174 175 if (!vc->gfx.gls) { 176 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 177 return; 178 } 179 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 180 vc->gfx.gls = qemu_gl_init_shader(); 181 if (vc->gfx.ds) { 182 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 183 } 184 } 185 186 graphic_hw_update(dcl->con); 187 188 if (vc->gfx.glupdates) { 189 vc->gfx.glupdates = 0; 190 gtk_gl_area_set_scanout_mode(vc, false); 191 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 192 } 193 } 194 195 void gd_gl_area_switch(DisplayChangeListener *dcl, 196 DisplaySurface *surface) 197 { 198 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 199 bool resized = true; 200 201 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 202 203 if (vc->gfx.ds && 204 surface_width(vc->gfx.ds) == surface_width(surface) && 205 surface_height(vc->gfx.ds) == surface_height(surface)) { 206 resized = false; 207 } 208 209 if (vc->gfx.gls) { 210 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 211 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 212 surface_gl_create_texture(vc->gfx.gls, surface); 213 } 214 vc->gfx.ds = surface; 215 216 if (resized) { 217 gd_update_windowsize(vc); 218 } 219 } 220 221 static int gd_cmp_gl_context_version(int major, int minor, QEMUGLParams *params) 222 { 223 if (major > params->major_ver) { 224 return 1; 225 } 226 if (major < params->major_ver) { 227 return -1; 228 } 229 if (minor > params->minor_ver) { 230 return 1; 231 } 232 if (minor < params->minor_ver) { 233 return -1; 234 } 235 return 0; 236 } 237 238 QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc, 239 QEMUGLParams *params) 240 { 241 VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc); 242 GdkWindow *window; 243 GdkGLContext *ctx; 244 GError *err = NULL; 245 int major, minor; 246 247 window = gtk_widget_get_window(vc->gfx.drawing_area); 248 ctx = gdk_window_create_gl_context(window, &err); 249 if (err) { 250 g_printerr("Create gdk gl context failed: %s\n", err->message); 251 g_error_free(err); 252 return NULL; 253 } 254 gdk_gl_context_set_required_version(ctx, 255 params->major_ver, 256 params->minor_ver); 257 gdk_gl_context_realize(ctx, &err); 258 if (err) { 259 g_printerr("Realize gdk gl context failed: %s\n", err->message); 260 g_error_free(err); 261 g_clear_object(&ctx); 262 return NULL; 263 } 264 265 gdk_gl_context_make_current(ctx); 266 gdk_gl_context_get_version(ctx, &major, &minor); 267 gdk_gl_context_clear_current(); 268 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 269 270 if (gd_cmp_gl_context_version(major, minor, params) == -1) { 271 /* created ctx version < requested version */ 272 g_clear_object(&ctx); 273 } 274 275 trace_gd_gl_area_create_context(ctx, params->major_ver, params->minor_ver); 276 return ctx; 277 } 278 279 void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx) 280 { 281 GdkGLContext *current_ctx = gdk_gl_context_get_current(); 282 283 trace_gd_gl_area_destroy_context(ctx, current_ctx); 284 if (ctx == current_ctx) { 285 gdk_gl_context_clear_current(); 286 } 287 g_clear_object(&ctx); 288 } 289 290 void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, 291 uint32_t backing_id, 292 bool backing_y_0_top, 293 uint32_t backing_width, 294 uint32_t backing_height, 295 uint32_t x, uint32_t y, 296 uint32_t w, uint32_t h, 297 void *d3d_tex2d) 298 { 299 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 300 301 vc->gfx.x = x; 302 vc->gfx.y = y; 303 vc->gfx.w = w; 304 vc->gfx.h = h; 305 vc->gfx.y0_top = backing_y_0_top; 306 307 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 308 309 if (backing_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { 310 gtk_gl_area_set_scanout_mode(vc, false); 311 return; 312 } 313 314 gtk_gl_area_set_scanout_mode(vc, true); 315 egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, 316 backing_id, false); 317 } 318 319 void gd_gl_area_scanout_disable(DisplayChangeListener *dcl) 320 { 321 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 322 323 gtk_gl_area_set_scanout_mode(vc, false); 324 } 325 326 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, 327 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 328 { 329 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 330 331 if (vc->gfx.guest_fb.dmabuf && 332 !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { 333 graphic_hw_gl_block(vc->gfx.dcl.con, true); 334 qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); 335 gtk_gl_area_set_scanout_mode(vc, true); 336 } 337 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 338 } 339 340 void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, 341 QemuDmaBuf *dmabuf) 342 { 343 #ifdef CONFIG_GBM 344 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 345 uint32_t x, y, width, height, backing_width, backing_height, texture; 346 bool y0_top; 347 348 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 349 egl_dmabuf_import_texture(dmabuf); 350 texture = qemu_dmabuf_get_texture(dmabuf); 351 if (!texture) { 352 return; 353 } 354 355 x = qemu_dmabuf_get_x(dmabuf); 356 y = qemu_dmabuf_get_y(dmabuf); 357 width = qemu_dmabuf_get_width(dmabuf); 358 height = qemu_dmabuf_get_height(dmabuf); 359 backing_width = qemu_dmabuf_get_backing_width(dmabuf); 360 backing_height = qemu_dmabuf_get_backing_height(dmabuf); 361 y0_top = qemu_dmabuf_get_y0_top(dmabuf); 362 363 gd_gl_area_scanout_texture(dcl, texture, y0_top, 364 backing_width, backing_height, 365 x, y, width, height, NULL); 366 367 if (qemu_dmabuf_get_allow_fences(dmabuf)) { 368 vc->gfx.guest_fb.dmabuf = dmabuf; 369 } 370 #endif 371 } 372 373 void gtk_gl_area_init(void) 374 { 375 display_opengl = 1; 376 } 377 378 int gd_gl_area_make_current(DisplayGLCtx *dgc, 379 QEMUGLContext ctx) 380 { 381 gdk_gl_context_make_current(ctx); 382 return 0; 383 } 384