1 /*
2 * Copyright (C) 2006-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20 #include "graphic/gl/initialize.h"
21
22 #include <csignal>
23
24 #include <SDL_messagebox.h>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/regex.hpp>
27
28 #include "base/i18n.h"
29 #include "base/macros.h"
30 #include "graphic/gl/utils.h"
31 #include "graphic/text/bidi.h"
32
33 namespace Gl {
34
initialize(const Trace & trace,SDL_Window * sdl_window,GLint * max_texture_size)35 SDL_GLContext initialize(
36 #ifdef USE_GLBINDING
37 const Trace& trace,
38 #else
39 const Trace&,
40 #endif
41 SDL_Window* sdl_window,
42 GLint* max_texture_size) {
43 // Request an OpenGL 2 context with double buffering.
44 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
45 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
46 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
47 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
48 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
49
50 SDL_GLContext gl_context = SDL_GL_CreateContext(sdl_window);
51 SDL_GL_MakeCurrent(sdl_window, gl_context);
52
53 #ifdef USE_GLBINDING
54 #ifndef GLBINDING3
55 glbinding::Binding::initialize();
56
57 // The undocumented command line argument --debug_gl_trace will set
58 // Trace::kYes. This will log every OpenGL call that is made, together with
59 // arguments, return values and glError status. This requires that Widelands
60 // is built using -DOPTION_USE_GLBINDING:BOOL=ON. It is a NoOp for GLEW.
61 if (trace == Trace::kYes) {
62 setCallbackMaskExcept(
63 glbinding::CallbackMask::After | glbinding::CallbackMask::ParametersAndReturnValue,
64 {"glGetError"});
65 glbinding::setAfterCallback([](const glbinding::FunctionCall& call) {
66 log("%s(", call.function->name());
67 for (size_t i = 0; i < call.parameters.size(); ++i) {
68 log("%s", call.parameters[i]->asString().c_str());
69 if (i < call.parameters.size() - 1)
70 log(", ");
71 }
72 log(")");
73 if (call.returnValue) {
74 log(" -> %s", call.returnValue->asString().c_str());
75 }
76 const auto error = glGetError();
77 log(" [%s]\n", gl_error_to_string(error));
78 // The next few lines will terminate Widelands if there was any OpenGL
79 // error. This is useful for super aggressive debugging, but probably
80 // not for regular builds. Comment it in if you need to understand
81 // OpenGL problems.
82 // if (error != GL_NO_ERROR) {
83 // std::raise(SIGINT);
84 // }
85 });
86 }
87 #else
88 const glbinding::GetProcAddress get_proc_address = [](const char* name) {
89 return reinterpret_cast<glbinding::ProcAddress>(SDL_GL_GetProcAddress(name));
90 };
91 glbinding::Binding::initialize(get_proc_address, true);
92
93 // The undocumented command line argument --debug_gl_trace will set
94 // Trace::kYes. This will log every OpenGL call that is made, together with
95 // arguments, return values and glError status. This requires that Widelands
96 // is built using -DOPTION_USE_GLBINDING:BOOL=ON. It is a NoOp for GLEW.
97 if (trace == Trace::kYes) {
98 glbinding::setCallbackMaskExcept(
99 glbinding::CallbackMask::After | glbinding::CallbackMask::ParametersAndReturnValue,
100 {"glGetError"});
101 glbinding::setAfterCallback([](const glbinding::FunctionCall& call) {
102 log("%s(", call.function->name());
103 for (size_t i = 0; i < call.parameters.size(); ++i) {
104 FORMAT_WARNINGS_OFF
105 log("%p", call.parameters[i].get());
106 FORMAT_WARNINGS_ON
107 if (i < call.parameters.size() - 1)
108 log(", ");
109 }
110 log(")");
111 if (call.returnValue) {
112 FORMAT_WARNINGS_OFF
113 log(" -> %p", call.returnValue.get());
114 FORMAT_WARNINGS_ON
115 }
116 const auto error = glGetError();
117 log(" [%s]\n", gl_error_to_string(error));
118 // The next few lines will terminate Widelands if there was any OpenGL
119 // error. This is useful for super aggressive debugging, but probably
120 // not for regular builds. Comment it in if you need to understand
121 // OpenGL problems.
122 // if (error != GL_NO_ERROR) {
123 // std::raise(SIGINT);
124 // }
125 });
126 }
127 #endif
128 #else
129 // See graphic/gl/system_headers.h for an explanation of the next line.
130 glewExperimental = GL_TRUE;
131 GLenum err = glewInit();
132 // LeakSanitizer reports a memory leak which is triggered somewhere above this line, probably
133 // coming from the gaphics drivers
134
135 if (err != GLEW_OK) {
136 log("glewInit returns %i\nYour OpenGL installation must be __very__ broken. %s\n", err,
137 glewGetErrorString(err));
138 throw wexception("glewInit returns %i: Broken OpenGL installation.", err);
139 }
140 #endif
141
142 log(
143 "Graphics: OpenGL: Version \"%s\"\n", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
144
145 #define LOG_SDL_GL_ATTRIBUTE(x) \
146 { \
147 int value; \
148 SDL_GL_GetAttribute(x, &value); \
149 log("Graphics: %s is %d\n", #x, value); \
150 }
151
152 LOG_SDL_GL_ATTRIBUTE(SDL_GL_RED_SIZE)
153 LOG_SDL_GL_ATTRIBUTE(SDL_GL_GREEN_SIZE)
154 LOG_SDL_GL_ATTRIBUTE(SDL_GL_BLUE_SIZE)
155 LOG_SDL_GL_ATTRIBUTE(SDL_GL_ALPHA_SIZE)
156 LOG_SDL_GL_ATTRIBUTE(SDL_GL_BUFFER_SIZE)
157 LOG_SDL_GL_ATTRIBUTE(SDL_GL_DOUBLEBUFFER)
158 LOG_SDL_GL_ATTRIBUTE(SDL_GL_DEPTH_SIZE)
159 LOG_SDL_GL_ATTRIBUTE(SDL_GL_STENCIL_SIZE)
160 LOG_SDL_GL_ATTRIBUTE(SDL_GL_ACCUM_RED_SIZE)
161 LOG_SDL_GL_ATTRIBUTE(SDL_GL_ACCUM_GREEN_SIZE)
162 LOG_SDL_GL_ATTRIBUTE(SDL_GL_ACCUM_BLUE_SIZE)
163 LOG_SDL_GL_ATTRIBUTE(SDL_GL_ACCUM_ALPHA_SIZE)
164 LOG_SDL_GL_ATTRIBUTE(SDL_GL_STEREO)
165 LOG_SDL_GL_ATTRIBUTE(SDL_GL_MULTISAMPLEBUFFERS)
166 LOG_SDL_GL_ATTRIBUTE(SDL_GL_MULTISAMPLESAMPLES)
167 LOG_SDL_GL_ATTRIBUTE(SDL_GL_ACCELERATED_VISUAL)
168 LOG_SDL_GL_ATTRIBUTE(SDL_GL_CONTEXT_MAJOR_VERSION)
169 LOG_SDL_GL_ATTRIBUTE(SDL_GL_CONTEXT_MINOR_VERSION)
170 LOG_SDL_GL_ATTRIBUTE(SDL_GL_CONTEXT_FLAGS)
171 LOG_SDL_GL_ATTRIBUTE(SDL_GL_CONTEXT_PROFILE_MASK)
172 LOG_SDL_GL_ATTRIBUTE(SDL_GL_SHARE_WITH_CURRENT_CONTEXT)
173 LOG_SDL_GL_ATTRIBUTE(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE)
174 #undef LOG_SDL_GL_ATTRIBUTE
175
176 GLboolean glBool;
177 glGetBooleanv(GL_DOUBLEBUFFER, &glBool);
178 log("Graphics: OpenGL: Double buffering %s\n", (glBool == GL_TRUE) ? "enabled" : "disabled");
179
180 glGetIntegerv(GL_MAX_TEXTURE_SIZE, max_texture_size);
181 log("Graphics: OpenGL: Max texture size: %u\n", *max_texture_size);
182
183 const char* const shading_language_version_string =
184 reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
185 log("Graphics: OpenGL: ShadingLanguage: \"%s\"\n", shading_language_version_string);
186
187 // Show a basic SDL window with an error message, and log it too, then exit 1. Since font support
188 // does not exist for all languages, we show both the original and a localized text.
189 auto show_opengl_error_and_exit = [](
190 const std::string& message, const std::string& localized_message) {
191 std::string display_message = "";
192 if (message != localized_message) {
193 display_message =
194 message + "\n\n" +
195 (i18n::has_rtl_character(localized_message.c_str()) ?
196 i18n::line2bidi(i18n::make_ligatures(localized_message.c_str()).c_str()) :
197 localized_message);
198 } else {
199 display_message = message;
200 }
201
202 /** TRANSLATORS: Error message printed to console/command line/log file */
203 log(_("ERROR: %s\n"), display_message.c_str());
204 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL Error", display_message.c_str(), NULL);
205 exit(1);
206 };
207
208 // Exit because we couldn't detect the shading language version, so there must be a problem
209 // communicating with the graphics adapter.
210 auto handle_unreadable_opengl_shading_language = [show_opengl_error_and_exit]() {
211 show_opengl_error_and_exit(
212 "Widelands won't work because we were unable to detect the shading language version.\n"
213 "There is an unknown problem with reading the information from the graphics driver.",
214 (boost::format("%s\n%s") %
215 /** TRANSLATORS: Basic error message when we can't handle the graphics driver. Font
216 support is limited here, so do not use advanced typography **/
217 _("Widelands won't work because we were unable to detect the shading language "
218 "version.") %
219 /** TRANSLATORS: Basic error message when we can't handle the graphics driver. Font
220 support is limited here, so do not use advanced typography **/
221 _("There is an unknown problem with reading the information from the graphics "
222 "driver."))
223 .str());
224 };
225
226 // glGetString returned an error for the shading language
227 if (glGetString(GL_SHADING_LANGUAGE_VERSION) == 0) {
228 handle_unreadable_opengl_shading_language();
229 }
230
231 std::vector<std::string> shading_language_version_vector;
232 boost::split(
233 shading_language_version_vector, shading_language_version_string, boost::is_any_of("."));
234 if (shading_language_version_vector.size() >= 2) {
235 // The shading language version has been detected properly. Exit if the shading language
236 // version is too old.
237 const int major_shading_language_version =
238 atoi(shading_language_version_vector.front().c_str());
239 const int minor_shading_language_version =
240 atoi(shading_language_version_vector.at(1).c_str());
241 if (major_shading_language_version < 1 ||
242 (major_shading_language_version == 1 && minor_shading_language_version < 20)) {
243 show_opengl_error_and_exit(
244 "Widelands won’t work because your graphics driver is too old.\n"
245 "The shading language needs to be version 1.20 or newer.",
246 (boost::format("%s\n%s") %
247 /** TRANSLATORS: Basic error message when we can't handle the graphics driver. Font
248 support is limited here, so do not use advanced typography **/
249 _("Widelands won’t work because your graphics driver is too old.") %
250 /** TRANSLATORS: Basic error message when we can't handle the graphics driver. Font
251 support is limited here, so do not use advanced typography **/
252 _("The shading language needs to be version 1.20 or newer."))
253 .str());
254 }
255 } else {
256 // We don't have a minor version. Ensure that the string to compare is a valid integer before
257 // conversion
258 boost::regex re("\\d+");
259 if (boost::regex_match(shading_language_version_string, re)) {
260 const int major_shading_language_version = atoi(shading_language_version_string);
261 if (major_shading_language_version < 2) {
262 show_opengl_error_and_exit(
263 "Widelands won’t work because your graphics driver is too old.\n"
264 "The shading language needs to be version 1.20 or newer.",
265 (boost::format("%s\n%s") %
266 /** TRANSLATORS: Basic error message when we can't handle the graphics driver. Font
267 support is limited here, so do not use advanced typography **/
268 _("Widelands won’t work because your graphics driver is too old.") %
269 /** TRANSLATORS: Basic error message when we can't handle the graphics driver. Font
270 support is limited here, so do not use advanced typography **/
271 _("The shading language needs to be version 1.20 or newer."))
272 .str());
273 }
274 } else {
275 // We don't know how to interpret the shading language info
276 handle_unreadable_opengl_shading_language();
277 }
278 }
279
280 glDrawBuffer(GL_BACK);
281
282 glDisable(GL_DEPTH_TEST);
283 glDepthFunc(GL_LEQUAL);
284
285 glEnable(GL_BLEND);
286 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
287
288 glClear(GL_COLOR_BUFFER_BIT);
289
290 return gl_context;
291 }
292
293 } // namespace Gl
294