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