gl-x11-egl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. /******************************************************************************
  2. Copyright (C) 2019 by Ivan Avdeev <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. /* GL context initialization using EGL instead of GLX
  15. * Which is essential for improved and more performant screen grabbing and
  16. * VA-API feeding techniques.
  17. *
  18. * Note: most of x11-related functionality was taken from gl-x11.c
  19. */
  20. #include <X11/Xlib.h>
  21. #include <X11/Xlib-xcb.h>
  22. #include <xcb/xcb.h>
  23. #include <stdio.h>
  24. #include "gl-egl-common.h"
  25. #include "gl-x11-egl.h"
  26. #include <glad/glad_egl.h>
  27. typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(
  28. EGLenum platform, void *native_display, const EGLint *attrib_list);
  29. static const int ctx_attribs[] = {
  30. #ifdef _DEBUG
  31. EGL_CONTEXT_OPENGL_DEBUG,
  32. EGL_TRUE,
  33. #endif
  34. EGL_CONTEXT_OPENGL_PROFILE_MASK,
  35. EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
  36. EGL_CONTEXT_MAJOR_VERSION,
  37. 3,
  38. EGL_CONTEXT_MINOR_VERSION,
  39. 3,
  40. EGL_NONE,
  41. };
  42. static int ctx_pbuffer_attribs[] = {EGL_WIDTH, 2, EGL_HEIGHT, 2, EGL_NONE};
  43. static const EGLint ctx_config_attribs[] = {EGL_STENCIL_SIZE,
  44. 0,
  45. EGL_DEPTH_SIZE,
  46. 0,
  47. EGL_BUFFER_SIZE,
  48. 24,
  49. EGL_ALPHA_SIZE,
  50. 0,
  51. EGL_RENDERABLE_TYPE,
  52. EGL_OPENGL_BIT,
  53. EGL_SURFACE_TYPE,
  54. EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
  55. EGL_NONE};
  56. struct gl_windowinfo {
  57. /* Windows in X11 are defined with integers (XID).
  58. * xcb_window_t is a define for this... they are
  59. * compatible with Xlib as well.
  60. */
  61. xcb_window_t window;
  62. EGLSurface surface;
  63. };
  64. struct gl_platform {
  65. Display *xdisplay;
  66. EGLDisplay edisplay;
  67. EGLConfig config;
  68. EGLContext context;
  69. EGLSurface pbuffer;
  70. bool close_xdisplay;
  71. };
  72. /* The following utility function is copied verbatim from GLX code. */
  73. static xcb_get_geometry_reply_t *get_window_geometry(xcb_connection_t *xcb_conn,
  74. xcb_drawable_t drawable)
  75. {
  76. xcb_get_geometry_cookie_t cookie;
  77. xcb_generic_error_t *error;
  78. xcb_get_geometry_reply_t *reply;
  79. cookie = xcb_get_geometry(xcb_conn, drawable);
  80. reply = xcb_get_geometry_reply(xcb_conn, cookie, &error);
  81. if (error) {
  82. blog(LOG_ERROR, "Failed to fetch parent window geometry!");
  83. free(error);
  84. free(reply);
  85. return 0;
  86. }
  87. return reply;
  88. }
  89. static const char *get_egl_error_string2(const EGLint error)
  90. {
  91. switch (error) {
  92. #define OBS_EGL_CASE_ERROR(e) \
  93. case e: \
  94. return #e;
  95. OBS_EGL_CASE_ERROR(EGL_SUCCESS)
  96. OBS_EGL_CASE_ERROR(EGL_NOT_INITIALIZED)
  97. OBS_EGL_CASE_ERROR(EGL_BAD_ACCESS)
  98. OBS_EGL_CASE_ERROR(EGL_BAD_ALLOC)
  99. OBS_EGL_CASE_ERROR(EGL_BAD_ATTRIBUTE)
  100. OBS_EGL_CASE_ERROR(EGL_BAD_CONTEXT)
  101. OBS_EGL_CASE_ERROR(EGL_BAD_CONFIG)
  102. OBS_EGL_CASE_ERROR(EGL_BAD_CURRENT_SURFACE)
  103. OBS_EGL_CASE_ERROR(EGL_BAD_DISPLAY)
  104. OBS_EGL_CASE_ERROR(EGL_BAD_SURFACE)
  105. OBS_EGL_CASE_ERROR(EGL_BAD_MATCH)
  106. OBS_EGL_CASE_ERROR(EGL_BAD_PARAMETER)
  107. OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_PIXMAP)
  108. OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_WINDOW)
  109. OBS_EGL_CASE_ERROR(EGL_CONTEXT_LOST)
  110. #undef OBS_EGL_CASE_ERROR
  111. default:
  112. return "Unknown";
  113. }
  114. }
  115. static const char *get_egl_error_string()
  116. {
  117. return get_egl_error_string2(eglGetError());
  118. }
  119. static EGLDisplay get_egl_display(struct gl_platform *plat)
  120. {
  121. Display *display = plat->xdisplay;
  122. EGLDisplay edisplay = EGL_NO_DISPLAY;
  123. const char *egl_client_extensions = NULL;
  124. egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
  125. PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
  126. (PFNEGLGETPLATFORMDISPLAYEXTPROC)(strstr(egl_client_extensions,
  127. "EGL_EXT_platform_base")
  128. ? eglGetProcAddress(
  129. "eglGetPlatformDisplayEXT")
  130. : NULL);
  131. if (eglGetPlatformDisplayEXT) {
  132. edisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,
  133. display, NULL);
  134. if (EGL_NO_DISPLAY == edisplay)
  135. blog(LOG_ERROR, "Failed to get EGL/X11 display");
  136. }
  137. if (EGL_NO_DISPLAY == edisplay)
  138. edisplay = eglGetDisplay(display);
  139. return edisplay;
  140. }
  141. static bool gl_context_create(struct gl_platform *plat)
  142. {
  143. Display *display = plat->xdisplay;
  144. int frame_buf_config_count = 0;
  145. EGLDisplay edisplay = EGL_NO_DISPLAY;
  146. EGLConfig config = NULL;
  147. EGLContext context = EGL_NO_CONTEXT;
  148. int egl_min = 0, egl_maj = 0;
  149. bool success = false;
  150. eglBindAPI(EGL_OPENGL_API);
  151. edisplay = get_egl_display(plat);
  152. if (EGL_NO_DISPLAY == edisplay) {
  153. blog(LOG_ERROR,
  154. "Failed to get EGL display using eglGetDisplay");
  155. return false;
  156. }
  157. if (!eglInitialize(edisplay, &egl_maj, &egl_min)) {
  158. blog(LOG_ERROR, "Failed to initialize EGL: %s",
  159. get_egl_error_string());
  160. return false;
  161. }
  162. if (!eglChooseConfig(edisplay, ctx_config_attribs, &config, 1,
  163. &frame_buf_config_count)) {
  164. blog(LOG_ERROR, "Unable to find suitable EGL config: %s",
  165. get_egl_error_string());
  166. goto error;
  167. }
  168. context =
  169. eglCreateContext(edisplay, config, EGL_NO_CONTEXT, ctx_attribs);
  170. #ifdef _DEBUG
  171. if (EGL_NO_CONTEXT == context) {
  172. const EGLint error = eglGetError();
  173. if (error == EGL_BAD_ATTRIBUTE) {
  174. /* Sometimes creation fails because debug gl is not supported */
  175. blog(LOG_ERROR,
  176. "Unable to create EGL context with DEBUG attrib, trying without");
  177. context = eglCreateContext(edisplay, config,
  178. EGL_NO_CONTEXT,
  179. ctx_attribs + 2);
  180. } else {
  181. blog(LOG_ERROR, "Unable to create EGL context: %s",
  182. get_egl_error_string2(error));
  183. goto error;
  184. }
  185. }
  186. #endif
  187. if (EGL_NO_CONTEXT == context) {
  188. blog(LOG_ERROR, "Unable to create EGL context: %s",
  189. get_egl_error_string());
  190. goto error;
  191. }
  192. plat->pbuffer =
  193. eglCreatePbufferSurface(edisplay, config, ctx_pbuffer_attribs);
  194. if (EGL_NO_SURFACE == plat->pbuffer) {
  195. blog(LOG_ERROR, "Failed to create OpenGL pbuffer: %s",
  196. get_egl_error_string());
  197. goto error;
  198. }
  199. plat->edisplay = edisplay;
  200. plat->config = config;
  201. plat->context = context;
  202. success = true;
  203. blog(LOG_DEBUG, "Created EGLDisplay %p", plat->edisplay);
  204. error:
  205. if (!success) {
  206. if (EGL_NO_CONTEXT != context)
  207. eglDestroyContext(edisplay, context);
  208. eglTerminate(edisplay);
  209. }
  210. XSync(display, false);
  211. return success;
  212. }
  213. static void gl_context_destroy(struct gl_platform *plat)
  214. {
  215. eglMakeCurrent(plat->edisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
  216. EGL_NO_CONTEXT);
  217. eglDestroyContext(plat->edisplay, plat->context);
  218. }
  219. static struct gl_windowinfo *
  220. gl_x11_egl_windowinfo_create(const struct gs_init_data *info)
  221. {
  222. UNUSED_PARAMETER(info);
  223. return bmalloc(sizeof(struct gl_windowinfo));
  224. }
  225. static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info)
  226. {
  227. bfree(info);
  228. }
  229. static Display *open_windowless_display(bool *should_close)
  230. {
  231. Display *platform_display = obs_get_nix_platform_display();
  232. Display *display;
  233. xcb_connection_t *xcb_conn;
  234. if (platform_display) {
  235. display = platform_display;
  236. *should_close = false;
  237. } else {
  238. display = XOpenDisplay(NULL);
  239. *should_close = true;
  240. }
  241. if (!display) {
  242. blog(LOG_ERROR, "Unable to open new X connection!");
  243. return NULL;
  244. }
  245. xcb_conn = XGetXCBConnection(display);
  246. if (!xcb_conn) {
  247. blog(LOG_ERROR, "Unable to get XCB connection to main display");
  248. goto error;
  249. }
  250. if (!gladLoadEGL()) {
  251. blog(LOG_ERROR, "Unable to load EGL entry functions.");
  252. goto error;
  253. }
  254. return display;
  255. error:
  256. if (*should_close)
  257. XCloseDisplay(display);
  258. return NULL;
  259. }
  260. static int x_error_handler(Display *display, XErrorEvent *error)
  261. {
  262. char str1[512];
  263. char str2[512];
  264. char str3[512];
  265. XGetErrorText(display, error->error_code, str1, sizeof(str1));
  266. XGetErrorText(display, error->request_code, str2, sizeof(str2));
  267. XGetErrorText(display, error->minor_code, str3, sizeof(str3));
  268. blog(LOG_ERROR,
  269. "X Error: %s, Major opcode: %s, "
  270. "Minor opcode: %s, Serial: %lu",
  271. str1, str2, str3, error->serial);
  272. return 0;
  273. }
  274. static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device,
  275. uint32_t adapter)
  276. {
  277. /* There's some trickery here... we're mixing libX11, xcb, and EGL
  278. For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
  279. Essentially, EGL requires Xlib. Everything else we use xcb. */
  280. struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
  281. Display *display = open_windowless_display(&plat->close_xdisplay);
  282. if (!display) {
  283. goto fail_display_open;
  284. }
  285. XSetEventQueueOwner(display, XCBOwnsEventQueue);
  286. XSetErrorHandler(x_error_handler);
  287. /* We assume later that cur_swap is already set. */
  288. device->plat = plat;
  289. plat->xdisplay = display;
  290. if (!gl_context_create(plat)) {
  291. blog(LOG_ERROR, "Failed to create context!");
  292. goto fail_context_create;
  293. }
  294. if (!eglMakeCurrent(plat->edisplay, plat->pbuffer, plat->pbuffer,
  295. plat->context)) {
  296. blog(LOG_ERROR, "Failed to make context current: %s",
  297. get_egl_error_string());
  298. goto fail_make_current;
  299. }
  300. if (!gladLoadGL()) {
  301. blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
  302. goto fail_load_gl;
  303. }
  304. goto success;
  305. fail_make_current:
  306. gl_context_destroy(plat);
  307. fail_context_create:
  308. fail_load_gl:
  309. if (plat->close_xdisplay)
  310. XCloseDisplay(plat->xdisplay);
  311. fail_display_open:
  312. bfree(plat);
  313. plat = NULL;
  314. success:
  315. UNUSED_PARAMETER(adapter);
  316. return plat;
  317. }
  318. static void gl_x11_egl_platform_destroy(struct gl_platform *plat)
  319. {
  320. if (!plat)
  321. return;
  322. gl_context_destroy(plat);
  323. eglTerminate(plat->edisplay);
  324. if (plat->close_xdisplay)
  325. XCloseDisplay(plat->xdisplay);
  326. bfree(plat);
  327. }
  328. static bool gl_x11_egl_platform_init_swapchain(struct gs_swap_chain *swap)
  329. {
  330. const struct gl_platform *plat = swap->device->plat;
  331. Display *display = plat->xdisplay;
  332. xcb_connection_t *xcb_conn = XGetXCBConnection(display);
  333. xcb_window_t wid = xcb_generate_id(xcb_conn);
  334. xcb_window_t parent = swap->info.window.id;
  335. xcb_get_geometry_reply_t *geometry =
  336. get_window_geometry(xcb_conn, parent);
  337. bool status = false;
  338. int visual;
  339. if (!geometry)
  340. goto fail_geometry_request;
  341. {
  342. if (!eglGetConfigAttrib(plat->edisplay, plat->config,
  343. EGL_NATIVE_VISUAL_ID,
  344. (EGLint *)&visual)) {
  345. blog(LOG_ERROR,
  346. "Cannot get visual id for EGL context: %s",
  347. get_egl_error_string());
  348. goto fail_visual_id;
  349. }
  350. }
  351. xcb_colormap_t colormap = xcb_generate_id(xcb_conn);
  352. uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP;
  353. uint32_t mask_values[] = {0, colormap, 0};
  354. xcb_create_colormap(xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, parent,
  355. visual);
  356. xcb_void_cookie_t window_cookie = xcb_create_window_checked(
  357. xcb_conn, 24 /* Hardcoded? */, wid, parent, 0, 0,
  358. geometry->width, geometry->height, 0, 0, visual, mask,
  359. mask_values);
  360. xcb_generic_error_t *err = xcb_request_check(xcb_conn, window_cookie);
  361. if (err != NULL) {
  362. char text[512];
  363. XGetErrorText(display, err->error_code, text, sizeof(text));
  364. blog(LOG_ERROR,
  365. "Swapchain window creation failed: %s"
  366. ", Major opcode: %d, Minor opcode: %d",
  367. text, err->major_code, err->minor_code);
  368. goto fail_window_surface;
  369. }
  370. const EGLSurface surface =
  371. eglCreateWindowSurface(plat->edisplay, plat->config, wid, 0);
  372. if (EGL_NO_SURFACE == surface) {
  373. blog(LOG_ERROR, "Cannot get window EGL surface: %s",
  374. get_egl_error_string());
  375. goto fail_window_surface;
  376. }
  377. swap->wi->window = wid;
  378. swap->wi->surface = surface;
  379. xcb_map_window(xcb_conn, wid);
  380. status = true;
  381. goto success;
  382. fail_window_surface:
  383. fail_visual_id:
  384. fail_geometry_request:
  385. success:
  386. free(geometry);
  387. return status;
  388. }
  389. static void gl_x11_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
  390. {
  391. UNUSED_PARAMETER(swap);
  392. /* Really nothing to clean up? */
  393. }
  394. static void gl_x11_egl_device_enter_context(gs_device_t *device)
  395. {
  396. const EGLContext context = device->plat->context;
  397. const EGLDisplay display = device->plat->edisplay;
  398. const EGLSurface surface = (device->cur_swap)
  399. ? device->cur_swap->wi->surface
  400. : device->plat->pbuffer;
  401. if (!eglMakeCurrent(display, surface, surface, context))
  402. blog(LOG_ERROR, "Failed to make context current: %s",
  403. get_egl_error_string());
  404. }
  405. static void gl_x11_egl_device_leave_context(gs_device_t *device)
  406. {
  407. const EGLDisplay display = device->plat->edisplay;
  408. if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  409. EGL_NO_CONTEXT)) {
  410. blog(LOG_ERROR, "Failed to reset current context: %s",
  411. get_egl_error_string());
  412. }
  413. }
  414. static void *gl_x11_egl_device_get_device_obj(gs_device_t *device)
  415. {
  416. return device->plat->context;
  417. }
  418. static void gl_x11_egl_getclientsize(const struct gs_swap_chain *swap,
  419. uint32_t *width, uint32_t *height)
  420. {
  421. xcb_connection_t *xcb_conn =
  422. XGetXCBConnection(swap->device->plat->xdisplay);
  423. xcb_window_t window = swap->wi->window;
  424. xcb_get_geometry_reply_t *geometry =
  425. get_window_geometry(xcb_conn, window);
  426. if (geometry) {
  427. *width = geometry->width;
  428. *height = geometry->height;
  429. }
  430. free(geometry);
  431. }
  432. static void gl_x11_egl_update(gs_device_t *device)
  433. {
  434. Display *display = device->plat->xdisplay;
  435. xcb_window_t window = device->cur_swap->wi->window;
  436. uint32_t values[] = {device->cur_swap->info.cx,
  437. device->cur_swap->info.cy};
  438. xcb_configure_window(XGetXCBConnection(display), window,
  439. XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
  440. values);
  441. }
  442. static void gl_x11_egl_clear_context(gs_device_t *device)
  443. {
  444. Display *display = device->plat->edisplay;
  445. if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  446. EGL_NO_CONTEXT)) {
  447. blog(LOG_ERROR, "Failed to reset current context.");
  448. }
  449. }
  450. static void gl_x11_egl_device_load_swapchain(gs_device_t *device,
  451. gs_swapchain_t *swap)
  452. {
  453. if (device->cur_swap == swap)
  454. return;
  455. device->cur_swap = swap;
  456. device_enter_context(device);
  457. }
  458. static void gl_x11_egl_device_present(gs_device_t *device)
  459. {
  460. Display *display = device->plat->xdisplay;
  461. xcb_connection_t *xcb_conn = XGetXCBConnection(display);
  462. xcb_generic_event_t *xcb_event;
  463. while ((xcb_event = xcb_poll_for_event(xcb_conn))) {
  464. free(xcb_event);
  465. }
  466. if (eglSwapInterval(device->plat->edisplay, 0) == EGL_FALSE) {
  467. blog(LOG_ERROR, "eglSwapInterval failed");
  468. }
  469. if (!eglSwapBuffers(device->plat->edisplay,
  470. device->cur_swap->wi->surface))
  471. blog(LOG_ERROR, "Cannot swap EGL buffers: %s",
  472. get_egl_error_string());
  473. }
  474. static struct gs_texture *gl_x11_egl_device_texture_create_from_dmabuf(
  475. gs_device_t *device, unsigned int width, unsigned int height,
  476. uint32_t drm_format, enum gs_color_format color_format,
  477. uint32_t n_planes, const int *fds, const uint32_t *strides,
  478. const uint32_t *offsets, const uint64_t *modifiers)
  479. {
  480. struct gl_platform *plat = device->plat;
  481. return gl_egl_create_dmabuf_image(plat->edisplay, width, height,
  482. drm_format, color_format, n_planes,
  483. fds, strides, offsets, modifiers);
  484. }
  485. static struct gs_texture *gl_x11_egl_device_texture_create_from_pixmap(
  486. gs_device_t *device, uint32_t width, uint32_t height,
  487. enum gs_color_format color_format, uint32_t target, void *pixmap)
  488. {
  489. struct gl_platform *plat = device->plat;
  490. return gl_egl_create_texture_from_pixmap(plat->edisplay, width, height,
  491. color_format, target,
  492. (EGLClientBuffer)pixmap);
  493. }
  494. static bool gl_x11_egl_device_query_dmabuf_capabilities(
  495. gs_device_t *device, enum gs_dmabuf_flags *dmabuf_flags,
  496. uint32_t **drm_formats, size_t *n_formats)
  497. {
  498. struct gl_platform *plat = device->plat;
  499. return gl_egl_query_dmabuf_capabilities(plat->edisplay, dmabuf_flags,
  500. drm_formats, n_formats);
  501. }
  502. static bool gl_x11_egl_device_query_dmabuf_modifiers_for_format(
  503. gs_device_t *device, uint32_t drm_format, uint64_t **modifiers,
  504. size_t *n_modifiers)
  505. {
  506. struct gl_platform *plat = device->plat;
  507. return gl_egl_query_dmabuf_modifiers_for_format(
  508. plat->edisplay, drm_format, modifiers, n_modifiers);
  509. }
  510. static const struct gl_winsys_vtable egl_x11_winsys_vtable = {
  511. .windowinfo_create = gl_x11_egl_windowinfo_create,
  512. .windowinfo_destroy = gl_x11_egl_windowinfo_destroy,
  513. .platform_create = gl_x11_egl_platform_create,
  514. .platform_destroy = gl_x11_egl_platform_destroy,
  515. .platform_init_swapchain = gl_x11_egl_platform_init_swapchain,
  516. .platform_cleanup_swapchain = gl_x11_egl_platform_cleanup_swapchain,
  517. .device_enter_context = gl_x11_egl_device_enter_context,
  518. .device_leave_context = gl_x11_egl_device_leave_context,
  519. .device_get_device_obj = gl_x11_egl_device_get_device_obj,
  520. .getclientsize = gl_x11_egl_getclientsize,
  521. .clear_context = gl_x11_egl_clear_context,
  522. .update = gl_x11_egl_update,
  523. .device_load_swapchain = gl_x11_egl_device_load_swapchain,
  524. .device_present = gl_x11_egl_device_present,
  525. .device_texture_create_from_dmabuf =
  526. gl_x11_egl_device_texture_create_from_dmabuf,
  527. .device_query_dmabuf_capabilities =
  528. gl_x11_egl_device_query_dmabuf_capabilities,
  529. .device_query_dmabuf_modifiers_for_format =
  530. gl_x11_egl_device_query_dmabuf_modifiers_for_format,
  531. .device_texture_create_from_pixmap =
  532. gl_x11_egl_device_texture_create_from_pixmap,
  533. };
  534. const struct gl_winsys_vtable *gl_x11_egl_get_winsys_vtable(void)
  535. {
  536. return &egl_x11_winsys_vtable;
  537. }