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 egl_fb_destroy(&vc->gfx.guest_fb); 30 if (vc->gfx.surface) { 31 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 32 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 33 } 34 } 35 } 36 37 /** DisplayState Callbacks (opengl version) **/ 38 39 void gd_gl_area_draw(VirtualConsole *vc) 40 { 41 int ww, wh, y1, y2; 42 43 if (!vc->gfx.gls) { 44 return; 45 } 46 47 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 48 ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); 49 wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); 50 51 if (vc->gfx.scanout_mode) { 52 if (!vc->gfx.guest_fb.framebuffer) { 53 return; 54 } 55 56 glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); 57 /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ 58 59 glViewport(0, 0, ww, wh); 60 y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; 61 y2 = vc->gfx.y0_top ? vc->gfx.h : 0; 62 glBlitFramebuffer(0, y1, vc->gfx.w, y2, 63 0, 0, ww, wh, 64 GL_COLOR_BUFFER_BIT, GL_NEAREST); 65 } else { 66 if (!vc->gfx.ds) { 67 return; 68 } 69 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 70 71 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); 72 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 73 } 74 75 #ifdef CONFIG_GBM 76 if (vc->gfx.guest_fb.dmabuf) { 77 egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf); 78 } 79 #endif 80 81 glFlush(); 82 #ifdef CONFIG_GBM 83 if (vc->gfx.guest_fb.dmabuf) { 84 QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; 85 86 egl_dmabuf_create_fence(dmabuf); 87 if (dmabuf->fence_fd > 0) { 88 qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); 89 return; 90 } 91 graphic_hw_gl_block(vc->gfx.dcl.con, false); 92 } 93 #endif 94 graphic_hw_gl_flushed(vc->gfx.dcl.con); 95 } 96 97 void gd_gl_area_update(DisplayChangeListener *dcl, 98 int x, int y, int w, int h) 99 { 100 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 101 102 if (!vc->gfx.gls || !vc->gfx.ds) { 103 return; 104 } 105 106 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 107 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); 108 vc->gfx.glupdates++; 109 } 110 111 void gd_gl_area_refresh(DisplayChangeListener *dcl) 112 { 113 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 114 115 if (!vc->gfx.gls) { 116 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 117 return; 118 } 119 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 120 vc->gfx.gls = qemu_gl_init_shader(); 121 if (vc->gfx.ds) { 122 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 123 } 124 } 125 126 graphic_hw_update(dcl->con); 127 128 if (vc->gfx.glupdates) { 129 vc->gfx.glupdates = 0; 130 gtk_gl_area_set_scanout_mode(vc, false); 131 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 132 } 133 } 134 135 void gd_gl_area_switch(DisplayChangeListener *dcl, 136 DisplaySurface *surface) 137 { 138 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 139 bool resized = true; 140 141 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 142 143 if (vc->gfx.ds && 144 surface_width(vc->gfx.ds) == surface_width(surface) && 145 surface_height(vc->gfx.ds) == surface_height(surface)) { 146 resized = false; 147 } 148 149 if (vc->gfx.gls) { 150 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 151 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 152 surface_gl_create_texture(vc->gfx.gls, surface); 153 } 154 vc->gfx.ds = surface; 155 156 if (resized) { 157 gd_update_windowsize(vc); 158 } 159 } 160 161 QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl, 162 QEMUGLParams *params) 163 { 164 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 165 GdkWindow *window; 166 GdkGLContext *ctx; 167 GError *err = NULL; 168 169 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 170 window = gtk_widget_get_window(vc->gfx.drawing_area); 171 ctx = gdk_window_create_gl_context(window, &err); 172 if (err) { 173 g_printerr("Create gdk gl context failed: %s\n", err->message); 174 g_error_free(err); 175 return NULL; 176 } 177 gdk_gl_context_set_required_version(ctx, 178 params->major_ver, 179 params->minor_ver); 180 gdk_gl_context_realize(ctx, &err); 181 if (err) { 182 g_printerr("Realize gdk gl context failed: %s\n", err->message); 183 g_error_free(err); 184 g_clear_object(&ctx); 185 return NULL; 186 } 187 return ctx; 188 } 189 190 void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx) 191 { 192 /* FIXME */ 193 } 194 195 void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, 196 uint32_t backing_id, 197 bool backing_y_0_top, 198 uint32_t backing_width, 199 uint32_t backing_height, 200 uint32_t x, uint32_t y, 201 uint32_t w, uint32_t h) 202 { 203 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 204 205 vc->gfx.x = x; 206 vc->gfx.y = y; 207 vc->gfx.w = w; 208 vc->gfx.h = h; 209 vc->gfx.y0_top = backing_y_0_top; 210 211 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 212 213 if (backing_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { 214 gtk_gl_area_set_scanout_mode(vc, false); 215 return; 216 } 217 218 gtk_gl_area_set_scanout_mode(vc, true); 219 egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, 220 backing_id, false); 221 } 222 223 void gd_gl_area_scanout_disable(DisplayChangeListener *dcl) 224 { 225 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 226 227 gtk_gl_area_set_scanout_mode(vc, false); 228 } 229 230 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, 231 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 232 { 233 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 234 235 if (vc->gfx.guest_fb.dmabuf) { 236 graphic_hw_gl_block(vc->gfx.dcl.con, true); 237 } 238 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 239 } 240 241 void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, 242 QemuDmaBuf *dmabuf) 243 { 244 #ifdef CONFIG_GBM 245 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 246 247 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 248 egl_dmabuf_import_texture(dmabuf); 249 if (!dmabuf->texture) { 250 return; 251 } 252 253 gd_gl_area_scanout_texture(dcl, dmabuf->texture, 254 false, dmabuf->width, dmabuf->height, 255 0, 0, dmabuf->width, dmabuf->height); 256 257 if (dmabuf->allow_fences) { 258 vc->gfx.guest_fb.dmabuf = dmabuf; 259 } 260 #endif 261 } 262 263 void gtk_gl_area_init(void) 264 { 265 display_opengl = 1; 266 } 267 268 int gd_gl_area_make_current(DisplayChangeListener *dcl, 269 QEMUGLContext ctx) 270 { 271 gdk_gl_context_make_current(ctx); 272 return 0; 273 } 274