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