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