gl-x11.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /******************************************************************************
  2. Copyright (C) 2014 by Zachary Lund <[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. #include <X11/Xlib.h>
  15. #include <stdio.h>
  16. #include "gl-subsystem.h"
  17. #include <glad/glad_glx.h>
  18. static const int fb_attribs[] = {
  19. /* Hardcoded for now... */
  20. GLX_STENCIL_SIZE, 8,
  21. GLX_DEPTH_SIZE, 24,
  22. GLX_BUFFER_SIZE, 32, /* Color buffer depth */
  23. GLX_DOUBLEBUFFER, true,
  24. GLX_X_RENDERABLE, true,
  25. GLX_RENDER_TYPE, GLX_RGBA_BIT,
  26. None
  27. };
  28. static const int ctx_attribs[] = {
  29. #ifdef _DEBUG
  30. GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
  31. #endif
  32. GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
  33. None,
  34. };
  35. struct gl_windowinfo {
  36. Display *display;
  37. uint32_t id;
  38. uint32_t int_id;
  39. uint32_t glxid;
  40. };
  41. struct gl_platform {
  42. GLXContext context;
  43. GLXFBConfig fbcfg;
  44. struct gs_swap_chain swap;
  45. };
  46. extern struct gs_swap_chain *gl_platform_getswap(struct gl_platform *platform)
  47. {
  48. return &platform->swap;
  49. }
  50. extern struct gl_windowinfo *gl_windowinfo_create(struct gs_init_data *info)
  51. {
  52. struct gl_windowinfo *wi = bzalloc(sizeof(struct gl_windowinfo));
  53. wi->id = info->window.id;
  54. wi->display = info->window.display;
  55. return wi;
  56. }
  57. extern void gl_windowinfo_destroy(struct gl_windowinfo *wi)
  58. {
  59. bfree(wi);
  60. }
  61. extern void gl_getclientsize(struct gs_swap_chain *swap,
  62. uint32_t *width, uint32_t *height)
  63. {
  64. XWindowAttributes info = { 0 };
  65. XGetWindowAttributes(swap->wi->display, swap->wi->id, &info);
  66. *height = info.height;
  67. *width = info.width;
  68. }
  69. static void print_info_stuff(struct gs_init_data *info)
  70. {
  71. blog( LOG_INFO,
  72. "X and Y: %i %i\n"
  73. "Backbuffers: %i\n"
  74. "Color Format: %i\n"
  75. "ZStencil Format: %i\n"
  76. "Adapter: %i\n",
  77. info->cx, info->cy,
  78. info->num_backbuffers,
  79. info->format, info->zsformat,
  80. info->adapter
  81. );
  82. }
  83. int wait_for_notify(Display *dpy, XEvent *e, char *arg)
  84. {
  85. UNUSED_PARAMETER(dpy);
  86. return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
  87. }
  88. static bool got_x_error = false;
  89. static int err_handler(Display *disp, XErrorEvent *e)
  90. {
  91. char estr[128];
  92. XGetErrorText(disp, e->error_code, estr, 128);
  93. blog(LOG_DEBUG, "Got X error: %s", estr);
  94. got_x_error = true;
  95. return 0;
  96. }
  97. static bool handle_x_error(Display *disp, const char *error_string)
  98. {
  99. XSync(disp, 0);
  100. if (got_x_error) {
  101. if (error_string)
  102. blog(LOG_ERROR, "%s", error_string);
  103. got_x_error = false;
  104. return true;
  105. }
  106. return false;
  107. }
  108. struct gl_platform *gl_platform_create(device_t device,
  109. struct gs_init_data *info)
  110. {
  111. int num_configs = 0;
  112. int error_base = 0, event_base = 0;
  113. Display *display = info->window.display;
  114. struct gl_platform *plat = bzalloc(sizeof(struct gl_platform));
  115. GLXFBConfig* configs;
  116. XWindowAttributes attrs;
  117. int screen;
  118. int major = 0, minor = 0;
  119. print_info_stuff(info);
  120. if (!display) {
  121. blog(LOG_ERROR, "Unable to find display. DISPLAY variable "
  122. "may not be set correctly.");
  123. goto fail0;
  124. }
  125. if (!XGetWindowAttributes(display, info->window.id, &attrs)) {
  126. blog(LOG_ERROR, "Failed getting window attributes");
  127. goto fail0;
  128. }
  129. screen = XScreenNumberOfScreen(attrs.screen);
  130. if (!gladLoadGLX(display, screen)) {
  131. blog(LOG_ERROR, "Unable to load GLX entry functions.");
  132. goto fail0;
  133. }
  134. if (!glXQueryExtension(display, &error_base, &event_base)) {
  135. blog(LOG_ERROR, "GLX not supported.");
  136. goto fail0;
  137. }
  138. /* We require glX version 1.3 */
  139. glXQueryVersion(display, &major, &minor);
  140. if (major < 1 || (major == 1 && minor < 3)) {
  141. blog(LOG_ERROR, "GLX version found: %i.%i\nRequired: "
  142. "1.3", major, minor);
  143. goto fail0;
  144. }
  145. if (!GLAD_GLX_ARB_create_context) {
  146. blog(LOG_ERROR, "ARB_GLX_create_context not supported!");
  147. goto fail0;
  148. }
  149. configs = glXChooseFBConfig(display, screen,
  150. fb_attribs, &num_configs);
  151. if (!configs) {
  152. blog(LOG_ERROR, "Attribute list or screen is invalid.");
  153. goto fail0;
  154. }
  155. if (num_configs == 0) {
  156. XFree(configs);
  157. blog(LOG_ERROR, "No framebuffer configurations found.");
  158. goto fail0;
  159. }
  160. plat->fbcfg = configs[0];
  161. XFree(configs);
  162. handle_x_error(display, NULL);
  163. /* We just use the first configuration found... as it does everything
  164. * we want at the very least. */
  165. plat->context = glXCreateContextAttribsARB(display, plat->fbcfg, NULL,
  166. true, ctx_attribs);
  167. if (!plat->context) {
  168. blog(LOG_ERROR, "Failed to create OpenGL context.");
  169. goto fail0;
  170. }
  171. if (handle_x_error(display, "Failed to create OpenGL context."))
  172. goto fail2;
  173. device->plat = plat;
  174. plat->swap.device = device;
  175. plat->swap.info.window.id = info->window.id;
  176. plat->swap.info.window.display = display;
  177. plat->swap.info.format = GS_RGBA;
  178. plat->swap.info.zsformat = GS_Z24_S8;
  179. plat->swap.info.num_backbuffers = 1;
  180. plat->swap.info.adapter = info->adapter;
  181. plat->swap.info.cx = attrs.width;
  182. plat->swap.info.cy = attrs.height;
  183. plat->swap.wi = gl_windowinfo_create(info);
  184. if (!gl_platform_init_swapchain(&plat->swap))
  185. goto fail2;
  186. if (!glXMakeCurrent(display, plat->swap.wi->glxid, plat->context)) {
  187. blog(LOG_ERROR, "Failed to make context current.");
  188. goto fail2;
  189. }
  190. if (!gladLoadGL()) {
  191. blog(LOG_ERROR, "Failed to load OpenGL entry functions.");
  192. goto fail2;
  193. }
  194. blog(LOG_INFO, "OpenGL version: %s\n", glGetString(GL_VERSION));
  195. /* We assume later that cur_swap is already set. */
  196. device->cur_swap = &plat->swap;
  197. XSync(display, false);
  198. blog(LOG_INFO, "Created new platform data");
  199. return plat;
  200. fail2:
  201. glXMakeCurrent(display, None, NULL);
  202. glXDestroyContext(display, plat->context);
  203. gl_platform_cleanup_swapchain(&plat->swap);
  204. fail0:
  205. bfree(plat);
  206. device->plat = 0;
  207. return NULL;
  208. }
  209. void gl_platform_destroy(struct gl_platform *platform)
  210. {
  211. if (!platform)
  212. return;
  213. Display *dpy = platform->swap.wi->display;
  214. glXMakeCurrent(dpy, None, NULL);
  215. glXDestroyContext(dpy, platform->context);
  216. gl_windowinfo_destroy(platform->swap.wi);
  217. gl_platform_cleanup_swapchain(&platform->swap);
  218. bfree(platform);
  219. }
  220. bool gl_platform_init_swapchain(struct gs_swap_chain *swap)
  221. {
  222. Display *display = swap->wi->display;
  223. struct gl_windowinfo *info = swap->wi;
  224. struct gl_platform *plat = swap->device->plat;
  225. XVisualInfo *vi = 0;
  226. Colormap cmap = 0;
  227. XSetWindowAttributes swa;
  228. XWindowAttributes attrs;
  229. XErrorHandler phandler = XSetErrorHandler(err_handler);
  230. gl_platform_cleanup_swapchain(swap);
  231. if (!XGetWindowAttributes(display, info->id, &attrs)) {
  232. blog(LOG_ERROR, "Failed getting window attributes");
  233. goto fail;
  234. }
  235. vi = glXGetVisualFromFBConfig(display, plat->fbcfg);
  236. if (handle_x_error(display, "Failed to get visual from fb config."))
  237. goto fail;
  238. cmap = XCreateColormap(display, info->id, vi->visual, AllocNone);
  239. if (handle_x_error(display, "Failed creating colormap"))
  240. goto fail;
  241. swa.colormap = cmap;
  242. swa.border_pixel = 0;
  243. info->int_id = XCreateWindow(display, info->id, 0, 0,
  244. attrs.width, attrs.height,
  245. 0, 24, InputOutput, vi->visual,
  246. CWBorderPixel|CWColormap, &swa);
  247. XMapWindow(display, info->int_id);
  248. if (handle_x_error(display, "Failed creating intermediate X window"))
  249. goto fail;
  250. info->glxid = glXCreateWindow(display, plat->fbcfg, info->int_id, 0);
  251. if (handle_x_error(display, "Failed creating intermediate GLX window"))
  252. goto fail;
  253. XFreeColormap(display, cmap);
  254. XFree(vi);
  255. return true;
  256. fail:
  257. gl_platform_cleanup_swapchain(swap);
  258. if (cmap)
  259. XFreeColormap(display, cmap);
  260. if (vi)
  261. XFree(vi);
  262. XSetErrorHandler(phandler);
  263. return false;
  264. }
  265. void gl_platform_cleanup_swapchain(struct gs_swap_chain *swap)
  266. {
  267. Display *display = swap->wi->display;
  268. struct gl_windowinfo *info = swap->wi;
  269. if (!info)
  270. return;
  271. if (info->glxid)
  272. glXDestroyWindow(display, info->glxid);
  273. if (info->int_id)
  274. XDestroyWindow(display, info->int_id);
  275. info->glxid = 0;
  276. info->int_id = 0;
  277. }
  278. void device_entercontext(device_t device)
  279. {
  280. GLXContext context = device->plat->context;
  281. XID window = device->cur_swap->wi->glxid;
  282. Display *display = device->cur_swap->wi->display;
  283. if (!glXMakeCurrent(display, window, context)) {
  284. blog(LOG_ERROR, "Failed to make context current.");
  285. }
  286. }
  287. void device_leavecontext(device_t device)
  288. {
  289. Display *display = device->cur_swap->wi->display;
  290. if (!glXMakeCurrent(display, None, NULL)) {
  291. blog(LOG_ERROR, "Failed to reset current context.");
  292. }
  293. }
  294. void gl_update(device_t device)
  295. {
  296. Display *display = device->cur_swap->wi->display;
  297. XID window = device->cur_swap->wi->int_id;
  298. XResizeWindow(display, window,
  299. device->cur_swap->info.cx, device->cur_swap->info.cy);
  300. }
  301. void device_load_swapchain(device_t device, swapchain_t swap)
  302. {
  303. if (!swap)
  304. swap = &device->plat->swap;
  305. if (device->cur_swap == swap)
  306. return;
  307. Display *dpy = swap->wi->display;
  308. XID window = swap->wi->glxid;
  309. GLXContext ctx = device->plat->context;
  310. device->cur_swap = swap;
  311. if (!glXMakeCurrent(dpy, window, ctx)) {
  312. blog(LOG_ERROR, "Failed to make context current.");
  313. }
  314. }
  315. void device_present(device_t device)
  316. {
  317. Display *display = device->cur_swap->wi->display;
  318. XID window = device->cur_swap->wi->glxid;
  319. glXSwapBuffers(display, window);
  320. }