doxygen: add newline after \file
[blender.git] / intern / ghost / intern / GHOST_ContextGLX.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
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  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2014 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup GHOST
22  *
23  * Definition of GHOST_ContextGLX class.
24  */
25
26 #include "GHOST_ContextGLX.h"
27 #include "GHOST_SystemX11.h"
28
29 #include <vector>
30
31 #include <cassert>
32 #include <cstdio>
33 #include <cstring>
34
35 /* needed for intel drivers (works w/ mesa-swrast & nvidia) */
36 #define USE_GLXEW_INIT_WORKAROUND
37
38 #ifdef USE_GLXEW_INIT_WORKAROUND
39 static GLuint _glewStrLen(const GLubyte *s);
40 static GLboolean _glewSearchExtension(const char *name, const GLubyte *start, const GLubyte *end);
41 #endif
42
43 GLXContext GHOST_ContextGLX::s_sharedContext = None;
44 int        GHOST_ContextGLX::s_sharedCount   = 0;
45
46
47 GHOST_ContextGLX::GHOST_ContextGLX(
48         bool stereoVisual,
49         GHOST_TUns16 numOfAASamples,
50         Window window,
51         Display *display,
52         GLXFBConfig fbconfig,
53         int contextProfileMask,
54         int contextMajorVersion,
55         int contextMinorVersion,
56         int contextFlags,
57         int contextResetNotificationStrategy)
58     : GHOST_Context(stereoVisual, numOfAASamples),
59       m_display(display),
60       m_fbconfig(fbconfig),
61       m_window(window),
62       m_contextProfileMask(contextProfileMask),
63       m_contextMajorVersion(contextMajorVersion),
64       m_contextMinorVersion(contextMinorVersion),
65       m_contextFlags(contextFlags),
66       m_contextResetNotificationStrategy(contextResetNotificationStrategy),
67       m_context(None)
68 {
69         assert(m_display != NULL);
70 }
71
72
73 GHOST_ContextGLX::~GHOST_ContextGLX()
74 {
75         if (m_display != NULL) {
76                 if (m_context != None) {
77                         if (m_window != 0 && m_context == ::glXGetCurrentContext())
78                                 ::glXMakeCurrent(m_display, None, NULL);
79
80                         if (m_context != s_sharedContext || s_sharedCount == 1) {
81                                 assert(s_sharedCount > 0);
82
83                                 s_sharedCount--;
84
85                                 if (s_sharedCount == 0)
86                                         s_sharedContext = NULL;
87
88                                 ::glXDestroyContext(m_display, m_context);
89                         }
90                 }
91         }
92 }
93
94
95 GHOST_TSuccess GHOST_ContextGLX::swapBuffers()
96 {
97         ::glXSwapBuffers(m_display, m_window);
98
99         return GHOST_kSuccess;
100 }
101
102
103 GHOST_TSuccess GHOST_ContextGLX::activateDrawingContext()
104 {
105         if (m_display) {
106                 return ::glXMakeCurrent(m_display, m_window, m_context) ? GHOST_kSuccess : GHOST_kFailure;
107         }
108         else {
109                 return GHOST_kFailure;
110         }
111 }
112
113 GHOST_TSuccess GHOST_ContextGLX::releaseDrawingContext()
114 {
115         if (m_display) {
116                 return ::glXMakeCurrent(m_display, None, NULL) ? GHOST_kSuccess : GHOST_kFailure;
117         }
118         else {
119                 return GHOST_kFailure;
120         }
121 }
122
123 void GHOST_ContextGLX::initContextGLXEW()
124 {
125         initContextGLEW();
126 }
127
128 GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
129 {
130         GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store);
131
132         /* -------------------------------------------------------------------- */
133         /* Begin Inline Glew  */
134
135 #ifdef USE_GLXEW_INIT_WORKAROUND
136         const GLubyte *extStart = (GLubyte *)"";
137         const GLubyte *extEnd;
138         if (glXQueryExtension(m_display, NULL, NULL)) {
139                 extStart = (const GLubyte *)glXGetClientString(m_display, GLX_EXTENSIONS);
140                 if ((extStart == NULL) ||
141                     (glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB(
142                              (const GLubyte *)"glXChooseFBConfig")) == NULL ||
143                     (glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB(
144                              (const GLubyte *)"glXCreateContextAttribsARB")) == NULL ||
145                     (glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC)glXGetProcAddressARB(
146                              (const GLubyte *)"glXCreatePbuffer")) == NULL)
147                 {
148                         extStart = (GLubyte *)"";
149                 }
150         }
151         extEnd = extStart + _glewStrLen(extStart);
152
153 #undef GLXEW_ARB_create_context
154         const bool GLXEW_ARB_create_context =
155                 _glewSearchExtension("GLX_ARB_create_context", extStart, extEnd);
156 #undef GLXEW_ARB_create_context_profile
157         const bool GLXEW_ARB_create_context_profile =
158                 _glewSearchExtension("GLX_ARB_create_context_profile", extStart, extEnd);
159 #undef GLXEW_ARB_create_context_robustness
160 const bool GLXEW_ARB_create_context_robustness =
161                 _glewSearchExtension("GLX_ARB_create_context_robustness", extStart, extEnd);
162 #ifdef WITH_GLEW_ES
163 #undef GLXEW_EXT_create_context_es_profile
164         const bool GLXEW_EXT_create_context_es_profile =
165                 _glewSearchExtension("GLX_EXT_create_context_es_profile", extStart, extEnd);
166 #undef GLXEW_EXT_create_context_es2_profile
167         const bool GLXEW_EXT_create_context_es2_profile =
168                 _glewSearchExtension("GLX_EXT_create_context_es2_profile", extStart, extEnd);
169 #endif  /* WITH_GLEW_ES */
170
171         /* End Inline Glew */
172         /* -------------------------------------------------------------------- */
173 #else
174         /* important to initialize only glxew (_not_ glew),
175          * since this breaks w/ Mesa's `swrast`, see: T46431 */
176         glxewInit();
177 #endif  /* USE_GLXEW_INIT_WORKAROUND */
178
179
180
181         if (GLXEW_ARB_create_context) {
182                 int profileBitCore   = m_contextProfileMask & GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
183                 int profileBitCompat = m_contextProfileMask & GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
184
185 #ifdef WITH_GLEW_ES
186                 int profileBitES     = m_contextProfileMask & GLX_CONTEXT_ES_PROFILE_BIT_EXT;
187 #endif
188
189                 if (!GLXEW_ARB_create_context_profile && profileBitCore)
190                         fprintf(stderr, "Warning! OpenGL core profile not available.\n");
191
192                 if (!GLXEW_ARB_create_context_profile && profileBitCompat)
193                         fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");
194
195 #ifdef WITH_GLEW_ES
196                 if (!GLXEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1)
197                         fprintf(stderr, "Warning! OpenGL ES profile not available.\n");
198
199                 if (!GLXEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2)
200                         fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n");
201 #endif
202
203                 int profileMask = 0;
204
205                 if (GLXEW_ARB_create_context_profile && profileBitCore)
206                         profileMask |= profileBitCore;
207
208                 if (GLXEW_ARB_create_context_profile && profileBitCompat)
209                         profileMask |= profileBitCompat;
210
211 #ifdef WITH_GLEW_ES
212                 if (GLXEW_EXT_create_context_es_profile && profileBitES)
213                         profileMask |= profileBitES;
214 #endif
215
216                 if (profileMask != m_contextProfileMask)
217                         fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
218
219
220                 /* max 10 attributes plus terminator */
221                 int attribs[11];
222                 int i = 0;
223
224                 if (profileMask) {
225                         attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB;
226                         attribs[i++] = profileMask;
227                 }
228
229                 if (m_contextMajorVersion != 0) {
230                         attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
231                         attribs[i++] = m_contextMajorVersion;
232                         attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
233                         attribs[i++] = m_contextMinorVersion;
234                 }
235
236                 if (m_contextFlags != 0) {
237                         attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
238                         attribs[i++] = m_contextFlags;
239                 }
240
241                 if (m_contextResetNotificationStrategy != 0) {
242                         if (GLXEW_ARB_create_context_robustness) {
243                                 attribs[i++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
244                                 attribs[i++] = m_contextResetNotificationStrategy;
245                         }
246                         else {
247                                 fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
248                         }
249                 }
250                 attribs[i++] = 0;
251
252                 /* Some drivers don't like having a true offscreen context.
253                  * Create a pixel buffer instead of a window to render to.
254                  * even if it will never be used for drawing. */
255                 int pbuffer_attribs[] = {
256                         GLX_PBUFFER_WIDTH, 1,
257                         GLX_PBUFFER_HEIGHT, 1,
258                         None
259                 };
260
261                 /* Create a GL 3.x context */
262                 if (m_fbconfig) {
263                         m_context = glXCreateContextAttribsARB(m_display, m_fbconfig, s_sharedContext, true, attribs);
264
265                         if (!m_window) {
266                                 m_window = (Window)glXCreatePbuffer(m_display, m_fbconfig, pbuffer_attribs);
267                         }
268                 }
269                 else {
270                         GLXFBConfig *framebuffer_config = NULL;
271                         {
272                                 int glx_attribs[64];
273                                 int fbcount = 0;
274
275                                 GHOST_X11_GL_GetAttributes(glx_attribs, 64, m_numOfAASamples, m_stereoVisual, false, true);
276
277                                 framebuffer_config = glXChooseFBConfig(m_display, DefaultScreen(m_display), glx_attribs, &fbcount);
278                         }
279
280                         if (framebuffer_config) {
281                                 m_context = glXCreateContextAttribsARB(m_display, framebuffer_config[0], s_sharedContext, True, attribs);
282
283                                 if (!m_window) {
284                                         m_window = (Window)glXCreatePbuffer(m_display, framebuffer_config[0], pbuffer_attribs);
285                                 }
286
287                                 XFree(framebuffer_config);
288                         }
289                 }
290         }
291         else {
292                 /* Don't create legacy context */
293                 fprintf(stderr, "Error! GLX_ARB_create_context not available.\n");
294         }
295
296         GHOST_TSuccess success;
297
298         if (m_context != NULL) {
299                 const unsigned char *version;
300
301                 if (!s_sharedContext)
302                         s_sharedContext = m_context;
303
304                 s_sharedCount++;
305
306                 glXMakeCurrent(m_display, m_window, m_context);
307
308                 // Seems that this has to be called after MakeCurrent,
309                 // which means we cannot use glX extensions until after we create a context
310                 initContextGLXEW();
311
312                 if (m_window) {
313                         initClearGL();
314                         ::glXSwapBuffers(m_display, m_window);
315                 }
316
317                 /* re initialize to get the extensions properly */
318                 initContextGLXEW();
319
320                 version = glGetString(GL_VERSION);
321
322                 if (!version || version[0] < '3' || ((version[0] == '3') && (version[2] < '3'))) {
323                         success = GHOST_kFailure;
324                 }
325                 else {
326                         success = GHOST_kSuccess;
327                 }
328         }
329         else {
330                 /* freeing well clean up the context initialized above */
331                 success = GHOST_kFailure;
332         }
333
334         GHOST_X11_ERROR_HANDLERS_RESTORE(handler_store);
335
336         return success;
337 }
338
339
340 GHOST_TSuccess GHOST_ContextGLX::releaseNativeHandles()
341 {
342         m_window = 0;
343
344         return GHOST_kSuccess;
345 }
346
347
348 GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval)
349 {
350         if (GLXEW_EXT_swap_control) {
351                 ::glXSwapIntervalEXT(m_display, m_window, interval);
352
353                 return GHOST_kSuccess;
354         }
355         else {
356                 return GHOST_kFailure;
357         }
358 }
359
360
361 GHOST_TSuccess GHOST_ContextGLX::getSwapInterval(int &intervalOut)
362 {
363         if (GLXEW_EXT_swap_control) {
364                 unsigned int interval = 0;
365
366                 ::glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &interval);
367
368                 intervalOut = static_cast<int>(interval);
369
370                 return GHOST_kSuccess;
371         }
372         else {
373                 return GHOST_kFailure;
374         }
375 }
376
377 /**
378  * Utility function to get GLX attributes.
379  *
380  * \param for_fb_config: There are some small differences in
381  * #glXChooseVisual and #glXChooseFBConfig's attribute encoding.
382  *
383  * \note Similar to SDL's 'X11_GL_GetAttributes'
384  */
385 int GHOST_X11_GL_GetAttributes(
386         int *attribs, int attribs_max,
387         int samples, bool is_stereo_visual,
388         bool need_alpha,
389         bool for_fb_config)
390 {
391         int i = 0;
392
393 #ifdef GHOST_OPENGL_STENCIL
394         const bool need_stencil = true;
395 #else
396         const bool need_stencil = false;
397 #endif
398
399         if (is_stereo_visual) {
400                 attribs[i++] = GLX_STEREO;
401                 if (for_fb_config) {
402                         attribs[i++] = True;
403                 }
404         }
405
406         if (for_fb_config) {
407                 attribs[i++] = GLX_RENDER_TYPE;
408                 attribs[i++] = GLX_RGBA_BIT;
409         }
410         else {
411                 attribs[i++] = GLX_RGBA;
412         }
413
414         attribs[i++] = GLX_DOUBLEBUFFER;
415         if (for_fb_config) {
416                 attribs[i++] = True;
417         }
418
419         attribs[i++] = GLX_RED_SIZE;
420         attribs[i++] = True;
421
422         attribs[i++] = GLX_BLUE_SIZE;
423         attribs[i++] = True;
424
425         attribs[i++] = GLX_GREEN_SIZE;
426         attribs[i++] = True;
427
428         attribs[i++] = GLX_DEPTH_SIZE;
429         attribs[i++] = True;
430
431         if (need_alpha) {
432                 attribs[i++] = GLX_ALPHA_SIZE;
433                 attribs[i++] = True;
434         }
435
436         if (need_stencil) {
437                 attribs[i++] = GLX_STENCIL_SIZE;
438                 attribs[i++] = True;
439         }
440
441         if (samples) {
442                 attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
443                 attribs[i++] = True;
444
445                 attribs[i++] = GLX_SAMPLES_ARB;
446                 attribs[i++] = samples;
447         }
448
449         attribs[i++] = 0;
450
451         GHOST_ASSERT(i <= attribs_max, "attribute size too small");
452
453         (void)attribs_max;
454
455         return i;
456 }
457
458
459 /* excuse inlining part of glew */
460 #ifdef USE_GLXEW_INIT_WORKAROUND
461 static GLuint _glewStrLen(const GLubyte *s)
462 {
463         GLuint i = 0;
464         if (s == NULL) return 0;
465         while (s[i] != '\0') i++;
466         return i;
467 }
468
469 static GLuint _glewStrCLen(const GLubyte *s, GLubyte c)
470 {
471         GLuint i = 0;
472         if (s == NULL) return 0;
473         while (s[i] != '\0' && s[i] != c) i++;
474         return (s[i] == '\0' || s[i] == c) ? i : 0;
475 }
476
477 static GLboolean _glewStrSame(const GLubyte *a, const GLubyte *b, GLuint n)
478 {
479         GLuint i = 0;
480         if (a == NULL || b == NULL)
481                 return (a == NULL && b == NULL && n == 0) ? GL_TRUE : GL_FALSE;
482         while (i < n && a[i] != '\0' && b[i] != '\0' && a[i] == b[i]) i++;
483         return i == n ? GL_TRUE : GL_FALSE;
484 }
485
486 static GLboolean _glewSearchExtension(const char *name, const GLubyte *start, const GLubyte *end)
487 {
488         const GLubyte *p;
489         GLuint len = _glewStrLen((const GLubyte *)name);
490         p = start;
491         while (p < end) {
492                 GLuint n = _glewStrCLen(p, ' ');
493                 if (len == n && _glewStrSame((const GLubyte *)name, p, n)) return GL_TRUE;
494                 p += n + 1;
495         }
496         return GL_FALSE;
497 }
498 #endif  /* USE_GLXEW_INIT_WORKAROUND */