Garwain: fix ShaderInterface buffer shrinking
[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                 const size_t shaderface_alloc =
206                         offsetof(ShaderInterface, inputs) + (input_ct * sizeof(ShaderInput)) + name_buffer_used;
207                 const char* shaderface_orig_start = (const char*)shaderface;
208                 const char* shaderface_orig_end = &shaderface_orig_start[shaderface_alloc];
209                 shaderface = realloc(shaderface, shaderface_alloc);
210                 const ptrdiff_t delta = (char*)shaderface - shaderface_orig_start;
211
212                 if (delta)
213                         {
214                         // each input->name will need adjustment (except static built-in names)
215                         for (uint32_t i = 0; i < input_ct; ++i)
216                                 {
217                                 ShaderInput* input = shaderface->inputs + i;
218
219                                 if (input->name >= shaderface_orig_start && input->name < shaderface_orig_end)
220                                         input->name += delta;
221                                 }
222                         }
223                 }
224
225         return shaderface;
226         }
227
228 void ShaderInterface_discard(ShaderInterface* shaderface)
229         {
230         // allocated as one chunk, so discard is simple
231         free(shaderface);
232         }
233
234 const ShaderInput* ShaderInterface_uniform(const ShaderInterface* shaderface, const char* name)
235         {
236         // search through custom uniforms first
237         for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
238                 {
239                 const ShaderInput* uniform = shaderface->inputs + i;
240
241                 if (uniform->builtin_type == UNIFORM_CUSTOM)
242                         {
243 #if SUPPORT_LEGACY_GLSL
244                         if (uniform->name == NULL) continue;
245 #endif
246
247                         if (match(uniform->name, name))
248                                 return uniform;
249                         }
250                 }
251
252         // search through builtin uniforms next
253         for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
254                 {
255                 const ShaderInput* uniform = shaderface->inputs + i;
256
257 #if SUPPORT_LEGACY_GLSL
258                 if (uniform->name == NULL) continue;
259 #endif
260                 if (uniform->builtin_type != UNIFORM_CUSTOM)
261                         if (match(uniform->name, name))
262                                 return uniform;
263
264                 // TODO: warn if we find a matching builtin, since these can be looked up much quicker --v
265                 }
266
267         return NULL; // not found
268         }
269
270 const ShaderInput* ShaderInterface_builtin_uniform(const ShaderInterface* shaderface, BuiltinUniform builtin)
271         {
272         // look up by enum, not name
273         for (uint32_t i = 0; i < shaderface->uniform_ct; ++i)
274                 {
275                 const ShaderInput* uniform = shaderface->inputs + i;
276
277                 if (uniform->builtin_type == builtin)
278                         return uniform;
279                 }
280         return NULL; // not found
281         }
282
283 const ShaderInput* ShaderInterface_attrib(const ShaderInterface* shaderface, const char* name)
284         {
285         // attribs are stored after uniforms
286         const uint32_t input_ct = shaderface->uniform_ct + shaderface->attrib_ct;
287         for (uint32_t i = shaderface->uniform_ct; i < input_ct; ++i)
288                 {
289                 const ShaderInput* attrib = shaderface->inputs + i;
290
291 #if SUPPORT_LEGACY_GLSL
292                 if (attrib->name == NULL) continue;
293 #endif
294
295                 if (match(attrib->name, name))
296                         return attrib;
297                 }
298         return NULL; // not found
299         }