filename | src/tools/genglsl.c |
changeset | 1229:dc935eee9767 |
prev | 1209:e606e65eaf54 |
next | 1234:1b836bf92653 |
author | nkeynes |
date | Thu Feb 23 19:43:24 2012 +1000 (11 years ago) |
permissions | -rw-r--r-- |
last change | Add missing float uniform type Fix previous sl cleanup being called after new sl setup is done |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * Tool to take an input .glsl file and write out a corresponding .c and .h
5 * file based on the content. The .glsl file contains a number of shaders
6 * marked with either #fragment <name> or #vertex <name>
7 * a C file with appropriate escaping, as well as program definitions
8 * written as #program <name> = <shader1> <shader2> ... <shaderN>
9 *
10 * Copyright (c) 2007-2010 Nathan Keynes.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <glib/gstrfuncs.h>
30 #include <glib/glist.h>
32 #define MAX_LINE 4096
33 #define DEF_ALLOC_SIZE 4096
34 #define MAX_SHADERS 128
36 typedef enum {
37 VERTEX_SHADER = 0,
38 FRAGMENT_SHADER = 1
39 } shader_type_t;
41 typedef struct variable {
42 gboolean uniform; /* TRUE = uniform, FALSE = attribute */
43 const char *name;
44 const char *type;
45 } *variable_t;
47 typedef struct shader {
48 shader_type_t type;
49 const char *name;
50 char *body;
51 GList *variables;
52 } *shader_t;
54 typedef struct program {
55 const char *name;
56 gchar **shader_names;
57 GList *shaders;
58 GList *variables;
59 } *program_t;
61 typedef struct glsldata {
62 const char *filename;
63 unsigned max_shaders;
64 GList *shaders;
65 GList *programs;
66 } *glsldata_t;
68 #define isident(c) (isalnum(c)||(c)=='_')
70 static void parseVarDecl( shader_t shader, gboolean uniform, char *input )
71 {
72 unsigned i;
73 char *p = g_strstrip(input);
74 for( i=0; isident(p[i]); i++)
75 if( p[i] == 0 ) {
76 fprintf( stderr, "Error: unable to parse variable decl '%s'\n", p );
77 return; /* incomplete decl? */
78 }
79 char *type = g_strndup(p, i);
80 p = g_strstrip(input+i);
81 for( i=0; isident(p[i]); i++)
82 if( p[i] == 0 ) {
83 fprintf( stderr, "Error: unable to parse variable decl '%s'\n", p );
84 return; /* incomplete decl? */
85 }
86 char *name = g_strndup(p, i);
87 variable_t var = g_malloc0(sizeof(struct variable));
88 var->uniform = uniform;
89 var->type = type;
90 var->name = name;
91 shader->variables = g_list_append(shader->variables,var);
92 }
94 static shader_t findShader( GList *shaders, const char *name )
95 {
96 GList *ptr = shaders;
97 while( ptr != NULL ) {
98 shader_t shader = ptr->data;
99 if( strcmp(shader->name, name) == 0 )
100 return shader;
101 ptr = ptr->next;
102 }
103 return NULL;
104 }
106 static gboolean addProgramVariable( program_t program, variable_t variable )
107 {
108 GList *ptr = program->variables;
109 while( ptr != NULL ) {
110 variable_t varp = ptr->data;
111 if( strcmp(varp->name, variable->name) == 0 ) {
112 if( varp->uniform == variable->uniform && strcmp(varp->type, variable->type) == 0 )
113 return TRUE; /* All ok */
114 fprintf( stderr, "Error: Variable type mismatch on '%s'\n", variable->name );
115 return FALSE;
116 }
117 ptr = ptr->next;
118 }
119 program->variables = g_list_append(program->variables, variable);
120 return TRUE;
121 }
123 static void linkPrograms( glsldata_t data )
124 {
125 GList *program_ptr = data->programs;
126 unsigned i;
127 while( program_ptr != NULL ) {
128 program_t program = program_ptr->data;
129 for( i=0; program->shader_names[i] != NULL; i++ ) {
130 shader_t shader = findShader(data->shaders, program->shader_names[i]);
131 if( shader == NULL ) {
132 fprintf( stderr, "Error: unable to resolve shader '%s'\n", program->shader_names[i] );\
133 } else {
134 GList *varptr = shader->variables;
135 while( varptr != NULL ) {
136 addProgramVariable(program, varptr->data);
137 varptr = varptr->next;
138 }
139 }
140 }
141 program_ptr = program_ptr->next;
142 }
144 }
147 static struct glsldata *readInput( const char *filename )
148 {
149 char buf[MAX_LINE];
150 size_t current_size = 0, current_posn = 0;
151 unsigned i;
153 FILE *f = fopen( filename, "ro" );
154 if( f == NULL ) {
155 fprintf( stderr, "Error: unable to open input file '%s': %s\n", filename, strerror(errno) );
156 exit(1);
157 }
159 shader_t shader = NULL;
160 glsldata_t result = g_malloc0(sizeof(struct glsldata));
161 assert( result != NULL );
162 result->filename = strdup(filename);
164 while( fgets(buf, sizeof(buf), f) != NULL ) {
165 if( strlen(buf) == 0 )
166 continue;
168 if( strncmp(buf, "#vertex ", 8) == 0 ) {
169 shader = g_malloc0(sizeof(struct shader));
170 assert( shader != NULL );
171 shader->type = VERTEX_SHADER;
172 shader->name = strdup(g_strstrip(buf+8));
173 shader->body = malloc(DEF_ALLOC_SIZE);
174 shader->body[0] = '\0';
175 current_size = DEF_ALLOC_SIZE;
176 current_posn = 0;
177 result->shaders = g_list_append(result->shaders, shader);
178 } else if( strncmp( buf, "#fragment ", 10 ) == 0 ) {
179 shader = g_malloc0(sizeof(struct shader));
180 assert( shader != NULL );
181 shader->type = FRAGMENT_SHADER;
182 shader->name = strdup(g_strstrip(buf+10));
183 shader->body = malloc(DEF_ALLOC_SIZE);
184 shader->body[0] = '\0';
185 current_size = DEF_ALLOC_SIZE;
186 current_posn = 0;
187 result->shaders = g_list_append(result->shaders, shader);
188 } else if( strncmp( buf, "#program ", 9 ) == 0 ) {
189 shader = NULL;
190 program_t program = g_malloc0(sizeof(struct program));
191 char *rest = buf+9;
192 char *equals = strchr(rest, '=');
193 if( equals == NULL ) {
194 fprintf( stderr, "Error: invalid program line %s\n", buf );
195 exit(2);
196 }
197 *equals = '\0';
198 program->name = g_strdup(g_strstrip(rest));
199 program->shader_names = g_strsplit_set(g_strstrip(equals+1), " \t\r,", 0);
200 result->programs = g_list_append(result->programs, program);
201 for(i=0;program->shader_names[i] != NULL; i++ );
202 if( i > result->max_shaders )
203 result->max_shaders = i;
204 } else if( shader != NULL ) {
205 size_t len = strlen(buf);
206 if( current_posn + len > current_size ) {
207 shader->body = realloc(shader->body, current_size*2);
208 assert( shader->body != NULL );
209 current_size *= 2;
210 }
211 strcpy( shader->body + current_posn, buf );
212 current_posn += len;
213 char *line = g_strstrip(buf);
214 if( strncmp( line, "uniform ", 8 ) == 0 ) {
215 parseVarDecl(shader, TRUE, line+8);
216 } else if( strncmp( line, "attribute ", 10 ) == 0 ) {
217 parseVarDecl(shader, FALSE, line+10);
218 }
219 }
220 }
222 fclose(f);
223 linkPrograms(result);
224 return result;
225 }
227 /**
228 * Copy input to output, quoting " characters as we go.
229 */
230 static void writeCString( FILE *out, const char *str )
231 {
232 const char *p = str;
234 while( *p != 0 ) {
235 if( *p == '\"' ) {
236 fputc( '\\', out );
237 } else if( *p == '\n' ) {
238 fputs( "\\n\\", out );
239 }
240 fputc( *p, out );
241 p++;
242 }
243 }
245 static const char *sl_type_map[][3] = {
246 {"int", "int", "int *"},
247 {"float", "float", "float *"},
248 {"short", "short", "short *"},
249 {"sampler", "int", "int *"},
250 {"vec", "GLfloat *", "GLfloat *"},
251 {"mat", "GLfloat *", "GLfloat *"},
252 {NULL, NULL}
253 };
255 static const char *getCType( const char *sl_type, gboolean isUniform ) {
256 for( unsigned i=0; sl_type_map[i][0] != NULL; i++ ) {
257 if( strncmp(sl_type_map[i][0], sl_type, strlen(sl_type_map[i][0])) == 0 ) {
258 if( isUniform ) {
259 return sl_type_map[i][1];
260 } else {
261 return sl_type_map[i][2];
262 }
263 }
264 }
265 return "void *";
266 }
268 static void writeHeader( FILE *out, glsldata_t data )
269 {
270 fprintf( out, "/*\n * This file automatically generated by genglsl from %s\n */\n", data->filename );
271 }
273 static void writeInterface( const char *filename, glsldata_t data )
274 {
275 FILE *f = fopen(filename, "wo");
276 if( f == NULL ) {
277 fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
278 exit(1);
279 }
281 writeHeader( f, data );
282 fprintf( f, "#ifndef lxdream_glsl_H\n#define lxdream_glsl_H 1\n\n" );
284 fprintf( f, "typedef enum {\n" );
285 const char *last_name = NULL;
286 int count = 0;
287 GList *shader_ptr;
288 for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
289 count++;
290 shader_t shader = (shader_t)shader_ptr->data;
291 fprintf( f, " %s,\n", shader->name );
292 last_name = shader->name;
293 }
294 fprintf( f, "} shader_id;\n\n" );
296 if( last_name == NULL )
297 last_name = "NULL";
298 fprintf( f, "#define GLSL_LAST_SHADER %s\n", last_name );
299 fprintf( f, "#define GLSL_NUM_SHADERS %d\n", count );
300 fprintf( f, "#define GLSL_NO_SHADER -1\n\n" );
301 fprintf( f, "#define GLSL_VERTEX_SHADER 1\n" );
302 fprintf( f, "#define GLSL_FRAGMENT_SHADER 2\n" );
304 count = 0;
305 GList *program_ptr;
306 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
307 count++;
308 }
309 fprintf( f, "#define GLSL_NUM_PROGRAMS %d\n", count );
311 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
312 program_t program = program_ptr->data;
313 GList *var_ptr;
314 fprintf( f, "void glsl_use_%s();\n", program->name );
315 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
316 variable_t var = var_ptr->data;
317 if( var->uniform ) {
318 fprintf( f, "void glsl_set_%s_%s(%s value); /* uniform %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name );
319 } else {
320 fprintf( f, "void glsl_set_%s_%s_pointer(%s ptr, GLint stride); /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
321 if( strcmp(var->type,"vec4") == 0 ) { /* Special case */
322 fprintf( f, "void glsl_set_%s_%s_vec3_pointer(%s ptr, GLint stride); /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
323 }
324 }
325 }
326 }
328 fprintf( f, "#endif /* !lxdream_glsl_H */\n" );
330 fclose(f);
331 }
333 static void writeSource( const char *filename, glsldata_t data )
334 {
335 FILE *f = fopen(filename, "wo");
336 if( f == NULL ) {
337 fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
338 exit(1);
339 }
341 writeHeader( f, data );
342 fprintf( f, "struct shader_def {\n int type;\n const char *source;\n};\n" );
344 fprintf( f, "const struct shader_def shader_source[] = {\n" );
345 GList *shader_ptr;
346 for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
347 shader_t shader = (shader_t)shader_ptr->data;
348 fprintf( f, " {%s,\"", (shader->type == VERTEX_SHADER ? "GLSL_VERTEX_SHADER" : "GLSL_FRAGMENT_SHADER") );
349 writeCString( f, shader->body );
350 fprintf( f, "\"},\n" );
351 }
352 fprintf( f, " {GLSL_NO_SHADER,NULL}};\n\n" );
354 fprintf( f, "const int program_list[][%d] = {\n", data->max_shaders+1 );
355 GList *program_ptr;
356 GList *var_ptr;
357 unsigned i;
358 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
359 program_t program = (program_t)program_ptr->data;
360 fprintf( f, " {" );
361 for( i=0; program->shader_names[i] != NULL; i++ ) {
362 fprintf(f, "%s,", program->shader_names[i] );
363 }
364 fprintf( f, "GLSL_NO_SHADER},\n" );
365 }
366 fprintf( f, " {GLSL_NO_SHADER}};\n" );
368 /* per-program functions */
369 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
370 program_t program = program_ptr->data;
371 fprintf( f, "\nstatic gl_program_t prog_%s_id;\n",program->name );
372 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
373 variable_t var = var_ptr->data;
374 fprintf( f, "static GLint var_%s_%s_loc;\n", program->name, var->name);
375 }
377 }
378 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
379 program_t program = program_ptr->data;
380 fprintf( f, "\nstatic void glsl_cleanup_%s() {\n", program->name );
381 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
382 variable_t var = var_ptr->data;
383 if( !var->uniform ) {
384 fprintf( f, " glsl_disable_attrib(var_%s_%s_loc);\n", program->name, var->name );
385 }
386 }
387 fprintf( f, "}\n");
389 fprintf( f, "\nvoid glsl_use_%s() {\n", program->name );
390 fprintf( f, " glsl_use_program(prog_%s_id);\n", program->name );
391 fprintf( f, " glsl_set_cleanup_fn(glsl_cleanup_%s);\n", program->name );
392 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
393 variable_t var = var_ptr->data;
394 if( !var->uniform ) {
395 fprintf( f, " glsl_enable_attrib(var_%s_%s_loc);\n", program->name, var->name );
396 }
397 }
398 fprintf( f, "}\n");
401 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
402 variable_t var = var_ptr->data;
403 if( var->uniform ) {
404 fprintf( f, "void glsl_set_%s_%s(%s value){ /* uniform %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name );
405 fprintf( f, " glsl_set_uniform_%s(var_%s_%s_loc,value);\n}\n", var->type, program->name, var->name );
406 } else {
407 fprintf( f, "void glsl_set_%s_%s_pointer(%s ptr, GLsizei stride){ /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
408 fprintf( f, " glsl_set_attrib_%s(var_%s_%s_loc,stride, ptr);\n}\n", var->type, program->name, var->name );
409 if( strcmp(var->type,"vec4") == 0 ) { /* Special case to load vec3 arrays into a vec4 */
410 fprintf( f, "void glsl_set_%s_%s_vec3_pointer(%s ptr, GLsizei stride){ /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
411 fprintf( f, " glsl_set_attrib_vec3(var_%s_%s_loc,stride, ptr);\n}\n", program->name, var->name );
412 }
413 }
414 }
415 }
417 fprintf( f, "\nstatic void glsl_init_programs( gl_program_t *ids ) {\n" );
418 for( program_ptr = data->programs, i=0; program_ptr != NULL; program_ptr = program_ptr->next, i++ ) {
419 program_t program = program_ptr->data;
421 fprintf( f, " prog_%s_id = ids[%d];\n\n", program->name, i );
422 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
423 variable_t var = var_ptr->data;
424 if( var->uniform ) {
425 fprintf( f, " var_%s_%s_loc = glsl_get_uniform_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
426 } else {
427 fprintf( f, " var_%s_%s_loc = glsl_get_attrib_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
428 }
429 }
430 }
431 fprintf( f, "}\n" );
433 fclose(f);
434 }
436 const char *makeExtension(const char *basename, const char *ext)
437 {
438 const char *oldext = strrchr(basename, '.');
439 if( oldext == NULL ) {
440 return g_strdup_printf("%s%s", basename, ext);
441 } else {
442 return g_strdup_printf("%.*s%s", (int)(oldext-basename), basename, ext);
443 }
444 }
446 int main( int argc, char *argv[] )
447 {
448 if( argc < 2 ) {
449 fprintf( stderr, "Usage: genglsl <glsl-source-file> [output.c [output.h]]\n");
450 exit(1);
451 }
453 glsldata_t data = readInput(argv[1]);
455 const char *sourcefile, *ifacefile;
456 if( argc > 2 ) {
457 sourcefile = argv[2];
458 } else {
459 sourcefile = makeExtension(argv[1], ".def");
460 }
462 if( argc > 3 ) {
463 ifacefile = argv[3];
464 } else {
465 ifacefile = makeExtension(sourcefile, ".h");
466 }
468 writeSource( sourcefile, data );
469 writeInterface( ifacefile, data );
470 return 0;
471 }
.