Cogs.Core
GLXContext.cpp
1#ifdef COGS_GLX
2
3#include "GLFuncPointers.h"
4#include "GLContext.h"
5#include "../IGraphicsDevice.h"
6
7#include "Foundation/Logging/Logger.h"
8#include "Foundation/Platform/WindowData.h"
9
10#include <dlfcn.h>
11
12#include <span>
13
14
15PFNGLXGETPROCADDRESSPROC _glXGetProcAddress = nullptr;
16GLXFBConfig Cogs::GLContext::fbConfig = nullptr;
17
18namespace {
19
20 Cogs::Logging::Log logger = Cogs::Logging::getLogger("GLXContext");
21
22 bool has_GLX_EXT_swap_control = false;
23 bool has_GLX_EXT_swap_control_tear = false;
24
25 struct LibFunc {
26 void** ptr;
27 const char* name;
28 };
29
30 // ======= X11 =============================================================
31 void* libX11 = nullptr;
32 static const char* libX11Names[] = {
33 "libX11.so"
34 };
35
36 Display*(*_XOpenDisplay)(const char*) = nullptr;
37 Colormap (*_XCreateColormap)(Display*, Window, Visual*, int) = nullptr;
38 Window (*_XCreateWindow)(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, int, unsigned int, Visual*, unsigned long, XSetWindowAttributes*) = nullptr;
39 int (*_XDestroyWindow)(Display* display, ::Window w);
40 int (*_XFreeColormap)(Display*, Colormap) = nullptr;
41 int (*_XFree)(void*) = nullptr;
42
43 LibFunc libX11Funcs[] = {
44 { (void**)&_XOpenDisplay, "XOpenDisplay" },
45 { (void**)&_XCreateColormap, "XCreateColormap" },
46 { (void**)&_XCreateWindow, "XCreateWindow" },
47 { (void**)&_XDestroyWindow, "XDestroyWindow" },
48 { (void**)&_XFreeColormap, "XFreeColormap" },
49 { (void**)&_XFree, "XFree" }
50 };
51 // =========================================================================
52
53 // ======= GLX =============================================================
54 void* libGLX = nullptr;
55 static const char* libGLXNames[] = {
56 "libGLX.so.0",
57 "libGLX.so",
58 };
59
60 bool (*_glXQueryVersion)(Display* dpy, int* maj, int* min) = nullptr;
61 const char* (*_glXQueryExtensionsString)( Display *dpy, int screen );
62 GLXContext (*_glXCreateContext)(Display *, XVisualInfo*, GLXContext, bool) = nullptr;
63 void (*_glXDestroyContext)(Display *, GLXContext);
64 bool (*_glXMakeCurrent)(Display *, GLXDrawable, GLXContext);
65 void (*_glXSwapBuffers)(Display *, GLXDrawable);
66 bool (*_glXIsDirect)(Display*, GLXContext);
67 PFNGLXCHOOSEFBCONFIGPROC _glXChooseFBConfig = nullptr;
68 PFNGLXGETFBCONFIGATTRIBPROC _glXGetFBConfigAttrib = nullptr;
69 PFNGLXGETVISUALFROMFBCONFIGPROC _glXGetVisualFromFBConfig = nullptr;
70 PFNGLXQUERYRENDERERINTEGERMESAPROC _glXQueryRendererIntegerMESA = nullptr;
71 PFNGLXCREATECONTEXTATTRIBSARBPROC _glXCreateContextAttribsARB = nullptr;
72 PFNGLXSWAPINTERVALEXTPROC _glXSwapIntervalEXT = nullptr;
73
74 LibFunc libGLXFuncs[] = {
75 { (void**)&_glXQueryVersion, "glXQueryVersion" },
76 { (void**)&_glXQueryExtensionsString, "glXQueryExtensionsString" },
77 { (void**)&_glXCreateContext, "glXCreateContext" },
78 { (void**)&_glXDestroyContext, "glXDestroyContext" },
79 { (void**)&_glXMakeCurrent, "glXMakeCurrent" },
80 { (void**)&_glXSwapBuffers, "glXSwapBuffers" },
81 { (void**)&_glXChooseFBConfig, "glXChooseFBConfig" },
82 { (void**)&_glXGetProcAddress, "glXGetProcAddress" },
83 { (void**)&_glXGetFBConfigAttrib, "glXGetFBConfigAttrib" },
84 { (void**)&_glXGetVisualFromFBConfig, "glXGetVisualFromFBConfig" },
85 { (void**)&_glXQueryRendererIntegerMESA, "glXQueryRendererIntegerMESA" },
86 { (void**)&_glXCreateContextAttribsARB, "glXCreateContextAttribsARB" },
87 { (void**)&_glXIsDirect, "glXIsDirect" },
88 { (void**)&_glXSwapIntervalEXT, "glXSwapIntervalEXT" },
89 };
90 // =========================================================================
91
92 void* openLib(const std::span<const char*>& libNameCandidates)
93 {
94 for (const char* libName : libNameCandidates) {
95 void* lib = dlopen(libName, RTLD_LAZY | RTLD_LOCAL);
96 if (lib) { return lib; }
97 }
98 return nullptr;
99 }
100
101 static bool initialized = false;
102
103 bool initialize()
104 {
105 if (initialized) {
106 return true;
107 }
108
109 if (libX11 == nullptr) {
110 libX11 = openLib(libX11Names);
111 if (libX11 == nullptr) {
112 LOG_FATAL(logger, "Failed to load libX11");
113 return false;
114 }
115 }
116
117 for (const LibFunc& libFunc : libX11Funcs) {
118 *libFunc.ptr = dlsym(libX11, libFunc.name);
119 if (*libFunc.ptr == nullptr) {
120 LOG_FATAL(logger, "Failed to lookup libX11 function %s", libFunc.name);
121 return false;
122 }
123 LOG_TRACE(logger, "Looked up %s", libFunc.name);
124 }
125
126 if (libGLX == nullptr) {
127 libGLX = openLib(libGLXNames);
128 if (libGLX == nullptr) {
129 LOG_FATAL(logger, "Failed to load libGLX");
130 return false;
131 }
132 }
133
134 if(_glXGetProcAddress == nullptr) {
135 _glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC)dlsym(libGLX, "glXGetProcAddress");
136 if(_glXGetProcAddress == nullptr) {
137 LOG_FATAL(logger, "Failed to lookup libGLX function glXGetProcAddress");
138 return false;
139 }
140 }
141
142 for (const LibFunc& libFunc : libGLXFuncs) {
143 *libFunc.ptr = (void*)_glXGetProcAddress((const GLubyte*)libFunc.name);
144 if (*libFunc.ptr == nullptr) {
145 LOG_FATAL(logger, "Failed to lookup libGLX function %s", libFunc.name);
146 return false;
147 }
148 LOG_TRACE(logger, "Looked up %s", libFunc.name);
149 }
150
151 initialized = true;
152 return true;
153 }
154
155 bool finalizeContext(Cogs::GLContextBase::Platform platform, bool debug, bool sRGB)
156 {
157 if (debug) {
158 Cogs::setUpGLDebugging(nullptr, platform);
159 }
160 if (sRGB) {
161 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
162 }
163 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
164 return true;
165 }
166
167}
168
169bool Cogs::GLContext::create(NativeDisplay display, WindowData* windowData, GLContext* shareContext, const GraphicsDeviceSettings* settings, Platform desiredPlatform, bool debug)
170{
171 if (!initialize()) {
172 return false;
173 }
174
175 assert(desiredPlatform == Platform::GL && "ES3 not yet implemented");
176
177 if(shareContext && shareContext->dpy) {
178 dpy = shareContext->dpy;
179 }
180 else if(display) {
181 dpy = display;
182 }
183 else {
184 dpy = _XOpenDisplay(nullptr);
185 if(dpy == 0) {
186 LOG_FATAL(logger, "Failed to open display");
187 return false;
188 }
189 }
190
191 int glxMajor = 0;
192 int glxMinor = 0;
193 if (!_glXQueryVersion(dpy, &glxMajor, &glxMinor)) {
194 LOG_FATAL(logger, "glXQueryVersion failed");
195 return false;
196 }
197 LOG_DEBUG(logger, "Got GLX version %d.%d", glxMajor, glxMinor);
198 if ((glxMajor < 1) || ((glxMajor == 1) && (glxMinor < 3))) {
199 LOG_FATAL(logger, "GLX version is less than the 1.3 minimum version");
200 return false;
201 }
202
203 int screen = DefaultScreen(dpy);
204 const char* glxExtensions = _glXQueryExtensionsString(dpy, screen);
205 if (strstr(glxExtensions, "GLX_EXT_swap_control") != nullptr) {
206 LOG_DEBUG(logger, "GLX_EXT_swap_control present");
207 has_GLX_EXT_swap_control = true;
208 }
209 if (strstr(glxExtensions, "GLX_EXT_swap_control_tear") != nullptr) {
210 LOG_DEBUG(logger, "GLX_EXT_swap_control_tear present");
211 has_GLX_EXT_swap_control_tear = true;
212 }
213 bool sRGB = false;
214 if (strstr(glxExtensions, "GLX_EXT_framebuffer_sRGB") != nullptr) {
215 LOG_DEBUG(logger, "GLX_EXT_framebuffer_sRGB present");
216 sRGB = true;
217 }
218
219 GLXFBConfig bestFbc = getFBConfig(dpy, settings ? settings->numSamples : 0);
220 XVisualInfo* visualInfo = _glXGetVisualFromFBConfig(dpy, bestFbc);
221
222 if(windowData) {
223 win = windowData->windowHandle;
224 }
225 else {
226 LOG_DEBUG(logger, "Creating headless dummy window");
227 ownWindow = true;
228
229 XSetWindowAttributes swa = {};
230 swa.event_mask = StructureNotifyMask;
231 swa.colormap = _XCreateColormap(dpy, RootWindow(dpy, screen), visualInfo->visual, AllocNone);
232
233 win = _XCreateWindow(dpy,
234 RootWindow(dpy, screen),
235 0, 0, 32, 32, 0,
236 visualInfo->depth,
237 InputOutput,
238 visualInfo->visual,
239 CWBorderPixel | CWColormap | CWEventMask,
240 &swa);
241 if (win == 0) {
242 LOG_ERROR(logger, "Error creating offscreen window handle");
243 return false;
244 }
245 }
246
247 if(strstr(glxExtensions, "GLX_ARB_create_context") != nullptr) {
248 LOG_DEBUG(logger, "GLX_ARB_create_context present, using glXCreateContextAttribsARB");
249
250 GLuint version[2] = { 3, 0};
251 if(strstr(glxExtensions, "GLX_MESA_query_renderer") != nullptr) {
252 if(_glXQueryRendererIntegerMESA(dpy, screen, 0, GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA, version)) {
253 LOG_DEBUG(logger, "GLX renderer version=%u.%u", version[0], version[1]);
254 }
255 }
256
257 GLint contextFlags = debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
258 const GLint attributes[] = {
259 GLX_CONTEXT_MAJOR_VERSION_ARB, (GLint)version[0],
260 GLX_CONTEXT_MINOR_VERSION_ARB, (GLint)version[1],
261 GLX_CONTEXT_FLAGS_ARB, contextFlags,
262 0, 0
263 };
264 glc = _glXCreateContextAttribsARB(dpy,
265 bestFbc,
266 shareContext ? shareContext->glc : nullptr,
267 GL_TRUE,
268 attributes);
269 }
270 else {
271 LOG_DEBUG(logger, "GLX_ARB_create_context missing, using glXCreateContext");
272 glc = _glXCreateContext(dpy,
273 visualInfo,
274 shareContext ? shareContext->glc : nullptr,
275 GL_TRUE);
276 }
277
278 if (glc == nullptr) {
279 LOG_ERROR(logger, "Failed to create context");
280 return false;
281 }
282
283 if (_glXIsDirect(dpy, glc)) {
284 LOG_DEBUG(logger, "Created direct GLX context");
285 }
286 else {
287 LOG_DEBUG(logger, "Created indirect GLX context");
288 }
289
290 if(!_glXMakeCurrent(dpy, win, glc)) {
291 LOG_ERROR(logger, "Failed to make context current");
292 }
293 getGLFuncPointers();
294 finalizeContext(platform, debug, sRGB);
295 return true;
296}
297
298bool Cogs::GLContext::makeCurrent()
299{
300 return _glXMakeCurrent(dpy, win, glc);
301}
302
303void Cogs::GLContext::swapBuffers(uint32_t syncInterval, uint32_t presentFlags)
304{
305 if ((presentFlags & PresentFlags::NoSwap) == 0) {
306 if (has_GLX_EXT_swap_control || has_GLX_EXT_swap_control_tear) {
307 _glXSwapIntervalEXT(dpy, win, syncInterval != 0 ? 1 : 0);
308 }
309 _glXSwapBuffers(dpy, win);
310 }
311}
312
313void Cogs::GLContext::destroy()
314{
315 if(glc) {
316 _glXDestroyContext(dpy, glc);
317 glc = nullptr;
318 }
319 if(win && ownWindow) {
320 _XDestroyWindow(dpy, win);
321 win = 0;
322 }
323}
324
325GLXFBConfig Cogs::GLContext::getFBConfig(NativeDisplay display, int requestedSampleCount) {
326 if (fbConfig) {
327 return fbConfig;
328 }
329
330 if (!initialize()) {
331 return nullptr;
332 }
333
334 std::vector<GLint> attributes = {
335 GLX_X_RENDERABLE, true,
336 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
337 GLX_RENDER_TYPE, GLX_RGBA_BIT,
338 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
339 GLX_RED_SIZE, 8,
340 GLX_GREEN_SIZE, 8,
341 GLX_BLUE_SIZE, 8,
342 GLX_ALPHA_SIZE, 0,
343 GLX_DEPTH_SIZE, 24,
344 GLX_STENCIL_SIZE, 8,
345 GLX_DOUBLEBUFFER, true,
346 };
347
348 int fbCount = 0;
349 GLXFBConfig* fbConfigs = nullptr;
350 int screen = DefaultScreen(display);
351 bool sRGB = false;
352
353 if (strstr(_glXQueryExtensionsString(display, screen), "GLX_EXT_framebuffer_sRGB") != nullptr) {
354 LOG_DEBUG(logger, "GLX_EXT_framebuffer_sRGB present");
355 sRGB = true;
356 }
357
358 if (sRGB) {
359 size_t o = attributes.size();
360 attributes.push_back(GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT);
361 attributes.push_back(GL_TRUE);
362 attributes.push_back(0);
363 attributes.push_back(0);
364 fbConfigs = _glXChooseFBConfig(display, screen, attributes.data(), &fbCount);
365 if (fbConfigs == nullptr || fbCount == 0) {
366 attributes.resize(o);
367 LOG_INFO(logger, "Failed to find sRGB config.");
368 sRGB = false;
369 }
370 }
371 if (fbConfigs == nullptr || fbCount == 0) {
372 attributes.push_back(0);
373 attributes.push_back(0);
374 fbConfigs = _glXChooseFBConfig(display, screen, attributes.data(), &fbCount);
375 }
376 if (!fbConfigs) {
377 LOG_FATAL(logger, "Failed to retrieve a list of framebuffer configurations.");
378 return nullptr;
379 }
380 LOG_DEBUG(logger, "Found %d matching framebuffer configs.", fbCount);
381
382 int best_fbc = -1;
383 int best_num_samp = -1;
384
385 for (int idx = 0; idx < fbCount; ++idx) {
386 XVisualInfo* vi = _glXGetVisualFromFBConfig(display, fbConfigs[idx]);
387
388 if (vi) {
389 int samp_buf;
390 int samples;
391
392 _glXGetFBConfigAttrib(display, fbConfigs[idx], GLX_SAMPLE_BUFFERS, &samp_buf);
393 _glXGetFBConfigAttrib(display, fbConfigs[idx], GLX_SAMPLES, &samples);
394
395 LOG_DEBUG(logger, " Matching fbconfig %d, visual ID 0x%2lx: SAMPLE_BUFFERS = %d, SAMPLES = %d", idx, vi->visualid, samp_buf, samples);
396
397 if ((best_fbc < 0) || (samp_buf && (samples > best_num_samp) && (samples <= requestedSampleCount))) {
398 best_fbc = idx;
399 best_num_samp = samples;
400 }
401 _XFree(vi);
402 }
403 }
404
405 fbConfig = fbConfigs[best_fbc];
406 _XFree(fbConfigs);
407
408 LOG_DEBUG(logger, "Requested %d samples, chose visual ID = 0x%lx", requestedSampleCount, _glXGetVisualFromFBConfig(display, fbConfig)->visualid);
409
410 return fbConfig;
411}
412
413XVisualInfo* Cogs::GLContext::getVisualInfo(Display* display, GLXFBConfig fbConfig) {
414 return _glXGetVisualFromFBConfig(display, fbConfig);
415}
416
417#endif // of COGS_GLX
Log implementation class.
Definition: LogManager.h:139
@ sRGB
Value is a color and is subject to gamma correction.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180