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-common.h" 11 12 #include "trace.h" 13 14 #include "ui/console.h" 15 #include "ui/gtk.h" 16 #include "ui/egl-helpers.h" 17 18 #include "sysemu/sysemu.h" 19 20 static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) 21 { 22 if (vc->gfx.scanout_mode == scanout) { 23 return; 24 } 25 26 vc->gfx.scanout_mode = scanout; 27 if (!vc->gfx.scanout_mode) { 28 if (vc->gfx.fbo_id) { 29 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 30 GL_COLOR_ATTACHMENT0_EXT, 31 GL_TEXTURE_2D, 0, 0); 32 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); 33 glDeleteFramebuffers(1, &vc->gfx.fbo_id); 34 vc->gfx.fbo_id = 0; 35 } 36 if (vc->gfx.surface) { 37 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 38 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 39 } 40 } 41 } 42 43 /** DisplayState Callbacks (opengl version) **/ 44 45 void gd_gl_area_draw(VirtualConsole *vc) 46 { 47 int ww, wh, y1, y2; 48 49 if (!vc->gfx.gls) { 50 return; 51 } 52 53 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 54 ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); 55 wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); 56 57 if (vc->gfx.scanout_mode) { 58 if (!vc->gfx.fbo_id) { 59 return; 60 } 61 62 glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id); 63 /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ 64 65 glViewport(0, 0, ww, wh); 66 y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; 67 y2 = vc->gfx.y0_top ? vc->gfx.h : 0; 68 glBlitFramebuffer(0, y1, vc->gfx.w, y2, 69 0, 0, ww, wh, 70 GL_COLOR_BUFFER_BIT, GL_NEAREST); 71 } else { 72 if (!vc->gfx.ds) { 73 return; 74 } 75 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 76 77 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); 78 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 79 } 80 } 81 82 void gd_gl_area_update(DisplayChangeListener *dcl, 83 int x, int y, int w, int h) 84 { 85 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 86 87 if (!vc->gfx.gls || !vc->gfx.ds) { 88 return; 89 } 90 91 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 92 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); 93 vc->gfx.glupdates++; 94 } 95 96 void gd_gl_area_refresh(DisplayChangeListener *dcl) 97 { 98 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 99 100 if (!vc->gfx.gls) { 101 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 102 return; 103 } 104 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 105 vc->gfx.gls = console_gl_init_context(); 106 if (vc->gfx.ds) { 107 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds); 108 } 109 } 110 111 graphic_hw_update(dcl->con); 112 113 if (vc->gfx.glupdates) { 114 vc->gfx.glupdates = 0; 115 gtk_gl_area_set_scanout_mode(vc, false); 116 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 117 } 118 } 119 120 void gd_gl_area_switch(DisplayChangeListener *dcl, 121 DisplaySurface *surface) 122 { 123 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 124 bool resized = true; 125 126 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 127 128 if (vc->gfx.ds && 129 surface_width(vc->gfx.ds) == surface_width(surface) && 130 surface_height(vc->gfx.ds) == surface_height(surface)) { 131 resized = false; 132 } 133 134 if (vc->gfx.gls) { 135 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 136 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); 137 surface_gl_create_texture(vc->gfx.gls, surface); 138 } 139 vc->gfx.ds = surface; 140 141 if (resized) { 142 gd_update_windowsize(vc); 143 } 144 } 145 146 QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl, 147 QEMUGLParams *params) 148 { 149 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 150 GdkWindow *window; 151 GdkGLContext *ctx; 152 GError *err = NULL; 153 154 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 155 window = gtk_widget_get_window(vc->gfx.drawing_area); 156 ctx = gdk_window_create_gl_context(window, &err); 157 gdk_gl_context_set_required_version(ctx, 158 params->major_ver, 159 params->minor_ver); 160 gdk_gl_context_realize(ctx, &err); 161 return ctx; 162 } 163 164 void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx) 165 { 166 /* FIXME */ 167 } 168 169 void gd_gl_area_scanout(DisplayChangeListener *dcl, 170 uint32_t backing_id, bool backing_y_0_top, 171 uint32_t x, uint32_t y, 172 uint32_t w, uint32_t h) 173 { 174 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 175 176 vc->gfx.x = x; 177 vc->gfx.y = y; 178 vc->gfx.w = w; 179 vc->gfx.h = h; 180 vc->gfx.tex_id = backing_id; 181 vc->gfx.y0_top = backing_y_0_top; 182 183 gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); 184 185 if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) { 186 gtk_gl_area_set_scanout_mode(vc, false); 187 return; 188 } 189 190 gtk_gl_area_set_scanout_mode(vc, true); 191 if (!vc->gfx.fbo_id) { 192 glGenFramebuffers(1, &vc->gfx.fbo_id); 193 } 194 195 glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id); 196 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 197 GL_TEXTURE_2D, vc->gfx.tex_id, 0); 198 } 199 200 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, 201 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 202 { 203 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 204 205 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 206 } 207 208 void gtk_gl_area_init(void) 209 { 210 display_opengl = 1; 211 } 212 213 QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl) 214 { 215 return gdk_gl_context_get_current(); 216 } 217 218 int gd_gl_area_make_current(DisplayChangeListener *dcl, 219 QEMUGLContext ctx) 220 { 221 gdk_gl_context_make_current(ctx); 222 return 0; 223 } 224