GRASS GIS 8 Programmer's Manual 8.2.0(2022)-exported
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)
20static int gl_funcs_found = 0;
21static PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
22static PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
23static PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
24static PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
25static PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
26static PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
27static PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
28
29/* https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions */
30static 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
43static 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 */
66struct render_window *Nviz_new_render_window(void)
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 */
81void Nviz_init_render_window(struct render_window *rwin)
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 */
110void Nviz_destroy_render_window(struct render_window *rwin)
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 */
144int 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 */
299int Nviz_make_current_render_window(const struct render_window *rwin)
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}
void G_free(void *buf)
Free allocated memory.
Definition: alloc.c:149
#define NULL
Definition: ccmath.h:32
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:160
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition: gis/error.c:204
const char * name
Definition: named_colr.c:7
struct render_window * Nviz_new_render_window(void)
Allocate memory for render window.
Definition: render.c:66
void Nviz_destroy_render_window(struct render_window *rwin)
Free render window.
Definition: render.c:110
void Nviz_init_render_window(struct render_window *rwin)
Initialize render window.
Definition: render.c:81
int Nviz_create_render_window(struct render_window *rwin, void *display, int width, int height)
Create render window.
Definition: render.c:144
int Nviz_make_current_render_window(const struct render_window *rwin)
Make window current for rendering.
Definition: render.c:299