Search
lxdream.org :: lxdream/src/tools/genglsl.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/genglsl.c
changeset 1229:dc935eee9767
prev1209:e606e65eaf54
next1234:1b836bf92653
author nkeynes
date Thu Feb 23 19:43:24 2012 +1000 (9 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 }
.