Euphoria
shader.cc
Go to the documentation of this file.
1 #include "render/shader.h"
2 
3 #include <iostream>
4 
5 #include <fstream>
6 #include <algorithm>
7 
8 
9 #include "assert/assert.h"
10 #include "log/log.h"
11 #include "base/stringutils.h"
12 #include "io/vfs.h"
13 
14 #include "render/gl.h"
15 #include "render/texture.h"
16 
17 
18 namespace
19 {
20 
21 
23  get_current_shader()
24  {
25  static const eu::render::ShaderProgram* current_shader = nullptr;
26  return current_shader;
27  }
28 
29 
30  bool
31  was_compilation_successful(GLuint object)
32  {
33  int r = GL_TRUE;
34  glGetShaderiv(object, GL_COMPILE_STATUS, &r);
35  return r == GL_TRUE;
36  }
37 
38 
39  bool
40  was_linking_successful(GLuint object)
41  {
42  int r = GL_TRUE;
43  glGetProgramiv(object, GL_LINK_STATUS, &r);
44  return r == GL_TRUE;
45  }
46 
47 
48  std::string
49  get_shader_log(GLuint shader)
50  {
51  int length = 0;
52  glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
53  if(length <= 0)
54  {
55  return "";
56  }
57  const int max_length = length + 1;
58  std::vector<char> str(max_length, 0);
59  glGetShaderInfoLog(shader, max_length, &length, str.data());
60  return str.data();
61  }
62 
63 
64  std::string
65  get_program_log(GLuint shader)
66  {
67  int length = 0;
68  glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &length);
69  if(length <= 0)
70  {
71  return "";
72  }
73  const int max_length = length + 1;
74  std::vector<char> str(max_length, 0);
75  glGetProgramInfoLog(shader, max_length, &length, str.data());
76  return str.data();
77  }
78 
79 
80 }
81 
82 
83 namespace eu::render
84 {
85 
86 
88  : id(glCreateProgram())
89  {
90  }
91 
92 
94  {
95  glDeleteProgram(id);
96  }
97 
98 
99  GLuint
101  {
102  return id;
103  }
104 
105 
106  bool
108  {
109  return this == get_current_shader();
110  }
111 
112 
113  void
114  use(const ShaderProgram* shader)
115  {
116  if(shader != nullptr)
117  {
118  glUseProgram(shader->get_id());
119  }
120  else
121  {
122  glUseProgram(0);
123  }
124  get_current_shader() = shader;
125  }
126 
127 
128  const ShaderProgram*
130  {
131  return get_current_shader();
132  }
133 
134 
135  void
136  report_shader_error(const std::string& log, const std::string& type)
137  {
138  LOG_ERROR
139  (
140  "ERROR::SHADER: Compile-time error: Type: {0} \n"
141  "{1}\n"
142  "---------------------------------------------------------",
143  type,
144  trim(log)
145  );
146  }
147 
148 
149  void
150  report_error_program(GLuint program)
151  {
152  const std::string& log = get_program_log(program);
153  report_shader_error(log, "PROGRAM");
154  }
155 
156 
157  void
158  report_error_shader(GLuint shader, const std::string& type)
159  {
160  const std::string& log = get_shader_log(shader);
161  report_shader_error(log, type);
162  }
163 
164 
165  GLuint
166  compile_shader(GLuint type, const GLchar* source, const std::string& name)
167  {
168  GLuint shader = glCreateShader(type);
169  glShaderSource(shader, 1, &source, nullptr);
170  glCompileShader(shader);
171  if(was_compilation_successful(shader))
172  {
173  return shader;
174  }
175  else
176  {
177  report_error_shader(shader, name);
178  return shader;
179  }
180  }
181 
182 
183  void
185  {
186  const std::string attribute_name = std::string(attribute.name);
187  glBindAttribLocation(get_id(), attribute.id, attribute_name.c_str());
188  bound_attributes.push_back(attribute);
189  }
190 
191 
192  bool
194  (
195  const GLchar* vertex_source,
196  const GLchar* fragment_source,
197  const GLchar* geometry_source
198  )
199  {
200  bool ret = true;
201 
202  GLuint vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_source, "VERTEX");
203  GLuint fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_source, "FRAGMENT");
204 
205  GLuint geometry_shader_id = 0;
206  if(geometry_source != nullptr)
207  {
208  geometry_shader_id = compile_shader(GL_GEOMETRY_SHADER, geometry_source, "GEOMETRY");
209  }
210 
211  glAttachShader(get_id(), vertex_shader_id);
212  glAttachShader(get_id(), fragment_shader_id);
213  if(geometry_source != nullptr)
214  {
215  glAttachShader(get_id(), geometry_shader_id);
216  }
217  glLinkProgram(get_id());
218  const bool link_ok = was_linking_successful(get_id());
219  if(!link_ok)
220  {
222  ret = false;
223  }
224 
225  glDeleteShader(vertex_shader_id);
226  glDeleteShader(fragment_shader_id);
227  if(geometry_source != nullptr)
228  {
229  glDeleteShader(geometry_shader_id);
230  }
231 
232  for(const auto& attribute: bound_attributes)
233  {
234  const std::string attribute_name = std::string(attribute.name);
235  const int actual_attribute_id = glGetAttribLocation(get_id(), attribute_name.c_str());
236 
237  if(actual_attribute_id == attribute.id)
238  {
239  continue;
240  }
241 
242  if(actual_attribute_id == -1)
243  {
244  continue;
245  }
246 
247  LOG_ERROR
248  (
249  "{0} was bound to {1} but was requested at {2}",
250  attribute.name,
251  actual_attribute_id,
252  attribute.id
253  );
254  ret = false;
255  }
256 
257  return ret;
258  }
259 
260 
262  ShaderProgram::get_uniform(const std::string& name)
263  {
264  int uniform_id = glGetUniformLocation(get_id(), name.c_str());
265  ShaderUniform uniform(name, uniform_id, this);
266  bound_uniforms.push_back(uniform);
267 
268  if(uniform.id == -1)
269  {
270  LOG_ERROR
271  (
272  "Failed to load uniform {0} from shader {1}",
273  uniform.name,
274  shader_name
275  );
276  }
277 
278  return uniform;
279  }
280 
281 
282  void
283  ShaderProgram::set_uniform(const ShaderUniform& attribute, float val)
284  {
286  ASSERT(has_bound_uniform(attribute));
287  glUniform1f(attribute.id, val);
288  }
289 
290 
291  void
293  {
295  ASSERT(has_bound_uniform(attribute));
296  glUniform1i(attribute.id, val);
297  }
298 
299 
300  void
301  ShaderProgram::set_uniform(const ShaderUniform& attribute, const Rgb& val)
302  {
304  ASSERT(has_bound_uniform(attribute));
305  if(attribute.id == -1)
306  {
307  return;
308  }
309  glUniform3f(attribute.id, val.r, val.g, val.b);
310  }
311 
312 
313  void
314  ShaderProgram::set_uniform(const ShaderUniform& attribute, const Rgba& val)
315  {
317  ASSERT(has_bound_uniform(attribute));
318  if(attribute.id == -1)
319  {
320  return;
321  }
322  glUniform4f(attribute.id, val.r, val.g, val.b, val.a);
323  }
324 
325 
326  void
327  ShaderProgram::set_uniform(const ShaderUniform& attribute, const vec3f& val)
328  {
330  ASSERT(has_bound_uniform(attribute));
331  if(attribute.id == -1)
332  {
333  return;
334  }
335  glUniform3f(attribute.id, val.x, val.y, val.z);
336  }
337 
338 
339  void
340  ShaderProgram::set_uniform(const ShaderUniform& attribute, const vec4f& val)
341  {
343  ASSERT(has_bound_uniform(attribute));
344  if(attribute.id == -1)
345  {
346  return;
347  }
348  glUniform4f(attribute.id, val.x, val.y, val.z, val.w);
349  }
350 
351 
352  void
353  ShaderProgram::set_uniform(const ShaderUniform& attribute, const mat3f& val)
354  {
356  ASSERT(has_bound_uniform(attribute));
357  if(attribute.id == -1)
358  {
359  return;
360  }
361  glUniformMatrix3fv(attribute.id, 1, GL_FALSE, val.get_data_ptr());
362  }
363 
364 
365  void
366  ShaderProgram::set_uniform(const ShaderUniform& attribute, const mat4f& val)
367  {
369  ASSERT(has_bound_uniform(attribute));
370  if(attribute.id == -1)
371  {
372  return;
373  }
374  glUniformMatrix4fv(attribute.id, 1, GL_FALSE, val.get_data_ptr());
375  }
376 
377 
378  void
379  ShaderProgram::set_uniform(const ShaderUniform& attribute, const Rectf& val)
380  {
382  ASSERT(has_bound_uniform(attribute));
383  if(attribute.id == -1)
384  {
385  return;
386  }
387  glUniform4f(attribute.id, val.left, val.right, val.bottom, val.top);
388  }
389 
390 
392  : shader_name("~/not_loaded_shader")
393  {
394  }
395 
396 
397  bool
399  {
400  shader_name = file_path;
401 
402  const auto load_path = [](io::FileSystem* afs, const io::FilePath& path) -> std::string
403  {
404  // todo(Gustav): replace with a template instead of basic string
405  auto content = afs->read_file_to_string(path);
406  if(!content)
407  {
408  return "";
409  }
410 
411  return *content;
412  };
413 
414  auto vert = load_path(fs, file_path.set_extension_copy("vert"));
415  auto frag = load_path(fs, file_path.set_extension_copy("frag"));
416  auto geom = load_path(fs, file_path.set_extension_copy("geom"));
417 
418  bool loaded_files = true;
419 
420  if(vert.empty())
421  {
422  LOG_ERROR("Failed to load vert shader {0}", file_path);
423  loaded_files = false;
424  }
425 
426  if(frag.empty())
427  {
428  LOG_ERROR("Failed to load frag shader {0}", file_path);
429  loaded_files = false;
430  }
431 
432  if(!loaded_files)
433  {
434  return false;
435  }
436 
437  const bool shader_compiled = compile
438  (
439  vert.c_str(),
440  frag.c_str(),
441  geom.empty() ? nullptr : geom.c_str()
442  );
443  if(!shader_compiled)
444  {
445  LOG_ERROR("Failed to compile shader {0}", file_path);
446  }
447  return shader_compiled;
448  }
449 
450 
451  const std::vector<ShaderAttribute>&
453  {
454  return bound_attributes;
455  }
456 
457 
458  const io::FilePath&
460  {
461  return shader_name;
462  }
463 
464 
465  bool
466  ShaderProgram::has_bound_attribute(const ShaderAttribute& attribute) const
467  {
468  const auto found = std::find
469  (
470  bound_attributes.begin(),
471  bound_attributes.end(),
472  attribute
473  );
474  return found != bound_attributes.end();
475  }
476 
477 
478  bool
479  ShaderProgram::has_bound_uniform(const ShaderUniform& uniform) const
480  {
481  const auto found = std::find(bound_uniforms.begin(), bound_uniforms.end(), uniform);
482  return found != bound_uniforms.end();
483  }
484 
485 
486  void
488  (
489  Texture2* texture,
490  ShaderProgram* shader,
491  const ShaderUniform& attribute,
492  gl::Int index
493  )
494  {
495  ASSERT(index < 16); // at most 16 texture units
496  GLenum gl_id = GL_TEXTURE0 + index;
497  glActiveTexture(gl_id);
498  use(texture);
499  shader->set_uniform(attribute, index);
500  }
501 
502 
503 }
504 
#define ASSERT(x)
Definition: assert.h:29
std::string trim(const std::string &string_to_trim, std::string_view trim_characters)
Remove characters from both the start and the end.
Definition: stringutils.cc:79
#define LOG_ERROR(...)
Definition: log.h:9
bool find(const std::string &target, const std::string &search)
Definition: findstring.cc:8
void use(const ShaderProgram *shader)
Definition: shader.cc:114
void report_error_program(GLuint program)
Definition: shader.cc:150
GLuint compile_shader(GLuint type, const GLchar *source, const std::string &name)
Definition: shader.cc:166
void bind_texture_to_shader(Texture2 *texture, ShaderProgram *shader, const ShaderUniform &attribute, gl::Int index)
Definition: shader.cc:488
void report_shader_error(const std::string &log, const std::string &type)
Definition: shader.cc:136
void report_error_shader(GLuint shader, const std::string &type)
Definition: shader.cc:158
String utility functions.
Definition: rect.h:27
float top
Definition: rect.h:30
float right
Definition: rect.h:29
float left
Definition: rect.h:28
float bottom
Definition: rect.h:31
Definition: rgb.h:62
float b
Definition: rgb.h:65
float g
Definition: rgb.h:64
float r
Definition: rgb.h:63
Definition: rgb.h:143
float r
Definition: rgb.h:144
float a
Definition: rgb.h:147
float g
Definition: rgb.h:145
float b
Definition: rgb.h:146
FilePath set_extension_copy(const std::string &ext) const
Definition: vfs_path.cc:170
std::optional< std::string > read_file_to_string(const FilePath &path)
Definition: vfs.cc:171
Definition: mat3.h:12
float * get_data_ptr()
Definition: mat3.cc:230
Definition: mat4.h:14
float * get_data_ptr()
Definition: mat4.cc:445
Represents a shader attribute like vertex, normal or uv coord.
gl::Int id
the id of the attribute
std::string_view name
the name of the shader attribute
bool is_currently_bound() const
Definition: shader.cc:107
gl::Uint get_id() const
Definition: shader.cc:100
void set_uniform(const ShaderUniform &attribute, gl::Int val)
Definition: shader.cc:292
bool load(io::FileSystem *fs, const io::FilePath &file_path)
Definition: shader.cc:398
bool compile(const gl::Char *vertex_source, const gl::Char *fragment_source, const gl::Char *geometry_source=nullptr)
Definition: shader.cc:194
const io::FilePath & get_name() const
Definition: shader.cc:459
ShaderUniform get_uniform(const std::string &name)
Definition: shader.cc:262
void add_attribute(const ShaderAttribute &attribute)
Definition: shader.cc:184
static const ShaderProgram * get_current_bound_for_debug()
Definition: shader.cc:129
const std::vector< ShaderAttribute > & get_attributes() const
Definition: shader.cc:452
Definition: vec3.h:48
float x
Definition: vec3.h:49
float y
Definition: vec3.h:50
float z
Definition: vec3.h:51
Definition: vec4.h:14
float w
Definition: vec4.h:18
float x
Definition: vec4.h:15
float y
Definition: vec4.h:16
float z
Definition: vec4.h:17