summaryrefslogtreecommitdiff
path: root/gfx/src/core/shader_program.c
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/src/core/shader_program.c')
-rw-r--r--gfx/src/core/shader_program.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/gfx/src/core/shader_program.c b/gfx/src/core/shader_program.c
new file mode 100644
index 0000000..3cbe48d
--- /dev/null
+++ b/gfx/src/core/shader_program.c
@@ -0,0 +1,291 @@
1#include "shader_program.h"
2
3#include "gl_util.h"
4#include "shader.h"
5#include "texture.h"
6#include <gfx_assert.h>
7
8#include <log/log.h>
9
10#include <stdlib.h>
11#include <string.h>
12
13/// Creates an OpenGL shader program.
14/// Returns non-zero on success, 0 on failure.
15static GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) {
16 const GLuint prog = glCreateProgram();
17 if (prog == 0) {
18 LOGE("Failed creating shader program");
19 return 0;
20 }
21 glAttachShader(prog, vertex_shader);
22 glAttachShader(prog, fragment_shader);
23 glLinkProgram(prog);
24 GLint result;
25 glGetProgramiv(prog, GL_LINK_STATUS, &result);
26 if (result == GL_FALSE) {
27 GLint log_len;
28 glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len);
29 if (log_len > 0) {
30 char* log = calloc(log_len, sizeof(char));
31 glGetProgramInfoLog(prog, log_len, NULL, log);
32 LOGE("Failed creating shader program: %s", log);
33 free(log);
34 } else {
35 LOGE("Failed creating shader program");
36 }
37 glDeleteProgram(prog);
38 return 0;
39 }
40 ASSERT_GL;
41 return prog;
42}
43
44bool gfx_build_shader_program(
45 ShaderProgram* prog, const ShaderProgramDesc* desc) {
46 assert(prog);
47 assert(desc);
48
49 prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id);
50 return prog->id != 0;
51}
52
53void gfx_del_shader_program(ShaderProgram* prog) {
54 assert(prog);
55
56 if (prog->id) {
57 glDeleteProgram(prog->id);
58 prog->id = 0;
59 }
60 ASSERT_GL;
61}
62
63void gfx_activate_shader_program(const ShaderProgram* prog) {
64 assert(prog);
65 glUseProgram(prog->id);
66 ASSERT_GL;
67}
68
69void gfx_deactivate_shader_program(const ShaderProgram* prog) {
70 assert(prog);
71 glUseProgram(0);
72 ASSERT_GL;
73}
74
75static void set_texture_uniform(
76 GLuint prog, const char* name, int texture_unit, const Texture* texture) {
77 assert(prog != 0);
78 assert(name);
79 assert(texture);
80
81 const GLint location = glGetUniformLocation(prog, name);
82 if (location >= 0) {
83 glActiveTexture(GL_TEXTURE0 + texture_unit);
84 glBindTexture(texture->target, texture->id);
85 glUniform1i(location, texture_unit);
86 }
87}
88
89static void set_mat4_uniform(
90 GLuint prog, const char* name, const mat4* mats, size_t count) {
91 assert(prog != 0);
92 assert(name);
93 assert(mats);
94
95 const GLint location = glGetUniformLocation(prog, name);
96 if (location >= 0) {
97 glUniformMatrix4fv(location, count, GL_FALSE, (const float*)mats);
98 }
99}
100
101static void set_vec3_uniform(GLuint prog, const char* name, vec3 value) {
102 assert(prog != 0);
103 assert(name);
104
105 const GLint location = glGetUniformLocation(prog, name);
106 if (location >= 0) {
107 glUniform3f(location, value.x, value.y, value.z);
108 }
109}
110
111static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) {
112 assert(prog != 0);
113 assert(name);
114
115 const GLint location = glGetUniformLocation(prog, name);
116 if (location >= 0) {
117 glUniform4f(location, value.x, value.y, value.z, value.w);
118 }
119}
120
121static void set_float_uniform(GLuint prog, const char* name, float value) {
122 assert(prog != 0);
123 assert(name);
124
125 const GLint location = glGetUniformLocation(prog, name);
126 if (location >= 0) {
127 glUniform1f(location, value);
128 }
129}
130
131void gfx_apply_uniforms(const ShaderProgram* prog) {
132 assert(prog);
133
134 int next_texture_unit = 0;
135 for (int i = 0; i < prog->num_uniforms; ++i) {
136 const ShaderUniform* uniform = &prog->uniforms[i];
137 switch (uniform->type) {
138 case UniformTexture:
139 set_texture_uniform(
140 prog->id, uniform->name.str, next_texture_unit,
141 uniform->value.texture);
142 next_texture_unit++;
143 break;
144 case UniformMat4:
145 set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1);
146 break;
147 case UniformVec3:
148 set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3);
149 break;
150 case UniformVec4:
151 set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4);
152 break;
153 case UniformFloat:
154 set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar);
155 break;
156 case UniformMat4Array:
157 set_mat4_uniform(
158 prog->id, uniform->name.str, uniform->value.array.values,
159 uniform->value.array.count);
160 break;
161 }
162 }
163}
164
165// Get the ShaderUniform object by name from the shader program if it already
166// exists, or allocate a new one otherwise.
167static ShaderUniform* get_or_allocate_uniform(
168 ShaderProgram* prog, const char* name) {
169 assert(prog);
170 assert(name);
171
172 // First search for the uniform in the list.
173 for (int i = 0; i < prog->num_uniforms; ++i) {
174 ShaderUniform* uniform = &prog->uniforms[i];
175 if (sstring_eq_cstr(uniform->name, name)) {
176 return uniform;
177 }
178 }
179
180 // Create the uniform if it does not exist.
181 if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) {
182 FAIL("Exceeded the maximum number of uniforms per shader. Please increase "
183 "this value.");
184 return 0;
185 }
186 ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms];
187 prog->num_uniforms++;
188 return uniform;
189}
190
191// The functions below save the value of a uniform in the shader program. If the
192// uniform does not even exist, then there is no need to store the value.
193
194void gfx_set_texture_uniform(
195 ShaderProgram* prog, const char* name, const Texture* texture) {
196 assert(prog);
197 assert(name);
198 assert(texture);
199
200 const GLint location = glGetUniformLocation(prog->id, name);
201 if (location < 0) {
202 return;
203 }
204 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
205 assert(uniform);
206 uniform->name = sstring_make(name);
207 uniform->type = UniformTexture;
208 uniform->value.texture = texture;
209}
210
211void gfx_set_mat4_uniform(
212 ShaderProgram* prog, const char* name, const mat4* mat) {
213 assert(prog);
214 assert(name);
215 assert(mat);
216
217 const GLint location = glGetUniformLocation(prog->id, name);
218 if (location < 0) {
219 return;
220 }
221 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
222 assert(uniform);
223 uniform->name = sstring_make(name);
224 uniform->type = UniformMat4;
225 uniform->value.mat4 = *mat;
226}
227
228void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) {
229 assert(prog);
230 assert(name);
231
232 const GLint location = glGetUniformLocation(prog->id, name);
233 if (location < 0) {
234 return;
235 }
236 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
237 assert(uniform);
238 uniform->name = sstring_make(name);
239 uniform->type = UniformVec3;
240 uniform->value.vec3 = value;
241}
242
243void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) {
244 assert(prog);
245 assert(name);
246
247 const GLint location = glGetUniformLocation(prog->id, name);
248 if (location < 0) {
249 return;
250 }
251 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
252 assert(uniform);
253 uniform->name = sstring_make(name);
254 uniform->type = UniformVec4;
255 uniform->value.vec4 = value;
256}
257
258void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) {
259 assert(prog);
260 assert(name);
261
262 // No need to store the uniform on our side if it does not exist in the
263 // program.
264 const GLint location = glGetUniformLocation(prog->id, name);
265 if (location < 0) {
266 return;
267 }
268 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
269 assert(uniform);
270 uniform->name = sstring_make(name);
271 uniform->type = UniformFloat;
272 uniform->value.scalar = value;
273}
274
275void gfx_set_mat4_array_uniform(
276 ShaderProgram* prog, const char* name, const mat4* mats, size_t count) {
277 assert(prog);
278 assert(name);
279 assert(mats);
280
281 const GLint location = glGetUniformLocation(prog->id, name);
282 if (location < 0) {
283 return;
284 }
285 ShaderUniform* uniform = get_or_allocate_uniform(prog, name);
286 assert(uniform);
287 uniform->name = sstring_make(name);
288 uniform->type = UniformMat4Array;
289 uniform->value.array.count = count;
290 uniform->value.array.values = mats;
291}