xref: /openbmc/qemu/ui/gtk-egl.c (revision 56411125)
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