1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Private/Linux/BsLinuxPlatform.h"
4#include "Linux/BsLinuxGLSupport.h"
5#include "Linux/BsLinuxContext.h"
6#include "Linux/BsLinuxRenderWindow.h"
7#include "Linux/BsLinuxVideoModeInfo.h"
8#include "BsGLRenderAPI.h"
9
10namespace bs { namespace ct
11{
12 bool extGLX_ARB_multisample = false;
13 bool extGLX_ARB_framebuffer_sRGB = false;
14 bool extGLX_EXT_framebuffer_sRGB = false;
15 bool extGLX_ARB_create_context = false;
16 bool extGLX_ARB_create_context_profile = false;
17 bool extGLX_EXT_swap_control = false;
18 bool extGLX_MESA_swap_control = false;
19 bool extGLX_SGI_swap_control = false;
20
21 typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
22 glXCreateContextAttribsARBProc glXCreateContextAttribsARB = nullptr;
23
24 bool Load_ARB_create_context()
25 {
26 glXCreateContextAttribsARB =
27 (glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
28
29 return glXCreateContextAttribsARB != nullptr;
30 }
31
32 glXSwapIntervalEXTProc glXSwapIntervalEXT = nullptr;
33 glXSwapIntervalMESAProc glXSwapIntervalMESA = nullptr;
34 glXSwapIntervalSGIProc glXSwapIntervalSGI = nullptr;
35
36 glXChooseFBConfigProc glXChooseFBConfig = nullptr;
37 glXGetFBConfigAttribProc glXGetFBConfigAttrib = nullptr;
38 glXGetVisualFromFBConfigProc glXGetVisualFromFBConfig = nullptr;
39
40 bool Load_EXT_swap_control()
41 {
42 glXSwapIntervalEXT = (glXSwapIntervalEXTProc)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT");
43
44 return glXSwapIntervalEXT != nullptr;
45 }
46
47 bool Load_MESA_swap_control()
48 {
49 glXSwapIntervalMESA = (glXSwapIntervalMESAProc)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalMESA");
50
51 return glXSwapIntervalMESA != nullptr;
52 }
53
54 bool Load_SGI_swap_control()
55 {
56 glXSwapIntervalSGI = (glXSwapIntervalSGIProc)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI");
57
58 return glXSwapIntervalSGI != nullptr;
59 }
60
61 typedef bool (*ExtensionFunc)(void);
62
63 struct GLExtension
64 {
65 const char* name;
66 bool* status;
67 ExtensionFunc func;
68 };
69
70 static GLExtension gExtensionMap[] = {
71 { "GLX_ARB_multisample", &extGLX_ARB_multisample, nullptr },
72 { "GLX_ARB_framebuffer_sRGB", &extGLX_ARB_framebuffer_sRGB, nullptr },
73 { "GLX_EXT_framebuffer_sRGB", &extGLX_EXT_framebuffer_sRGB, nullptr },
74 { "GLX_ARB_create_context", &extGLX_ARB_create_context, Load_ARB_create_context },
75 { "GLX_ARB_create_context_profile", &extGLX_ARB_create_context_profile, nullptr },
76 { "GLX_EXT_swap_control", &extGLX_EXT_swap_control, Load_EXT_swap_control },
77 { "GLX_MESA_swap_control", &extGLX_MESA_swap_control, Load_MESA_swap_control },
78 { "GLX_SGI_swap_control", &extGLX_SGI_swap_control, Load_SGI_swap_control },
79 };
80
81 LinuxGLSupport::LinuxGLSupport()
82 { }
83
84 SPtr<bs::RenderWindow> LinuxGLSupport::newWindow(RENDER_WINDOW_DESC& desc, UINT32 windowId,
85 SPtr<bs::RenderWindow> parentWindow)
86 {
87 if(parentWindow != nullptr)
88 {
89 ::Window x11window;
90 parentWindow->getCustomAttribute("WINDOW", &x11window);
91 desc.platformSpecific["parentWindowHandle"] = toString((UINT64)x11window);
92 }
93
94 bs::LinuxRenderWindow* window = new (bs_alloc<bs::LinuxRenderWindow>()) bs::LinuxRenderWindow(desc, windowId, *this);
95 return SPtr<bs::RenderWindow>(window, &bs::CoreObject::_delete<bs::LinuxRenderWindow, GenAlloc>);
96 }
97
98 void LinuxGLSupport::start()
99 {
100 // Retrieve all essential extensions
101 LinuxPlatform::lockX();
102
103 ::Display* display = LinuxPlatform::getXDisplay();
104 const char* glExtensions = glXQueryExtensionsString(display, DefaultScreen(display));
105
106 const char* iter = glExtensions;
107 do
108 {
109 const char* start = iter;
110 while(*iter != ' ' && *iter)
111 iter++;
112
113 const char* end = iter;
114 std::string name(start, end);
115
116 UINT32 numExtensions = sizeof(gExtensionMap) / sizeof(gExtensionMap[0]);
117 for (UINT32 i = 0; i < numExtensions; ++i)
118 {
119 if(strcmp(name.c_str(), gExtensionMap[i].name) == 0)
120 {
121 if(gExtensionMap[i].status != nullptr)
122 {
123 if (gExtensionMap[i].func != nullptr)
124 *gExtensionMap[i].status = gExtensionMap[i].func();
125 else
126 *gExtensionMap[i].status = true;
127 }
128 else
129 {
130 if (gExtensionMap[i].func != nullptr)
131 gExtensionMap[i].func();
132 }
133 }
134
135 }
136
137 } while(*iter++);
138
139 glXChooseFBConfig = (glXChooseFBConfigProc)glXGetProcAddressARB((const GLubyte*)"glXChooseFBConfig");
140 glXGetFBConfigAttrib = (glXGetFBConfigAttribProc)glXGetProcAddressARB((const GLubyte*)"glXGetFBConfigAttrib");
141 glXGetVisualFromFBConfig = (glXGetVisualFromFBConfigProc)glXGetProcAddressARB((const GLubyte*)"glXGetVisualFromFBConfig");
142
143 LinuxPlatform::unlockX();
144 }
145
146 void LinuxGLSupport::stop()
147 {
148 // Do nothing
149 }
150
151 SPtr<LinuxContext> LinuxGLSupport::createContext(::Display* x11display, XVisualInfo& visualInfo)
152 {
153 GLRenderAPI* rapi = static_cast<GLRenderAPI*>(RenderAPI::instancePtr());
154
155 // If RenderAPI has initialized a context use that, otherwise we create our own
156 if (!rapi->_isContextInitialized())
157 return bs_shared_ptr_new<LinuxContext>(x11display, visualInfo);
158 else
159 {
160 SPtr<GLContext> context = rapi->_getMainContext();
161 return std::static_pointer_cast<LinuxContext>(context);
162 }
163 }
164
165 void* LinuxGLSupport::getProcAddress(const String& procname)
166 {
167 return (void*)glXGetProcAddressARB((const GLubyte*)procname.c_str());
168 }
169
170 GLVisualConfig LinuxGLSupport::findBestVisual(::Display* display, bool depthStencil, UINT32 multisample, bool srgb) const
171 {
172 INT32 VISUAL_ATTRIBS[] =
173 {
174 GLX_X_RENDERABLE, True,
175 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
176 GLX_RENDER_TYPE, GLX_RGBA_BIT,
177 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
178 GLX_RED_SIZE, 8,
179 GLX_GREEN_SIZE, 8,
180 GLX_BLUE_SIZE, 8,
181 GLX_ALPHA_SIZE, 8,
182 GLX_DOUBLEBUFFER, True,
183 GLX_DEPTH_SIZE, depthStencil ? 24 : 0,
184 GLX_STENCIL_SIZE, depthStencil ? 8 : 0,
185 GLX_SAMPLE_BUFFERS, multisample > 1 ? 1 : 0,
186 0
187 };
188
189 INT32 numConfigs;
190 GLXFBConfig* configs = glXChooseFBConfig(display, DefaultScreen(display), VISUAL_ATTRIBS, &numConfigs);
191 GLVisualCapabilities* caps = bs_stack_new<GLVisualCapabilities>(numConfigs);
192
193 // Find a config that best matches the requested properties
194 INT32 bestScore = 0;
195 INT32 bestConfig = -1;
196 for (int i = 0; i < numConfigs; ++i)
197 {
198 INT32 configScore = 0;
199
200 // Depth buffer contributes the most to score
201 INT32 depth, stencil;
202 glXGetFBConfigAttrib(display, configs[i], GLX_DEPTH_SIZE, &depth);
203 glXGetFBConfigAttrib(display, configs[i], GLX_STENCIL_SIZE, &stencil);
204
205 // Depth buffer was requested
206 if(depthStencil)
207 {
208 INT32 score = 0;
209 if (depth == 24 && stencil == 8)
210 score = 10000;
211 else if (depth == 32 && stencil == 8)
212 score = 9000;
213 else if (depth == 32)
214 score = 8000;
215 else if (depth == 24)
216 score = 7000;
217 else if (depth == 16)
218 score = 6000;
219
220 if (score > 0)
221 {
222 configScore += score;
223 caps[i].depthStencil = true;
224 }
225 }
226 else // Depth buffer not requested, prefer configs without it
227 {
228 if(depth == 0 && stencil == 0)
229 configScore += 10000;
230 }
231
232 // sRGB contributes second most
233 if(srgb)
234 {
235 INT32 hasSRGB = 0;
236
237 if(extGLX_EXT_framebuffer_sRGB)
238 glXGetFBConfigAttrib(display, configs[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &hasSRGB);
239
240 if(!hasSRGB && extGLX_ARB_framebuffer_sRGB)
241 glXGetFBConfigAttrib(display, configs[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &hasSRGB);
242
243 if(hasSRGB)
244 {
245 configScore += 500;
246 caps[i].srgb = true;
247 }
248 }
249
250 if((multisample >= 1) && extGLX_ARB_multisample)
251 {
252 INT32 hasMultisample, numSamples;
253 glXGetFBConfigAttrib(display, configs[i], GLX_SAMPLE_BUFFERS, &hasMultisample);
254 glXGetFBConfigAttrib(display, configs[i], GLX_SAMPLES, &numSamples);
255
256 if(hasMultisample && (numSamples <= (INT32)multisample))
257 {
258 configScore += (32 - (multisample - numSamples)) * 10;
259 caps[i].numSamples = (UINT32)numSamples;
260 }
261 }
262
263 if(configScore > bestScore)
264 {
265 bestScore = configScore;
266 bestConfig = i;
267 }
268 }
269
270 GLVisualConfig output;
271
272 if(bestConfig == -1)
273 {
274 if(numConfigs > 0)
275 bestConfig = 0;
276 else
277 {
278 // Something went wrong
279 XFree(configs);
280 bs_stack_delete(caps, (UINT32) numConfigs);
281
282 return output;
283 }
284 }
285
286 XVisualInfo* visualInfo = glXGetVisualFromFBConfig(display, configs[bestConfig]);
287
288 output.visualInfo = *visualInfo;
289 output.caps = caps[bestConfig];
290
291 XFree(configs);
292 XFree(visualInfo);
293 bs_stack_delete(caps, numConfigs);
294
295 return output;
296 }
297
298 SPtr<VideoModeInfo> LinuxGLSupport::getVideoModeInfo() const
299 {
300 return bs_shared_ptr_new<LinuxVideoModeInfo>();
301 }
302}}