2d0eb353aafbfd3c5e6174be6cab4fc353d5b1a2
[blender.git] / intern / gawain / src / shader_interface.c
1
2 // Gawain shader interface (C --> GLSL)
3 //
4 // This code is part of the Gawain library, with modifications
5 // specific to integration with Blender.
6 //
7 // Copyright 2017 Mike Erwin
8 //
9 // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
10 // the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
11
12 #include "shader_interface.h"
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <string.h>
16
17 #define SUPPORT_LEGACY_GLSL 1
18 #define DEBUG_SHADER_INTERFACE 0
19
20 #if DEBUG_SHADER_INTERFACE
21  #include <stdio.h>
22 #endif
23
24 static const char* BuiltinUniform_name(BuiltinUniform u)
25         {
26         static const char* names[] =
27                 {
28                 [UNIFORM_NONE] = NULL,
29
30                 [UNIFORM_MODELVIEW] = "ModelViewMatrix",
31                 [UNIFORM_PROJECTION] = "ProjectionMatrix",
32                 [UNIFORM_MVP] = "ModelViewProjectionMatrix",
33
34                 [UNIFORM_MODELVIEW_INV] = "ModelViewInverseMatrix",
35                 [UNIFORM_PROJECTION_INV] = "ProjectionInverseMatrix",
36
37                 [UNIFORM_NORMAL] = "NormalMatrix",
38
39                 [UNIFORM_COLOR] = "color",
40
41                 [UNIFORM_CUSTOM] = NULL
42                 };
43
44         return names[u];
45         }
46
47 static bool match(const char* a, const char* b)
48         {
49         return strcmp(a, b) == 0;
50         }
51
52 // keep these in sync with BuiltinUniform order
53 #define FIRST_MAT4_UNIFORM UNIFORM_MODELVIEW
54 #define LAST_MAT4_UNIFORM UNIFORM_PROJECTION_INV
55
56 static bool setup_builtin_uniform(ShaderInput* input, const char* name)
57         {
58         // TODO: reject DOUBLE, IMAGE, ATOMIC_COUNTER gl_types
59
60         // detect built-in uniforms (gl_type and name must match)
61         // if a match is found, use BuiltinUniform_name so name buffer space can be reclaimed
62         switch (input->gl_type)
63                 {
64                 case GL_FLOAT_MAT4:
65                         for (BuiltinUniform u = FIRST_MAT4_UNIFORM; u <= LAST_MAT4_UNIFORM; ++u)
66                                 {
67                                 const char* builtin_name = BuiltinUniform_name(u);
68                                 if (match(name, builtin_name))
69                                         {
70                                         input->name = builtin_name;
71                                         input->builtin_type = u;
72                                         return true;
73                                         }
74                                 }
75                         break;
76                 case GL_FLOAT_MAT3:
77                         {
78                         const char* builtin_name = BuiltinUniform_name(UNIFORM_NORMAL);
79                         if (match(name, builtin_name))
80                                 {
81                                 input->name = builtin_name;
82                                 input->builtin_type = UNIFORM_NORMAL;
83                                 return true;
84                                 }
85                         }
86                         break;
87                 case GL_FLOAT_VEC4:
88                         {
89                         const char* builtin_name = BuiltinUniform_name(UNIFORM_COLOR);
90                         if (match(name, builtin_name))
91                                 {
92                                 input->name = builtin_name;
93                                 input->builtin_type = UNIFORM_COLOR;
94                                 return true;
95                                 }
96                         }
97                         break;
98                 default:
99                         ;
100                 } 
101
102         input->builtin_type = UNIFORM_CUSTOM;
103         return false;
104         }
105
106 ShaderInterface* ShaderInterface_create(GLint program)
107         {
108 #if DEBUG_SHADER_INTERFACE
109         printf("%s {\n", __func__); // enter function
110 #endif
111
112         GLint uniform_ct, attrib_ct;
113         glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniform_ct);
114         glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attrib_ct);
115         const GLint input_ct = uniform_ct + attrib_ct;
116
117         GLint max_uniform_name_len, max_attrib_name_len;
118         glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len);
119         glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attrib_name_len);
120         const uint32_t name_buffer_len = uniform_ct * max_uniform_name_len + attrib_ct * max_attrib_name_len;
121
122         // allocate enough space for input counts, details for each input, and a buffer for name strings
123         ShaderInterface* shaderface = calloc(1, offsetof(ShaderInterface, inputs) + input_ct * sizeof(ShaderInput) + name_buffer_len);
124         shaderface->uniform_ct = uniform_ct;
125         shaderface->attrib_ct = attrib_ct;
126
127         char* name_buffer = (char*)shaderface + offsetof(ShaderInterface, inputs) + input_ct * sizeof(ShaderInput);
128         uint32_t name_buffer_offset = 0;
129
130         for (uint32_t i = 0; i < uniform_ct; ++i)
131                 {
132                 ShaderInput* input = shaderface->inputs + i;
133                 GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
134                 char* name = name_buffer + name_buffer_offset;
135                 GLsizei name_len = 0;
136
137                 glGetActiveUniform(program, i, remaining_buffer, &name_len, &input->size, &input->gl_type, name);
138
139                 input->location = glGetUniformLocation(program, name);
140
141 #if SUPPORT_LEGACY_GLSL
142                 if (input->location != -1)
143                         {
144 #elif TRUST_NO_ONE
145                         assert(input->location != -1);
146 #endif
147
148                         if (setup_builtin_uniform(input, name))
149                                 ; // reclaim space from name buffer (don't advance offset)
150                         else
151                                 {
152                                 input->name = name;
153                                 name_buffer_offset += name_len + 1; // include NULL terminator
154                                 }
155 #if SUPPORT_LEGACY_GLSL
156                         }
157 #endif
158
159 #if DEBUG_SHADER_INTERFACE
160                 printf("uniform[%u] '%s' at location %d\n", i, name, input->location);
161 #endif
162                 }
163
164         for (uint32_t i = 0; i < attrib_ct; ++i)
165                 {
166                 ShaderInput* input = shaderface->inputs + uniform_ct + i;
167                 GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
168                 char* name = name_buffer + name_buffer_offset;
169                 GLsizei name_len = 0;
170
171                 glGetActiveAttrib(program, i, remaining_buffer, &name_len, &input->size, &input->gl_type, name);
172
173                 // TODO: reject DOUBLE gl_types
174
175                 input->location = glGetAttribLocation(program, name);
176
177 #if SUPPORT_LEGACY_GLSL
178                 if (input->location != -1)
179                         {
180 #elif TRUST_NO_ONE
181                         assert(input->location != -1);
182 #endif
183
184                         input->name = name;
185                         name_buffer_offset += name_len + 1; // include NULL terminator
186 #if SUPPORT_LEGACY_GLSL
187                         }
188 #endif
189
190 #if DEBUG_SHADER_INTERFACE
191                 printf("attrib[%u] '%s' at location %d\n", i, name, input->location);
192 #endif
193                 }
194
195         const uint32_t name_buffer_used = name_buffer_offset;
196
197 #if DEBUG_SHADER_INTERFACE
198         printf("using %u of %u bytes from name buffer\n", name_buffer_used, name_buffer_len);
199         printf("}\n"); // exit function
200 #endif
201
202         if (name_buffer_used < name_buffer_len)
203                 {
204                 // realloc shaderface to shrink name buffer
205                 ShaderInterface* orig_pointer = shaderface;
206                 shaderface = realloc(shaderface, offsetof(ShaderInterface, inputs) + input_ct * sizeof(ShaderInput) + name_buffer_used);
207                 const ptrdiff_t delta = (char*)shaderface - (char*)orig_pointer;
208
209                 if (delta)
210                         {
211                         // each input->name will need adjustment (except static built-in names)
212                         const uint32_t input_ct_new = shaderface->uniform_ct + shaderface->attrib_ct;
213                         for (uint32_t i = 0; i < input_ct_new; ++i)
214                                 {
215                                 ShaderInput* input = shaderface->inputs + i;
216
217                                 if (input->builtin_type == UNIFORM_CUSTOM || input->builtin_type == UNIFORM_NONE)
218                                         input->name += delta;
219                                 }
220                         }
221                 }
222
223         return shaderface;
224         }
225
226 void ShaderInterface_discard(ShaderInterface* shaderface)
227         {
228         // allocated as one chunk, so discard is simple
229         free(shaderface);
230         }
231
232 const ShaderInput* ShaderInterface_uniform(const ShaderInterface* shaderface, const char* name)
233         {
234         // search through custom uniforms first
235         for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
236                 {
237                 const ShaderInput* uniform = shaderface->inputs + i;
238
239                 if (uniform->builtin_type == UNIFORM_CUSTOM)
240                         {
241 #if SUPPORT_LEGACY_GLSL
242                         if (uniform->name == NULL) continue;
243 #endif
244
245                         if (match(uniform->name, name))
246                                 return uniform;
247                         }
248                 }
249
250         // search through builtin uniforms next
251         for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
252                 {
253                 const ShaderInput* uniform = shaderface->inputs + i;
254
255 #if SUPPORT_LEGACY_GLSL
256                 if (uniform->name == NULL) continue;
257 #endif
258                 if (uniform->builtin_type != UNIFORM_CUSTOM)
259                         if (match(uniform->name, name))
260                                 return uniform;
261
262                 // TODO: warn if we find a matching builtin, since these can be looked up much quicker --v
263                 }
264
265         return NULL; // not found
266         }
267
268 const ShaderInput* ShaderInterface_builtin_uniform(const ShaderInterface* shaderface, BuiltinUniform builtin)
269         {
270         // look up by enum, not name
271         for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
272                 {
273                 const ShaderInput* uniform = shaderface->inputs + i;
274
275                 if (uniform->builtin_type == builtin)
276                         return uniform;
277                 }
278         return NULL; // not found
279         }
280
281 const ShaderInput* ShaderInterface_attrib(const ShaderInterface* shaderface, const char* name)
282         {
283         // attribs are stored after uniforms
284         const uint32_t input_ct = shaderface->uniform_ct + shaderface->attrib_ct;
285         for (uint32_t i = shaderface->uniform_ct; i < input_ct; ++i)
286                 {
287                 const ShaderInput* attrib = shaderface->inputs + i;
288
289 #if SUPPORT_LEGACY_GLSL
290                 if (attrib->name == NULL) continue;
291 #endif
292
293                 if (match(attrib->name, name))
294                         return attrib;
295                 }
296         return NULL; // not found
297         }