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 | |
10 | namespace 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 | }} |