gl-x11-egl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  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)(
  170. strstr(egl_client_extensions, "EGL_EXT_platform_base")
  171. ? eglGetProcAddress("eglGetPlatformDisplayEXT")
  172. : NULL);
  173. if (eglGetPlatformDisplayEXT) {
  174. edisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,
  175. display, NULL);
  176. if (EGL_NO_DISPLAY == edisplay)
  177. blog(LOG_ERROR, "Failed to get EGL/X11 display");
  178. }
  179. if (EGL_NO_DISPLAY == edisplay)
  180. edisplay = eglGetDisplay(display);
  181. return edisplay;
  182. }
  183. static bool gl_context_create(struct gl_platform *plat)
  184. {
  185. Display *display = plat->xdisplay;
  186. int frame_buf_config_count = 0;
  187. EGLDisplay edisplay = EGL_NO_DISPLAY;
  188. EGLConfig config = NULL;
  189. EGLContext context = EGL_NO_CONTEXT;
  190. int egl_min = 0, egl_maj = 0;
  191. bool success = false;
  192. eglBindAPI(EGL_OPENGL_API);
  193. edisplay = get_egl_display(plat);
  194. if (EGL_NO_DISPLAY == edisplay) {
  195. blog(LOG_ERROR,
  196. "Failed to get EGL display using eglGetDisplay");
  197. return false;
  198. }
  199. if (!eglInitialize(edisplay, &egl_maj, &egl_min)) {
  200. blog(LOG_ERROR, "Failed to initialize EGL: %s",
  201. get_egl_error_string());
  202. return false;
  203. }
  204. if (!eglChooseConfig(edisplay, ctx_config_attribs, &config, 1,
  205. &frame_buf_config_count)) {
  206. blog(LOG_ERROR, "Unable to find suitable EGL config: %s",
  207. get_egl_error_string());
  208. goto error;
  209. }
  210. context =
  211. eglCreateContext(edisplay, config, EGL_NO_CONTEXT, ctx_attribs);
  212. #ifdef _DEBUG
  213. if (EGL_NO_CONTEXT == context) {
  214. const EGLint error = eglGetError();
  215. if (error == EGL_BAD_ATTRIBUTE) {
  216. /* Sometimes creation fails because debug gl is not supported */
  217. blog(LOG_ERROR,
  218. "Unable to create EGL context with DEBUG attrib, trying without");
  219. context = eglCreateContext(edisplay, config,
  220. EGL_NO_CONTEXT,
  221. ctx_attribs + 2);
  222. } else {
  223. blog(LOG_ERROR, "Unable to create EGL context: %s",
  224. get_egl_error_string2(error));
  225. goto error;
  226. }
  227. }
  228. #endif
  229. if (EGL_NO_CONTEXT == context) {
  230. blog(LOG_ERROR, "Unable to create EGL context: %s",
  231. get_egl_error_string());
  232. goto error;
  233. }
  234. plat->pbuffer =
  235. eglCreatePbufferSurface(edisplay, config, ctx_pbuffer_attribs);
  236. if (EGL_NO_SURFACE == plat->pbuffer) {
  237. blog(LOG_ERROR, "Failed to create OpenGL pbuffer: %s",
  238. get_egl_error_string());
  239. goto error;
  240. }
  241. plat->edisplay = edisplay;
  242. plat->config = config;
  243. plat->context = context;
  244. success = true;
  245. blog(LOG_DEBUG, "Created EGLDisplay %p", plat->edisplay);
  246. error:
  247. if (!success) {
  248. if (EGL_NO_CONTEXT != context)
  249. eglDestroyContext(edisplay, context);
  250. eglTerminate(edisplay);
  251. }
  252. XSync(display, false);
  253. return success;
  254. }
  255. static void gl_context_destroy(struct gl_platform *plat)
  256. {
  257. eglMakeCurrent(plat->edisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
  258. EGL_NO_CONTEXT);
  259. eglDestroyContext(plat->edisplay, plat->context);
  260. }
  261. static struct gl_windowinfo *
  262. gl_x11_egl_windowinfo_create(const struct gs_init_data *info)
  263. {
  264. UNUSED_PARAMETER(info);
  265. return bmalloc(sizeof(struct gl_windowinfo));
  266. }
  267. static void gl_x11_egl_windowinfo_destroy(struct gl_windowinfo *info)
  268. {
  269. UNUSED_PARAMETER(info);
  270. bfree(info);
  271. }
  272. static Display *open_windowless_display(Display *platform_display)
  273. {
  274. Display *display;
  275. xcb_connection_t *xcb_conn;
  276. xcb_screen_iterator_t screen_iterator;
  277. xcb_screen_t *screen;
  278. int screen_num;
  279. if (platform_display)
  280. display = platform_display;
  281. else
  282. display = XOpenDisplay(NULL);
  283. if (!display) {
  284. blog(LOG_ERROR, "Unable to open new X connection!");
  285. return NULL;
  286. }
  287. xcb_conn = XGetXCBConnection(display);
  288. if (!xcb_conn) {
  289. blog(LOG_ERROR, "Unable to get XCB connection to main display");
  290. goto error;
  291. }
  292. screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(xcb_conn));
  293. screen = screen_iterator.data;
  294. if (!screen) {
  295. blog(LOG_ERROR, "Unable to get screen root");
  296. goto error;
  297. }
  298. screen_num = get_screen_num_from_root(xcb_conn, screen->root);
  299. if (screen_num == -1) {
  300. blog(LOG_ERROR, "Unable to get screen number from root");
  301. goto error;
  302. }
  303. if (!gladLoadEGL()) {
  304. blog(LOG_ERROR, "Unable to load EGL entry functions.");
  305. goto error;
  306. }
  307. return display;
  308. error:
  309. if (display)
  310. XCloseDisplay(display);
  311. return NULL;
  312. }
  313. static int x_error_handler(Display *display, XErrorEvent *error)
  314. {
  315. char str1[512];
  316. char str2[512];
  317. char str3[512];
  318. XGetErrorText(display, error->error_code, str1, sizeof(str1));
  319. XGetErrorText(display, error->request_code, str2, sizeof(str2));
  320. XGetErrorText(display, error->minor_code, str3, sizeof(str3));
  321. blog(LOG_ERROR,
  322. "X Error: %s, Major opcode: %s, "
  323. "Minor opcode: %s, Serial: %lu",
  324. str1, str2, str3, error->serial);
  325. return 0;
  326. }
  327. static struct gl_platform *gl_x11_egl_platform_create(gs_device_t *device,
  328. uint32_t adapter)
  329. {
  330. /* There's some trickery here... we're mixing libX11, xcb, and EGL
  331. For an explanation see here: http://xcb.freedesktop.org/MixingCalls/
  332. Essentially, EGL requires Xlib. Everything else we use xcb. */
  333. struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
  334. Display *platform_display = obs_get_nix_platform_display();
  335. Display *display = open_windowless_display(platform_display);
  336. if (!display) {
  337. goto fail_display_open;
  338. }
  339. XSetEventQueueOwner(display, XCBOwnsEventQueue);
  340. XSetErrorHandler(x_error_handler);
  341. /* We assume later that cur_swap is already set. */
  342. device->plat = plat;
  343. plat->xdisplay = display;
  344. if (!gl_context_create(plat)) {
  345. blog(LOG_ERROR, "Failed to create context!");
  346. goto fail_context_create;
  347. }
  348. if (!eglMakeCurrent(plat->edisplay, plat->pbuffer, plat->pbuffer,
  349. plat->context)) {
  350. blog(LOG_ERROR, "Failed to make context current: %s",
  351. get_egl_error_string());
  352. goto fail_make_current;
  353. }
  354. if (!gladLoadGL()) {
  355. blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
  356. goto fail_load_gl;
  357. }
  358. goto success;
  359. fail_make_current:
  360. gl_context_destroy(plat);
  361. fail_context_create:
  362. fail_load_gl:
  363. XCloseDisplay(display);
  364. fail_display_open:
  365. bfree(plat);
  366. plat = NULL;
  367. success:
  368. UNUSED_PARAMETER(adapter);
  369. return plat;
  370. }
  371. static void gl_x11_egl_platform_destroy(struct gl_platform *plat)
  372. {
  373. if (!plat)
  374. return;
  375. gl_context_destroy(plat);
  376. eglTerminate(plat->edisplay);
  377. bfree(plat);
  378. }
  379. static bool gl_x11_egl_platform_init_swapchain(struct gs_swap_chain *swap)
  380. {
  381. const struct gl_platform *plat = swap->device->plat;
  382. Display *display = plat->xdisplay;
  383. xcb_connection_t *xcb_conn = XGetXCBConnection(display);
  384. xcb_window_t wid = xcb_generate_id(xcb_conn);
  385. xcb_window_t parent = swap->info.window.id;
  386. xcb_get_geometry_reply_t *geometry =
  387. get_window_geometry(xcb_conn, parent);
  388. bool status = false;
  389. int screen_num;
  390. int visual;
  391. if (!geometry)
  392. goto fail_geometry_request;
  393. screen_num = get_screen_num_from_root(xcb_conn, geometry->root);
  394. if (screen_num == -1) {
  395. goto fail_screen;
  396. }
  397. {
  398. if (!eglGetConfigAttrib(plat->edisplay, plat->config,
  399. EGL_NATIVE_VISUAL_ID,
  400. (EGLint *)&visual)) {
  401. blog(LOG_ERROR,
  402. "Cannot get visual id for EGL context: %s",
  403. get_egl_error_string());
  404. goto fail_visual_id;
  405. }
  406. }
  407. xcb_colormap_t colormap = xcb_generate_id(xcb_conn);
  408. uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP;
  409. uint32_t mask_values[] = {0, colormap, 0};
  410. xcb_create_colormap(xcb_conn, XCB_COLORMAP_ALLOC_NONE, colormap, parent,
  411. visual);
  412. xcb_create_window(xcb_conn, 24 /* Hardcoded? */, wid, parent, 0, 0,
  413. geometry->width, geometry->height, 0, 0, visual, mask,
  414. mask_values);
  415. const EGLSurface surface =
  416. eglCreateWindowSurface(plat->edisplay, plat->config, wid, 0);
  417. if (EGL_NO_SURFACE == surface) {
  418. blog(LOG_ERROR, "Cannot get window EGL surface: %s",
  419. get_egl_error_string());
  420. goto fail_window_surface;
  421. }
  422. swap->wi->config = plat->config;
  423. swap->wi->window = wid;
  424. swap->wi->surface = surface;
  425. swap->wi->screen = screen_num;
  426. xcb_map_window(xcb_conn, wid);
  427. status = true;
  428. goto success;
  429. fail_window_surface:
  430. fail_visual_id:
  431. fail_screen:
  432. fail_geometry_request:
  433. success:
  434. free(geometry);
  435. return status;
  436. }
  437. static void gl_x11_egl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
  438. {
  439. UNUSED_PARAMETER(swap);
  440. /* Really nothing to clean up? */
  441. }
  442. static void gl_x11_egl_device_enter_context(gs_device_t *device)
  443. {
  444. const EGLContext context = device->plat->context;
  445. const EGLDisplay display = device->plat->edisplay;
  446. const EGLSurface surface = (device->cur_swap)
  447. ? device->cur_swap->wi->surface
  448. : device->plat->pbuffer;
  449. if (!eglMakeCurrent(display, surface, surface, context))
  450. blog(LOG_ERROR, "Failed to make context current: %s",
  451. get_egl_error_string());
  452. }
  453. static void gl_x11_egl_device_leave_context(gs_device_t *device)
  454. {
  455. const EGLDisplay display = device->plat->edisplay;
  456. if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  457. EGL_NO_CONTEXT)) {
  458. blog(LOG_ERROR, "Failed to reset current context: %s",
  459. get_egl_error_string());
  460. }
  461. }
  462. static void *gl_x11_egl_device_get_device_obj(gs_device_t *device)
  463. {
  464. return device->plat->context;
  465. }
  466. static void gl_x11_egl_getclientsize(const struct gs_swap_chain *swap,
  467. uint32_t *width, uint32_t *height)
  468. {
  469. xcb_connection_t *xcb_conn =
  470. XGetXCBConnection(swap->device->plat->xdisplay);
  471. xcb_window_t window = swap->wi->window;
  472. xcb_get_geometry_reply_t *geometry =
  473. get_window_geometry(xcb_conn, window);
  474. if (geometry) {
  475. *width = geometry->width;
  476. *height = geometry->height;
  477. }
  478. free(geometry);
  479. }
  480. static void gl_x11_egl_update(gs_device_t *device)
  481. {
  482. Display *display = device->plat->xdisplay;
  483. xcb_window_t window = device->cur_swap->wi->window;
  484. uint32_t values[] = {device->cur_swap->info.cx,
  485. device->cur_swap->info.cy};
  486. xcb_configure_window(XGetXCBConnection(display), window,
  487. XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
  488. values);
  489. }
  490. static void gl_x11_egl_clear_context(gs_device_t *device)
  491. {
  492. Display *display = device->plat->edisplay;
  493. if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
  494. EGL_NO_CONTEXT)) {
  495. blog(LOG_ERROR, "Failed to reset current context.");
  496. }
  497. }
  498. static void gl_x11_egl_device_load_swapchain(gs_device_t *device,
  499. gs_swapchain_t *swap)
  500. {
  501. if (device->cur_swap == swap)
  502. return;
  503. device->cur_swap = swap;
  504. device_enter_context(device);
  505. }
  506. enum swap_type {
  507. SWAP_TYPE_NORMAL,
  508. SWAP_TYPE_EXT,
  509. SWAP_TYPE_MESA,
  510. SWAP_TYPE_SGI,
  511. };
  512. static void gl_x11_egl_device_present(gs_device_t *device)
  513. {
  514. Display *display = device->plat->xdisplay;
  515. xcb_connection_t *xcb_conn = XGetXCBConnection(display);
  516. xcb_generic_event_t *xcb_event;
  517. while ((xcb_event = xcb_poll_for_event(xcb_conn))) {
  518. free(xcb_event);
  519. }
  520. if (!eglSwapBuffers(device->plat->edisplay,
  521. device->cur_swap->wi->surface))
  522. blog(LOG_ERROR, "Cannot swap EGL buffers: %s",
  523. get_egl_error_string());
  524. }
  525. static struct gs_texture *gl_x11_egl_device_texture_create_from_dmabuf(
  526. gs_device_t *device, unsigned int width, unsigned int height,
  527. uint32_t drm_format, enum gs_color_format color_format,
  528. uint32_t n_planes, const int *fds, const uint32_t *strides,
  529. const uint32_t *offsets, const uint64_t *modifiers)
  530. {
  531. struct gl_platform *plat = device->plat;
  532. return gl_egl_create_dmabuf_image(plat->edisplay, width, height,
  533. drm_format, color_format, n_planes,
  534. fds, strides, offsets, modifiers);
  535. }
  536. static const struct gl_winsys_vtable egl_x11_winsys_vtable = {
  537. .windowinfo_create = gl_x11_egl_windowinfo_create,
  538. .windowinfo_destroy = gl_x11_egl_windowinfo_destroy,
  539. .platform_create = gl_x11_egl_platform_create,
  540. .platform_destroy = gl_x11_egl_platform_destroy,
  541. .platform_init_swapchain = gl_x11_egl_platform_init_swapchain,
  542. .platform_cleanup_swapchain = gl_x11_egl_platform_cleanup_swapchain,
  543. .device_enter_context = gl_x11_egl_device_enter_context,
  544. .device_leave_context = gl_x11_egl_device_leave_context,
  545. .device_get_device_obj = gl_x11_egl_device_get_device_obj,
  546. .getclientsize = gl_x11_egl_getclientsize,
  547. .clear_context = gl_x11_egl_clear_context,
  548. .update = gl_x11_egl_update,
  549. .device_load_swapchain = gl_x11_egl_device_load_swapchain,
  550. .device_present = gl_x11_egl_device_present,
  551. .device_texture_create_from_dmabuf =
  552. gl_x11_egl_device_texture_create_from_dmabuf,
  553. };
  554. const struct gl_winsys_vtable *gl_x11_egl_get_winsys_vtable(void)
  555. {
  556. return &egl_x11_winsys_vtable;
  557. }