1 /** @file sys_opengl.cpp
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2007-2015 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 2006 Jamie Jones <yagisan@dengine.net>
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, see:
18 * http://www.gnu.org/licenses</small>
19 */
20
21 #include "de_base.h"
22 #include "gl/sys_opengl.h"
23
24 #include <QSet>
25 #include <QStringList>
26 #include <de/libcore.h>
27 #include <de/concurrency.h>
28 #include <de/GLInfo>
29 #include <de/GLState>
30 #include "sys_system.h"
31 #include "gl/gl_main.h"
32
33 #ifdef WIN32
34 # define GETPROC(Type, x) x = de::function_cast<Type>(wglGetProcAddress(#x))
35 #elif defined (DENG_X11)
36 # include <GL/glx.h>
37 # undef None
38 # define GETPROC(Type, x) x = de::function_cast<Type>(glXGetProcAddress((GLubyte const *)#x))
39 #endif
40
41 gl_state_t GL_state;
42
43 static dd_bool doneEarlyInit = false;
44 static dd_bool sysOpenGLInited = false;
45 static dd_bool firstTimeInit = true;
46
initialize(void)47 static void initialize(void)
48 {
49 de::GLInfo::Extensions const &ext = de::GLInfo::extensions();
50
51 if(CommandLine_Exists("-noanifilter"))
52 {
53 GL_state.features.texFilterAniso = false;
54 }
55
56 if (CommandLine_Exists("-texcomp") && !CommandLine_Exists("-notexcomp"))
57 {
58 GL_state.features.texCompression = true;
59 }
60 #ifdef USE_TEXTURE_COMPRESSION_S3
61 // Enabled by default if available.
62 if (ext.EXT_texture_compression_s3tc)
63 {
64 GLint iVal;
65 LIBGUI_GL.glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &iVal);
66 LIBGUI_ASSERT_GL_OK();
67 if (iVal == 0)// || LIBGUI_GL.glGetError() != GL_NO_ERROR)
68 GL_state.features.texCompression = false;
69 }
70 #else
71 GL_state.features.texCompression = false;
72 #endif
73 }
74
75 #define TABBED(A, B) _E(Ta) " " _E(l) A _E(.) " " _E(Tb) << B << "\n"
76
Sys_GLDescription()77 de::String Sys_GLDescription()
78 {
79 DENG_ASSERT_IN_MAIN_THREAD();
80 DENG_ASSERT_GL_CONTEXT_ACTIVE();
81
82 de::String str;
83 QTextStream os(&str);
84
85 os << _E(b) "OpenGL information:\n" << _E(.);
86
87 os << TABBED("Version:", (char const *) LIBGUI_GL.glGetString(GL_VERSION));
88 os << TABBED("Renderer:", (char const *) LIBGUI_GL.glGetString(GL_RENDERER));
89 os << TABBED("Vendor:", (char const *) LIBGUI_GL.glGetString(GL_VENDOR));
90
91 LIBGUI_ASSERT_GL_OK();
92
93 os << _E(T`) "Capabilities:\n";
94
95 GLint iVal;
96
97 #ifdef USE_TEXTURE_COMPRESSION_S3
98 if(de::GLInfo::extensions().EXT_texture_compression_s3tc)
99 {
100 LIBGUI_GL.glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &iVal);
101 LIBGUI_ASSERT_GL_OK();
102 os << TABBED("Compressed texture formats:", iVal);
103 }
104 #endif
105
106 os << TABBED("Use texture compression:", (GL_state.features.texCompression? "yes" : "no"));
107
108 os << TABBED("Available texture units:", de::GLInfo::limits().maxTexUnits);
109
110 if(de::GLInfo::extensions().EXT_texture_filter_anisotropic)
111 {
112 os << TABBED("Maximum texture anisotropy:", de::GLInfo::limits().maxTexFilterAniso);
113 }
114 else
115 {
116 os << _E(Ta) " Variable texture anisotropy unavailable.";
117 }
118
119 os << TABBED("Maximum texture size:", de::GLInfo::limits().maxTexSize);
120
121 os << TABBED("Line width granularity:", de::GLInfo::limits().smoothLineWidthGranularity);
122
123 os << TABBED("Line width range:",
124 de::GLInfo::limits().smoothLineWidth.start << "..." <<
125 de::GLInfo::limits().smoothLineWidth.end);
126
127 return str.rightStrip();
128
129 #undef TABBED
130 }
131
printGLUInfo(void)132 static void printGLUInfo(void)
133 {
134 LOG_GL_MSG("%s") << Sys_GLDescription();
135
136 Sys_GLPrintExtensions();
137 }
138
Sys_GLPreInit(void)139 dd_bool Sys_GLPreInit(void)
140 {
141 if(novideo) return true;
142 if(doneEarlyInit) return true; // Already been here??
143
144 GL_state.features.texCompression = false;
145 GL_state.features.texFilterAniso = true;
146 GL_state.currentLineWidth = 1.5f;
147 GL_state.currentPointSize = 1.5f;
148
149 doneEarlyInit = true;
150 return true;
151 }
152
Sys_GLInitialize(void)153 dd_bool Sys_GLInitialize(void)
154 {
155 if(novideo) return true;
156
157 LOG_AS("Sys_GLInitialize");
158
159 assert(doneEarlyInit);
160
161 DENG_ASSERT_IN_MAIN_THREAD();
162 DENG_ASSERT_GL_CONTEXT_ACTIVE();
163
164 assert(!Sys_GLCheckError());
165
166 if(firstTimeInit)
167 {
168 #if defined (DENG_OPENGL)
169 const GLubyte* versionStr = LIBGUI_GL.glGetString(GL_VERSION);
170 double version = (versionStr? strtod((const char*) versionStr, NULL) : 0);
171 if(version == 0)
172 {
173 LOG_GL_WARNING("Failed to determine OpenGL version; driver reports: %s")
174 << LIBGUI_GL.glGetString(GL_VERSION);
175 }
176 else if(version < 3.3)
177 {
178 if(!CommandLine_Exists("-noglcheck"))
179 {
180 Sys_CriticalMessagef("Your OpenGL is too old!\n"
181 " Driver version: %s\n"
182 " The minimum supported version is 2.0",
183 LIBGUI_GL.glGetString(GL_VERSION));
184 return false;
185 }
186 else
187 {
188 LOG_GL_WARNING("OpenGL may be too old (3.3+ required, "
189 "but driver reports %s)") << LIBGUI_GL.glGetString(GL_VERSION);
190 }
191 }
192 #endif
193
194 initialize();
195 printGLUInfo();
196
197 firstTimeInit = false;
198 }
199
200 // GL system is now fully initialized.
201 sysOpenGLInited = true;
202
203 /**
204 * We can now (re)configure GL state that is dependent upon extensions
205 * which may or may not be present on the host system.
206 */
207
208 // Use nice quality for mipmaps please.
209 //if(GL_state.features.genMipmap && de::GLInfo::extensions().SGIS_generate_mipmap)
210 //LIBGUI_GL.glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
211
212 assert(!Sys_GLCheckError());
213
214 return true;
215 }
216
Sys_GLShutdown(void)217 void Sys_GLShutdown(void)
218 {
219 if(!sysOpenGLInited) return;
220 // No cleanup.
221 sysOpenGLInited = false;
222 }
223
Sys_GLConfigureDefaultState(void)224 void Sys_GLConfigureDefaultState(void)
225 {
226 LIBGUI_ASSERT_GL_OK();
227
228 GLfloat fogcol[4] = { .54f, .54f, .54f, 1 };
229
230 /**
231 * @note Only core OpenGL features can be configured at this time
232 * because we have not yet queried for available extensions,
233 * or configured our prefered feature default state.
234 *
235 * This means that GL_state.extensions and GL_state.features
236 * cannot be accessed here during initial startup.
237 */
238 assert(doneEarlyInit);
239
240 DENG_ASSERT_IN_MAIN_THREAD();
241 DENG_ASSERT_GL_CONTEXT_ACTIVE();
242
243 LIBGUI_GL.glFrontFace(GL_CW);
244 LIBGUI_ASSERT_GL_OK();
245
246 DGL_CullFace(DGL_NONE);
247 DGL_Disable(DGL_DEPTH_TEST);
248 DGL_DepthFunc(DGL_LESS);
249
250 DGL_Disable(DGL_TEXTURE_2D);
251
252 #if defined (DENG_OPENGL)
253 LIBGUI_GL.glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
254 LIBGUI_ASSERT_GL_OK();
255 #endif
256
257 // The projection matrix.
258 DGL_MatrixMode(DGL_PROJECTION);
259 DGL_LoadIdentity();
260
261 // Initialize the modelview matrix.
262 DGL_MatrixMode(DGL_MODELVIEW);
263 DGL_LoadIdentity();
264
265 // Also clear the texture matrix.
266 DGL_MatrixMode(DGL_TEXTURE);
267 DGL_LoadIdentity();
268
269 // de::GLInfo::setLineWidth(GL_state.currentLineWidth);
270
271 #if defined (DENG_OPENGL)
272 // Setup for antialiased lines/points.
273 LIBGUI_GL.glEnable(GL_LINE_SMOOTH);
274 LIBGUI_ASSERT_GL_OK();
275 LIBGUI_GL.glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
276 LIBGUI_ASSERT_GL_OK();
277
278 LIBGUI_GL.glPointSize(GL_state.currentPointSize);
279 LIBGUI_ASSERT_GL_OK();
280
281 // Prefer good quality in texture compression.
282 LIBGUI_GL.glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
283 LIBGUI_ASSERT_GL_OK();
284 #endif
285
286 // Default state for the white fog is off.
287 DGL_Disable(DGL_FOG);
288 DGL_Fogi(DGL_FOG_MODE, GL_LINEAR);
289 DGL_Fogi(DGL_FOG_END, 2100); // This should be tweaked a bit.
290 DGL_Fogfv(DGL_FOG_COLOR, fogcol);
291
292 LIBGUI_ASSERT_GL_OK();
293
294 // Configure the default GLState (bottom of the stack).
295 DGL_BlendFunc(DGL_SRC_ALPHA, DGL_ONE_MINUS_SRC_ALPHA);
296 }
297
omitGLPrefix(de::String str)298 static de::String omitGLPrefix(de::String str)
299 {
300 if(str.startsWith("GL_")) return str.substr(3);
301 return str;
302 }
303
printExtensions(QStringList extensions)304 static void printExtensions(QStringList extensions)
305 {
306 qSort(extensions);
307
308 // Find all the prefixes.
309 QSet<QString> prefixes;
310 foreach(QString ext, extensions)
311 {
312 ext = omitGLPrefix(ext);
313 int pos = ext.indexOf("_");
314 if(pos > 0)
315 {
316 prefixes.insert(ext.left(pos));
317 }
318 }
319
320 QStringList sortedPrefixes = prefixes.toList();
321 qSort(sortedPrefixes);
322 foreach(QString prefix, sortedPrefixes)
323 {
324 de::String str;
325 QTextStream os(&str);
326
327 os << " " << prefix << " extensions:\n " _E(>) _E(2);
328
329 bool first = true;
330 foreach(QString ext, extensions)
331 {
332 ext = omitGLPrefix(ext);
333 if(ext.startsWith(prefix + "_"))
334 {
335 ext.remove(0, prefix.size() + 1);
336 if(!first) os << ", ";
337 os << ext;
338 first = false;
339 }
340 }
341
342 LOG_GL_MSG("%s") << str;
343 }
344 }
345
Sys_GLPrintExtensions(void)346 void Sys_GLPrintExtensions(void)
347 {
348 LOG_GL_MSG(_E(b) "OpenGL Extensions:");
349 QStringList exts;
350 foreach (QByteArray extName, QOpenGLContext::currentContext()->extensions())
351 {
352 exts << extName;
353 }
354 printExtensions(exts);
355
356 /*
357 #if WIN32
358 // List the WGL extensions too.
359 if(wglGetExtensionsStringARB)
360 {
361 LOG_GL_MSG(" Extensions (WGL):");
362 printExtensions(QString((char const *) ((GLubyte const *(__stdcall *)(HDC))wglGetExtensionsStringARB)(wglGetCurrentDC())).split(" ", QString::SkipEmptyParts));
363 }
364 #endif
365
366 #ifdef Q_WS_X11
367 // List GLX extensions.
368 LOG_GL_MSG(" Extensions (GLX):");
369 printExtensions(QString(getGLXExtensionsString()).split(" ", QString::SkipEmptyParts));
370 #endif
371 */
372 }
373
Sys_GLCheckErrorArgs(char const * file,int line)374 dd_bool Sys_GLCheckErrorArgs(char const *file, int line)
375 {
376 if (novideo) return false;
377 #ifdef DENG_DEBUG
378 de::GLInfo::checkError(file, line);
379 #endif
380 return false;
381 }
382