render.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*!
  2. \file lib/nviz/render.c
  3. \brief Nviz library -- GLX context manipulation
  4. Based on visualization/nviz/src/togl.c
  5. (C) 2008, 2010, 2018 by the GRASS Development Team
  6. This program is free software under the GNU General Public License
  7. (>=v2). Read the file COPYING that comes with GRASS for details.
  8. \author Updated/modified by Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
  9. \author Support for framebuffer objects by Huidae Cho <grass4u gmail.com> (July 2018)
  10. */
  11. #include <grass/glocale.h>
  12. #include <grass/nviz.h>
  13. #if defined(OPENGL_WINDOWS) && defined(OPENGL_FBO)
  14. static int gl_funcs_found = 0;
  15. static PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
  16. static PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
  17. static PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
  18. static PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
  19. static PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
  20. static PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
  21. static PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
  22. /* https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions */
  23. static void *GetAnyGLFuncAddress(const char *name)
  24. {
  25. void *p = (void *)wglGetProcAddress(name);
  26. if (p == 0 || p == (void*)0x1 || p == (void*)0x2 || p == (void*)0x3 ||
  27. p == (void*)-1) {
  28. HMODULE module = LoadLibraryA("opengl32.dll");
  29. p = (void *)GetProcAddress(module, name);
  30. }
  31. if (!p)
  32. G_fatal_error(_("Unable to get function address for %s"), name);
  33. return p;
  34. }
  35. static void find_gl_funcs()
  36. {
  37. if (gl_funcs_found)
  38. return;
  39. glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)GetAnyGLFuncAddress("glGenFramebuffers");
  40. glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)GetAnyGLFuncAddress("glBindFramebuffer");
  41. glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)GetAnyGLFuncAddress("glGenRenderbuffers");
  42. glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)GetAnyGLFuncAddress("glBindRenderbuffer");
  43. glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)GetAnyGLFuncAddress("glRenderbufferStorage");
  44. glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)GetAnyGLFuncAddress("glFramebufferRenderbuffer");
  45. glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)GetAnyGLFuncAddress("glCheckFramebufferStatus");
  46. gl_funcs_found = 1;
  47. }
  48. #endif
  49. /*!
  50. \brief Allocate memory for render window
  51. \return pointer to render_window struct
  52. \return NULL on failure
  53. */
  54. struct render_window *Nviz_new_render_window(void)
  55. {
  56. struct render_window *rwin;
  57. /* G_malloc() calls G_fatal_error() on failure */
  58. rwin = (struct render_window *)G_malloc(sizeof(struct render_window));
  59. return rwin;
  60. }
  61. /*!
  62. \brief Initialize render window
  63. \param win pointer to render_window struct
  64. */
  65. void Nviz_init_render_window(struct render_window *rwin)
  66. {
  67. #if defined(OPENGL_X11)
  68. rwin->displayId = NULL;
  69. rwin->contextId = NULL;
  70. rwin->pixmap = 0;
  71. rwin->windowId = 0;
  72. #elif defined(OPENGL_AQUA)
  73. #if defined(OPENGL_AGL)
  74. rwin->pixelFmtId = NULL;
  75. rwin->contextId = NULL;
  76. rwin->windowId = NULL;
  77. #else
  78. rwin->contextId = NULL;
  79. #endif
  80. #elif defined(OPENGL_WINDOWS)
  81. rwin->displayId = NULL;
  82. rwin->contextId = NULL;
  83. #endif
  84. rwin->width = 0;
  85. rwin->height = 0;
  86. }
  87. /*!
  88. \brief Free render window
  89. \param win pointer to render_window struct
  90. */
  91. void Nviz_destroy_render_window(struct render_window *rwin)
  92. {
  93. #if defined(OPENGL_X11)
  94. glXDestroyGLXPixmap(rwin->displayId, rwin->windowId);
  95. XFreePixmap(rwin->displayId, rwin->pixmap);
  96. glXDestroyContext(rwin->displayId, rwin->contextId);
  97. XCloseDisplay(rwin->displayId);
  98. #elif defined(OPENGL_AQUA)
  99. #if defined(OPENGL_AGL)
  100. aglDestroyPixelFormat(rwin->pixelFmtId);
  101. aglDestroyContext(rwin->contextId);
  102. aglDestroyPBuffer(rwin->windowId);
  103. #else
  104. CGLDestroyContext(rwin->contextId);
  105. #endif
  106. #elif defined(OPENGL_WINDOWS)
  107. wglDeleteContext(rwin->contextId);
  108. DeleteDC(rwin->displayId);
  109. #endif
  110. G_free((void *)rwin);
  111. }
  112. /*!
  113. \brief Create render window
  114. \param rwin pointer to render_window struct
  115. \param display display instance (NULL for offscreen)
  116. \param width window width
  117. \param height window height
  118. \return 0 on success
  119. \return -1 on error
  120. */
  121. int Nviz_create_render_window(struct render_window *rwin, void *display,
  122. int width, int height)
  123. {
  124. #if defined(OPENGL_X11)
  125. int attributeList[] = {
  126. GLX_RGBA,
  127. GLX_RED_SIZE, 1,
  128. GLX_GREEN_SIZE, 1,
  129. GLX_BLUE_SIZE, 1,
  130. GLX_DEPTH_SIZE, 1,
  131. #if !defined(OPENGL_FBO)
  132. GLX_DOUBLEBUFFER,
  133. #endif
  134. None
  135. };
  136. XVisualInfo *v;
  137. rwin->displayId = XOpenDisplay((char *)display);
  138. if (!rwin->displayId) {
  139. G_fatal_error(_("Bad server connection"));
  140. }
  141. v = glXChooseVisual(rwin->displayId,
  142. DefaultScreen(rwin->displayId), attributeList);
  143. if (!v) {
  144. G_warning(_("Unable to get visual info"));
  145. return -1;
  146. }
  147. rwin->contextId = glXCreateContext(rwin->displayId, v, NULL, GL_TRUE);
  148. if (!rwin->contextId) {
  149. G_warning(_("Unable to create rendering context"));
  150. return -1;
  151. }
  152. /* create win pixmap to render to (same depth as RootWindow) */
  153. rwin->pixmap = XCreatePixmap(rwin->displayId,
  154. RootWindow(rwin->displayId, v->screen),
  155. width, height, v->depth);
  156. /* create an off-screen GLX rendering area */
  157. rwin->windowId = glXCreateGLXPixmap(rwin->displayId, v, rwin->pixmap);
  158. XFree(v);
  159. #elif defined(OPENGL_AQUA)
  160. #if defined(OPENGL_AGL)
  161. int attributeList[] = {
  162. AGL_RGBA,
  163. AGL_RED_SIZE, 1,
  164. AGL_GREEN_SIZE, 1,
  165. AGL_BLUE_SIZE, 1,
  166. AGL_DEPTH_SIZE, 1,
  167. #if !defined(OPENGL_FBO)
  168. AGL_DOUBLEBUFFER,
  169. #endif
  170. AGL_NONE
  171. };
  172. /* TODO: open mac display */
  173. /* TODO: dev = NULL, ndev = 0 ? */
  174. rwin->pixelFmtId = aglChoosePixelFormat(NULL, 0, attributeList);
  175. rwin->contextId = aglCreateContext(rwin->pixelFmtId, NULL);
  176. /* create an off-screen AGL rendering area */
  177. aglCreatePBuffer(width, height, GL_TEXTURE_2D, GL_RGBA, 0, &(rwin->windowId));
  178. aglSetPBuffer(rwin->contextId, rwin->windowId, 0, 0, 0);
  179. #else
  180. CGLPixelFormatAttribute attributeList[] = {
  181. kCGLPFAColorSize, 24,
  182. kCGLPFADepthSize, 32,
  183. (CGLPixelFormatAttribute) 0
  184. };
  185. CGLPixelFormatObj pix;
  186. GLint nvirt;
  187. CGLError error;
  188. error = CGLChoosePixelFormat(attributeList, &pix, &nvirt);
  189. if (error) {
  190. G_warning(_("Unable to choose pixel format (CGL error = %d)"), error);
  191. return -1;
  192. }
  193. error = CGLCreateContext(pix, NULL, &rwin->contextId);
  194. if (error) {
  195. G_warning(_("Unable to create context (CGL error = %d)"), error);
  196. return -1;
  197. }
  198. CGLDestroyPixelFormat(pix);
  199. #endif
  200. #elif defined(OPENGL_WINDOWS)
  201. WNDCLASS wc = {0};
  202. HWND hWnd;
  203. PIXELFORMATDESCRIPTOR pfd = {
  204. sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */
  205. 1, /* version number */
  206. PFD_DRAW_TO_WINDOW | /* support window */
  207. PFD_SUPPORT_OPENGL | /* support OpenGL */
  208. PFD_DOUBLEBUFFER, /* double buffered */
  209. PFD_TYPE_RGBA, /* RGBA type */
  210. 24, /* 24-bit color depth */
  211. 0, 0, 0, 0, 0, 0, /* color bits ignored */
  212. 0, /* no alpha buffer */
  213. 0, /* shift bit ignored */
  214. 0, /* no accumulation buffer */
  215. 0, 0, 0, 0, /* accum bits ignored */
  216. 32, /* 32-bit z-buffer */
  217. 0, /* no stencil buffer */
  218. 0, /* no auxiliary buffer */
  219. PFD_MAIN_PLANE, /* main layer */
  220. 0, /* reserved */
  221. 0, 0, 0 /* layer masks ignored */
  222. };
  223. int iPixelFormat;
  224. wc.lpfnWndProc = DefWindowProc;
  225. wc.lpszClassName = "nviz";
  226. if (!RegisterClass(&wc)) {
  227. G_warning(_("Unable to register window class"));
  228. return -1;
  229. }
  230. hWnd = CreateWindow(wc.lpszClassName, wc.lpszClassName, WS_POPUP,
  231. CW_USEDEFAULT, CW_USEDEFAULT, width, height,
  232. NULL, NULL, wc.hInstance, NULL);
  233. if (!hWnd) {
  234. G_warning(_("Unable to create window"));
  235. return -1;
  236. }
  237. rwin->displayId = GetDC(hWnd);
  238. iPixelFormat = ChoosePixelFormat(rwin->displayId, &pfd);
  239. SetPixelFormat(rwin->displayId, iPixelFormat, &pfd);
  240. rwin->contextId = wglCreateContext(rwin->displayId);
  241. #endif
  242. rwin->width = width;
  243. rwin->height = height;
  244. return 0;
  245. }
  246. /*!
  247. \brief Make window current for rendering
  248. \param win pointer to render_window struct
  249. \return 1 on success
  250. \return 0 on failure
  251. */
  252. int Nviz_make_current_render_window(const struct render_window *rwin)
  253. {
  254. #if defined(OPENGL_X11)
  255. if (!rwin->displayId || !rwin->contextId)
  256. return 0;
  257. if (rwin->contextId == glXGetCurrentContext())
  258. return 1;
  259. glXMakeCurrent(rwin->displayId, rwin->windowId, rwin->contextId);
  260. #elif defined(OPENGL_AQUA)
  261. #if defined(OPENGL_AGL)
  262. if (!rwin->contextId)
  263. return 0;
  264. if (rwin->contextId == aglGetCurrentContext())
  265. return 1;
  266. aglSetCurrentContext(rwin->contextId);
  267. #else
  268. CGLError error;
  269. error = CGLSetCurrentContext(rwin->contextId);
  270. if (error) {
  271. G_warning(_("Unable to set current context (CGL error = %d)"), error);
  272. return 0;
  273. }
  274. #endif
  275. #elif defined(OPENGL_WINDOWS)
  276. if (!rwin->displayId || !rwin->contextId)
  277. return 0;
  278. wglMakeCurrent(rwin->displayId, rwin->contextId);
  279. #endif
  280. #if defined(OPENGL_FBO)
  281. #if defined(OPENGL_WINDOWS)
  282. find_gl_funcs();
  283. #endif
  284. GLuint framebuf, renderbuf, depthbuf;
  285. GLenum status;
  286. glGenFramebuffers(1, &framebuf);
  287. glBindFramebuffer(GL_FRAMEBUFFER, framebuf);
  288. glGenRenderbuffers(1, &renderbuf);
  289. glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
  290. glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
  291. rwin->width, rwin->height);
  292. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  293. GL_RENDERBUFFER, renderbuf);
  294. glGenRenderbuffers(1, &depthbuf);
  295. glBindRenderbuffer(GL_RENDERBUFFER, depthbuf);
  296. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
  297. rwin->width, rwin->height);
  298. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  299. GL_RENDERBUFFER, depthbuf);
  300. status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  301. if (status != GL_FRAMEBUFFER_COMPLETE) {
  302. G_warning(_("Incomplete framebuffer status (status = %d)"), status);
  303. return 0;
  304. }
  305. #endif
  306. glViewport(0, 0, rwin->width, rwin->height);
  307. return 1;
  308. }