Search
lxdream.org :: lxdream/src/tools/genglsl.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/genglsl.c
changeset 1235:8da2f3dad9c0
prev1234:1b836bf92653
next1240:190df8a791ca
author nkeynes
date Fri Feb 24 21:11:58 2012 +1000 (9 years ago)
permissions -rw-r--r--
last change Add preprocessing support to genglsl
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_vec3_pointer(%s ptr, GLint stride); /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
   386                 }
   387             }
   388         }
   389     }
   391     fprintf( f, "#endif /* !lxdream_glsl_H */\n" );
   393     fclose(f);
   394 }
   396 static void writeSource( const char *filename, glsldata_t data )
   397 {
   398     FILE *f = fopen(filename, "wo");
   399     if( f == NULL ) {
   400         fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
   401         exit(1);
   402     }
   404     writeHeader( f, data );
   405     fprintf( f, "struct shader_def {\n    int type;\n    const char *source;\n};\n" );
   407     fprintf( f, "const struct shader_def shader_source[] = {\n" );
   408     GList *shader_ptr;
   409     for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
   410         shader_t shader = (shader_t)shader_ptr->data;
   411         fprintf( f, "    {%s,\"", (shader->type == VERTEX_SHADER ? "GLSL_VERTEX_SHADER" : "GLSL_FRAGMENT_SHADER") );
   412         writeCString( f, shader->body );
   413         fprintf( f, "\"},\n" );
   414     }
   415     fprintf( f, "    {GLSL_NO_SHADER,NULL}};\n\n" );
   417     fprintf( f, "const int program_list[][%d] = {\n", data->max_shaders+1 );
   418     GList *program_ptr;
   419     GList *var_ptr;
   420     unsigned i;
   421     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   422         program_t program = (program_t)program_ptr->data;
   423         fprintf( f, "    {" );
   424         for( i=0; program->shader_names[i] != NULL; i++ ) {
   425             fprintf(f, "%s,", program->shader_names[i] );
   426         }
   427         fprintf( f, "GLSL_NO_SHADER},\n" );
   428     }
   429     fprintf( f, "    {GLSL_NO_SHADER}};\n" );
   431     /* per-program functions */
   432     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   433         program_t program = program_ptr->data;
   434         fprintf( f, "\nstatic gl_program_t prog_%s_id;\n",program->name );
   435         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   436             variable_t var = var_ptr->data;
   437             fprintf( f, "static GLint var_%s_%s_loc;\n", program->name, var->name);
   438         }
   440     }
   441     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   442         program_t program = program_ptr->data;
   443         fprintf( f, "\nstatic void glsl_cleanup_%s() {\n", program->name );
   444         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   445             variable_t var = var_ptr->data;
   446             if( !var->uniform ) {
   447                 fprintf( f, "    glsl_disable_attrib(var_%s_%s_loc);\n", program->name, var->name );
   448             }
   449         }
   450         fprintf( f, "}\n");
   452         fprintf( f, "\nvoid glsl_use_%s() {\n", program->name );
   453         fprintf( f, "    glsl_use_program(prog_%s_id);\n", program->name );
   454         fprintf( f, "    glsl_set_cleanup_fn(glsl_cleanup_%s);\n", program->name );
   455         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   456             variable_t var = var_ptr->data;
   457             if( !var->uniform ) {
   458                 fprintf( f, "    glsl_enable_attrib(var_%s_%s_loc);\n", program->name, var->name );
   459             }
   460         }
   461         fprintf( f, "}\n");
   464         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   465             variable_t var = var_ptr->data;
   466             if( var->uniform ) {
   467                 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 );
   468                 fprintf( f, "    glsl_set_uniform_%s(var_%s_%s_loc,value);\n}\n", var->type, program->name, var->name );
   469             } else {
   470                 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);
   471                 fprintf( f, "    glsl_set_attrib_%s(var_%s_%s_loc,stride, ptr);\n}\n", var->type, program->name, var->name );
   472                 if( strcmp(var->type,"vec4") == 0 ) { /* Special case to load vec3 arrays into a vec4 */
   473                     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);
   474                     fprintf( f, "    glsl_set_attrib_vec3(var_%s_%s_loc,stride, ptr);\n}\n", program->name, var->name );
   475                 }
   476             }
   477         }
   478     }
   480     fprintf( f, "\nstatic void glsl_init_programs( gl_program_t *ids ) {\n" );
   481     for( program_ptr = data->programs, i=0; program_ptr != NULL; program_ptr = program_ptr->next, i++ ) {
   482         program_t program = program_ptr->data;
   484         fprintf( f, "    prog_%s_id = ids[%d];\n\n", program->name, i );
   485         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   486             variable_t var = var_ptr->data;
   487             if( var->uniform ) {
   488                 fprintf( f, "    var_%s_%s_loc = glsl_get_uniform_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
   489             } else {
   490                 fprintf( f, "    var_%s_%s_loc = glsl_get_attrib_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
   491             }
   492         }
   493     }
   494     fprintf( f, "}\n" );
   496     fclose(f);
   497 }
   499 static const char *makeExtension(const char *basename, const char *ext)
   500 {
   501     const char *oldext = strrchr(basename, '.');
   502     if( oldext == NULL ) {
   503         return g_strdup_printf("%s%s", basename, ext);
   504     } else {
   505         return g_strdup_printf("%.*s%s", (int)(oldext-basename), basename, ext);
   506     }
   507 }
   509 static char *option_list = "hi:I:D:U:o:";
   510 static struct option long_option_list[] = {
   511         { "help", no_argument, NULL, 'h' },
   512         { "interface", required_argument, NULL, 'i' },
   513         { "output", required_argument, NULL, 'o' },
   514         { NULL, 0, 0, 0 } };
   516 static void usage() {
   517     fprintf( stderr, "Usage: genglsl <glsl-source-list> [-o output.def] [-i output.h]\n");
   518 }
   519 int main( int argc, char *argv[] )
   520 {
   521     const char *output_file = NULL;
   522     const char *iface_file = NULL;
   523     GList *cpp_opts = NULL;
   524     int opt;
   526     while( (opt = getopt_long( argc, argv, option_list, long_option_list, NULL )) != -1 ) {
   527         switch( opt ) {
   528         case 'h':
   529             usage();
   530             exit(0);
   531             break;
   532         case 'D': case 'I': case 'U':
   533             cpp_opts = g_list_append(cpp_opts, g_strdup_printf( "-%c%s", opt, optarg ));
   534             break;
   535         case 'i':
   536             if( iface_file != NULL ) {
   537                 fprintf( stderr, "Error: at most one interface file can be supplied\n" );
   538                 usage();
   539                 exit(1);
   540             }
   541             iface_file = optarg;
   542             break;
   543         case 'o':
   544             if( output_file != NULL ) {
   545                 fprintf( stderr, "Error: at most one output file can be supplied\n" );
   546                 usage();
   547                 exit(1);
   548             }
   549             output_file = optarg;
   550         }
   551     }
   553     if( optind == argc ) {
   554         usage();
   555         exit(1);
   556     }
   558     if( output_file == NULL ) {
   559         output_file = makeExtension(argv[optind], ".def");
   560     }
   561     if( iface_file == NULL ) {
   562         iface_file = makeExtension(output_file, ".h");
   563     }
   565     atexit(cleanup_tempfiles);
   566     glsldata_t data = g_malloc0(sizeof(struct glsldata));
   567     while( optind < argc ) {
   568         readInput(argv[optind++], cpp_opts, data);
   569     }
   570     linkPrograms(data);
   572     writeSource( output_file, data );
   573     writeInterface( iface_file, data );
   574     return 0;
   575 }
.