gtk-egl.c 12 KB

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