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