1 #include "libslic3r/libslic3r.h"
2 #include "GLShader.hpp"
3
4 #include "3DScene.hpp"
5 #include "libslic3r/Utils.hpp"
6 #include "libslic3r/format.hpp"
7
8 #include <boost/nowide/fstream.hpp>
9 #include <GL/glew.h>
10 #include <cassert>
11
12 #include <boost/log/trivial.hpp>
13
14 namespace Slic3r {
15
~GLShaderProgram()16 GLShaderProgram::~GLShaderProgram()
17 {
18 if (m_id > 0)
19 glsafe(::glDeleteProgram(m_id));
20 }
21
init_from_files(const std::string & name,const ShaderFilenames & filenames,const std::initializer_list<std::string_view> & defines)22 bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilenames& filenames, const std::initializer_list<std::string_view> &defines)
23 {
24 // Load a shader program from file, prepend defs block.
25 auto load_from_file = [](const std::string& filename, const std::string &defs) {
26 std::string path = resources_dir() + "/shaders/" + filename;
27 boost::nowide::ifstream s(path, boost::nowide::ifstream::binary);
28 if (!s.good()) {
29 BOOST_LOG_TRIVIAL(error) << "Couldn't open file: '" << path << "'";
30 return std::string();
31 }
32
33 s.seekg(0, s.end);
34 int file_length = static_cast<int>(s.tellg());
35 s.seekg(0, s.beg);
36 std::string source(defs.size() + file_length, '\0');
37 memcpy(source.data(), defs.c_str(), defs.size());
38 s.read(source.data() + defs.size(), file_length);
39 if (!s.good()) {
40 BOOST_LOG_TRIVIAL(error) << "Error while loading file: '" << path << "'";
41 return std::string();
42 }
43 s.close();
44
45 if (! defs.empty()) {
46 // Extract the version and flip the order of "defines" and version in the source block.
47 size_t idx = source.find("\n", defs.size());
48 if (idx != std::string::npos && strncmp(source.c_str() + defs.size(), "#version", 8) == 0) {
49 // Swap the version line with the defines.
50 size_t len = idx - defs.size() + 1;
51 memmove(source.data(), source.c_str() + defs.size(), len);
52 memcpy(source.data() + len, defs.c_str(), defs.size());
53 }
54 }
55
56 return source;
57 };
58
59 // Create a block of C "defines" from list of symbols.
60 std::string defines_program;
61 for (std::string_view def : defines)
62 // Our shaders are stored with "\r\n", thus replicate the same here for consistency. Likely "\n" would suffice,
63 // but we don't know all the OpenGL shader compilers around.
64 defines_program += format("#define %s\r\n", def);
65
66 ShaderSources sources = {};
67 for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
68 sources[i] = filenames[i].empty() ? std::string() : load_from_file(filenames[i], defines_program);
69 }
70
71 bool valid = !sources[static_cast<size_t>(EShaderType::Vertex)].empty() && !sources[static_cast<size_t>(EShaderType::Fragment)].empty() && sources[static_cast<size_t>(EShaderType::Compute)].empty();
72 valid |= !sources[static_cast<size_t>(EShaderType::Compute)].empty() && sources[static_cast<size_t>(EShaderType::Vertex)].empty() && sources[static_cast<size_t>(EShaderType::Fragment)].empty() &&
73 sources[static_cast<size_t>(EShaderType::Geometry)].empty() && sources[static_cast<size_t>(EShaderType::TessEvaluation)].empty() && sources[static_cast<size_t>(EShaderType::TessControl)].empty();
74
75 return valid ? init_from_texts(name, sources) : false;
76 }
77
init_from_texts(const std::string & name,const ShaderSources & sources)78 bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSources& sources)
79 {
80 auto shader_type_as_string = [](EShaderType type) {
81 switch (type)
82 {
83 case EShaderType::Vertex: { return "vertex"; }
84 case EShaderType::Fragment: { return "fragment"; }
85 case EShaderType::Geometry: { return "geometry"; }
86 case EShaderType::TessEvaluation: { return "tesselation evaluation"; }
87 case EShaderType::TessControl: { return "tesselation control"; }
88 case EShaderType::Compute: { return "compute"; }
89 default: { return "unknown"; }
90 }
91 };
92
93 auto create_shader = [](EShaderType type) {
94 GLuint id = 0;
95 switch (type)
96 {
97 case EShaderType::Vertex: { id = ::glCreateShader(GL_VERTEX_SHADER); glcheck(); break; }
98 case EShaderType::Fragment: { id = ::glCreateShader(GL_FRAGMENT_SHADER); glcheck(); break; }
99 case EShaderType::Geometry: { id = ::glCreateShader(GL_GEOMETRY_SHADER); glcheck(); break; }
100 case EShaderType::TessEvaluation: { id = ::glCreateShader(GL_TESS_EVALUATION_SHADER); glcheck(); break; }
101 case EShaderType::TessControl: { id = ::glCreateShader(GL_TESS_CONTROL_SHADER); glcheck(); break; }
102 case EShaderType::Compute: { id = ::glCreateShader(GL_COMPUTE_SHADER); glcheck(); break; }
103 default: { break; }
104 }
105
106 return (id == 0) ? std::make_pair(false, GLuint(0)) : std::make_pair(true, id);
107 };
108
109 auto release_shaders = [](const std::array<GLuint, static_cast<size_t>(EShaderType::Count)>& shader_ids) {
110 for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
111 if (shader_ids[i] > 0)
112 glsafe(::glDeleteShader(shader_ids[i]));
113 }
114 };
115
116 assert(m_id == 0);
117
118 m_name = name;
119
120 std::array<GLuint, static_cast<size_t>(EShaderType::Count)> shader_ids = { 0 };
121
122 for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
123 const std::string& source = sources[i];
124 if (!source.empty())
125 {
126 EShaderType type = static_cast<EShaderType>(i);
127 auto [result, id] = create_shader(type);
128 if (result)
129 shader_ids[i] = id;
130 else {
131 BOOST_LOG_TRIVIAL(error) << "glCreateShader() failed for " << shader_type_as_string(type) << " shader of shader program '" << name << "'";
132
133 // release shaders
134 release_shaders(shader_ids);
135 return false;
136 }
137
138 const char* source_ptr = source.c_str();
139 glsafe(::glShaderSource(id, 1, &source_ptr, nullptr));
140 glsafe(::glCompileShader(id));
141 GLint params;
142 glsafe(::glGetShaderiv(id, GL_COMPILE_STATUS, ¶ms));
143 if (params == GL_FALSE) {
144 // Compilation failed.
145 glsafe(::glGetShaderiv(id, GL_INFO_LOG_LENGTH, ¶ms));
146 std::vector<char> msg(params);
147 glsafe(::glGetShaderInfoLog(id, params, ¶ms, msg.data()));
148 BOOST_LOG_TRIVIAL(error) << "Unable to compile " << shader_type_as_string(type) << " shader of shader program '" << name << "':\n" << msg.data();
149
150 // release shaders
151 release_shaders(shader_ids);
152 return false;
153 }
154 }
155 }
156
157 m_id = ::glCreateProgram();
158 glcheck();
159 if (m_id == 0) {
160 BOOST_LOG_TRIVIAL(error) << "glCreateProgram() failed for shader program '" << name << "'";
161
162 // release shaders
163 release_shaders(shader_ids);
164 return false;
165 }
166
167 for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
168 if (shader_ids[i] > 0)
169 glsafe(::glAttachShader(m_id, shader_ids[i]));
170 }
171
172 glsafe(::glLinkProgram(m_id));
173 GLint params;
174 glsafe(::glGetProgramiv(m_id, GL_LINK_STATUS, ¶ms));
175 if (params == GL_FALSE) {
176 // Linking failed.
177 glsafe(::glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, ¶ms));
178 std::vector<char> msg(params);
179 glsafe(::glGetProgramInfoLog(m_id, params, ¶ms, msg.data()));
180 BOOST_LOG_TRIVIAL(error) << "Unable to link shader program '" << name << "':\n" << msg.data();
181
182 // release shaders
183 release_shaders(shader_ids);
184
185 // release shader program
186 glsafe(::glDeleteProgram(m_id));
187 m_id = 0;
188
189 return false;
190 }
191
192 // release shaders, they are no more needed
193 release_shaders(shader_ids);
194
195 return true;
196 }
197
start_using() const198 void GLShaderProgram::start_using() const
199 {
200 assert(m_id > 0);
201 glsafe(::glUseProgram(m_id));
202 }
203
stop_using() const204 void GLShaderProgram::stop_using() const
205 {
206 glsafe(::glUseProgram(0));
207 }
208
set_uniform(const char * name,int value) const209 bool GLShaderProgram::set_uniform(const char* name, int value) const
210 {
211 int id = get_uniform_location(name);
212 if (id >= 0) {
213 glsafe(::glUniform1i(id, static_cast<GLint>(value)));
214 return true;
215 }
216 return false;
217 }
218
set_uniform(const char * name,bool value) const219 bool GLShaderProgram::set_uniform(const char* name, bool value) const
220 {
221 return set_uniform(name, value ? 1 : 0);
222 }
223
set_uniform(const char * name,float value) const224 bool GLShaderProgram::set_uniform(const char* name, float value) const
225 {
226 int id = get_uniform_location(name);
227 if (id >= 0) {
228 glsafe(::glUniform1f(id, static_cast<GLfloat>(value)));
229 return true;
230 }
231 return false;
232 }
233
set_uniform(const char * name,double value) const234 bool GLShaderProgram::set_uniform(const char* name, double value) const
235 {
236 return set_uniform(name, static_cast<float>(value));
237 }
238
set_uniform(const char * name,const std::array<int,2> & value) const239 bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 2>& value) const
240 {
241 int id = get_uniform_location(name);
242 if (id >= 0) {
243 glsafe(::glUniform2iv(id, 1, static_cast<const GLint*>(value.data())));
244 return true;
245 }
246 return false;
247 }
248
set_uniform(const char * name,const std::array<int,3> & value) const249 bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 3>& value) const
250 {
251 int id = get_uniform_location(name);
252 if (id >= 0) {
253 glsafe(::glUniform3iv(id, 1, static_cast<const GLint*>(value.data())));
254 return true;
255 }
256 return false;
257 }
258
set_uniform(const char * name,const std::array<int,4> & value) const259 bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 4>& value) const
260 {
261 int id = get_uniform_location(name);
262 if (id >= 0) {
263 glsafe(::glUniform4iv(id, 1, static_cast<const GLint*>(value.data())));
264 return true;
265 }
266 return false;
267 }
268
set_uniform(const char * name,const std::array<float,2> & value) const269 bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
270 {
271 int id = get_uniform_location(name);
272 if (id >= 0) {
273 glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value.data())));
274 return true;
275 }
276 return false;
277 }
278
set_uniform(const char * name,const std::array<float,3> & value) const279 bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 3>& value) const
280 {
281 int id = get_uniform_location(name);
282 if (id >= 0) {
283 glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
284 return true;
285 }
286 return false;
287 }
288
set_uniform(const char * name,const std::array<float,4> & value) const289 bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 4>& value) const
290 {
291 int id = get_uniform_location(name);
292 if (id >= 0) {
293 glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value.data())));
294 return true;
295 }
296 return false;
297 }
298
set_uniform(const char * name,const float * value,size_t size) const299 bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const
300 {
301 if (size == 1)
302 return set_uniform(name, value[0]);
303 else if (size < 5) {
304 int id = get_uniform_location(name);
305 if (id >= 0) {
306 if (size == 2)
307 glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
308 else if (size == 3)
309 glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
310 else
311 glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
312
313 return true;
314 }
315 }
316 return false;
317 }
318
set_uniform(const char * name,const Transform3f & value) const319 bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const
320 {
321 int id = get_uniform_location(name);
322 if (id >= 0) {
323 glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.matrix().data())));
324 return true;
325 }
326 return false;
327 }
328
set_uniform(const char * name,const Transform3d & value) const329 bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const
330 {
331 return set_uniform(name, value.cast<float>());
332 }
333
set_uniform(const char * name,const Matrix3f & value) const334 bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const
335 {
336 int id = get_uniform_location(name);
337 if (id >= 0) {
338 glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.data())));
339 return true;
340 }
341 return false;
342 }
343
set_uniform(const char * name,const Vec3f & value) const344 bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const
345 {
346 int id = get_uniform_location(name);
347 if (id >= 0) {
348 glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
349 return true;
350 }
351 return false;
352 }
353
set_uniform(const char * name,const Vec3d & value) const354 bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const
355 {
356 return set_uniform(name, static_cast<Vec3f>(value.cast<float>()));
357 }
358
get_attrib_location(const char * name) const359 int GLShaderProgram::get_attrib_location(const char* name) const
360 {
361 return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1;
362 }
363
get_uniform_location(const char * name) const364 int GLShaderProgram::get_uniform_location(const char* name) const
365 {
366 return (m_id > 0) ? ::glGetUniformLocation(m_id, name) : -1;
367 }
368
369 } // namespace Slic3r
370