gtk-egl.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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_borrowed_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_texture(DisplayChangeListener *dcl, uint32_t backing_id,
  221. DisplayGLTextureBorrower backing_borrow,
  222. uint32_t x, uint32_t y,
  223. uint32_t w, uint32_t h)
  224. {
  225. bool backing_y_0_top;
  226. uint32_t backing_width;
  227. uint32_t backing_height;
  228. void *d3d_tex2d;
  229. GLuint backing_texture = backing_borrow(backing_id, &backing_y_0_top,
  230. &backing_width, &backing_height,
  231. &d3d_tex2d);
  232. gd_egl_scanout_borrowed_texture(dcl, backing_texture, backing_y_0_top,
  233. backing_width, backing_height,
  234. x, y, w, h, d3d_tex2d);
  235. }
  236. void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf)
  237. {
  238. #ifdef CONFIG_GBM
  239. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  240. uint32_t x, y, width, height, backing_width, backing_height, texture;
  241. bool y0_top;
  242. eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
  243. vc->gfx.esurface, vc->gfx.ectx);
  244. egl_dmabuf_import_texture(dmabuf);
  245. texture = qemu_dmabuf_get_texture(dmabuf);
  246. if (!texture) {
  247. return;
  248. }
  249. x = qemu_dmabuf_get_x(dmabuf);
  250. y = qemu_dmabuf_get_y(dmabuf);
  251. width = qemu_dmabuf_get_width(dmabuf);
  252. height = qemu_dmabuf_get_height(dmabuf);
  253. backing_width = qemu_dmabuf_get_backing_width(dmabuf);
  254. backing_height = qemu_dmabuf_get_backing_height(dmabuf);
  255. y0_top = qemu_dmabuf_get_y0_top(dmabuf);
  256. gd_egl_scanout_borrowed_texture(dcl, texture, y0_top, backing_width,
  257. backing_height, x, y, width, height, NULL);
  258. if (qemu_dmabuf_get_allow_fences(dmabuf)) {
  259. vc->gfx.guest_fb.dmabuf = dmabuf;
  260. }
  261. #endif
  262. }
  263. void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
  264. QemuDmaBuf *dmabuf, bool have_hot,
  265. uint32_t hot_x, uint32_t hot_y)
  266. {
  267. #ifdef CONFIG_GBM
  268. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  269. uint32_t backing_width, backing_height, texture;
  270. if (dmabuf) {
  271. egl_dmabuf_import_texture(dmabuf);
  272. texture = qemu_dmabuf_get_texture(dmabuf);
  273. if (!texture) {
  274. return;
  275. }
  276. backing_width = qemu_dmabuf_get_backing_width(dmabuf);
  277. backing_height = qemu_dmabuf_get_backing_height(dmabuf);
  278. egl_fb_setup_for_tex(&vc->gfx.cursor_fb, backing_width, backing_height,
  279. texture, false);
  280. } else {
  281. egl_fb_destroy(&vc->gfx.cursor_fb);
  282. }
  283. #endif
  284. }
  285. void gd_egl_cursor_position(DisplayChangeListener *dcl,
  286. uint32_t pos_x, uint32_t pos_y)
  287. {
  288. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  289. vc->gfx.cursor_x = pos_x * vc->gfx.scale_x;
  290. vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
  291. }
  292. void gd_egl_scanout_flush(DisplayChangeListener *dcl,
  293. uint32_t x, uint32_t y, uint32_t w, uint32_t h)
  294. {
  295. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  296. GdkWindow *window;
  297. int ww, wh, ws;
  298. if (!vc->gfx.scanout_mode) {
  299. return;
  300. }
  301. if (!vc->gfx.guest_fb.framebuffer) {
  302. return;
  303. }
  304. eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
  305. vc->gfx.esurface, vc->gfx.ectx);
  306. window = gtk_widget_get_window(vc->gfx.drawing_area);
  307. ws = gdk_window_get_scale_factor(window);
  308. ww = gdk_window_get_width(window) * ws;
  309. wh = gdk_window_get_height(window) * ws;
  310. egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
  311. if (vc->gfx.cursor_fb.texture) {
  312. egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
  313. vc->gfx.y0_top, false);
  314. egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
  315. vc->gfx.y0_top, false,
  316. vc->gfx.cursor_x, vc->gfx.cursor_y,
  317. vc->gfx.scale_x, vc->gfx.scale_y);
  318. } else {
  319. egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
  320. }
  321. #ifdef CONFIG_GBM
  322. if (vc->gfx.guest_fb.dmabuf) {
  323. egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
  324. }
  325. #endif
  326. eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
  327. }
  328. void gd_egl_flush(DisplayChangeListener *dcl,
  329. uint32_t x, uint32_t y, uint32_t w, uint32_t h)
  330. {
  331. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  332. GtkWidget *area = vc->gfx.drawing_area;
  333. if (vc->gfx.guest_fb.dmabuf &&
  334. !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
  335. graphic_hw_gl_block(vc->gfx.dcl.con, true);
  336. qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
  337. gtk_egl_set_scanout_mode(vc, true);
  338. gtk_widget_queue_draw_area(area, x, y, w, h);
  339. return;
  340. }
  341. gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h);
  342. }
  343. void gtk_egl_init(DisplayGLMode mode)
  344. {
  345. GdkDisplay *gdk_display = gdk_display_get_default();
  346. Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display);
  347. if (qemu_egl_init_dpy_x11(x11_display, mode) < 0) {
  348. return;
  349. }
  350. display_opengl = 1;
  351. }
  352. int gd_egl_make_current(DisplayGLCtx *dgc,
  353. QEMUGLContext ctx)
  354. {
  355. VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
  356. if (!eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
  357. vc->gfx.esurface, ctx)) {
  358. error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string());
  359. return -1;
  360. }
  361. return 0;
  362. }