gl-x11-egl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  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. 32,
  49. EGL_ALPHA_SIZE,
  50. 8,
  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. EGLConfig config;
  58. /* Windows in X11 are defined with integers (XID).
  59. * xcb_window_t is a define for this... they are
  60. * compatible with Xlib as well.
  61. */
  62. xcb_window_t window;
  63. EGLSurface surface;
  64. /* We can't fetch screen without a request so we cache it. */
  65. int screen;
  66. };
  67. struct gl_platform {
  68. Display *xdisplay;
  69. EGLDisplay edisplay;
  70. EGLConfig config;
  71. EGLContext context;
  72. EGLSurface pbuffer;
  73. };
  74. /* The following utility functions are copied verbatim from GLX code. */
  75. /*
  76. * Since we cannot take advantage of the asynchronous nature of xcb,
  77. * all of the helper functions are synchronous but thread-safe.
  78. *
  79. * They check for errors and will return 0 on problems
  80. * with the exception of when 0 is a valid return value... in which case
  81. * read the specific function comments.
  82. */
  83. /* Returns -1 on invalid screen. */
  84. static int get_screen_num_from_xcb_screen(xcb_connection_t *xcb_conn,
  85. xcb_screen_t *screen)
  86. {
  87. xcb_screen_iterator_t iter =
  88. xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
  89. int screen_num = 0;
  90. for (; iter.rem; xcb_screen_next(&iter), ++screen_num)
  91. if (iter.data == screen)
  92. return screen_num;
  93. return -1;
  94. }
  95. static xcb_screen_t *get_screen_from_root(xcb_connection_t *xcb_conn,
  96. xcb_window_t root)
  97. {
  98. xcb_screen_iterator_t iter =
  99. xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
  100. while (iter.rem) {
  101. if (iter.data->root == root)
  102. return iter.data;
  103. xcb_screen_next(&iter);
  104. }
  105. return 0;
  106. }
  107. static inline int get_screen_num_from_root(xcb_connection_t *xcb_conn,
  108. xcb_window_t root)
  109. {
  110. xcb_screen_t *screen = get_screen_from_root(xcb_conn, root);
  111. if (!screen)
  112. return -1;
  113. return get_screen_num_from_xcb_screen(xcb_conn, screen);
  114. }
  115. static xcb_get_geometry_reply_t *get_window_geometry(xcb_connection_t *xcb_conn,
  116. xcb_drawable_t drawable)
  117. {
  118. xcb_get_geometry_cookie_t cookie;
  119. xcb_generic_error_t *error;
  120. xcb_get_geometry_reply_t *reply;
  121. cookie = xcb_get_geometry(xcb_conn, drawable);
  122. reply = xcb_get_geometry_reply(xcb_conn, cookie, &error);
  123. if (error) {
  124. blog(LOG_ERROR, "Failed to fetch parent window geometry!");
  125. free(error);
  126. free(reply);
  127. return 0;
  128. }
  129. free(error);
  130. return reply;
  131. }
  132. static const char *get_egl_error_string2(const EGLint error)
  133. {
  134. switch (error) {
  135. #define OBS_EGL_CASE_ERROR(e) \
  136. case e: \
  137. return #e;
  138. OBS_EGL_CASE_ERROR(EGL_SUCCESS)
  139. OBS_EGL_CASE_ERROR(EGL_NOT_INITIALIZED)
  140. OBS_EGL_CASE_ERROR(EGL_BAD_ACCESS)
  141. OBS_EGL_CASE_ERROR(EGL_BAD_ALLOC)
  142. OBS_EGL_CASE_ERROR(EGL_BAD_ATTRIBUTE)
  143. OBS_EGL_CASE_ERROR(EGL_BAD_CONTEXT)
  144. OBS_EGL_CASE_ERROR(EGL_BAD_CONFIG)
  145. OBS_EGL_CASE_ERROR(EGL_BAD_CURRENT_SURFACE)
  146. OBS_EGL_CASE_ERROR(EGL_BAD_DISPLAY)
  147. OBS_EGL_CASE_ERROR(EGL_BAD_SURFACE)
  148. OBS_EGL_CASE_ERROR(EGL_BAD_MATCH)
  149. OBS_EGL_CASE_ERROR(EGL_BAD_PARAMETER)
  150. OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_PIXMAP)
  151. OBS_EGL_CASE_ERROR(EGL_BAD_NATIVE_WINDOW)
  152. OBS_EGL_CASE_ERROR(EGL_CONTEXT_LOST)
  153. #undef OBS_EGL_CASE_ERROR
  154. default:
  155. return "Unknown";
  156. }
  157. }
  158. static const char *get_egl_error_string()
  159. {
  160. return get_egl_error_string2(eglGetError());
  161. }
  162. static EGLDisplay get_egl_display(struct gl_platform *plat)
  163. {
  164. Display *display = plat->xdisplay;
  165. EGLDisplay edisplay = EGL_NO_DISPLAY;
  166. const char *egl_client_extensions = NULL;
  167. egl_client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
  168. PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
  169. (PFNEGLGETPLATFORMDISPLAYEXTPROC)(strstr(egl_client_extensions,
  170. "EGL_EXT_platform_base")
  171. ? eglGetProcAddress(
  172. "eglGetPlatformDisplayEXT")
  173. : NULL);
  174. if (eglGetPlatformDisplayEXT) {
  175. edisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,
  176. display, NULL);
  177. if (EGL_NO_DISPLAY == edisplay)
  178. blog(LOG_ERROR, "Failed to get EGL/X11 display");
  179. }
  180. if (EGL_NO_DISPLAY == edisplay)
  181. edisplay = eglGetDisplay(display);
  182. return edisplay;
  183. }
  184. static bool gl_context_create(struct gl_platform *plat)
  185. {
  186. Display *display = plat->xdisplay;
  187. int frame_buf_config_count = 0;
  188. EGLDisplay edisplay = EGL_NO_DISPLAY;
  189. EGLConfig config = NULL;
  190. EGLContext context = EGL_NO_CONTEXT;
  191. int egl_min = 0, egl_maj = 0;
  192. bool success = false;
  193. eglBindAPI(EGL_OPENGL_API);
  194. edisplay = get_egl_display(plat);
  195. if (EGL_NO_DISPLAY == edisplay) {
  196. blog(LOG_ERROR,
  197. "Failed to get EGL display using eglGetDisplay");
  198. return false;
  199. }
  200. if (!eglInitialize(edisplay, &egl_maj, &egl_min)) {
  201. blog(LOG_ERROR, "Failed to initialize EGL: %s",
  202. get_egl_error_string());
  203. return false;
  204. }
  205. if (!eglChooseConfig(edisplay, ctx_config_attribs, &config, 1,
  206. &frame_buf_config_count)) {
  207. blog(LOG_ERROR, "Unable to find suitable EGL config: %s",
  208. get_egl_error_string());
  209. goto error;
  210. }
  211. context =
  212. eglCreateContext(edisplay, config, EGL_NO_CONTEXT, ctx_attribs);
  213. #ifdef _DEBUG
  214. if (EGL_NO_CONTEXT == context) {
  215. const EGLint error = eglGetError();
  216. if (error == EGL_BAD_ATTRIBUTE) {
  217. /* Sometimes creation fails because debug gl is not supported */
  218. blog(LOG_ERROR,
  219. "Unable to create EGL context with DEBUG attrib, trying without");
  220. context = eglCreateContext(edisplay, config,
  221. EGL_NO_CONTEXT,
  222. ctx_attribs + 2);
  223. } else {
  224. blog(LOG_ERROR, "Unable to create EGL context: %s",
  225. get_egl_error_string2(error));
  226. goto error;
  227. }
  228. }
  229. #endif
  230. if (EGL_NO_CONTEXT == context) {
  231. blog(LOG_ERROR, "Unable to create EGL context: %s",
  232. get_egl_error_string());
  233. goto error;
  234. }
  235. plat->pbuffer =
  236. eglCreatePbufferSurface(edisplay, config, ctx_pbuffer_attribs);
  237. if (EGL_NO_SURFACE == plat->pbuffer) {
  238. blog(LOG_ERROR, "Failed to create OpenGL pbuffer: %s",
  239. get_egl_error_string());
  240. goto error;
  241. }
  242. plat->edisplay = edisplay;
  243. plat->config = config;
  244. plat->context = context;
  245. success = true;
  246. blog(LOG_DEBUG, "Created EGLDisplay %p", plat->edisplay);
  247. error:
  248. if (!success) {
  249. if (EGL_NO_CONTEXT != context)
  250. eglDestroyContext(edisplay, context);
  251. eglTerminate(edisplay);
  252. }
  253. XSync(display, false);
  254. return success;
  255. }
  256. static void gl_context_destroy(struct gl_platform *plat)
  257. {
  258. eglMakeCurrent(plat->edisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
  259. EGL_NO_CONTEXT);
  260. eglDestroyContext(plat->edisplay, plat->context);
  261. }
  262. static struct gl_windowinfo *
  263. gl_x11_egl_windowinfo_create(const struct gs_init_data *info)
  264. {
  265. UNUSED_PARAMETER(info);
  266. return bmalloc(sizeof(struct gl_windowinfo));
  267. }
  268. static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info)
  269. {
  270. UNUSED_PARAMETER(info);
  271. bfree(info);
  272. }
  273. static Display *open_windowless_display(Display *platform_display)
  274. {
  275. Display *display;
  276. xcb_connection_t *xcb_conn;
  277. xcb_screen_iterator_t screen_iterator;
  278. xcb_screen_t *screen;
  279. int screen_num;
  280. if (platform_display)
  281. display = platform_display;
  282. else
  283. display = XOpenDisplay(NULL);
  284. if (!display) {
  285. blog(LOG_ERROR, "Unable to open new X connection!");
  286. return NULL;
  287. }
  288. xcb_conn = XGetXCBConnection(display);
  289. if (!xcb_conn) {
  290. blog(LOG_ERROR, "Unable to get XCB connection to main display");
  291. goto error;
  292. }
  293. screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
  294. screen = screen_iterator.data;
  295. if (!screen) {
  296. blog(LOG_ERROR, "Unable to get screen root");
  297. goto error;
  298. }
  299. screen_num = get_screen_num_from_root(xcb_conn, screen->root);
  300. if (screen_num == -1) {
  301. blog(LOG_ERROR, "Unable to get screen number from root");
  302. goto error;
  303. }
  304. if (!gladLoadEGL()) {
  305. blog(LOG_ERROR, "Unable to load EGL entry functions.");
  306. goto error;
  307. }
  308. return display;
  309. error:
  310. if (display)
  311. XCloseDisplay(display);
  312. return NULL;
  313. }
  314. static int x_error_handler(Display *display, XErrorEvent *error)
  315. {
  316. char str1[512];
  317. char str2[512];
  318. char str3[512];
  319. XGetErrorText(display, error->error_code, str1, sizeof(str1));
  320. XGetErrorText(display, error->request_code, str2, sizeof(str2));
  321. XGetErrorText(display, error->minor_code, str3, sizeof(str3));
  322. blog(LOG_ERROR,
  323. "X Error: %s, Major opcode: %s, "
  324. "Minor opcode: %s, Serial: %lu",
  325. str1, str2, str3, error->serial);
  326. return 0;
  327. }
  328. static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device,
  329. uint32_t adapter)
  330. {
  331. /* There's some trickery here... we're mixing libX11, xcb, and EGL
  332. For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
  333. Essentially, EGL requires Xlib. Everything else we use xcb. */
  334. struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
  335. Display *platform_display = obs_get_nix_platform_display();
  336. Display *display = open_windowless_display(platform_display);
  337. if (!display) {
  338. goto fail_display_open;
  339. }
  340. XSetEventQueueOwner(display, XCBOwnsEventQueue);
  341. XSetErrorHandler(x_error_handler);
  342. /* We assume later that cur_swap is already set. */
  343. device->plat = plat;
  344. plat->xdisplay = display;
  345. if (!gl_context_create(plat)) {
  346. blog(LOG_ERROR, "Failed to create context!");
  347. goto fail_context_create;
  348. }
  349. if (!eglMakeCurrent(plat->edisplay, plat->pbuffer, plat->pbuffer,
  350. plat->context)) {
  351. blog(LOG_ERROR, "Failed to make context current: %s",
  352. get_egl_error_string());
  353. goto fail_make_current;
  354. }
  355. if (!gladLoadGL()) {
  356. blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
  357. goto fail_load_gl;
  358. }
  359. goto success;
  360. fail_make_current:
  361. gl_context_destroy(plat);
  362. fail_context_create:
  363. fail_load_gl:
  364. XCloseDisplay(display);
  365. fail_display_open:
  366. bfree(plat);
  367. plat = NULL;
  368. success:
  369. UNUSED_PARAMETER(adapter);
  370. return plat;
  371. }
  372. static void gl_x11_egl_platform_destroy(struct gl_platform *plat)
  373. {
  374. if (!plat)
  375. return;
  376. gl_context_destroy(plat);
  377. eglTerminate(plat->edisplay);
  378. bfree(plat);
  379. }
  380. static bool gl_x11_egl_platform_init_swapchain(struct gs_swap_chain *swap)
  381. {
  382. const struct gl_platform *plat = swap->device->plat;
  383. Display *display = plat->xdisplay;
  384. xcb_connection_t *xcb_conn = XGetXCBConnection(display);
  385. xcb_window_t wid = xcb_generate_id(xcb_conn);
  386. xcb_window_t parent = swap->info.window.id;
  387. xcb_get_geometry_reply_t *geometry =
  388. get_window_geometry(xcb_conn, parent);
  389. bool status = false;
  390. int screen_num;
  391. int visual;
  392. if (!geometry)
  393. goto fail_geometry_request;
  394. screen_num = get_screen_num_from_root(xcb_conn, geometry->root);
  395. if (screen_num == -1) {
  396. goto fail_screen;
  397. }
  398. {
  399. if (!eglGetConfigAttrib(plat->edisplay, plat->config,
  400. EGL_NATIVE_VISUAL_ID,
  401. (EGLint *)&visual)) {
  402. blog(LOG_ERROR,
  403. "Cannot get visual id for EGL context: %s",
  404. get_egl_error_string());
  405. goto fail_visual_id;
  406. }
  407. }
  408. xcb_colormap_t colormap = xcb_generate_id(xcb_conn);
  409. uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP;
  410. uint32_t mask_values[] = {0, colormap, 0};
  411. xcb_create_colormap(xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, parent,
  412. visual);
  413. xcb_create_window(xcb_conn, 24 /* Hardcoded? */, wid, parent, 0, 0,
  414. geometry->width, geometry->height, 0, 0, visual, mask,
  415. mask_values);
  416. const EGLSurface surface =
  417. eglCreateWindowSurface(plat->edisplay, plat->config, wid, 0);
  418. if (EGL_NO_SURFACE == surface) {
  419. blog(LOG_ERROR, "Cannot get window EGL surface: %s",
  420. get_egl_error_string());
  421. goto fail_window_surface;
  422. }
  423. swap->wi->config = plat->config;
  424. swap->wi->window = wid;
  425. swap->wi->surface = surface;
  426. swap->wi->screen = screen_num;
  427. xcb_map_window(xcb_conn, wid);
  428. status = true;
  429. goto success;
  430. fail_window_surface:
  431. fail_visual_id:
  432. fail_screen:
  433. fail_geometry_request:
  434. success:
  435. free(geometry);
  436. return status;
  437. }
  438. static void gl_x11_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
  439. {
  440. UNUSED_PARAMETER(swap);
  441. /* Really nothing to clean up? */
  442. }
  443. static void gl_x11_egl_device_enter_context(gs_device_t *device)
  444. {
  445. const EGLContext context = device->plat->context;
  446. const EGLDisplay display = device->plat->edisplay;
  447. const EGLSurface surface = (device->cur_swap)
  448. ? device->cur_swap->wi->surface
  449. : device->plat->pbuffer;
  450. if (!eglMakeCurrent(display, surface, surface, context))
  451. blog(LOG_ERROR, "Failed to make context current: %s",
  452. get_egl_error_string());
  453. }
  454. static void gl_x11_egl_device_leave_context(gs_device_t *device)
  455. {
  456. const EGLDisplay display = device->plat->edisplay;
  457. if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  458. EGL_NO_CONTEXT)) {
  459. blog(LOG_ERROR, "Failed to reset current context: %s",
  460. get_egl_error_string());
  461. }
  462. }
  463. static void *gl_x11_egl_device_get_device_obj(gs_device_t *device)
  464. {
  465. return device->plat->context;
  466. }
  467. static void gl_x11_egl_getclientsize(const struct gs_swap_chain *swap,
  468. uint32_t *width, uint32_t *height)
  469. {
  470. xcb_connection_t *xcb_conn =
  471. XGetXCBConnection(swap->device->plat->xdisplay);
  472. xcb_window_t window = swap->wi->window;
  473. xcb_get_geometry_reply_t *geometry =
  474. get_window_geometry(xcb_conn, window);
  475. if (geometry) {
  476. *width = geometry->width;
  477. *height = geometry->height;
  478. }
  479. free(geometry);
  480. }
  481. static void gl_x11_egl_update(gs_device_t *device)
  482. {
  483. Display *display = device->plat->xdisplay;
  484. xcb_window_t window = device->cur_swap->wi->window;
  485. uint32_t values[] = {device->cur_swap->info.cx,
  486. device->cur_swap->info.cy};
  487. xcb_configure_window(XGetXCBConnection(display), window,
  488. XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
  489. values);
  490. }
  491. static void gl_x11_egl_clear_context(gs_device_t *device)
  492. {
  493. Display *display = device->plat->edisplay;
  494. if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  495. EGL_NO_CONTEXT)) {
  496. blog(LOG_ERROR, "Failed to reset current context.");
  497. }
  498. }
  499. static void gl_x11_egl_device_load_swapchain(gs_device_t *device,
  500. gs_swapchain_t *swap)
  501. {
  502. if (device->cur_swap == swap)
  503. return;
  504. device->cur_swap = swap;
  505. device_enter_context(device);
  506. }
  507. enum swap_type {
  508. SWAP_TYPE_NORMAL,
  509. SWAP_TYPE_EXT,
  510. SWAP_TYPE_MESA,
  511. SWAP_TYPE_SGI,
  512. };
  513. static void gl_x11_egl_device_present(gs_device_t *device)
  514. {
  515. Display *display = device->plat->xdisplay;
  516. xcb_connection_t *xcb_conn = XGetXCBConnection(display);
  517. xcb_generic_event_t *xcb_event;
  518. while ((xcb_event = xcb_poll_for_event(xcb_conn))) {
  519. free(xcb_event);
  520. }
  521. if (!eglSwapBuffers(device->plat->edisplay,
  522. device->cur_swap->wi->surface))
  523. blog(LOG_ERROR, "Cannot swap EGL buffers: %s",
  524. get_egl_error_string());
  525. }
  526. static struct gs_texture *gl_x11_egl_device_texture_create_from_dmabuf(
  527. gs_device_t *device, unsigned int width, unsigned int height,
  528. uint32_t drm_format, enum gs_color_format color_format,
  529. uint32_t n_planes, const int *fds, const uint32_t *strides,
  530. const uint32_t *offsets, const uint64_t *modifiers)
  531. {
  532. struct gl_platform *plat = device->plat;
  533. return gl_egl_create_dmabuf_image(plat->edisplay, width, height,
  534. drm_format, color_format, n_planes,
  535. fds, strides, offsets, modifiers);
  536. }
  537. static const struct gl_winsys_vtable egl_x11_winsys_vtable = {
  538. .windowinfo_create = gl_x11_egl_windowinfo_create,
  539. .windowinfo_destroy = gl_x11_egl_windowinfo_destroy,
  540. .platform_create = gl_x11_egl_platform_create,
  541. .platform_destroy = gl_x11_egl_platform_destroy,
  542. .platform_init_swapchain = gl_x11_egl_platform_init_swapchain,
  543. .platform_cleanup_swapchain = gl_x11_egl_platform_cleanup_swapchain,
  544. .device_enter_context = gl_x11_egl_device_enter_context,
  545. .device_leave_context = gl_x11_egl_device_leave_context,
  546. .device_get_device_obj = gl_x11_egl_device_get_device_obj,
  547. .getclientsize = gl_x11_egl_getclientsize,
  548. .clear_context = gl_x11_egl_clear_context,
  549. .update = gl_x11_egl_update,
  550. .device_load_swapchain = gl_x11_egl_device_load_swapchain,
  551. .device_present = gl_x11_egl_device_present,
  552. .device_texture_create_from_dmabuf =
  553. gl_x11_egl_device_texture_create_from_dmabuf,
  554. };
  555. const struct gl_winsys_vtable *gl_x11_egl_get_winsys_vtable(void)
  556. {
  557. return &egl_x11_winsys_vtable;
  558. }