GCC Code Coverage Report


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

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