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