gl-x11.c 9.8 KB

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