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