Blender V4.3
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
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 return ::glXMakeCurrent(m_display, m_window, m_context) ? GHOST_kSuccess : GHOST_kFailure;
89}
90
92{
93 if (m_display == nullptr) {
94 return GHOST_kFailure;
95 }
96 return ::glXMakeCurrent(m_display, None, nullptr) ? GHOST_kSuccess : GHOST_kFailure;
97}
98
100{
102
103 /* -------------------------------------------------------------------- */
104 /* Begin Inline GLEW. */
105
106#ifdef USE_GLXEW_INIT_WORKAROUND
107 const GLubyte *extStart = (GLubyte *)"";
108 const GLubyte *extEnd;
109 if (glXQueryExtension(m_display, nullptr, nullptr)) {
110 extStart = (const GLubyte *)glXGetClientString(m_display, GLX_EXTENSIONS);
111 if ((extStart == nullptr) ||
112 (glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB(
113 (const GLubyte *)"glXChooseFBConfig")) == nullptr ||
114 (glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB(
115 (const GLubyte *)"glXCreateContextAttribsARB")) == nullptr ||
116 (glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC)glXGetProcAddressARB(
117 (const GLubyte *)"glXCreatePbuffer")) == nullptr)
118 {
119 extStart = (GLubyte *)"";
120 }
121 }
122 extEnd = extStart + _glewStrLen(extStart);
123
124# undef GLXEW_ARB_create_context
125 const bool GLXEW_ARB_create_context = _glewSearchExtension(
126 "GLX_ARB_create_context", extStart, extEnd);
127# undef GLXEW_ARB_create_context_profile
128 const bool GLXEW_ARB_create_context_profile = _glewSearchExtension(
129 "GLX_ARB_create_context_profile", extStart, extEnd);
130# undef GLXEW_ARB_create_context_robustness
131 const bool GLXEW_ARB_create_context_robustness = _glewSearchExtension(
132 "GLX_ARB_create_context_robustness", extStart, extEnd);
133# ifdef WITH_GLEW_ES
134# undef GLXEW_EXT_create_context_es_profile
135 const bool GLXEW_EXT_create_context_es_profile = _glewSearchExtension(
136 "GLX_EXT_create_context_es_profile", extStart, extEnd);
137# undef GLXEW_EXT_create_context_es2_profile
138 const bool GLXEW_EXT_create_context_es2_profile = _glewSearchExtension(
139 "GLX_EXT_create_context_es2_profile", extStart, extEnd);
140# endif /* WITH_GLEW_ES */
141
142 /* End Inline GLEW. */
143 /* -------------------------------------------------------------------- */
144#else
145 /* Important to initialize only GLXEW (_not_ GLEW),
146 * since this breaks w/ Mesa's `swrast`, see: #46431. */
147 glxewInit();
148#endif /* USE_GLXEW_INIT_WORKAROUND */
149
150 if (GLXEW_ARB_create_context) {
151 int profileBitCore = m_contextProfileMask & GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
152 int profileBitCompat = m_contextProfileMask & GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
153
154#ifdef WITH_GLEW_ES
155 int profileBitES = m_contextProfileMask & GLX_CONTEXT_ES_PROFILE_BIT_EXT;
156#endif
157
158 if (!GLXEW_ARB_create_context_profile && profileBitCore) {
159 fprintf(stderr, "Warning! OpenGL core profile not available.\n");
160 }
161 if (!GLXEW_ARB_create_context_profile && profileBitCompat) {
162 fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");
163 }
164
165#ifdef WITH_GLEW_ES
166 if (!GLXEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1)
167 fprintf(stderr, "Warning! OpenGL ES profile not available.\n");
168
169 if (!GLXEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2)
170 fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n");
171#endif
172
173 int profileMask = 0;
174
175 if (GLXEW_ARB_create_context_profile && profileBitCore) {
176 profileMask |= profileBitCore;
177 }
178 if (GLXEW_ARB_create_context_profile && profileBitCompat) {
179 profileMask |= profileBitCompat;
180 }
181
182#ifdef WITH_GLEW_ES
183 if (GLXEW_EXT_create_context_es_profile && profileBitES)
184 profileMask |= profileBitES;
185#endif
186
187 if (profileMask != m_contextProfileMask) {
188 fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
189 }
190 /* max 10 attributes plus terminator */
191 int attribs[11];
192 int i = 0;
193
194 if (profileMask) {
195 attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB;
196 attribs[i++] = profileMask;
197 }
198
199 if (m_contextMajorVersion != 0) {
200 attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
201 attribs[i++] = m_contextMajorVersion;
202 attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
203 attribs[i++] = m_contextMinorVersion;
204 }
205
206 if (m_contextFlags != 0) {
207 attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
208 attribs[i++] = m_contextFlags;
209 }
210
211 if (m_contextResetNotificationStrategy != 0) {
212 if (GLXEW_ARB_create_context_robustness) {
213 attribs[i++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
214 attribs[i++] = m_contextResetNotificationStrategy;
215 }
216 else {
217 fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
218 }
219 }
220 attribs[i++] = 0;
221
222 /* Some drivers don't like having a true off-screen context.
223 * Create a pixel buffer instead of a window to render to.
224 * even if it will never be used for drawing. */
225 int pbuffer_attribs[] = {GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None};
226
227 /* Create a GL 3.x context */
228 if (m_fbconfig) {
229 m_context = glXCreateContextAttribsARB(
230 m_display, m_fbconfig, s_sharedContext, true, attribs);
231
232 if (!m_window) {
233 m_window = (Window)glXCreatePbuffer(m_display, m_fbconfig, pbuffer_attribs);
234 }
235 }
236 else {
237 GLXFBConfig *framebuffer_config = nullptr;
238 {
239 int glx_attribs[64];
240 int fbcount = 0;
241
242 GHOST_X11_GL_GetAttributes(glx_attribs, 64, m_stereoVisual, false, true);
243
244 framebuffer_config = glXChooseFBConfig(
245 m_display, DefaultScreen(m_display), glx_attribs, &fbcount);
246 }
247
248 if (framebuffer_config) {
249 m_context = glXCreateContextAttribsARB(
250 m_display, framebuffer_config[0], s_sharedContext, True, attribs);
251
252 if (!m_window) {
253 m_window = (Window)glXCreatePbuffer(m_display, framebuffer_config[0], pbuffer_attribs);
254 }
255
256 m_fbconfig = framebuffer_config[0];
257 XFree(framebuffer_config);
258 }
259 }
260 }
261 else {
262 /* Don't create legacy context */
263 fprintf(stderr, "Error! GLX_ARB_create_context not available.\n");
264 }
265
266 GHOST_TSuccess success;
267
268 if (m_context != nullptr) {
269 const uchar *version;
270
271 if (!s_sharedContext) {
272 s_sharedContext = m_context;
273 }
274 s_sharedCount++;
275
276 glXMakeCurrent(m_display, m_window, m_context);
277
278 if (m_window) {
279 initClearGL();
280 ::glXSwapBuffers(m_display, m_window);
281 }
282
283 version = glGetString(GL_VERSION);
284
285 if (!version || version[0] < '3' || ((version[0] == '3') && (version[2] < '3'))) {
286 success = GHOST_kFailure;
287 }
288 else {
289 success = GHOST_kSuccess;
290 }
291 }
292 else {
293 /* freeing well clean up the context initialized above */
294 success = GHOST_kFailure;
295 }
296
298
299 return success;
300}
301
303{
304 m_window = 0;
305
306 return GHOST_kSuccess;
307}
308
310{
311 if (epoxy_has_glx_extension(m_display, DefaultScreen(m_display), "GLX_EXT_swap_control")) {
312 ::glXSwapIntervalEXT(m_display, m_window, interval);
313 return GHOST_kSuccess;
314 }
315 return GHOST_kFailure;
316}
317
319{
320 if (epoxy_has_glx_extension(m_display, DefaultScreen(m_display), "GLX_EXT_swap_control")) {
321 uint interval = 0;
322
323 ::glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &interval);
324
325 intervalOut = int(interval);
326
327 return GHOST_kSuccess;
328 }
329 return GHOST_kFailure;
330}
331
341 int *attribs, int attribs_max, bool is_stereo_visual, bool need_alpha, bool for_fb_config)
342{
343 int i = 0;
344
345 if (is_stereo_visual) {
346 attribs[i++] = GLX_STEREO;
347 if (for_fb_config) {
348 attribs[i++] = True;
349 }
350 }
351
352 if (for_fb_config) {
353 attribs[i++] = GLX_RENDER_TYPE;
354 attribs[i++] = GLX_RGBA_BIT;
355 }
356 else {
357 attribs[i++] = GLX_RGBA;
358 }
359
360 attribs[i++] = GLX_DOUBLEBUFFER;
361 if (for_fb_config) {
362 attribs[i++] = True;
363 }
364
365 attribs[i++] = GLX_RED_SIZE;
366 attribs[i++] = True;
367
368 attribs[i++] = GLX_BLUE_SIZE;
369 attribs[i++] = True;
370
371 attribs[i++] = GLX_GREEN_SIZE;
372 attribs[i++] = True;
373
374 if (need_alpha) {
375 attribs[i++] = GLX_ALPHA_SIZE;
376 attribs[i++] = True;
377 }
378
379 attribs[i++] = 0;
380
381 GHOST_ASSERT(i <= attribs_max, "attribute size too small");
382
383 (void)attribs_max;
384
385 return i;
386}
387
388/* Excuse inlining part of GLEW. */
389#ifdef USE_GLXEW_INIT_WORKAROUND
390static GLuint _glewStrLen(const GLubyte *s)
391{
392 GLuint i = 0;
393 if (s == nullptr) {
394 return 0;
395 }
396 while (s[i] != '\0') {
397 i++;
398 }
399 return i;
400}
401
402static GLuint _glewStrCLen(const GLubyte *s, GLubyte c)
403{
404 GLuint i = 0;
405 if (s == nullptr) {
406 return 0;
407 }
408 while (s[i] != '\0' && s[i] != c) {
409 i++;
410 }
411 return (s[i] == '\0' || s[i] == c) ? i : 0;
412}
413
414static GLboolean _glewStrSame(const GLubyte *a, const GLubyte *b, GLuint n)
415{
416 GLuint i = 0;
417 if (a == nullptr || b == nullptr) {
418 return (a == nullptr && b == nullptr && n == 0) ? GL_TRUE : GL_FALSE;
419 }
420 while (i < n && a[i] != '\0' && b[i] != '\0' && a[i] == b[i]) {
421 i++;
422 }
423 return i == n ? GL_TRUE : GL_FALSE;
424}
425
426static GLboolean _glewSearchExtension(const char *name, const GLubyte *start, const GLubyte *end)
427{
428 const GLubyte *p;
429 GLuint len = _glewStrLen((const GLubyte *)name);
430 p = start;
431 while (p < end) {
432 GLuint n = _glewStrCLen(p, ' ');
433 if (len == n && _glewStrSame((const GLubyte *)name, p, n)) {
434 return GL_TRUE;
435 }
436 p += n + 1;
437 }
438 return GL_FALSE;
439}
440#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 GHOST_ASSERT(x, info)
#define GHOST_X11_ERROR_HANDLERS_OVERRIDE(var)
#define GHOST_X11_ERROR_HANDLERS_RESTORE(var)
GHOST_TSuccess
Definition GHOST_Types.h:87
@ GHOST_kFailure
Definition GHOST_Types.h:87
@ GHOST_kSuccess
Definition GHOST_Types.h:87
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
local_group_size(16, 16) .push_constant(Type b
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int