Search
lxdream.org :: lxdream/src/tools/genglsl.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/genglsl.c
changeset 1234:1b836bf92653
prev1229:dc935eee9767
next1235:8da2f3dad9c0
author nkeynes
date Fri Feb 24 17:31:18 2012 +1000 (9 years ago)
permissions -rw-r--r--
last change Add some real option processing to genglsl and let it accept multiple glsl
input files (basically concatenate them together)
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/glist.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 }
   148 static void readInput( const char *filename, glsldata_t result )
   149 {
   150     char buf[MAX_LINE];
   151     size_t current_size = 0, current_posn = 0;
   152     unsigned i;
   154     FILE *f = fopen( filename, "ro" );
   155     if( f == NULL ) {
   156         fprintf( stderr, "Error: unable to open input file '%s': %s\n", filename, strerror(errno) );
   157         exit(2);
   158     }
   160     shader_t shader = NULL;
   161     if( result->filename == NULL ) {
   162         result->filename = g_strdup(filename);
   163     } else {
   164         const gchar *tmp = result->filename;
   165         result->filename = g_strdup_printf("%s, %s", tmp, filename);
   166         g_free((gchar *)tmp);
   167     }
   169     while( fgets(buf, sizeof(buf), f) != NULL ) {
   170         if( strlen(buf) == 0 )
   171             continue;
   173         if( strncmp(buf, "#vertex ", 8) == 0 ) {
   174             shader = g_malloc0(sizeof(struct shader));
   175             assert( shader != NULL );
   176             shader->type = VERTEX_SHADER;
   177             shader->name = strdup(g_strstrip(buf+8));
   178             shader->body = malloc(DEF_ALLOC_SIZE);
   179             shader->body[0] = '\0';
   180             current_size = DEF_ALLOC_SIZE;
   181             current_posn = 0;
   182             result->shaders = g_list_append(result->shaders, shader);
   183         } else if( strncmp( buf, "#fragment ", 10 ) == 0 ) {
   184             shader = g_malloc0(sizeof(struct shader));
   185             assert( shader != NULL );
   186             shader->type = FRAGMENT_SHADER;
   187             shader->name = strdup(g_strstrip(buf+10));
   188             shader->body = malloc(DEF_ALLOC_SIZE);
   189             shader->body[0] = '\0';
   190             current_size = DEF_ALLOC_SIZE;
   191             current_posn = 0;
   192             result->shaders = g_list_append(result->shaders, shader);
   193         } else if( strncmp( buf, "#program ", 9 ) == 0 ) {
   194             shader = NULL;
   195             program_t program = g_malloc0(sizeof(struct program));
   196             char *rest = buf+9;
   197             char *equals = strchr(rest, '=');
   198             if( equals == NULL ) {
   199                 fprintf( stderr, "Error: invalid program line %s\n", buf );
   200                 exit(2);
   201             }
   202             *equals = '\0';
   203             program->name = g_strdup(g_strstrip(rest));
   204             program->shader_names = g_strsplit_set(g_strstrip(equals+1), " \t\r,", 0);
   205             result->programs = g_list_append(result->programs, program);
   206             for(i=0;program->shader_names[i] != NULL; i++ );
   207             if( i > result->max_shaders )
   208                 result->max_shaders = i;
   209         } else if( shader != NULL ) {
   210             size_t len = strlen(buf);
   211             if( current_posn + len > current_size ) {
   212                 shader->body = realloc(shader->body, current_size*2);
   213                 assert( shader->body != NULL );
   214                 current_size *= 2;
   215             }
   216             strcpy( shader->body + current_posn, buf );
   217             current_posn += len;
   218             char *line = g_strstrip(buf);
   219             if( strncmp( line, "uniform ", 8 ) == 0 ) {
   220                 parseVarDecl(shader, TRUE, line+8);
   221             } else if( strncmp( line, "attribute ", 10 ) == 0 ) {
   222                 parseVarDecl(shader, FALSE, line+10);
   223             }
   224         }
   225     }
   227     fclose(f);
   228 }
   230 /**
   231  * Copy input to output, quoting " characters as we go.
   232  */
   233 static void writeCString( FILE *out, const char *str )
   234 {
   235     const char *p = str;
   237     while( *p != 0 ) {
   238         if( *p == '\"' ) {
   239             fputc( '\\', out );
   240         } else if( *p == '\n' ) {
   241             fputs( "\\n\\", out );
   242         }
   243         fputc( *p, out );
   244         p++;
   245     }
   246 }
   248 static const char *sl_type_map[][3] = {
   249         {"int", "int", "int *"},
   250         {"float", "float", "float *"},
   251         {"short", "short", "short *"},
   252         {"sampler", "int", "int *"},
   253         {"vec", "GLfloat *", "GLfloat *"},
   254         {"mat", "GLfloat *", "GLfloat *"},
   255         {NULL, NULL}
   256 };
   258 static const char *getCType( const char *sl_type, gboolean isUniform ) {
   259     for( unsigned i=0; sl_type_map[i][0] != NULL; i++ ) {
   260         if( strncmp(sl_type_map[i][0], sl_type, strlen(sl_type_map[i][0])) == 0 ) {
   261             if( isUniform ) {
   262                 return sl_type_map[i][1];
   263             } else {
   264                 return sl_type_map[i][2];
   265             }
   266         }
   267     }
   268     return "void *";
   269 }
   271 static void writeHeader( FILE *out, glsldata_t data )
   272 {
   273     fprintf( out, "/*\n * This file automatically generated by genglsl from %s\n */\n", data->filename );
   274 }
   276 static void writeInterface( const char *filename, glsldata_t data )
   277 {
   278     FILE *f = fopen(filename, "wo");
   279     if( f == NULL ) {
   280         fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
   281         exit(1);
   282     }
   284     writeHeader( f, data );
   285     fprintf( f, "#ifndef lxdream_glsl_H\n#define lxdream_glsl_H 1\n\n" );
   287     fprintf( f, "typedef enum {\n" );
   288     const char *last_name = NULL;
   289     int count = 0;
   290     GList *shader_ptr;
   291     for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
   292         count++;
   293         shader_t shader = (shader_t)shader_ptr->data;
   294         fprintf( f, "    %s,\n", shader->name );
   295         last_name = shader->name;
   296     }
   297     fprintf( f, "} shader_id;\n\n" );
   299     if( last_name == NULL )
   300         last_name = "NULL";
   301     fprintf( f, "#define GLSL_LAST_SHADER %s\n", last_name );
   302     fprintf( f, "#define GLSL_NUM_SHADERS %d\n", count );
   303     fprintf( f, "#define GLSL_NO_SHADER -1\n\n" );
   304     fprintf( f, "#define GLSL_VERTEX_SHADER 1\n" );
   305     fprintf( f, "#define GLSL_FRAGMENT_SHADER 2\n" );
   307     count = 0;
   308     GList *program_ptr;
   309     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   310         count++;
   311     }
   312     fprintf( f, "#define GLSL_NUM_PROGRAMS %d\n", count );
   314     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   315         program_t program = program_ptr->data;
   316         GList *var_ptr;
   317         fprintf( f, "void glsl_use_%s();\n", program->name );
   318         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   319             variable_t var = var_ptr->data;
   320             if( var->uniform ) {
   321                 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 );
   322             } else {
   323                 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);
   324                 if( strcmp(var->type,"vec4") == 0 ) { /* Special case */
   325                     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);
   326                 }
   327             }
   328         }
   329     }
   331     fprintf( f, "#endif /* !lxdream_glsl_H */\n" );
   333     fclose(f);
   334 }
   336 static void writeSource( 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, "struct shader_def {\n    int type;\n    const char *source;\n};\n" );
   347     fprintf( f, "const struct shader_def shader_source[] = {\n" );
   348     GList *shader_ptr;
   349     for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
   350         shader_t shader = (shader_t)shader_ptr->data;
   351         fprintf( f, "    {%s,\"", (shader->type == VERTEX_SHADER ? "GLSL_VERTEX_SHADER" : "GLSL_FRAGMENT_SHADER") );
   352         writeCString( f, shader->body );
   353         fprintf( f, "\"},\n" );
   354     }
   355     fprintf( f, "    {GLSL_NO_SHADER,NULL}};\n\n" );
   357     fprintf( f, "const int program_list[][%d] = {\n", data->max_shaders+1 );
   358     GList *program_ptr;
   359     GList *var_ptr;
   360     unsigned i;
   361     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   362         program_t program = (program_t)program_ptr->data;
   363         fprintf( f, "    {" );
   364         for( i=0; program->shader_names[i] != NULL; i++ ) {
   365             fprintf(f, "%s,", program->shader_names[i] );
   366         }
   367         fprintf( f, "GLSL_NO_SHADER},\n" );
   368     }
   369     fprintf( f, "    {GLSL_NO_SHADER}};\n" );
   371     /* per-program functions */
   372     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   373         program_t program = program_ptr->data;
   374         fprintf( f, "\nstatic gl_program_t prog_%s_id;\n",program->name );
   375         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   376             variable_t var = var_ptr->data;
   377             fprintf( f, "static GLint var_%s_%s_loc;\n", program->name, var->name);
   378         }
   380     }
   381     for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
   382         program_t program = program_ptr->data;
   383         fprintf( f, "\nstatic void glsl_cleanup_%s() {\n", program->name );
   384         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   385             variable_t var = var_ptr->data;
   386             if( !var->uniform ) {
   387                 fprintf( f, "    glsl_disable_attrib(var_%s_%s_loc);\n", program->name, var->name );
   388             }
   389         }
   390         fprintf( f, "}\n");
   392         fprintf( f, "\nvoid glsl_use_%s() {\n", program->name );
   393         fprintf( f, "    glsl_use_program(prog_%s_id);\n", program->name );
   394         fprintf( f, "    glsl_set_cleanup_fn(glsl_cleanup_%s);\n", program->name );
   395         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   396             variable_t var = var_ptr->data;
   397             if( !var->uniform ) {
   398                 fprintf( f, "    glsl_enable_attrib(var_%s_%s_loc);\n", program->name, var->name );
   399             }
   400         }
   401         fprintf( f, "}\n");
   404         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   405             variable_t var = var_ptr->data;
   406             if( var->uniform ) {
   407                 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 );
   408                 fprintf( f, "    glsl_set_uniform_%s(var_%s_%s_loc,value);\n}\n", var->type, program->name, var->name );
   409             } else {
   410                 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);
   411                 fprintf( f, "    glsl_set_attrib_%s(var_%s_%s_loc,stride, ptr);\n}\n", var->type, program->name, var->name );
   412                 if( strcmp(var->type,"vec4") == 0 ) { /* Special case to load vec3 arrays into a vec4 */
   413                     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);
   414                     fprintf( f, "    glsl_set_attrib_vec3(var_%s_%s_loc,stride, ptr);\n}\n", program->name, var->name );
   415                 }
   416             }
   417         }
   418     }
   420     fprintf( f, "\nstatic void glsl_init_programs( gl_program_t *ids ) {\n" );
   421     for( program_ptr = data->programs, i=0; program_ptr != NULL; program_ptr = program_ptr->next, i++ ) {
   422         program_t program = program_ptr->data;
   424         fprintf( f, "    prog_%s_id = ids[%d];\n\n", program->name, i );
   425         for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
   426             variable_t var = var_ptr->data;
   427             if( var->uniform ) {
   428                 fprintf( f, "    var_%s_%s_loc = glsl_get_uniform_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
   429             } else {
   430                 fprintf( f, "    var_%s_%s_loc = glsl_get_attrib_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
   431             }
   432         }
   433     }
   434     fprintf( f, "}\n" );
   436     fclose(f);
   437 }
   439 static const char *makeExtension(const char *basename, const char *ext)
   440 {
   441     const char *oldext = strrchr(basename, '.');
   442     if( oldext == NULL ) {
   443         return g_strdup_printf("%s%s", basename, ext);
   444     } else {
   445         return g_strdup_printf("%.*s%s", (int)(oldext-basename), basename, ext);
   446     }
   447 }
   449 static char *option_list = "hi:o:";
   450 static struct option long_option_list[] = {
   451         { "help", no_argument, NULL, 'h' },
   452         { "interface", required_argument, 'i' },
   453         { "output", required_argument, NULL, 'o' },
   454         { NULL, 0, 0, 0 } };
   456 static void usage() {
   457     fprintf( stderr, "Usage: genglsl <glsl-source-list> [-o output.def] [-i output.h]\n");
   458 }
   459 int main( int argc, char *argv[] )
   460 {
   461     const char *output_file = NULL;
   462     const char *iface_file = NULL;
   463     int opt;
   465     while( (opt = getopt_long( argc, argv, option_list, long_option_list, NULL )) != -1 ) {
   466         switch( opt ) {
   467         case 'h':
   468             usage();
   469             exit(0);
   470             break;
   471         case 'i':
   472             if( iface_file != NULL ) {
   473                 fprintf( stderr, "Error: at most one interface file can be supplied\n" );
   474                 usage();
   475                 exit(1);
   476             }
   477             iface_file = optarg;
   478             break;
   479         case 'o':
   480             if( output_file != NULL ) {
   481                 fprintf( stderr, "Error: at most one output file can be supplied\n" );
   482                 usage();
   483                 exit(1);
   484             }
   485             output_file = optarg;
   486         }
   487     }
   489     if( optind == argc ) {
   490         usage();
   491         exit(1);
   492     }
   494     if( output_file == NULL ) {
   495         output_file = makeExtension(argv[optind], ".def");
   496     }
   497     if( iface_file == NULL ) {
   498         iface_file = makeExtension(output_file, ".h");
   499     }
   501     glsldata_t data = g_malloc0(sizeof(struct glsldata));
   502     while( optind < argc ) {
   503         readInput(argv[optind++], data);
   504     }
   505     linkPrograms(data);
   507     writeSource( output_file, data );
   508     writeInterface( iface_file, data );
   509     return 0;
   510 }
.