shader compilation
[fishladder.git] / vg / vg_shader.h
diff --git a/vg/vg_shader.h b/vg/vg_shader.h
new file mode 100644 (file)
index 0000000..19c1fc2
--- /dev/null
@@ -0,0 +1,165 @@
+// 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;
+}