Blender V5.0
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(context_params),
41 display_(display),
42 fbconfig_(fbconfig),
43 window_(window),
44 context_profile_mask_(contextProfileMask),
45 context_major_version_(contextMajorVersion),
46 context_minor_version_(contextMinorVersion),
47 context_flags_(contextFlags),
48 context_reset_notification_strategy_(contextResetNotificationStrategy),
49 context_(None)
50{
51 assert(display_ != nullptr);
52}
53
55{
56 if (display_ != nullptr) {
57 if (context_ != None) {
58 if (window_ != 0 && context_ == ::glXGetCurrentContext()) {
59 ::glXMakeCurrent(display_, None, nullptr);
60 }
61 if (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(display_, context_);
71 }
72 }
73 }
74}
75
77{
78 ::glXSwapBuffers(display_, window_);
79
80 return GHOST_kSuccess;
81}
82
84{
85 if (display_ == nullptr) {
86 return GHOST_kFailure;
87 }
88 active_context_ = this;
89 return ::glXMakeCurrent(display_, window_, context_) ? GHOST_kSuccess : GHOST_kFailure;
90}
91
93{
94 if (display_ == nullptr) {
95 return GHOST_kFailure;
96 }
97 active_context_ = nullptr;
98 return ::glXMakeCurrent(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(display_, nullptr, nullptr)) {
112 extStart = (const GLubyte *)glXGetClientString(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 = context_profile_mask_ & GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
154 int profileBitCompat = context_profile_mask_ & GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
155
156#ifdef WITH_GLEW_ES
157 int profileBitES = context_profile_mask_ & 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 && context_major_version_ == 1) {
169 fprintf(stderr, "Warning! OpenGL ES profile not available.\n");
170 }
171
172 if (!GLXEW_EXT_create_context_es2_profile && profileBitES && context_major_version_ == 2) {
173 fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n");
174 }
175#endif
176
177 int profileMask = 0;
178
179 if (GLXEW_ARB_create_context_profile && profileBitCore) {
180 profileMask |= profileBitCore;
181 }
182 if (GLXEW_ARB_create_context_profile && profileBitCompat) {
183 profileMask |= profileBitCompat;
184 }
185
186#ifdef WITH_GLEW_ES
187 if (GLXEW_EXT_create_context_es_profile && profileBitES) {
188 profileMask |= profileBitES;
189 }
190#endif
191
192 if (profileMask != context_profile_mask_) {
193 fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
194 }
195 /* max 10 attributes plus terminator */
196 int attribs[11];
197 int i = 0;
198
199 if (profileMask) {
200 attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB;
201 attribs[i++] = profileMask;
202 }
203
204 if (context_major_version_ != 0) {
205 attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
206 attribs[i++] = context_major_version_;
207 attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
208 attribs[i++] = context_minor_version_;
209 }
210
211 if (context_flags_ != 0) {
212 attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
213 attribs[i++] = context_flags_;
214 }
215
216 if (context_reset_notification_strategy_ != 0) {
217 if (GLXEW_ARB_create_context_robustness) {
218 attribs[i++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
219 attribs[i++] = context_reset_notification_strategy_;
220 }
221 else {
222 fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
223 }
224 }
225 attribs[i++] = 0;
226
227 /* Some drivers don't like having a true off-screen context.
228 * Create a pixel buffer instead of a window to render to.
229 * even if it will never be used for drawing. */
230 int pbuffer_attribs[] = {GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None};
231
232 /* Create a GL 3.x context */
233 if (fbconfig_) {
234 context_ = glXCreateContextAttribsARB(display_, fbconfig_, s_sharedContext, true, attribs);
235
236 if (!window_) {
237 window_ = (Window)glXCreatePbuffer(display_, fbconfig_, pbuffer_attribs);
238 }
239 }
240 else {
241 GLXFBConfig *framebuffer_config = nullptr;
242 {
243 int glx_attribs[64];
244 int fbcount = 0;
245
246 GHOST_X11_GL_GetAttributes(glx_attribs, 64, context_params_.is_stereo_visual, false, true);
247
248 framebuffer_config = glXChooseFBConfig(
249 display_, DefaultScreen(display_), glx_attribs, &fbcount);
250 }
251
252 if (framebuffer_config) {
253 context_ = glXCreateContextAttribsARB(
254 display_, framebuffer_config[0], s_sharedContext, True, attribs);
255
256 if (!window_) {
257 window_ = (Window)glXCreatePbuffer(display_, framebuffer_config[0], pbuffer_attribs);
258 }
259
260 fbconfig_ = framebuffer_config[0];
261 XFree(framebuffer_config);
262 }
263 }
264 }
265 else {
266 /* Don't create legacy context */
267 fprintf(stderr, "Error! GLX_ARB_create_context not available.\n");
268 }
269
270 GHOST_TSuccess success;
271
272 if (context_ != nullptr) {
273 const uchar *version;
274
275 if (!s_sharedContext) {
276 s_sharedContext = context_;
277 }
278 s_sharedCount++;
279
280 glXMakeCurrent(display_, window_, context_);
281
282 /* For performance measurements with VSync disabled. */
283 {
284 const GHOST_TVSyncModes vsync = getVSync();
285 if (vsync != GHOST_kVSyncModeUnset) {
286 setSwapInterval(int(vsync));
287 }
288 }
289
290 if (window_) {
291 initClearGL();
292 ::glXSwapBuffers(display_, window_);
293 }
294
295 version = glGetString(GL_VERSION);
296
297 if (!version || version[0] < '3' || ((version[0] == '3') && (version[2] < '3'))) {
298 success = GHOST_kFailure;
299 }
300 else {
301 success = GHOST_kSuccess;
302 }
303 }
304 else {
305 /* freeing well clean up the context initialized above */
306 success = GHOST_kFailure;
307 }
308
310
311 active_context_ = this;
312 return success;
313}
314
316{
317 window_ = 0;
318
319 return GHOST_kSuccess;
320}
321
323{
324 if (epoxy_has_glx_extension(display_, DefaultScreen(display_), "GLX_EXT_swap_control")) {
325 ::glXSwapIntervalEXT(display_, window_, interval);
326 return GHOST_kSuccess;
327 }
328 return GHOST_kFailure;
329}
330
332{
333 if (epoxy_has_glx_extension(display_, DefaultScreen(display_), "GLX_EXT_swap_control")) {
334 uint interval = 0;
335
336 ::glXQueryDrawable(display_, window_, GLX_SWAP_INTERVAL_EXT, &interval);
337
338 interval_out = int(interval);
339
340 return GHOST_kSuccess;
341 }
342 return GHOST_kFailure;
343}
344
354 int *attribs, int attribs_max, bool is_stereo_visual, bool need_alpha, bool for_fb_config)
355{
356 int i = 0;
357
358 if (is_stereo_visual) {
359 attribs[i++] = GLX_STEREO;
360 if (for_fb_config) {
361 attribs[i++] = True;
362 }
363 }
364
365 if (for_fb_config) {
366 attribs[i++] = GLX_RENDER_TYPE;
367 attribs[i++] = GLX_RGBA_BIT;
368 }
369 else {
370 attribs[i++] = GLX_RGBA;
371 }
372
373 attribs[i++] = GLX_DOUBLEBUFFER;
374 if (for_fb_config) {
375 attribs[i++] = True;
376 }
377
378 attribs[i++] = GLX_RED_SIZE;
379 attribs[i++] = True;
380
381 attribs[i++] = GLX_BLUE_SIZE;
382 attribs[i++] = True;
383
384 attribs[i++] = GLX_GREEN_SIZE;
385 attribs[i++] = True;
386
387 if (need_alpha) {
388 attribs[i++] = GLX_ALPHA_SIZE;
389 attribs[i++] = True;
390 }
391
392 attribs[i++] = 0;
393
394 GHOST_ASSERT(i <= attribs_max, "attribute size too small");
395
396 (void)attribs_max;
397
398 return i;
399}
400
401/* Excuse inlining part of GLEW. */
402#ifdef USE_GLXEW_INIT_WORKAROUND
403static GLuint _glewStrLen(const GLubyte *s)
404{
405 GLuint i = 0;
406 if (s == nullptr) {
407 return 0;
408 }
409 while (s[i] != '\0') {
410 i++;
411 }
412 return i;
413}
414
415static GLuint _glewStrCLen(const GLubyte *s, GLubyte c)
416{
417 GLuint i = 0;
418 if (s == nullptr) {
419 return 0;
420 }
421 while (s[i] != '\0' && s[i] != c) {
422 i++;
423 }
424 return (s[i] == '\0' || s[i] == c) ? i : 0;
425}
426
427static GLboolean _glewStrSame(const GLubyte *a, const GLubyte *b, GLuint n)
428{
429 GLuint i = 0;
430 if (a == nullptr || b == nullptr) {
431 return (a == nullptr && b == nullptr && n == 0) ? GL_TRUE : GL_FALSE;
432 }
433 while (i < n && a[i] != '\0' && b[i] != '\0' && a[i] == b[i]) {
434 i++;
435 }
436 return i == n ? GL_TRUE : GL_FALSE;
437}
438
439static GLboolean _glewSearchExtension(const char *name, const GLubyte *start, const GLubyte *end)
440{
441 const GLubyte *p;
442 GLuint len = _glewStrLen((const GLubyte *)name);
443 p = start;
444 while (p < end) {
445 GLuint n = _glewStrCLen(p, ' ');
446 if (len == n && _glewStrSame((const GLubyte *)name, p, n)) {
447 return GL_TRUE;
448 }
449 p += n + 1;
450 }
451 return GL_FALSE;
452}
453#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:57
@ GHOST_kFailure
Definition GHOST_Types.h:57
@ GHOST_kSuccess
Definition GHOST_Types.h:57
GHOST_TVSyncModes
@ GHOST_kVSyncModeUnset
GHOST_TSuccess releaseNativeHandles() override
GHOST_ContextGLX(const GHOST_ContextParams &context_params, Window window, Display *display, GLXFBConfig fbconfig, int contextProfileMask, int contextMajorVersion, int contextMinorVersion, int contextFlags, int contextResetNotificationStrategy)
GHOST_TSuccess releaseDrawingContext() override
GHOST_TSuccess activateDrawingContext() override
GHOST_TSuccess getSwapInterval(int &interval_out) override
GHOST_TSuccess swapBufferRelease() override
GHOST_TSuccess setSwapInterval(int interval) override
~GHOST_ContextGLX() override
GHOST_TSuccess initializeDrawingContext() override
GHOST_Context(const GHOST_ContextParams &context_params)
static GHOST_Context * active_context_
virtual GHOST_TVSyncModes getVSync()
GHOST_ContextParams context_params_
#define assert(assertion)
const char * name
i
Definition text_draw.cc:230
uint len