gl-windows.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[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 3 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. #define WIN32_LEAN_AND_MEAN
  15. #include <windows.h>
  16. #include "util/darray.h"
  17. #include "gl-subsystem.h"
  18. #include "glew/include/GL/wglew.h"
  19. /* Basically swapchain-specific information. Fortunately for windows this is
  20. * super basic stuff */
  21. struct gl_windowinfo {
  22. HWND hwnd;
  23. HDC hdc;
  24. };
  25. /* Like the other subsystems, the GL subsystem has one swap chain created by
  26. * default. */
  27. struct gl_platform {
  28. HGLRC hrc;
  29. struct gs_swap_chain swap;
  30. };
  31. /* For now, only support basic 32bit formats for graphics output. */
  32. static inline int get_color_format_bits(enum gs_color_format format)
  33. {
  34. switch (format) {
  35. case GS_RGBA:
  36. return 32;
  37. default:
  38. return 0;
  39. }
  40. }
  41. static inline int get_depth_format_bits(enum gs_zstencil_format zsformat)
  42. {
  43. switch (zsformat) {
  44. case GS_Z16:
  45. return 16;
  46. case GS_Z24_S8:
  47. return 24;
  48. default:
  49. return 0;
  50. }
  51. }
  52. static inline int get_stencil_format_bits(enum gs_zstencil_format zsformat)
  53. {
  54. switch (zsformat) {
  55. case GS_Z24_S8:
  56. return 8;
  57. default:
  58. return 0;
  59. }
  60. }
  61. /* would use designated initializers but microsoft sort of sucks */
  62. static inline void init_dummy_pixel_format(PIXELFORMATDESCRIPTOR *pfd)
  63. {
  64. memset(pfd, 0, sizeof(pfd));
  65. pfd->nSize = sizeof(pfd);
  66. pfd->nVersion = 1;
  67. pfd->iPixelType = PFD_TYPE_RGBA;
  68. pfd->cColorBits = 32;
  69. pfd->cDepthBits = 24;
  70. pfd->cStencilBits = 8;
  71. pfd->iLayerType = PFD_MAIN_PLANE;
  72. pfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
  73. PFD_DOUBLEBUFFER;
  74. }
  75. static const char *dummy_window_class = "GLDummyWindow";
  76. static bool registered_dummy_window_class = false;
  77. struct dummy_context {
  78. HWND hwnd;
  79. HGLRC hrc;
  80. HDC hdc;
  81. };
  82. /* Need a dummy window for the dummy context */
  83. static bool gl_register_dummy_window_class(void)
  84. {
  85. WNDCLASSA wc;
  86. if (registered_dummy_window_class)
  87. return true;
  88. memset(&wc, 0, sizeof(wc));
  89. wc.style = CS_OWNDC;
  90. wc.hInstance = GetModuleHandle(NULL);
  91. wc.lpfnWndProc = DefWindowProc;
  92. wc.lpszClassName = dummy_window_class;
  93. if (!RegisterClassA(&wc)) {
  94. blog(LOG_ERROR, "Could not create dummy window class");
  95. return false;
  96. }
  97. registered_dummy_window_class = true;
  98. return true;
  99. }
  100. static inline HWND gl_create_dummy_window(void)
  101. {
  102. HWND hwnd = CreateWindowExA(0, dummy_window_class, "Dummy GL Window",
  103. WS_POPUP,
  104. 0, 0, 2, 2,
  105. NULL, NULL, GetModuleHandle(NULL), NULL);
  106. if (!hwnd)
  107. blog(LOG_ERROR, "Could not create dummy context window");
  108. return hwnd;
  109. }
  110. static inline HGLRC gl_init_context(HDC hdc)
  111. {
  112. HGLRC hglrc = wglCreateContext(hdc);
  113. if (!hglrc) {
  114. blog(LOG_ERROR, "wglCreateContext failed, %u", GetLastError());
  115. return NULL;
  116. }
  117. if (!wglMakeCurrent(hdc, hglrc)) {
  118. blog(LOG_ERROR, "wglMakeCurrent failed, %u", GetLastError());
  119. wglDeleteContext(hglrc);
  120. return NULL;
  121. }
  122. return hglrc;
  123. }
  124. static bool gl_dummy_context_init(struct dummy_context *dummy)
  125. {
  126. PIXELFORMATDESCRIPTOR pfd;
  127. int format_index;
  128. if (!gl_register_dummy_window_class())
  129. return false;
  130. dummy->hwnd = gl_create_dummy_window();
  131. if (!dummy->hwnd)
  132. return false;
  133. dummy->hdc = GetDC(dummy->hwnd);
  134. init_dummy_pixel_format(&pfd);
  135. format_index = ChoosePixelFormat(dummy->hdc, &pfd);
  136. if (!format_index) {
  137. blog(LOG_ERROR, "Dummy ChoosePixelFormat failed, %u",
  138. GetLastError());
  139. return false;
  140. }
  141. if (!SetPixelFormat(dummy->hdc, format_index, &pfd)) {
  142. blog(LOG_ERROR, "Dummy SetPixelFormat failed, %u",
  143. GetLastError());
  144. return false;
  145. }
  146. dummy->hrc = gl_init_context(dummy->hdc);
  147. if (!dummy->hrc) {
  148. blog(LOG_ERROR, "Failed to initialize dummy context");
  149. return false;
  150. }
  151. return true;
  152. }
  153. static inline void gl_dummy_context_free(struct dummy_context *dummy)
  154. {
  155. wglMakeCurrent(NULL, NULL);
  156. wglDeleteContext(dummy->hrc);
  157. DestroyWindow(dummy->hwnd);
  158. memset(dummy, 0, sizeof(struct dummy_context));
  159. }
  160. static inline void required_extension_error(const char *extension)
  161. {
  162. blog(LOG_ERROR, "OpenGL extension %s is required", extension);
  163. }
  164. static bool gl_init_extensions(void)
  165. {
  166. GLenum errorcode = glewInit();
  167. if (errorcode != GLEW_OK) {
  168. blog(LOG_ERROR, "glewInit failed, %u", errorcode);
  169. return false;
  170. }
  171. if (!GLEW_VERSION_2_1) {
  172. blog(LOG_ERROR, "OpenGL 2.1 minimum required by the graphics "
  173. "adapter");
  174. return false;
  175. }
  176. if (!GLEW_ARB_framebuffer_object) {
  177. required_extension_error("GL_ARB_framebuffer_object");
  178. return false;
  179. }
  180. if (!WGLEW_ARB_pixel_format) {
  181. required_extension_error("WGL_ARB_pixel_format");
  182. return false;
  183. }
  184. return true;
  185. }
  186. static inline void add_attrib(struct darray *list, int attrib, int val)
  187. {
  188. darray_push_back(sizeof(int), list, &attrib);
  189. darray_push_back(sizeof(int), list, &val);
  190. }
  191. /* Creates the real pixel format for the target window */
  192. static int gl_choose_pixel_format(HDC hdc, struct gs_init_data *info)
  193. {
  194. struct darray attribs;
  195. int color_bits = get_color_format_bits(info->format);
  196. int depth_bits = get_depth_format_bits(info->zsformat);
  197. int stencil_bits = get_stencil_format_bits(info->zsformat);
  198. UINT num_formats;
  199. BOOL success;
  200. int format;
  201. if (!color_bits) {
  202. blog(LOG_ERROR, "gl_init_pixel_format: color format not "
  203. "supported");
  204. return false;
  205. }
  206. darray_init(&attribs);
  207. add_attrib(&attribs, WGL_DRAW_TO_WINDOW_ARB, GL_TRUE);
  208. add_attrib(&attribs, WGL_SUPPORT_OPENGL_ARB, GL_TRUE);
  209. add_attrib(&attribs, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB);
  210. add_attrib(&attribs, WGL_DOUBLE_BUFFER_ARB, GL_TRUE);
  211. add_attrib(&attribs, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB);
  212. add_attrib(&attribs, WGL_COLOR_BITS_ARB, color_bits);
  213. add_attrib(&attribs, WGL_DEPTH_BITS_ARB, depth_bits);
  214. add_attrib(&attribs, WGL_STENCIL_BITS_ARB, stencil_bits);
  215. add_attrib(&attribs, 0, 0);
  216. success = wglChoosePixelFormatARB(hdc, attribs.array, NULL, 1, &format,
  217. &num_formats);
  218. if (!success || !num_formats) {
  219. blog(LOG_ERROR, "wglChoosePixelFormatARB failed, %u",
  220. GetLastError());
  221. format = 0;
  222. }
  223. darray_free(&attribs);
  224. return format;
  225. }
  226. static inline bool gl_get_pixel_format(HDC hdc, struct gs_init_data *info,
  227. int *format, PIXELFORMATDESCRIPTOR *pfd)
  228. {
  229. *format = gl_choose_pixel_format(hdc, info);
  230. if (!format)
  231. return false;
  232. if (!DescribePixelFormat(hdc, *format, sizeof(*pfd), pfd)) {
  233. blog(LOG_ERROR, "DescribePixelFormat failed, %u",
  234. GetLastError());
  235. return false;
  236. }
  237. return true;
  238. }
  239. static inline bool gl_set_pixel_format(HDC hdc, int format,
  240. PIXELFORMATDESCRIPTOR *pfd)
  241. {
  242. if (!SetPixelFormat(hdc, format, pfd)) {
  243. blog(LOG_ERROR, "SetPixelFormat failed, %u", GetLastError());
  244. return false;
  245. }
  246. return true;
  247. }
  248. static struct gl_windowinfo *gl_windowinfo_bare(struct gs_init_data *info)
  249. {
  250. struct gl_windowinfo *wi = bmalloc(sizeof(struct gl_windowinfo));
  251. memset(wi, 0, sizeof(struct gl_windowinfo));
  252. wi->hwnd = info->hwnd;
  253. wi->hdc = GetDC(wi->hwnd);
  254. if (!wi->hdc) {
  255. blog(LOG_ERROR, "Unable to get device context from window");
  256. bfree(wi);
  257. return NULL;
  258. }
  259. return wi;
  260. }
  261. static bool init_default_swap(struct gl_platform *plat, device_t device,
  262. int pixel_format, PIXELFORMATDESCRIPTOR *pfd,
  263. struct gs_init_data *info)
  264. {
  265. plat->swap.device = device;
  266. plat->swap.info = *info;
  267. plat->swap.wi = gl_windowinfo_bare(info);
  268. if (!plat->swap.wi)
  269. return false;
  270. if (!gl_set_pixel_format(plat->swap.wi->hdc, pixel_format, pfd))
  271. return false;
  272. return true;
  273. }
  274. struct gl_platform *gl_platform_create(device_t device,
  275. struct gs_init_data *info)
  276. {
  277. struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
  278. struct dummy_context dummy;
  279. int pixel_format;
  280. PIXELFORMATDESCRIPTOR pfd;
  281. memset(plat, 0, sizeof(struct gl_platform));
  282. memset(&dummy, 0, sizeof(struct dummy_context));
  283. if (!gl_dummy_context_init(&dummy))
  284. goto fail;
  285. if (!gl_init_extensions())
  286. goto fail;
  287. /* you have to have a dummy context open before you can actually
  288. * use wglChoosePixelFormatARB */
  289. if (!gl_get_pixel_format(dummy.hdc, info, &pixel_format, &pfd))
  290. goto fail;
  291. gl_dummy_context_free(&dummy);
  292. if (!init_default_swap(plat, device, pixel_format, &pfd, info))
  293. goto fail;
  294. plat->hrc = gl_init_context(plat->swap.wi->hdc);
  295. if (!plat->hrc)
  296. goto fail;
  297. if (GLEW_ARB_seamless_cube_map) {
  298. glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
  299. gl_success("GL_TEXTURE_CUBE_MAP_SEAMLESS");
  300. }
  301. return plat;
  302. fail:
  303. blog(LOG_ERROR, "gl_platform_create failed");
  304. gl_platform_destroy(plat);
  305. gl_dummy_context_free(&dummy);
  306. return NULL;
  307. }
  308. struct gs_swap_chain *gl_platform_getswap(struct gl_platform *platform)
  309. {
  310. return &platform->swap;
  311. }
  312. void gl_platform_destroy(struct gl_platform *plat)
  313. {
  314. if (plat) {
  315. if (plat->hrc) {
  316. wglMakeCurrent(NULL, NULL);
  317. wglDeleteContext(plat->hrc);
  318. }
  319. gl_windowinfo_destroy(plat->swap.wi);
  320. bfree(plat);
  321. }
  322. }
  323. struct gl_windowinfo *gl_windowinfo_create(struct gs_init_data *info)
  324. {
  325. struct gl_windowinfo *wi = gl_windowinfo_bare(info);
  326. PIXELFORMATDESCRIPTOR pfd;
  327. int pixel_format;
  328. if (!wi)
  329. return NULL;
  330. if (!gl_get_pixel_format(wi->hdc, info, &pixel_format, &pfd))
  331. goto fail;
  332. if (!gl_set_pixel_format(wi->hdc, pixel_format, &pfd))
  333. goto fail;
  334. return wi;
  335. fail:
  336. blog(LOG_ERROR, "gl_windowinfo_create failed");
  337. gl_windowinfo_destroy(wi);
  338. return NULL;
  339. }
  340. void gl_windowinfo_destroy(struct gl_windowinfo *wi)
  341. {
  342. if (wi) {
  343. if (wi->hdc)
  344. ReleaseDC(wi->hwnd, wi->hdc);
  345. bfree(wi);
  346. }
  347. }