master
c 440 lines 12.7 KB
Raw
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/osdep.h"
15 #include "qemu/main-loop.h"
16 #include "qemu/error-report.h"
17
18 #include "trace.h"
19
20 #include "ui/console.h"
21 #include "ui/gtk.h"
22 #include "ui/egl-helpers.h"
23 #include "ui/shader.h"
24
25 #include "system/system.h"
26
27 static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
28 {
29 if (vc->gfx.scanout_mode == scanout) {
30 return;
31 }
32
33 vc->gfx.scanout_mode = scanout;
34 if (!vc->gfx.scanout_mode) {
35 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
36 vc->gfx.esurface, vc->gfx.ectx);
37 egl_fb_destroy(&vc->gfx.guest_fb);
38 if (vc->gfx.surface) {
39 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
40 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
41 }
42 }
43 }
44
45 /** DisplayState Callbacks (opengl version) **/
46
47 void gd_egl_init(VirtualConsole *vc)
48 {
49 GdkWindow *gdk_window = gtk_widget_get_window(vc->gfx.drawing_area);
50 if (!gdk_window) {
51 return;
52 }
53
54 Window x11_window = gdk_x11_window_get_xid(gdk_window);
55 if (!x11_window) {
56 return;
57 }
58
59 vc->gfx.ectx = qemu_egl_init_ctx();
60 vc->gfx.esurface = qemu_egl_init_surface_x11
61 (vc->gfx.ectx, (EGLNativeWindowType)x11_window);
62
63 assert(vc->gfx.esurface);
64 }
65
66 void gd_egl_draw(VirtualConsole *vc)
67 {
68 GdkWindow *window;
69 #ifdef CONFIG_GBM
70 QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
71 int fence_fd;
72 #endif
73 int ww, wh, pw, ph, gs;
74
75 if (!vc->gfx.gls || !vc->gfx.ds) {
76 return;
77 }
78
79 window = gtk_widget_get_window(vc->gfx.drawing_area);
80 gs = gdk_window_get_scale_factor(window);
81 ww = gdk_window_get_width(window);
82 wh = gdk_window_get_height(window);
83 pw = ww * gs;
84 ph = wh * gs;
85
86 if (vc->gfx.scanout_mode) {
87 #ifdef CONFIG_GBM
88 if (dmabuf) {
89 if (!qemu_dmabuf_get_draw_submitted(dmabuf)) {
90 return;
91 } else {
92 qemu_dmabuf_set_draw_submitted(dmabuf, false);
93 }
94 qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
95 }
96 #endif
97 gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
98
99 gd_update_scale(vc, ww, wh,
100 surface_width(vc->gfx.ds),
101 surface_height(vc->gfx.ds));
102
103 glFlush();
104 #ifdef CONFIG_GBM
105 if (dmabuf) {
106 egl_dmabuf_create_fence(dmabuf);
107 fence_fd = qemu_dmabuf_get_fence_fd(dmabuf);
108 if (fence_fd >= 0) {
109 qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc);
110 return;
111 }
112 qemu_console_hw_gl_block(vc->gfx.dcl.con, false);
113 }
114 #endif
115 } else {
116 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
117 vc->gfx.esurface, vc->gfx.ectx);
118
119 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph);
120 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
121
122 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
123
124 gd_update_scale(vc, ww, wh,
125 surface_width(vc->gfx.ds),
126 surface_height(vc->gfx.ds));
127
128 glFlush();
129 }
130 }
131
132 void gd_egl_update(DisplayChangeListener *dcl,
133 int x, int y, int w, int h)
134 {
135 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
136
137 if (!vc->gfx.gls || !vc->gfx.ds) {
138 return;
139 }
140
141 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
142 vc->gfx.esurface, vc->gfx.ectx);
143 surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
144 vc->gfx.glupdates++;
145 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
146 EGL_NO_SURFACE, EGL_NO_CONTEXT);
147 }
148
149 void gd_egl_refresh(DisplayChangeListener *dcl)
150 {
151 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
152
153 gd_update_monitor_refresh_rate(
154 vc, vc->window ? vc->window : vc->gfx.drawing_area);
155
156 if (!vc->gfx.esurface) {
157 gd_egl_init(vc);
158 if (!vc->gfx.esurface) {
159 return;
160 }
161 vc->gfx.gls = qemu_gl_init_shader();
162 if (vc->gfx.ds) {
163 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
164 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
165 }
166 #ifdef CONFIG_GBM
167 if (vc->gfx.guest_fb.dmabuf) {
168 egl_dmabuf_release_texture(vc->gfx.guest_fb.dmabuf);
169 gd_egl_scanout_dmabuf(dcl, vc->gfx.guest_fb.dmabuf);
170 }
171 #endif
172 }
173
174 if (vc->gfx.guest_fb.dmabuf &&
175 qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
176 gd_egl_draw(vc);
177 return;
178 }
179
180 qemu_console_hw_update(dcl->con);
181
182 if (vc->gfx.glupdates) {
183 vc->gfx.glupdates = 0;
184 gd_egl_draw(vc);
185 }
186 }
187
188 void gd_egl_switch(DisplayChangeListener *dcl,
189 DisplaySurface *surface)
190 {
191 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
192 bool resized = true;
193
194 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
195
196 if (vc->gfx.ds &&
197 surface_width(vc->gfx.ds) == surface_width(surface) &&
198 surface_height(vc->gfx.ds) == surface_height(surface)) {
199 resized = false;
200 }
201 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
202 vc->gfx.esurface, vc->gfx.ectx);
203
204 surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
205 vc->gfx.ds = surface;
206 if (vc->gfx.gls) {
207 surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
208 }
209
210 if (resized) {
211 gd_update_windowsize(vc);
212 }
213
214 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
215 EGL_NO_CONTEXT);
216 }
217
218 QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
219 QEMUGLParams *params)
220 {
221 VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
222
223 return qemu_egl_create_context(dgc, params, vc->gfx.ectx);
224 }
225
226 void gd_egl_scanout_disable(DisplayChangeListener *dcl)
227 {
228 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
229
230 vc->gfx.w = 0;
231 vc->gfx.h = 0;
232 gtk_egl_set_scanout_mode(vc, false);
233 }
234
235 void gd_egl_scanout_texture(DisplayChangeListener *dcl,
236 uint32_t backing_id, bool backing_y_0_top,
237 uint32_t backing_width, uint32_t backing_height,
238 uint32_t x, uint32_t y,
239 uint32_t w, uint32_t h,
240 void *d3d_tex2d)
241 {
242 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
243
244 vc->gfx.x = x;
245 vc->gfx.y = y;
246 vc->gfx.w = w;
247 vc->gfx.h = h;
248 vc->gfx.y0_top = backing_y_0_top;
249
250 if (!vc->gfx.esurface) {
251 gd_egl_init(vc);
252 if (!vc->gfx.esurface) {
253 return;
254 }
255 }
256
257 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
258 vc->gfx.esurface, vc->gfx.ectx);
259
260 gtk_egl_set_scanout_mode(vc, true);
261 egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
262 backing_id, false);
263 }
264
265 void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
266 QemuDmaBuf *dmabuf)
267 {
268 #ifdef CONFIG_GBM
269 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
270 uint32_t x, y, width, height, backing_width, backing_height, texture;
271 bool y0_top;
272
273 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
274 vc->gfx.esurface, vc->gfx.ectx);
275
276 egl_dmabuf_import_texture(dmabuf);
277 texture = qemu_dmabuf_get_texture(dmabuf);
278 if (!texture) {
279 return;
280 }
281
282 x = qemu_dmabuf_get_x(dmabuf);
283 y = qemu_dmabuf_get_y(dmabuf);
284 width = qemu_dmabuf_get_width(dmabuf);
285 height = qemu_dmabuf_get_height(dmabuf);
286 backing_width = qemu_dmabuf_get_backing_width(dmabuf);
287 backing_height = qemu_dmabuf_get_backing_height(dmabuf);
288 y0_top = qemu_dmabuf_get_y0_top(dmabuf);
289
290 gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height,
291 x, y, width, height, NULL);
292
293 if (qemu_dmabuf_get_allow_fences(dmabuf)) {
294 vc->gfx.guest_fb.dmabuf = dmabuf;
295 }
296 #endif
297 }
298
299 void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
300 QemuDmaBuf *dmabuf, bool have_hot,
301 uint32_t hot_x, uint32_t hot_y)
302 {
303 #ifdef CONFIG_GBM
304 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
305 uint32_t backing_width, backing_height, texture;
306
307 if (dmabuf) {
308 egl_dmabuf_import_texture(dmabuf);
309 texture = qemu_dmabuf_get_texture(dmabuf);
310 if (!texture) {
311 return;
312 }
313
314 backing_width = qemu_dmabuf_get_backing_width(dmabuf);
315 backing_height = qemu_dmabuf_get_backing_height(dmabuf);
316 egl_fb_setup_for_tex(&vc->gfx.cursor_fb, backing_width, backing_height,
317 texture, false);
318 } else {
319 egl_fb_destroy(&vc->gfx.cursor_fb);
320 }
321 #endif
322 }
323
324 void gd_egl_cursor_position(DisplayChangeListener *dcl,
325 uint32_t pos_x, uint32_t pos_y)
326 {
327 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
328
329 vc->gfx.cursor_x = pos_x * vc->gfx.scale_x;
330 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
331 }
332
333 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
334 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
335 {
336 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
337 GdkWindow *window;
338 int px_offset, py_offset;
339 int gs;
340 int pw_widget, ph_widget, pw_surface, ph_surface;
341 int ww_widget, wh_widget, ww_surface, wh_surface;
342 int fbw, fbh;
343
344 if (!vc->gfx.scanout_mode) {
345 return;
346 }
347 if (!vc->gfx.guest_fb.framebuffer) {
348 return;
349 }
350
351 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
352 vc->gfx.esurface, vc->gfx.ectx);
353
354 window = gtk_widget_get_window(vc->gfx.drawing_area);
355 gs = gdk_window_get_scale_factor(window);
356 ww_widget = gdk_window_get_width(window);
357 wh_widget = gdk_window_get_height(window);
358 fbw = surface_width(vc->gfx.ds);
359 fbh = surface_height(vc->gfx.ds);
360
361 gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh);
362
363 ww_surface = fbw * vc->gfx.scale_x;
364 wh_surface = fbh * vc->gfx.scale_y;
365 pw_widget = ww_widget * gs;
366 ph_widget = wh_widget * gs;
367 pw_surface = ww_surface * gs;
368 ph_surface = wh_surface * gs;
369
370 px_offset = 0;
371 py_offset = 0;
372 if (pw_widget > pw_surface) {
373 px_offset = (pw_widget - pw_surface) / 2;
374 }
375 if (ph_widget > ph_surface) {
376 py_offset = (ph_widget - ph_surface) / 2;
377 }
378
379 egl_fb_setup_default(&vc->gfx.win_fb, pw_surface, ph_surface,
380 px_offset, py_offset);
381 if (vc->gfx.cursor_fb.texture) {
382 egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
383 vc->gfx.y0_top);
384 egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
385 vc->gfx.y0_top,
386 vc->gfx.cursor_x, vc->gfx.cursor_y,
387 vc->gfx.scale_x, vc->gfx.scale_y);
388 } else {
389 egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
390 }
391
392 #ifdef CONFIG_GBM
393 if (vc->gfx.guest_fb.dmabuf) {
394 egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
395 }
396 #endif
397
398 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
399 }
400
401 void gd_egl_flush(DisplayChangeListener *dcl,
402 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
403 {
404 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
405 GtkWidget *area = vc->gfx.drawing_area;
406
407 if (vc->gfx.guest_fb.dmabuf &&
408 !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
409 qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
410 gtk_egl_set_scanout_mode(vc, true);
411 }
412
413 gtk_widget_queue_draw_area(area, x, y, w, h);
414 }
415
416 void gtk_egl_init(DisplayGLMode mode)
417 {
418 GdkDisplay *gdk_display = gdk_display_get_default();
419 Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display);
420
421 if (qemu_egl_init_dpy_x11(x11_display, mode) < 0) {
422 return;
423 }
424
425 display_opengl = 1;
426 }
427
428 int gd_egl_make_current(DisplayGLCtx *dgc,
429 QEMUGLContext ctx)
430 {
431 VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
432
433 if (!eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
434 vc->gfx.esurface, ctx)) {
435 error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string());
436 return -1;
437 }
438
439 return 0;
440 }