1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2018 SuperTuxKart-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 3
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #include "graphics/sp/sp_shader.hpp"
19 #include "config/user_config.hpp"
20 #include "graphics/central_settings.hpp"
21 #include "graphics/shader_files_manager.hpp"
22 #include "graphics/sp/sp_base.hpp"
23 #include "graphics/sp/sp_uniform_assigner.hpp"
24 #include "guiengine/message_queue.hpp"
25 #include "utils/string_utils.hpp"
26
27 #include <map>
28
29 namespace SP
30 {
31 std::map<std::string, std::pair<unsigned, SamplerType> >
32 SPShader::m_prefilled_names;
33 bool SPShader::m_sp_shader_debug = false;
34
SPShader(const std::string & name,const std::function<void (SPShader *)> & init_func,bool transparent_shader,int drawing_priority,bool use_alpha_channel,bool use_tangents,const std::array<bool,6> & srgb)35 SPShader::SPShader(const std::string& name,
36 const std::function<void(SPShader*)>& init_func,
37 bool transparent_shader, int drawing_priority,
38 bool use_alpha_channel, bool use_tangents,
39 const std::array<bool, 6>& srgb)
40 : m_name(name), m_init_function(init_func),
41 m_drawing_priority(drawing_priority),
42 m_transparent_shader(transparent_shader),
43 m_use_alpha_channel(use_alpha_channel),
44 m_use_tangents(use_tangents), m_srgb(srgb)
45 {
46 #ifndef SERVER_ONLY
47 if (CVS->isARBTextureBufferObjectUsable())
48 {
49 #ifndef USE_GLES2
50 m_prefilled_names["skinning_tex"] = std::make_pair<unsigned,
51 SamplerType>(0, ST_TEXTURE_BUFFER);
52 #endif
53 }
54 else
55 {
56 m_prefilled_names["skinning_tex"] = std::make_pair<unsigned,
57 SamplerType>(0, ST_NEAREST_CLAMPED);
58 }
59 #endif
60
61 memset(m_program, 0, 12);
62 m_init_function(this);
63 }
64 // ----------------------------------------------------------------------------
addShaderFile(const std::string & name,GLint shader_type,RenderPass rp)65 void SPShader::addShaderFile(const std::string& name, GLint shader_type,
66 RenderPass rp)
67 {
68 #ifndef SERVER_ONLY
69 if (m_program[rp] == 0)
70 {
71 m_program[rp] = glCreateProgram();
72 }
73 auto shader_file = ShaderFilesManager::getInstance()
74 ->getShaderFile(name, shader_type);
75 if (shader_file)
76 {
77 m_shader_files.push_back(shader_file);
78 glAttachShader(m_program[rp], *shader_file);
79 }
80 #endif
81 } // addShaderFile
82
83 // ----------------------------------------------------------------------------
linkShaderFiles(RenderPass rp)84 void SPShader::linkShaderFiles(RenderPass rp)
85 {
86 #ifndef SERVER_ONLY
87 glLinkProgram(m_program[rp]);
88 GLint result = GL_FALSE;
89 glGetProgramiv(m_program[rp], GL_LINK_STATUS, &result);
90 if (result == GL_FALSE)
91 {
92 Log::error("SPShader", "Error when linking shader %s in pass %d",
93 m_name.c_str(), (int)rp);
94 int info_length;
95 glGetProgramiv(m_program[rp], GL_INFO_LOG_LENGTH, &info_length);
96 char *error_message = new char[info_length];
97 glGetProgramInfoLog(m_program[rp], info_length, NULL, error_message);
98 Log::error("SPShader", error_message);
99 delete[] error_message;
100 }
101 // After linking all shaders can be detached
102 GLuint shaders[10] = {};
103 GLsizei count = 0;
104 glGetAttachedShaders(m_program[rp], 10, &count, shaders);
105 for (unsigned i = 0; i < (unsigned)count; i++)
106 {
107 glDetachShader(m_program[rp], shaders[i]);
108 }
109 if (result == GL_FALSE)
110 {
111 if (UserConfigParams::m_artist_debug_mode)
112 {
113 core::stringw err = StringUtils::insertValues(L"Shader %s failed"
114 " to link, check stdout.log or console for details",
115 m_name.c_str());
116 MessageQueue::add(MessageQueue::MT_ERROR, err);
117 }
118 glDeleteProgram(m_program[rp]);
119 m_program[rp] = 0;
120 }
121 #endif
122 } // linkShaderFiles
123
124 // ----------------------------------------------------------------------------
addAllTextures(RenderPass rp)125 void SPShader::addAllTextures(RenderPass rp)
126 {
127 #ifndef SERVER_ONLY
128 // Built-in prefilled shaders first
129 for (auto &p : m_prefilled_names)
130 {
131 const char* s = p.first.c_str();
132 GLuint loc = glGetUniformLocation(m_program[rp], s);
133 if (loc == GL_INVALID_INDEX)
134 {
135 continue;
136 }
137 const unsigned i = (unsigned)m_prefilled_samplers[rp].size();
138 glUniform1i(loc, i);
139 #ifdef USE_GLES2
140 m_prefilled_samplers[rp].emplace_back(i, p.first, p.second.second,
141 p.first == "tex_array" ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D);
142 #else
143 m_prefilled_samplers[rp].emplace_back(i, p.first, p.second.second,
144 p.second.second == ST_TEXTURE_BUFFER ?
145 GL_TEXTURE_BUFFER :
146 p.first == "tex_array" ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D);
147 #endif
148 }
149
150 // Add tex_layer_0-5 if exists in shader, sampler is always ST_TRILINEAR,
151 // texture type is always GL_TEXTURE_2D
152 for (unsigned i = 0; i < 6; i++)
153 {
154 std::string texture_name = "tex_layer_";
155 texture_name += StringUtils::toString(i);
156 GLuint loc = glGetUniformLocation(m_program[rp], texture_name.c_str());
157 if (loc == GL_INVALID_INDEX)
158 {
159 continue;
160 }
161 const unsigned idx =
162 unsigned(m_prefilled_samplers[rp].size() + m_samplers[rp].size());
163 glUniform1i(loc, idx);
164 m_samplers[rp][i] = idx;
165 }
166 #endif
167 } // addPrefilledTextures
168
169 // ----------------------------------------------------------------------------
addCustomPrefilledTextures(SamplerType st,GLuint texture_type,const std::string & name,std::function<GLuint ()> func,RenderPass rp)170 void SPShader::addCustomPrefilledTextures(SamplerType st, GLuint texture_type,
171 const std::string& name,
172 std::function<GLuint()> func,
173 RenderPass rp)
174 {
175 #ifndef SERVER_ONLY
176 assert(func != NULL);
177 const char* s = name.c_str();
178 GLuint loc = glGetUniformLocation(m_program[rp], s);
179 if (loc == GL_INVALID_INDEX)
180 {
181 Log::warn("SPShader", "Missing custom prefilled texture %s in shader"
182 " files.", s);
183 return;
184 }
185 const unsigned i =
186 unsigned(m_samplers[rp].size() + m_prefilled_samplers[rp].size());
187 glUniform1i(loc, i);
188 m_prefilled_samplers[rp].emplace_back(i, name, st, texture_type);
189 m_custom_prefilled_getter[rp][name] = func;
190 #endif
191 } // addCustomPrefilledTextures
192
193 // ----------------------------------------------------------------------------
bindPrefilledTextures(RenderPass rp) const194 void SPShader::bindPrefilledTextures(RenderPass rp) const
195 {
196 #ifndef SERVER_ONLY
197 for (auto& p : m_prefilled_samplers[rp])
198 {
199 glActiveTexture(GL_TEXTURE0 + std::get<0>(p));
200 auto it = m_prefilled_names.find(std::get<1>(p));
201 if (it != m_prefilled_names.end())
202 {
203 glBindTexture(std::get<3>(p), sp_prefilled_tex[it->second.first]);
204 glBindSampler(std::get<0>(p), getSampler(std::get<2>(p)));
205 }
206 else
207 {
208 glBindTexture(std::get<3>(p),
209 m_custom_prefilled_getter[rp].at(std::get<1>(p))());
210 glBindSampler(std::get<0>(p), getSampler(std::get<2>(p)));
211 }
212 }
213 #endif
214 } // bindPrefilledTextures
215
216 // ----------------------------------------------------------------------------
bindTextures(const std::array<GLuint,6> & tex,RenderPass rp) const217 void SPShader::bindTextures(const std::array<GLuint, 6>& tex,
218 RenderPass rp) const
219 {
220 #ifndef SERVER_ONLY
221 for (auto& p : m_samplers[rp])
222 {
223 glActiveTexture(GL_TEXTURE0 + p.second);
224 glBindTexture(GL_TEXTURE_2D, tex[p.first]);
225 glBindSampler(p.second, getSampler(ST_TRILINEAR));
226 }
227 #endif
228 } // bindTextures
229
230 // ----------------------------------------------------------------------------
addAllUniforms(RenderPass rp)231 void SPShader::addAllUniforms(RenderPass rp)
232 {
233 #ifndef SERVER_ONLY
234 GLint total_uniforms = 0;
235 glGetProgramiv(m_program[rp], GL_ACTIVE_UNIFORMS, &total_uniforms);
236 static const std::map<GLenum, std::type_index> supported_types =
237 {
238 { GL_INT, std::type_index(typeid(int)) },
239 { GL_FLOAT, std::type_index(typeid(float)) },
240 { GL_FLOAT_MAT4, std::type_index(typeid(irr::core::matrix4)) },
241 { GL_FLOAT_VEC4, std::type_index(typeid(std::array<float, 4>)) },
242 { GL_FLOAT_VEC3, std::type_index(typeid(irr::core::vector3df)) },
243 { GL_FLOAT_VEC2, std::type_index(typeid(irr::core::vector2df)) }
244 };
245
246 for (int i = 0; i < total_uniforms; i++)
247 {
248 GLint size;
249 GLenum type;
250 char name[100] = {};
251 glGetActiveUniform(m_program[rp], i, 99, NULL, &size, &type, name);
252 if (size != 1)
253 {
254 if (m_sp_shader_debug)
255 {
256 Log::debug("SPShader", "Array of uniforms is not supported in"
257 " shader %s for %s.", m_name.c_str(), name);
258 }
259 continue;
260 }
261 auto ret = supported_types.find(type);
262 if (ret == supported_types.end())
263 {
264 if (m_sp_shader_debug)
265 Log::debug("SPShader", "%d type not supported", (unsigned)type);
266 continue;
267 }
268 GLuint location = glGetUniformLocation(m_program[rp], name);
269 if (location == GL_INVALID_INDEX)
270 {
271 if (m_sp_shader_debug)
272 Log::debug("SPShader", "%s uniform not found", name);
273 continue;
274 }
275 m_uniforms[rp][name] = new SPUniformAssigner(ret->second, location);
276 }
277 #endif
278 } // addAllUniforms
279
280 // ----------------------------------------------------------------------------
setUniformsPerObject(SPPerObjectUniform * sppou,std::vector<SPUniformAssigner * > * ua_used,RenderPass rp)281 void SPShader::setUniformsPerObject(SPPerObjectUniform* sppou,
282 std::vector<SPUniformAssigner*>* ua_used,
283 RenderPass rp)
284 {
285 #ifndef SERVER_ONLY
286 if (sppou->isEmpty())
287 {
288 return;
289 }
290 for (auto& p : m_uniforms[rp])
291 {
292 if (sppou->assignUniform(p.first, p.second))
293 {
294 ua_used->push_back(p.second);
295 }
296 }
297 #endif
298 } // setUniformsPerObject
299
300 // ----------------------------------------------------------------------------
getUniformAssigner(const std::string & name,RenderPass rp) const301 SPUniformAssigner* SPShader::getUniformAssigner(const std::string& name,
302 RenderPass rp) const
303 {
304 auto ret = m_uniforms[rp].find(name);
305 if (ret == m_uniforms[rp].end())
306 {
307 return NULL;
308 }
309 return ret->second;
310 } // getUniformAssigner
311
312 // ----------------------------------------------------------------------------
unload()313 void SPShader::unload()
314 {
315 #ifndef SERVER_ONLY
316 for (unsigned rp = RP_1ST; rp < RP_COUNT; rp++)
317 {
318 if (m_program[rp] != 0)
319 {
320 glDeleteProgram(m_program[rp]);
321 m_program[rp] = 0;
322 }
323 for (auto& p : m_uniforms[rp])
324 {
325 delete p.second;
326 }
327 m_uniforms[rp].clear();
328 m_custom_prefilled_getter[rp].clear();
329 m_prefilled_samplers[rp].clear();
330 m_samplers[rp].clear();
331 m_use_function[rp] = nullptr;
332 m_unuse_function[rp] = nullptr;
333 }
334 m_shader_files.clear();
335 #endif
336 } // unload
337
338 // ----------------------------------------------------------------------------
isSrgbForTextureLayer(unsigned layer) const339 bool SPShader::isSrgbForTextureLayer(unsigned layer) const
340 {
341 #ifndef SERVER_ONLY
342 if (!CVS->isDeferredEnabled())
343 {
344 return false;
345 }
346 #endif
347 assert(layer < 6);
348 return m_srgb[layer];
349 } // isSrgbForTextureLayer
350
351 }
352