--- /dev/null
+// Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
+
+const char *vg_shader_gl_ver = "#version 330 core\n";
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreturn-type"
+static inline int static_str_index( const char *list[], int len, const char *str )
+{
+ for( int i = 0; i < len; i ++ )
+ {
+ if( !strcmp(list[i],str) )
+ return i;
+ }
+
+ #ifndef VG_RELEASE
+ fprintf( stderr, "That was not a static string index!! (%s)\n", str );
+ abort();
+ #endif
+}
+#pragma GCC diagnostic pop
+
+#define SHADER_UNIFORM( NAME, U ) NAME##_auto_uniforms[ STR_STATIC_INDEX( NAME##_auto_names, U ) ]
+#define SHADER_UNIFORM_NAME( NAME, UID ) NAME##_auto_names[ UID ]
+#define STR_STATIC_INDEX( LIST, STR ) static_str_index( LIST, vg_list_size(LIST), STR )
+
+#define UNIFORMS(...) __VA_ARGS__
+
+#define SHADER_DEFINE( NAME, VERT, FRAG, UNIFORMS ) \
+ const char * NAME##_shader_names[] = UNIFORMS; \
+ GLint NAME##_shader_uniforms[ vg_list_size( NAME##_shader_names ) ]; \
+ struct vg_shader NAME##_static_shader = { \
+ .src_vert = VERT, .src_frag = FRAG, \
+ .sym = #NAME "_static.shader", \
+ .uniform_names = NAME##_shader_names, \
+ .uniforms = NAME##_shader_uniforms, \
+ .uniform_count = vg_list_size( NAME##_shader_names ), \
+ };
+
+#define SHADER_INIT( NAME ) arrpush( vg_shaders_active, &NAME##_static_shader )
+
+#define SHADER_STATUS_NONE 0x00
+#define SHADER_STATUS_COMPILED 0x1
+
+struct vg_shader
+{
+ GLuint program;
+
+ const char *src_vert;
+ const char *src_frag;
+ const char *src_geo;
+ const char *sym;
+
+ const char **uniform_names;
+ GLint *uniforms;
+ u32 uniform_count;
+ u32 status;
+}
+** vg_shaders_active = NULL;
+
+static GLuint vg_shader_subshader( const char *src, GLint gliShaderType )
+{
+ GLint shader = glCreateShader( gliShaderType );
+ glShaderSource( shader, 2, (const char *[2]){ vg_shader_gl_ver, src }, NULL );
+ glCompileShader( shader );
+
+ int success;
+ char infoLog[512];
+ glGetShaderiv( shader, GL_COMPILE_STATUS, &success );
+
+ if( !success )
+ {
+ glGetShaderInfoLog( shader, 512, NULL, infoLog );
+ vg_error( "Error info:\n%s\n", infoLog );
+ return 0;
+ }
+
+ return shader;
+}
+
+static int vg_shader_compile( struct vg_shader *shader )
+{
+ vg_info( "Compile shader '%s'\n", shader->sym );
+
+ GLuint vert, frag, geo;
+
+ vert = vg_shader_subshader( shader->src_vert, GL_VERTEX_SHADER );
+ frag = vg_shader_subshader( shader->src_frag, GL_FRAGMENT_SHADER );
+
+ if( !vert || !frag )
+ return 0;
+
+ if( shader->src_geo )
+ {
+ geo = vg_shader_subshader( shader->src_geo, GL_GEOMETRY_SHADER );
+
+ if( !geo )
+ return 0;
+ }
+
+ shader->program = glCreateProgram();
+ if( shader->src_geo )
+ glAttachShader( shader->program, geo );
+
+ glAttachShader( shader->program, vert );
+ glAttachShader( shader->program, frag );
+ glLinkProgram( shader->program );
+
+ glDeleteShader( vert );
+ glDeleteShader( frag );
+
+ if( shader->src_geo )
+ glDeleteShader( geo );
+
+ // Check for link errors
+ char infoLog[ 512 ];
+ int success_link = 1;
+
+ glGetProgramiv( shader->program, GL_LINK_STATUS, &success_link );
+ if( !success_link )
+ {
+ glGetProgramInfoLog( shader->program, 512, NULL, infoLog );
+ vg_error( "Link failed: %s\n", infoLog );
+ glDeleteProgram( shader->program );
+
+ return 0;
+ }
+
+ // Complete linkeage
+ for( int i = 0; i < shader->uniform_count; i ++ )
+ shader->uniforms[ i ] = glGetUniformLocation( shader->program, shader->uniform_names[i] );
+
+ shader->status |= SHADER_STATUS_COMPILED;
+ return 1;
+}
+
+static void vg_shaders_free(void)
+{
+ for( int i = 0; i < arrlen( vg_shaders_active ); i ++ )
+ {
+ struct vg_shader *shader = vg_shaders_active[i];
+
+ if( shader->status & SHADER_STATUS_COMPILED )
+ glDeleteProgram( shader->program );
+ }
+
+ arrfree( vg_shaders_active );
+}
+
+static int vg_shaders_compile(void)
+{
+ vg_info( "Compiling shaders\n" );
+
+ for( int i = 0; i < arrlen( vg_shaders_active ); i ++ )
+ {
+ struct vg_shader *shader = vg_shaders_active[i];
+ if( !vg_shader_compile( shader ) )
+ {
+ vg_shaders_free();
+ return 0;
+ }
+ }
+
+ vg_register_exit( &vg_shaders_free, "vg_shaders_free" );
+ return 1;
+}