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