filename | src/tools/genglsl.c |
changeset | 1296:30ecee61f811 |
prev | 1288:fdb8f59143c7 |
author | nkeynes |
date | Sat Jan 26 14:00:48 2013 +1000 (9 years ago) |
permissions | -rw-r--r-- |
last change | Change glib includes to #include <glib.h> rather than the individual headers, as recent glib versions are breaking on this |
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-2012 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 <getopt.h>
30 #include <glib.h>
31 #include "../../config.h"
33 #define MAX_LINE 4096
34 #define DEF_ALLOC_SIZE 4096
35 #define MAX_SHADERS 128
37 typedef enum {
38 VERTEX_SHADER = 0,
39 FRAGMENT_SHADER = 1
40 } shader_type_t;
42 typedef struct variable {
43 gboolean uniform; /* TRUE = uniform, FALSE = attribute */
44 const char *name;
45 const char *type;
46 } *variable_t;
48 typedef struct shader {
49 shader_type_t type;
50 const char *name;
51 char *body;
52 GList *variables;
53 } *shader_t;
55 typedef struct program {
56 const char *name;
57 gchar **shader_names;
58 GList *shaders;
59 GList *variables;
60 } *program_t;
62 typedef struct glsldata {
63 const char *filename;
64 unsigned max_shaders;
65 GList *shaders;
66 GList *programs;
67 } *glsldata_t;
69 #define isident(c) (isalnum(c)||(c)=='_')
71 static void parseVarDecl( shader_t shader, gboolean uniform, char *input )
72 {
73 unsigned i;
74 char *p = g_strstrip(input);
75 for( i=0; isident(p[i]); i++)
76 if( p[i] == 0 ) {
77 fprintf( stderr, "Error: unable to parse variable decl '%s'\n", p );
78 return; /* incomplete decl? */
79 }
80 char *type = g_strndup(p, i);
81 p = g_strstrip(input+i);
82 for( i=0; isident(p[i]); i++)
83 if( p[i] == 0 ) {
84 fprintf( stderr, "Error: unable to parse variable decl '%s'\n", p );
85 return; /* incomplete decl? */
86 }
87 char *name = g_strndup(p, i);
88 variable_t var = g_malloc0(sizeof(struct variable));
89 var->uniform = uniform;
90 var->type = type;
91 var->name = name;
92 shader->variables = g_list_append(shader->variables,var);
93 }
95 static shader_t findShader( GList *shaders, const char *name )
96 {
97 GList *ptr = shaders;
98 while( ptr != NULL ) {
99 shader_t shader = ptr->data;
100 if( strcmp(shader->name, name) == 0 )
101 return shader;
102 ptr = ptr->next;
103 }
104 return NULL;
105 }
107 static gboolean addProgramVariable( program_t program, variable_t variable )
108 {
109 GList *ptr = program->variables;
110 while( ptr != NULL ) {
111 variable_t varp = ptr->data;
112 if( strcmp(varp->name, variable->name) == 0 ) {
113 if( varp->uniform == variable->uniform && strcmp(varp->type, variable->type) == 0 )
114 return TRUE; /* All ok */
115 fprintf( stderr, "Error: Variable type mismatch on '%s'\n", variable->name );
116 return FALSE;
117 }
118 ptr = ptr->next;
119 }
120 program->variables = g_list_append(program->variables, variable);
121 return TRUE;
122 }
124 static void linkPrograms( glsldata_t data )
125 {
126 GList *program_ptr = data->programs;
127 unsigned i;
128 while( program_ptr != NULL ) {
129 program_t program = program_ptr->data;
130 for( i=0; program->shader_names[i] != NULL; i++ ) {
131 shader_t shader = findShader(data->shaders, program->shader_names[i]);
132 if( shader == NULL ) {
133 fprintf( stderr, "Error: unable to resolve shader '%s'\n", program->shader_names[i] );\
134 } else {
135 GList *varptr = shader->variables;
136 while( varptr != NULL ) {
137 addProgramVariable(program, varptr->data);
138 varptr = varptr->next;
139 }
140 }
141 }
142 program_ptr = program_ptr->next;
143 }
145 }
147 static GList *temp_filenames = NULL;
149 static void cleanup_tempfiles(void)
150 {
151 while( temp_filenames != NULL ) {
152 unlink( (char *)temp_filenames->data );
153 g_free(temp_filenames->data);
154 temp_filenames = temp_filenames->next;
155 }
156 }
158 /**
159 * Preprocess the input file and return a temporary filename containing the result
160 */
161 static FILE *preprocessInput( const char *filename, GList *cpp_opts )
162 {
163 char tmpname[] = "/tmp/genglsl.XXXXXXXX";
164 int fd = mkstemp(tmpname);
165 if( fd == -1 ) {
166 fprintf( stderr, "Error: unable to get a temporary filename (%s)\n", strerror(errno) );
167 exit(2);
168 }
169 char *quoted_filename = g_shell_quote(filename);
171 int nOpts = g_list_length(cpp_opts);
172 gchar *quoted_opts_arr[nOpts];
173 int length = 0, count=0;
174 for( GList *ptr = cpp_opts; ptr != NULL; ptr = ptr->next ) {
175 quoted_opts_arr[count] = g_shell_quote((gchar *)ptr->data);
176 length += strlen(quoted_opts_arr[count++]) + 1;
177 }
178 gchar quoted_cpp_opts[length];
179 quoted_cpp_opts[0] = '\0';
180 for( count=0; count<nOpts; count++ ) {
181 if( count != 0 )
182 strcat(quoted_cpp_opts, " ");
183 strcat(quoted_cpp_opts, quoted_opts_arr[count]);
184 g_free(quoted_opts_arr[count++]);
185 }
187 const char *command = g_strdup_printf("%s -e 's/^#program/#pragma program/' -e 's/^#fragment/#pragma fragment/' -e 's/#vertex/#pragma vertex/' %s | %s %s - > %s",
188 BUILD_SED_PROG, quoted_filename, BUILD_CPP_PROG, quoted_cpp_opts, tmpname );
189 if( system(command) != 0 ) {
190 fprintf( stderr, "Error: unable to run preprocessor command '%s' (%s)\n", command, strerror(errno) );
191 exit(2);
192 }
194 temp_filenames = g_list_append(temp_filenames, g_strdup(tmpname));
195 return fdopen(fd, "r");
196 }
198 static void readInput( const char *filename, GList *cpp_opts, glsldata_t result )
199 {
200 char buf[MAX_LINE];
201 size_t current_size = 0, current_posn = 0;
202 unsigned i;
205 FILE *f = preprocessInput(filename, cpp_opts );
206 if( f == NULL ) {
207 fprintf( stderr, "Error: unable to open input file '%s': %s\n", filename, strerror(errno) );
208 exit(2);
209 }
211 shader_t shader = NULL;
212 if( result->filename == NULL ) {
213 result->filename = g_strdup(filename);
214 } else {
215 const gchar *tmp = result->filename;
216 result->filename = g_strdup_printf("%s, %s", tmp, filename);
217 g_free((gchar *)tmp);
218 }
220 while( fgets(buf, sizeof(buf), f) != NULL ) {
221 if( strlen(buf) == 0 )
222 continue;
224 if( buf[0] == '#' ) {
225 char *p = buf+1;
226 if( strncmp(p, "pragma ", 7) == 0 ) {
227 p += 7;
228 }
229 if( strncmp(p, "vertex ", 7) == 0 ) {
230 shader = g_malloc0(sizeof(struct shader));
231 assert( shader != NULL );
232 shader->type = VERTEX_SHADER;
233 shader->name = strdup(g_strstrip(p+7));
234 shader->body = malloc(DEF_ALLOC_SIZE);
235 shader->body[0] = '\0';
236 current_size = DEF_ALLOC_SIZE;
237 current_posn = 0;
238 result->shaders = g_list_append(result->shaders, shader);
239 } else if( strncmp( p, "fragment ", 9 ) == 0 ) {
240 shader = g_malloc0(sizeof(struct shader));
241 assert( shader != NULL );
242 shader->type = FRAGMENT_SHADER;
243 shader->name = strdup(g_strstrip(p+9));
244 shader->body = malloc(DEF_ALLOC_SIZE);
245 shader->body[0] = '\0';
246 current_size = DEF_ALLOC_SIZE;
247 current_posn = 0;
248 result->shaders = g_list_append(result->shaders, shader);
249 } else if( strncmp( p, "program ", 8 ) == 0 ) {
250 shader = NULL;
251 program_t program = g_malloc0(sizeof(struct program));
252 char *rest = p+8;
253 char *equals = strchr(rest, '=');
254 if( equals == NULL ) {
255 fprintf( stderr, "Error: invalid program line %s\n", buf );
256 exit(2);
257 }
258 *equals = '\0';
259 program->name = g_strdup(g_strstrip(rest));
260 program->shader_names = g_strsplit_set(g_strstrip(equals+1), " \t\r,", 0);
261 result->programs = g_list_append(result->programs, program);
262 for(i=0;program->shader_names[i] != NULL; i++ );
263 if( i > result->max_shaders )
264 result->max_shaders = i;
265 }
266 /* Else discard any other # lines */
267 } else if( shader != NULL ) {
268 size_t len = strlen(buf);
269 if( current_posn + len > current_size ) {
270 shader->body = realloc(shader->body, current_size*2);
271 assert( shader->body != NULL );
272 current_size *= 2;
273 }
274 strcpy( shader->body + current_posn, buf );
275 current_posn += len;
276 char *line = g_strstrip(buf);
277 if( strncmp( line, "uniform ", 8 ) == 0 ) {
278 parseVarDecl(shader, TRUE, line+8);
279 } else if( strncmp( line, "attribute ", 10 ) == 0 ) {
280 parseVarDecl(shader, FALSE, line+10);
281 }
282 }
283 }
285 fclose(f);
286 }
288 /**
289 * Copy input to output, quoting " characters as we go.
290 */
291 static void writeCString( FILE *out, const char *str )
292 {
293 const char *p = str;
295 while( *p != 0 ) {
296 if( *p == '\"' ) {
297 fputc( '\\', out );
298 } else if( *p == '\n' ) {
299 fputs( "\\n\\", out );
300 }
301 fputc( *p, out );
302 p++;
303 }
304 }
306 static const char *sl_type_map[][3] = {
307 {"int", "int", "int *"},
308 {"float", "float", "float *"},
309 {"short", "short", "short *"},
310 {"sampler", "int", "int *"},
311 {"vec", "GLfloat *", "GLfloat *"},
312 {"mat", "GLfloat *", "GLfloat *"},
313 {NULL, NULL}
314 };
316 static const char *getCType( const char *sl_type, gboolean isUniform ) {
317 for( unsigned i=0; sl_type_map[i][0] != NULL; i++ ) {
318 if( strncmp(sl_type_map[i][0], sl_type, strlen(sl_type_map[i][0])) == 0 ) {
319 if( isUniform ) {
320 return sl_type_map[i][1];
321 } else {
322 return sl_type_map[i][2];
323 }
324 }
325 }
326 return "void *";
327 }
329 static void writeHeader( FILE *out, glsldata_t data )
330 {
331 fprintf( out, "/*\n * This file automatically generated by genglsl from %s\n */\n", data->filename );
332 }
334 static void writeInterface( const char *filename, glsldata_t data )
335 {
336 FILE *f = fopen(filename, "wo");
337 if( f == NULL ) {
338 fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
339 exit(1);
340 }
342 writeHeader( f, data );
343 fprintf( f, "#ifndef lxdream_glsl_H\n#define lxdream_glsl_H 1\n\n" );
345 fprintf( f, "typedef enum {\n" );
346 const char *last_name = NULL;
347 int count = 0;
348 GList *shader_ptr;
349 for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
350 count++;
351 shader_t shader = (shader_t)shader_ptr->data;
352 fprintf( f, " %s,\n", shader->name );
353 last_name = shader->name;
354 }
355 fprintf( f, "} shader_id;\n\n" );
357 if( last_name == NULL )
358 last_name = "NULL";
359 fprintf( f, "#define GLSL_LAST_SHADER %s\n", last_name );
360 fprintf( f, "#define GLSL_NUM_SHADERS %d\n", count );
361 fprintf( f, "#define GLSL_NO_SHADER -1\n\n" );
362 fprintf( f, "#define GLSL_VERTEX_SHADER 1\n" );
363 fprintf( f, "#define GLSL_FRAGMENT_SHADER 2\n" );
365 count = 0;
366 GList *program_ptr;
367 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
368 count++;
369 }
370 fprintf( f, "#define GLSL_NUM_PROGRAMS %d\n", count );
372 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
373 program_t program = program_ptr->data;
374 GList *var_ptr;
375 fprintf( f, "void glsl_use_%s();\n", program->name );
376 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
377 variable_t var = var_ptr->data;
378 if( var->uniform ) {
379 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 );
380 } else {
381 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);
382 if( strcmp(var->type,"vec4") == 0 ) { /* Special case */
383 fprintf( f, "void glsl_set_%s_%s_vec2_pointer(%s ptr, GLint stride); /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
384 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);
385 }
386 }
387 }
388 }
390 fprintf( f, "void glsl_clear_shader();\n" );
392 fprintf( f, "#endif /* !lxdream_glsl_H */\n" );
394 fclose(f);
395 }
397 static void writeSource( const char *filename, glsldata_t data )
398 {
399 FILE *f = fopen(filename, "wo");
400 if( f == NULL ) {
401 fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
402 exit(1);
403 }
405 writeHeader( f, data );
406 fprintf( f, "struct shader_def {\n int type;\n const char *source;\n};\n" );
408 fprintf( f, "const struct shader_def shader_source[] = {\n" );
409 GList *shader_ptr;
410 for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
411 shader_t shader = (shader_t)shader_ptr->data;
412 fprintf( f, " {%s,\"", (shader->type == VERTEX_SHADER ? "GLSL_VERTEX_SHADER" : "GLSL_FRAGMENT_SHADER") );
413 writeCString( f, shader->body );
414 fprintf( f, "\"},\n" );
415 }
416 fprintf( f, " {GLSL_NO_SHADER,NULL}};\n\n" );
418 fprintf( f, "const int program_list[][%d] = {\n", data->max_shaders+1 );
419 GList *program_ptr;
420 GList *var_ptr;
421 unsigned i;
422 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
423 program_t program = (program_t)program_ptr->data;
424 fprintf( f, " {" );
425 for( i=0; program->shader_names[i] != NULL; i++ ) {
426 fprintf(f, "%s,", program->shader_names[i] );
427 }
428 fprintf( f, "GLSL_NO_SHADER},\n" );
429 }
430 fprintf( f, " {GLSL_NO_SHADER}};\n" );
432 /* per-program functions */
433 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
434 program_t program = program_ptr->data;
435 fprintf( f, "\nstatic gl_program_t prog_%s_id;\n",program->name );
436 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
437 variable_t var = var_ptr->data;
438 fprintf( f, "static GLint var_%s_%s_loc;\n", program->name, var->name);
439 }
441 }
442 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
443 program_t program = program_ptr->data;
444 fprintf( f, "\nstatic void glsl_cleanup_%s() {\n", program->name );
445 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
446 variable_t var = var_ptr->data;
447 if( !var->uniform ) {
448 fprintf( f, " glsl_disable_attrib(var_%s_%s_loc);\n", program->name, var->name );
449 }
450 }
451 fprintf( f, "}\n");
453 fprintf( f, "\nvoid glsl_use_%s() {\n", program->name );
454 fprintf( f, " glsl_use_program(prog_%s_id);\n", program->name );
455 fprintf( f, " glsl_set_cleanup_fn(glsl_cleanup_%s);\n", program->name );
456 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
457 variable_t var = var_ptr->data;
458 if( !var->uniform ) {
459 fprintf( f, " glsl_enable_attrib(var_%s_%s_loc);\n", program->name, var->name );
460 }
461 }
462 fprintf( f, "}\n");
465 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
466 variable_t var = var_ptr->data;
467 if( var->uniform ) {
468 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 );
469 fprintf( f, " glsl_set_uniform_%s(var_%s_%s_loc,value);\n}\n", var->type, program->name, var->name );
470 } else {
471 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);
472 fprintf( f, " glsl_set_attrib_%s(var_%s_%s_loc,stride, ptr);\n}\n", var->type, program->name, var->name );
473 if( strcmp(var->type,"vec4") == 0 ) { /* Special case to load vec3 arrays into a vec4 */
474 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);
475 fprintf( f, " glsl_set_attrib_vec3(var_%s_%s_loc,stride, ptr);\n}\n", program->name, var->name );
476 fprintf( f, "void glsl_set_%s_%s_vec2_pointer(%s ptr, GLsizei stride){ /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
477 fprintf( f, " glsl_set_attrib_vec2(var_%s_%s_loc,stride, ptr);\n}\n", program->name, var->name );
478 }
479 }
480 }
481 }
483 fprintf( f, "\nvoid glsl_clear_shader() {\n" );
484 fprintf( f, " glsl_run_cleanup_fn();\n glsl_use_program(0);\n}\n" );
486 fprintf( f, "\nstatic void glsl_init_programs( gl_program_t *ids ) {\n" );
487 for( program_ptr = data->programs, i=0; program_ptr != NULL; program_ptr = program_ptr->next, i++ ) {
488 program_t program = program_ptr->data;
490 fprintf( f, " prog_%s_id = ids[%d];\n\n", program->name, i );
491 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
492 variable_t var = var_ptr->data;
493 if( var->uniform ) {
494 fprintf( f, " var_%s_%s_loc = glsl_get_uniform_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
495 } else {
496 fprintf( f, " var_%s_%s_loc = glsl_get_attrib_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
497 }
498 }
499 }
501 fprintf( f, "}\n" );
503 fclose(f);
504 }
506 static const char *makeExtension(const char *basename, const char *ext)
507 {
508 const char *oldext = strrchr(basename, '.');
509 if( oldext == NULL ) {
510 return g_strdup_printf("%s%s", basename, ext);
511 } else {
512 return g_strdup_printf("%.*s%s", (int)(oldext-basename), basename, ext);
513 }
514 }
516 static char *option_list = "hi:I:D:U:o:";
517 static struct option long_option_list[] = {
518 { "help", no_argument, NULL, 'h' },
519 { "interface", required_argument, NULL, 'i' },
520 { "output", required_argument, NULL, 'o' },
521 { NULL, 0, 0, 0 } };
523 static void usage() {
524 fprintf( stderr, "Usage: genglsl <glsl-source-list> [-o output.def] [-i output.h]\n");
525 }
526 int main( int argc, char *argv[] )
527 {
528 const char *output_file = NULL;
529 const char *iface_file = NULL;
530 GList *cpp_opts = NULL;
531 int opt;
533 while( (opt = getopt_long( argc, argv, option_list, long_option_list, NULL )) != -1 ) {
534 switch( opt ) {
535 case 'h':
536 usage();
537 exit(0);
538 break;
539 case 'D': case 'I': case 'U':
540 cpp_opts = g_list_append(cpp_opts, g_strdup_printf( "-%c%s", opt, optarg ));
541 break;
542 case 'i':
543 if( iface_file != NULL ) {
544 fprintf( stderr, "Error: at most one interface file can be supplied\n" );
545 usage();
546 exit(1);
547 }
548 iface_file = optarg;
549 break;
550 case 'o':
551 if( output_file != NULL ) {
552 fprintf( stderr, "Error: at most one output file can be supplied\n" );
553 usage();
554 exit(1);
555 }
556 output_file = optarg;
557 }
558 }
560 if( optind == argc ) {
561 usage();
562 exit(1);
563 }
565 if( output_file == NULL ) {
566 output_file = makeExtension(argv[optind], ".def");
567 }
568 if( iface_file == NULL ) {
569 iface_file = makeExtension(output_file, ".h");
570 }
572 atexit(cleanup_tempfiles);
573 glsldata_t data = g_malloc0(sizeof(struct glsldata));
574 while( optind < argc ) {
575 readInput(argv[optind++], cpp_opts, data);
576 }
577 linkPrograms(data);
579 writeSource( output_file, data );
580 writeInterface( iface_file, data );
581 return 0;
582 }
.