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