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