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