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-common.h" 15 16 #include "trace.h" 17 18 #include "ui/console.h" 19 #include "ui/gtk.h" 20 #include "ui/egl-helpers.h" 21 22 #include "sysemu/sysemu.h" 23 24 static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) 25 { 26 if (vc->gfx.scanout_mode == scanout) { 27 return; 28 } 29 30 vc->gfx.scanout_mode = scanout; 31 if (!vc->gfx.scanout_mode) { 32 if (vc->gfx.fbo_id) { 33 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 34 GL_COLOR_ATTACHMENT0_EXT, 35 GL_TEXTURE_2D, 0, 0); 36 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); 37 glDeleteFramebuffers(1, &vc->gfx.fbo_id); 38 vc->gfx.fbo_id = 0; 39 } 40 if (vc->gfx.surface) { 41 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 42 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 43 } 44 } 45 } 46 47 /** DisplayState Callbacks (opengl version) **/ 48 49 void gd_egl_init(VirtualConsole *vc) 50 { 51 GdkWindow *gdk_window = gtk_widget_get_window(vc->gfx.drawing_area); 52 if (!gdk_window) { 53 return; 54 } 55 56 #if GTK_CHECK_VERSION(3, 0, 0) 57 Window x11_window = gdk_x11_window_get_xid(gdk_window); 58 #else 59 Window x11_window = gdk_x11_drawable_get_xid(gdk_window); 60 #endif 61 if (!x11_window) { 62 return; 63 } 64 65 vc->gfx.ectx = qemu_egl_init_ctx(); 66 vc->gfx.esurface = qemu_egl_init_surface_x11(vc->gfx.ectx, x11_window); 67 68 assert(vc->gfx.esurface); 69 } 70 71 void gd_egl_draw(VirtualConsole *vc) 72 { 73 GdkWindow *window; 74 int ww, wh; 75 76 if (!vc->gfx.gls) { 77 return; 78 } 79 80 if (vc->gfx.scanout_mode) { 81 gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); 82 } else { 83 if (!vc->gfx.ds) { 84 return; 85 } 86 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 87 vc->gfx.esurface, vc->gfx.ectx); 88 89 window = gtk_widget_get_window(vc->gfx.drawing_area); 90 gdk_drawable_get_size(window, &ww, &wh); 91 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); 92 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 93 94 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); 95 } 96 } 97 98 void gd_egl_update(DisplayChangeListener *dcl, 99 int x, int y, int w, int h) 100 { 101 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 102 103 if (!vc->gfx.gls || !vc->gfx.ds) { 104 return; 105 } 106 107 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 108 vc->gfx.esurface, vc->gfx.ectx); 109 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); 110 vc->gfx.glupdates++; 111 } 112 113 void gd_egl_refresh(DisplayChangeListener *dcl) 114 { 115 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 116 117 if (!vc->gfx.esurface) { 118 gd_egl_init(vc); 119 if (!vc->gfx.esurface) { 120 return; 121 } 122 vc->gfx.gls = console_gl_init_context(); 123 if (vc->gfx.ds) { 124 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 125 } 126 } 127 128 graphic_hw_update(dcl->con); 129 130 if (vc->gfx.glupdates) { 131 vc->gfx.glupdates = 0; 132 gtk_egl_set_scanout_mode(vc, false); 133 gd_egl_draw(vc); 134 } 135 } 136 137 void gd_egl_switch(DisplayChangeListener *dcl, 138 DisplaySurface *surface) 139 { 140 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 141 bool resized = true; 142 143 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 144 145 if (vc->gfx.ds && 146 surface_width(vc->gfx.ds) == surface_width(surface) && 147 surface_height(vc->gfx.ds) == surface_height(surface)) { 148 resized = false; 149 } 150 151 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 152 vc->gfx.ds = surface; 153 if (vc->gfx.gls) { 154 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 155 } 156 157 if (resized) { 158 gd_update_windowsize(vc); 159 } 160 } 161 162 QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl, 163 QEMUGLParams *params) 164 { 165 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 166 167 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 168 vc->gfx.esurface, vc->gfx.ectx); 169 return qemu_egl_create_context(dcl, params); 170 } 171 172 void gd_egl_scanout(DisplayChangeListener *dcl, 173 uint32_t backing_id, bool backing_y_0_top, 174 uint32_t x, uint32_t y, 175 uint32_t w, uint32_t h) 176 { 177 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 178 179 vc->gfx.x = x; 180 vc->gfx.y = y; 181 vc->gfx.w = w; 182 vc->gfx.h = h; 183 vc->gfx.tex_id = backing_id; 184 vc->gfx.y0_top = backing_y_0_top; 185 186 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 187 vc->gfx.esurface, vc->gfx.ectx); 188 189 if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { 190 gtk_egl_set_scanout_mode(vc, false); 191 return; 192 } 193 194 gtk_egl_set_scanout_mode(vc, true); 195 if (!vc->gfx.fbo_id) { 196 glGenFramebuffers(1, &vc->gfx.fbo_id); 197 } 198 199 glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id); 200 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 201 GL_TEXTURE_2D, vc->gfx.tex_id, 0); 202 } 203 204 void gd_egl_scanout_flush(DisplayChangeListener *dcl, 205 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 206 { 207 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 208 GdkWindow *window; 209 int ww, wh, y1, y2; 210 211 if (!vc->gfx.scanout_mode) { 212 return; 213 } 214 if (!vc->gfx.fbo_id) { 215 return; 216 } 217 218 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 219 vc->gfx.esurface, vc->gfx.ectx); 220 221 glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id); 222 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 223 224 window = gtk_widget_get_window(vc->gfx.drawing_area); 225 gdk_drawable_get_size(window, &ww, &wh); 226 glViewport(0, 0, ww, wh); 227 y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; 228 y2 = vc->gfx.y0_top ? vc->gfx.h : 0; 229 glBlitFramebuffer(0, y1, vc->gfx.w, y2, 230 0, 0, ww, wh, 231 GL_COLOR_BUFFER_BIT, GL_NEAREST); 232 glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id); 233 234 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); 235 } 236 237 void gtk_egl_init(void) 238 { 239 GdkDisplay *gdk_display = gdk_display_get_default(); 240 Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display); 241 242 if (qemu_egl_init_dpy(x11_display, false, false) < 0) { 243 return; 244 } 245 246 display_opengl = 1; 247 } 248 249 int gd_egl_make_current(DisplayChangeListener *dcl, 250 QEMUGLContext ctx) 251 { 252 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 253 254 return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, 255 vc->gfx.esurface, ctx); 256 } 257