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