GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 201, 0 excluded
0.0%
Functions:
0 of 31, 0 excluded
0.0%
Branches:
0 of 210, 0 excluded
0.0%

libs/render/src/render/shader.cc
Line Branch Exec Source
1 #include "render/shader.h"
2
3 #include "base/cint.h"
4
5 #include "render/constants.h"
6 #include "base/mat4.h"
7 #include "base/vec2.h"
8 #include "render/opengl_utils.h"
9 #include "render/uniform_buffer.h"
10
11 namespace eu::render
12 {
13
14 // internal "header", defined later
15 void set_shader_program(unsigned int new_program, const VertexTypes& types);
16
17 constexpr GLsizei max_log_length = 1024;
18
19 bool check_shader_compilation_error(const char* name, unsigned int shader)
20 {
21 GLint success = 0;
22 char log[max_log_length] = {
23 0,
24 };
25 glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
26
27 if (success == GL_FALSE)
28 {
29 glGetShaderInfoLog(shader, max_log_length, nullptr, log);
30 log[max_log_length-1] = '\0';
31 LOG_ERR("%s shader compilation failed\n{}\n", name, log);
32 return false;
33 }
34
35 return true;
36 }
37
38 bool check_shader_link_error(unsigned int program)
39 {
40 GLint success = 0;
41 char log[max_log_length] = {
42 0,
43 };
44 glGetProgramiv(program, GL_LINK_STATUS, &success);
45 if (success == GL_FALSE)
46 {
47 glGetProgramInfoLog(program, max_log_length, nullptr, log);
48 log[max_log_length - 1] = '\0';
49 LOG_ERR("shader linking failed\n{}\n", log);
50 return false;
51 }
52
53 return true;
54 }
55
56 void upload_shader_source(unsigned int shader, std::string_view source)
57 {
58 const char* const s = source.data();
59 const int length = int_from_sizet(source.length()); // should be GLint
60 glShaderSource(shader, 1, &s, &length);
61 }
62
63 void bind_shader_attribute_location(unsigned int shader_program, const CompiledShaderVertexAttributes& layout)
64 {
65 for (const auto& b: layout.elements)
66 {
67 glBindAttribLocation(shader_program, gluint_from_int(b.index), b.name.c_str());
68 }
69 }
70
71 void verify_shader_attribute_location(unsigned int shader_program, const CompiledShaderVertexAttributes& layout)
72 {
73 for (const auto& b: layout.elements)
74 {
75 const auto actual_index = glGetAttribLocation(shader_program, b.name.c_str());
76
77 if (actual_index != b.index)
78 {
79 LOG_ERR("ERROR: {} was bound to {} but requested at {}", b.name.c_str(), actual_index, b.index);
80 }
81 }
82 }
83
84 void log_source(const std::string& src)
85 {
86 std::istringstream ss(src);
87 std::string line;
88 int index = 1;
89 while (std::getline(ss, line))
90 {
91 LOG_INFO("%d: %s", index, line.c_str());
92 index += 1;
93 }
94 }
95
96 void load_shader_source(
97 DEBUG_LABEL_ARG_MANY
98 ShaderProgram* self,
99 const std::string& vertex_source,
100 const std::string& fragment_source,
101 const CompiledShaderVertexAttributes& layout
102 )
103 {
104 const auto vertex_shader = glCreateShader(GL_VERTEX_SHADER);
105 SET_DEBUG_LABEL_NAMED(vertex_shader, DebugLabelFor::Shader, fmt::format("SHADER {} VERT", debug_label));
106 upload_shader_source(vertex_shader, vertex_source);
107 glCompileShader(vertex_shader);
108 const auto vertex_ok = check_shader_compilation_error("vertex", vertex_shader);
109 if (vertex_ok == false)
110 {
111 LOG_INFO("Vertex source:");
112 log_source(vertex_source);
113 }
114
115 const auto fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
116 SET_DEBUG_LABEL_NAMED(fragment_shader, DebugLabelFor::Shader, fmt::format("SHADER {} FRAG", debug_label));
117 upload_shader_source(fragment_shader, fragment_source);
118 glCompileShader(fragment_shader);
119 const auto fragment_ok = check_shader_compilation_error("fragment", fragment_shader);
120 if (fragment_ok == false)
121 {
122 LOG_INFO("Fragment source:");
123 log_source(fragment_source);
124 }
125
126 glAttachShader(self->shader_program, vertex_shader);
127 glAttachShader(self->shader_program, fragment_shader);
128 bind_shader_attribute_location(self->shader_program, layout);
129 glLinkProgram(self->shader_program);
130 const auto link_ok = check_shader_link_error(self->shader_program);
131
132 glDeleteShader(vertex_shader);
133 glDeleteShader(fragment_shader);
134
135 clear_shader_program();
136
137 if (vertex_ok && fragment_ok && link_ok)
138 {
139 // nop
140 verify_shader_attribute_location(self->shader_program, layout);
141 }
142 else
143 {
144 clear_shader_program();
145 glDeleteProgram(self->shader_program);
146 self->shader_program = 0;
147 }
148 }
149
150 ShaderProgram::ShaderProgram(
151 DEBUG_LABEL_ARG_MANY const std::string& vertex_source, const std::string& fragment_source, const CompiledShaderVertexAttributes& layout
152 )
153 : shader_program(glCreateProgram())
154 , debug_vertex_types(layout.debug_types)
155 {
156 SET_DEBUG_LABEL_NAMED(shader_program, DebugLabelFor::Program, fmt::format("PROGRAM {}", debug_label));
157 load_shader_source(USE_DEBUG_LABEL_MANY(debug_label) this, vertex_source, fragment_source, layout);
158 }
159
160 void ShaderProgram::use() const
161 {
162 set_shader_program(shader_program, debug_vertex_types);
163 }
164
165 ShaderProgram::ShaderProgram(ShaderProgram&& other) noexcept
166 : shader_program(other.shader_program)
167 , debug_vertex_types(std::move(other.debug_vertex_types))
168 {
169 other.shader_program = 0;
170 other.debug_vertex_types = {};
171 }
172
173 ShaderProgram& ShaderProgram::operator=(ShaderProgram&& rhs) noexcept
174 {
175 clear();
176
177 shader_program = rhs.shader_program;
178 debug_vertex_types = rhs.debug_vertex_types;
179
180 rhs.shader_program = 0;
181 rhs.debug_vertex_types = {};
182
183 return *this;
184 }
185
186 ShaderProgram::~ShaderProgram()
187 {
188 clear();
189 }
190
191 bool ShaderProgram::is_loaded() const
192 {
193 return shader_program != 0;
194 }
195
196 void ShaderProgram::clear()
197 {
198 clear_shader_program();
199 glDeleteProgram(shader_program);
200 shader_program = 0;
201 }
202
203 Uniform ShaderProgram::get_uniform(const std::string& name) const
204 {
205 const auto uni = Uniform{name, glGetUniformLocation(shader_program, name.c_str()), shader_program};
206 if (uni.is_valid() == false)
207 {
208 LOG_ERR("Uniform {} not found", name);
209 }
210 return uni;
211 }
212
213 void ShaderProgram::set_float(const Uniform& uniform, float value) // NOLINT(readability-make-member-function-const)
214 {
215 ASSERT(is_shader_bound(shader_program));
216 if (uniform.is_valid() == false)
217 {
218 return;
219 }
220 ASSERT(uniform.debug_shader_program == shader_program);
221
222 ASSERT(uniform.texture == -1 && "uniform is a texture not a float");
223 glUniform1f(uniform.location, value);
224 }
225
226 void ShaderProgram::set_bool(const Uniform& uniform, bool value) // NOLINT(readability-make-member-function-const)
227 {
228 ASSERT(is_shader_bound(shader_program));
229 if (uniform.is_valid() == false)
230 {
231 return;
232 }
233 ASSERT(uniform.debug_shader_program == shader_program);
234
235 ASSERT(uniform.texture == -1 && "uniform is a texture not a float");
236 glUniform1i(uniform.location, value ? 1 : 0);
237 }
238
239 void ShaderProgram::set_vec2(const Uniform& uniform, float x, float y) // NOLINT(readability-make-member-function-const)
240 {
241 ASSERT(is_shader_bound(shader_program));
242 if (uniform.is_valid() == false)
243 {
244 return;
245 }
246 ASSERT(uniform.debug_shader_program == shader_program);
247
248 ASSERT(uniform.texture == -1 && "uniform is a texture not a vec3");
249 glUniform2f(uniform.location, x, y);
250 }
251
252 void ShaderProgram::set_vec2(const Uniform& uniform, const v2& v)
253 {
254 set_vec2(uniform, v.x, v.y);
255 }
256
257 void ShaderProgram::set_vec3(const Uniform& uniform, float x, float y, float z) // NOLINT(readability-make-member-function-const)
258 {
259 ASSERT(is_shader_bound(shader_program));
260 if (uniform.is_valid() == false)
261 {
262 return;
263 }
264 ASSERT(uniform.debug_shader_program == shader_program);
265
266 ASSERT(uniform.texture == -1 && "uniform is a texture not a vec3");
267 glUniform3f(uniform.location, x, y, z);
268 }
269
270 void ShaderProgram::set_vec3(const Uniform& uniform, const v3& v)
271 {
272 set_vec3(uniform, v.x, v.y, v.z);
273 }
274
275 void ShaderProgram::set_vec4(const Uniform& uniform, float x, float y, float z, float w) // NOLINT(readability-make-member-function-const)
276 {
277 ASSERT(is_shader_bound(shader_program));
278 if (uniform.is_valid() == false)
279 {
280 return;
281 }
282 ASSERT(uniform.debug_shader_program == shader_program);
283
284 ASSERT(uniform.texture == -1 && "uniform is a texture not a vec4");
285 glUniform4f(uniform.location, x, y, z, w);
286 }
287
288 void ShaderProgram::set_vec4(const Uniform& uniform, const v4& v)
289 {
290 set_vec4(uniform, v.x, v.y, v.z, v.w);
291 }
292
293 void ShaderProgram::set_texture(const Uniform& uniform) // NOLINT(readability-make-member-function-const)
294 {
295 ASSERT(is_shader_bound(shader_program));
296 if (uniform.is_valid() == false)
297 {
298 return;
299 }
300 ASSERT(uniform.debug_shader_program == shader_program);
301
302 ASSERT(uniform.texture >= 0 && "uniform needs to be a texture");
303 glUniform1i(uniform.location, uniform.texture);
304 }
305
306 void ShaderProgram::set_mat(const Uniform& uniform, const m4& mat) // NOLINT(readability-make-member-function-const)
307 {
308 ASSERT(is_shader_bound(shader_program));
309 if (uniform.is_valid() == false)
310 {
311 return;
312 }
313 ASSERT(uniform.debug_shader_program == shader_program);
314
315 ASSERT(uniform.texture == -1 && "uniform is a texture not a matrix4");
316 glUniformMatrix4fv(uniform.location, 1, GL_FALSE, mat.get_column_major_data_ptr());
317 }
318
319 void ShaderProgram::setup_uniform_block(const UniformBufferSetup& setup) // NOLINT(readability-make-member-function-const)
320 {
321 const unsigned int shader_block_index = glGetUniformBlockIndex(shader_program, setup.name.c_str());
322 if (shader_block_index == GL_INVALID_INDEX)
323 {
324 LOG_ERR("Shader missing uniform block %s", setup.name.c_str());
325 return;
326 }
327 glUniformBlockBinding(shader_program, shader_block_index, gluint_from_int(setup.binding_point));
328 }
329
330 void setup_textures(ShaderProgram* shader, const std::vector<Uniform*>& uniform_list)
331 {
332 ASSERT(uniform_list.size() <= MAX_TEXTURES_SUPPORTED);
333
334 shader->use();
335
336 int index = 0;
337 for (const auto& uniform: uniform_list)
338 {
339 uniform->texture = index;
340 index += 1;
341 shader->set_texture(*uniform);
342 }
343 }
344
345 namespace
346 {
347 VertexTypes debug_current_shader_types;
348 unsigned int debug_current_shader_program = 0;
349 } // namespace
350
351 void set_shader_program(unsigned int new_program, const VertexTypes& types)
352 {
353 debug_current_shader_program = new_program;
354 debug_current_shader_types = types;
355 glUseProgram(new_program);
356 }
357
358 bool is_bound_for_shader(const std::unordered_set<VertexType>& debug_geom_shader_types)
359 {
360 for (auto t: debug_current_shader_types)
361 {
362 if (debug_geom_shader_types.find(t) == debug_geom_shader_types.end())
363 {
364 // if shader type isn't found in geom
365 // then error out
366 return false;
367 }
368 }
369 return true;
370 }
371
372 bool is_shader_bound(unsigned int program)
373 {
374 return debug_current_shader_program == program;
375 }
376
377 void clear_shader_program()
378 {
379 set_shader_program(0, {});
380 }
381
382 }
383