}
static _threads;
+struct hook_event
+{
+ struct stream alias, returns, proto;
+ struct stretchy_allocator users;
+};
+struct hook_user
+{
+ struct stream function_name;
+};
+struct
+{
+ bool using;
+ struct stretchy_allocator events;
+}
+static _hooks;
+
struct
{
bool using;
}
static _console;
+enum subshader_type
+{
+ k_subshader_vertex,
+ k_subshader_fragment,
+ k_subshader_geometry
+};
+struct shader
+{
+ struct stream name;
+ struct subshader
+ {
+ enum subshader_type type;
+ struct stream debug_source_list, static_source, uniform_source;
+ u32 debug_source_count;
+ }
+ subshaders[3];
+ u32 subshader_count;
+
+ u32 uniform_start, uniform_count;
+};
+struct
+{
+ bool using;
+ struct stream shader_enum, uniform_enum, uniform_aliases, uniform_func_protos, uniform_funcs;
+ u32 uniform_count;
+ struct stretchy_allocator shaders;
+}
+static _shaders;
+
struct block_context
{
enum target_type
k_target_shader,
k_target_cvar,
k_target_ccmd,
- k_target_thread
+ k_target_thread,
+ k_target_hook,
+ k_target_subshader
}
target;
u32 feature_count;
const c8 *block_key = keyvalues_key( kvs, block, NULL );
if( block_key != NULL )
{
- ASSERT_CRITICAL( context.target == k_target_main );
- if( compare_buffers( block_key, 0, "input_layer", 0 ))
+ if( context.target == k_target_shader )
+ {
+ if( compare_buffers( block_key, 0, "subshader", 0 ))
+ {
+ context.target = k_target_subshader;
+
+ struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 );
+ struct subshader *subshader = &shader->subshaders[ shader->subshader_count ++ ];
+
+ const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
+ ASSERT_CRITICAL( type );
+
+ if( compare_buffers( type, 0, "vertex", 0 ) )
+ subshader->type = k_subshader_vertex;
+ else if( compare_buffers( type, 0, "fragment", 0 ) )
+ subshader->type = k_subshader_fragment;
+ else if( compare_buffers( type, 0, "geometry", 0 ) )
+ subshader->type = k_subshader_geometry;
+ else
+ {
+ $log( $warning, {"Invalid subshader type '"}, {type}, {"'"} );
+ return;
+ }
+
+ subshader->debug_source_count = 0;
+ stream_open_auto( &subshader->debug_source_list, k_stream_null_terminate );
+ stream_open_auto( &subshader->static_source, k_stream_null_terminate );
+ stream_open_auto( &subshader->uniform_source, k_stream_null_terminate );
+ }
+ else
+ {
+ $log( $warning, {"We don't have a compiler:shader definition for block '"}, {block_key}, {"'"} );
+ return;
+ }
+ }
+ else if( context.target == k_target_subshader )
{
- _input.using = 1;
- context.target = k_target_input_layer;
- const c8 *layer_name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( layer_name );
+ struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 );
+ struct subshader *subshader = &shader->subshaders[ shader->subshader_count -1 ];
- $v_string( &_input.layer_enums, {" k_input_layer_"}, {layer_name}, {",\n"} );
+ if( compare_buffers( block_key, 0, "uniform", 0 ))
+ {
+ const c8 *alias = keyvalues_read_string( kvs, block, "alias", NULL );
+ const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
+ ASSERT_CRITICAL( type && alias );
+
+ struct glsl_trans
+ {
+ const c8 *type, *args, *call, *end;
+ }
+ type_list[] =
+ {
+ { "int", "i32 b", "glUniform1i", "b" },
+ { "vec2", "v2f v", "glUniform2fv", "1,v" },
+ { "vec3", "v3f v", "glUniform3fv", "1,v" },
+ { "vec4", "v4f v", "glUniform4fv", "1,v" },
+ { "bool", "i32 b", "glUniform1i", "b" },
+ { "mat2", "m2x2f m", "glUniformMatrix2fv", "1,GL_FALSE,(f32*)m" },
+ { "mat3", "m3x3f m", "glUniformMatrix3fv", "1,GL_FALSE,(f32*)m" },
+ { "mat4", "m4x4f m", "glUniformMatrix4fv", "1,GL_FALSE,(f32*)m" },
+ { "float", "f32 f", "glUniform1f", "f" },
+ { "mat4x3", "m4x3f m", "glUniformMatrix4x3fv", "1,GL_FALSE,(f32*)m" },
+ { "sampler2D", "i32 i", "glUniform1i", "i" },
+ { "usampler3D", "i32 i", "glUniform1i", "i" },
+ { "samplerCube", "i32 i", "glUniform1i", "i" },
+ { "samplerBuffer","i32 i", "glUniform1i", "i" },
+ },
+ *trans = NULL;
+
+ for( u32 i=0; i<ARRAY_COUNT( type_list ); i ++ )
+ {
+ if( compare_buffers( type_list[i].type, 0, type, 0 ) )
+ {
+ trans = &type_list[i];
+ break;
+ }
+ }
+ ASSERT_CRITICAL( trans );
+
+ $v_string( &_shaders.uniform_enum, {" k_shader_"}, {string_get(&shader->name)}, {"_"}, {alias}, {",\n"} );
+ $v_string( &_shaders.uniform_aliases, {" [k_shader_"}, {string_get(&shader->name)}, {"_"}, {alias}, {"] = \""},
+ {alias},{"\",\n"} );
+ $v_string( &subshader->uniform_source, {"uniform "}, {type}, {" "}, {alias}, {"\\n"} );
+
+ $v_string( &_shaders.uniform_func_protos, {"void _shader"}, {string_get(&shader->name)}, {"_"}, {alias}, {"( "},{trans->args},{" );\n"} );
+ $v_string( &_shaders.uniform_funcs, {"void _shader"}, {string_get(&shader->name)}, {"_"}, {alias}, {"( "},{trans->args},{" )"},
+ {"{ "},{trans->call},{"( _uniform_locations["},
+ {"k_shader_"}, {string_get(&shader->name)}, {"_"}, {alias},
+ {"], "},{trans->end},{" ); }\n"});
+
+ shader->uniform_count ++;
+ _shaders.uniform_count ++;
+ }
+ else
+ {
+ $log( $warning, {"We don't have a compiler:subshader definition for block '"}, {block_key}, {"'"} );
+ return;
+ }
}
- else if( compare_buffers( block_key, 0, "input", 0 ))
+ else
{
- _input.using = 1;
- context.target = k_target_input;
+ ASSERT_CRITICAL( context.target == k_target_main );
+ if( compare_buffers( block_key, 0, "input_layer", 0 ))
+ {
+ _input.using = 1;
+ context.target = k_target_input_layer;
+ const c8 *layer_name = keyvalues_read_string( kvs, block, "name", NULL );
+ ASSERT_CRITICAL( layer_name );
+
+ $v_string( &_input.layer_enums, {" k_input_layer_"}, {layer_name}, {",\n"} );
+ }
+ else if( compare_buffers( block_key, 0, "input", 0 ))
+ {
+ _input.using = 1;
+ context.target = k_target_input;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( name );
+ const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
+ ASSERT_CRITICAL( name );
- const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
- ASSERT_CRITICAL( type );
+ const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
+ ASSERT_CRITICAL( type );
- const c8 *layer_mask = keyvalues_read_string( kvs, block, "layer_mask", NULL );
-
- $v_string( &_input.input_enums, {" k_input_"},{type},{"_"}, {name}, {",\n"} );
- $v_string( &_input.input_structures, {" [k_input_"},{type},{"_"},{name},{"]=\n {\n"} );
- $v_string( &_input.input_structures, {" .name = \""},{name},{"\",\n"} );
- $v_string( &_input.input_structures, {" .type = k_input_type_"},{type},{",\n"} );
+ const c8 *layer_mask = keyvalues_read_string( kvs, block, "layer_mask", NULL );
+
+ $v_string( &_input.input_enums, {" k_input_"},{type},{"_"}, {name}, {",\n"} );
+ $v_string( &_input.input_structures, {" [k_input_"},{type},{"_"},{name},{"]=\n {\n"} );
+ $v_string( &_input.input_structures, {" .name = \""},{name},{"\",\n"} );
+ $v_string( &_input.input_structures, {" .type = k_input_type_"},{type},{",\n"} );
- if( layer_mask )
- $v_string( &_input.input_structures, {" .layer_mask = 1<<k_input_layer_"}, {layer_mask}, {",\n"} );
+ if( layer_mask )
+ $v_string( &_input.input_structures, {" .layer_mask = 1<<k_input_layer_"}, {layer_mask}, {",\n"} );
#if 0
- const c8 *keyboard_key = keyvalues_read_string( kvs, block, "keyboard", NULL );
- if( keyboard_key )
- $v_string( &_input.button_structures, {" .key_id = GLFW_KEY_"}, {keyboard_key}, {",\n"} );
+ const c8 *keyboard_key = keyvalues_read_string( kvs, block, "keyboard", NULL );
+ if( keyboard_key )
+ $v_string( &_input.button_structures, {" .key_id = GLFW_KEY_"}, {keyboard_key}, {",\n"} );
#endif
- $v_string( &_input.input_structures, {" },\n"} );
- }
- else if( compare_buffers( block_key, 0, "cvar", 0 ))
- {
- _console.using = 1;
- context.target = k_target_cvar;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
- const c8 *default_value = keyvalues_read_string( kvs, block, "default", NULL );
- ASSERT_CRITICAL( name && type && default_value );
-
- $v_string( &_console.cvar_header, {"extern "}, {type}, {" cvar_"}, {name}, {";\n"} );
- $v_string( &_console.cvar_definitions, {type}, {" cvar_"}, {name}, {" = "}, {default_value}, {";\n"} );
- }
- else if( compare_buffers( block_key, 0, "ccmd", 0 ))
- {
- _console.using = 1;
- _console.command_count ++;
- context.target = k_target_ccmd;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- const c8 *function = keyvalues_read_string( kvs, block, "function", NULL );
- ASSERT_CRITICAL( name && function );
- $v_string( &_console.command_definitions, {" {\n .alias = \""}, {name}, {"\",\n .fn = "},
- {function}, {"\n },\n"} );
- $v_string( &_console.command_prototypes, {"i32 "}, {function}, {"( struct console_arguments *args );\n"} );
- // TODO: process parameter descriptions
- return;
- }
- else if( compare_buffers( block_key, 0, "shader", 0 ))
- context.target = k_target_shader;
- else if( compare_buffers( block_key, 0, "thread", 0 ))
- {
- _threads.using = 1;
- context.target = k_target_thread;
- const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
- ASSERT_CRITICAL( name );
+ $v_string( &_input.input_structures, {" },\n"} );
+ }
+ else if( compare_buffers( block_key, 0, "cvar", 0 ))
+ {
+ _console.using = 1;
+ context.target = k_target_cvar;
+ const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
+ const c8 *type = keyvalues_read_string( kvs, block, "type", NULL );
+ const c8 *default_value = keyvalues_read_string( kvs, block, "default", NULL );
+ ASSERT_CRITICAL( name && type && default_value );
+
+ $v_string( &_console.cvar_header, {"extern "}, {type}, {" cvar_"}, {name}, {";\n"} );
+ $v_string( &_console.cvar_definitions, {type}, {" cvar_"}, {name}, {" = "}, {default_value}, {";\n"} );
+ }
+ else if( compare_buffers( block_key, 0, "ccmd", 0 ))
+ {
+ _console.using = 1;
+ _console.command_count ++;
+ context.target = k_target_ccmd;
+ const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
+ const c8 *function = keyvalues_read_string( kvs, block, "function", NULL );
+ ASSERT_CRITICAL( name && function );
+ $v_string( &_console.command_definitions, {" {\n .alias = \""}, {name}, {"\",\n .fn = "},
+ {function}, {"\n },\n"} );
+ $v_string( &_console.command_prototypes, {"i32 "}, {function}, {"( struct console_arguments *args );\n"} );
+ // TODO: process parameter descriptions
+ return;
+ }
+ else if( compare_buffers( block_key, 0, "shader", 0 ))
+ {
+ _shaders.using = 1;
+ context.target = k_target_shader;
+ struct shader *shader = stretchy_append( &_shaders.shaders );
+ shader->subshader_count = 0;
+ shader->uniform_start = _shaders.uniform_count;
+ shader->uniform_count = 0;
+
+ stream_open_auto( &shader->name, k_stream_null_terminate );
+ $v_string( &shader->name, {keyvalues_read_string( kvs, block, "name", NULL )} );
+ $v_string( &_shaders.shader_enum, {" k_shader_"}, {string_get(&shader->name)}, {",\n"} );
+ }
+ else if( compare_buffers( block_key, 0, "thread", 0 ))
+ {
+ _threads.using = 1;
+ context.target = k_target_thread;
+ const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
+ ASSERT_CRITICAL( name );
- $v_string( &_threads.enums, {" k_thread_"},{name}, {",\n"} );
- $v_string( &_threads.structures, {" [k_thread_"},{name},{"]=\n {\n"} );
- $v_string( &_threads.structures, {" .name = \""}, {name}, {"\",\n"} );
+ $v_string( &_threads.enums, {" k_thread_"},{name}, {",\n"} );
+ $v_string( &_threads.structures, {" [k_thread_"},{name},{"]=\n {\n"} );
+ $v_string( &_threads.structures, {" .name = \""}, {name}, {"\",\n"} );
- u32 queue_size_m = 0;
- if( keyvalues_read_u32s( kvs, block, "queue_size_m", NULL, &queue_size_m, 1 ) )
- $v_string( &_threads.structures, {" .queue_size_m = "}, $unsigned(queue_size_m), {",\n"} );
+ u32 queue_size_m = 0;
+ if( keyvalues_read_u32s( kvs, block, "queue_size_m", NULL, &queue_size_m, 1 ) )
+ $v_string( &_threads.structures, {" .queue_size_m = "}, $unsigned(queue_size_m), {",\n"} );
- $v_string( &_threads.structures, {" },\n"} );
- }
- else
- {
- $log( $warning, {"We don't have a compiler definition for block '"}, {block_key}, {"'"} );
- return;
+ $v_string( &_threads.structures, {" },\n"} );
+ }
+ else if( compare_buffers( block_key, 0, "event", 0 ))
+ {
+ _hooks.using = 1;
+ context.target = k_target_hook;
+ const c8 *name = keyvalues_read_string( kvs, block, "name", NULL );
+ const c8 *prototype = keyvalues_read_string( kvs, block, "prototype", NULL );
+ const c8 *returns = keyvalues_read_string( kvs, block, "returns", "void" );
+ ASSERT_CRITICAL( name && prototype && returns );
+
+ struct hook_event *event = stretchy_append( &_hooks.events );
+
+ stream_open_auto( &event->alias, k_stream_null_terminate );
+ $v_string( &event->alias, {name} );
+ stream_open_auto( &event->returns, k_stream_null_terminate );
+ $v_string( &event->returns, {returns} );
+ stream_open_auto( &event->proto, k_stream_null_terminate );
+ $v_string( &event->proto, {prototype} );
+
+ stretchy_init( &event->users, sizeof( struct hook_user ) );
+ }
+ else if( compare_buffers( block_key, 0, "hook", 0 ))
+ {
+ _hooks.using = 1;
+ const c8 *event_alias = keyvalues_read_string( kvs, block, "event", NULL );
+ const c8 *function = keyvalues_read_string( kvs, block, "function", NULL );
+
+ ASSERT_CRITICAL( event_alias && function );
+
+ bool found = 0;
+
+ for( u32 i=0; i<stretchy_count( &_hooks.events ); i ++ )
+ {
+ struct hook_event *event = stretchy_get( &_hooks.events, i );
+ if( compare_buffers( string_get( &event->alias ), 0, event_alias, 0 ) )
+ {
+ struct hook_user *user = stretchy_append( &event->users );
+ stream_open_auto( &user->function_name, k_stream_null_terminate );
+ $v_string( &user->function_name, {function} );
+ found = 1;
+ break;
+ }
+ }
+
+ if( !found )
+ {
+ $log( $fatal, {"We don't have an event registered called '"}, {event_alias}, {"'"} );
+ _fatal_exit();
+ }
+ }
+ else
+ {
+ $log( $warning, {"We don't have a compiler definition for block '"}, {block_key}, {"'"} );
+ return;
+ }
}
}
if( compare_buffers( key, 0, "define", 0 ) )
$v_string( &_metacompiler.include_path_list, {" -D"}, {value}, {" \\\n"} );
}
+ else if( context.target == k_target_subshader )
+ {
+ struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 );
+ struct subshader *subshader = &shader->subshaders[ shader->subshader_count -1 ];
+
+ if( compare_buffers( key, 0, "add", 0 ) )
+ {
+ u32 temp_frame = _start_temporary_frame();
+ {
+ struct stream path_string;
+ stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
+ $v_string( &path_string, {context.folder}, {"/"}, {value} );
+
+ struct stream source;
+ ASSERT_CRITICAL( stream_open_file( &source, string_get( &path_string ), k_stream_read ) );
+
+ u32 block_size = BYTES_KB(128);
+ c8 *temp = _temporary_allocate( block_size, 4 );
+ while(1)
+ {
+ u32 l = stream_read( &source, temp, block_size );
+ $v_string( &subshader->static_source, {temp, .length = l} );
+ if( l != block_size )
+ break;
+ }
+
+ stream_close( &source );
+ $v_string( &subshader->debug_source_list, {" \""}, {string_get(&path_string)}, {"\",\n"} );
+ subshader->debug_source_count ++;
+ }
+ _end_temporary_frame( temp_frame );
+ }
+ }
kv = keyvalues_get_next( kvs, kv );
}
{
buffer_copy( "project", 0, _metacompiler.project_name, sizeof(_metacompiler.project_name) );
- u32 size = BYTES_MB(8),
- options = k_stream_null_terminate|k_stream_overflow_error;
- stream_open_buffer_write( &_metacompiler.source_list, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_metacompiler.include_path_list, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_metacompiler.define_list, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_metacompiler.configuration, _heap_allocate(size), size, options );
-
- stream_open_buffer_write( &_input.layer_enums, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_input.input_enums, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_input.input_structures, _heap_allocate(size), size, options );
-
- stream_open_buffer_write( &_console.cvar_header, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_console.cvar_definitions, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_console.command_definitions, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_console.command_prototypes, _heap_allocate(size), size, options );
-
- stream_open_buffer_write( &_threads.enums, _heap_allocate(size), size, options );
- stream_open_buffer_write( &_threads.structures, _heap_allocate(size), size, options );
+ u32 options = k_stream_null_terminate;
+ stream_open_auto( &_metacompiler.source_list, options );
+ stream_open_auto( &_metacompiler.include_path_list, options );
+ stream_open_auto( &_metacompiler.define_list, options );
+ stream_open_auto( &_metacompiler.configuration, options );
+ stream_open_auto( &_input.layer_enums, options );
+ stream_open_auto( &_input.input_enums, options );
+ stream_open_auto( &_input.input_structures, options );
+ stream_open_auto( &_console.cvar_header, options );
+ stream_open_auto( &_console.cvar_definitions, options );
+ stream_open_auto( &_console.command_definitions, options );
+ stream_open_auto( &_console.command_prototypes, options );
+ stream_open_auto( &_threads.enums, options );
+ stream_open_auto( &_threads.structures, options );
+ stream_open_auto( &_shaders.shader_enum, options );
+ stream_open_auto( &_shaders.uniform_enum, options );
+ stream_open_auto( &_shaders.uniform_aliases, options );
+ stream_open_auto( &_shaders.uniform_funcs, options );
+ stream_open_auto( &_shaders.uniform_func_protos, options );
+ stretchy_init( &_hooks.events, sizeof( struct hook_event ) );
+ stretchy_init( &_shaders.shaders, sizeof( struct shader ) );
if( platform == k_platform_linux ) _metacompiler.enabled_features[0] = "linux";
if( platform == k_platform_windows ) _metacompiler.enabled_features[0] = "windows";
_append_kv_list( target_file_path, context );
system_call( "mkdir -p generated" );
-
+
+ $log( $info, {"Generating input header"} );
if( _input.using )
{
struct stream input_header, input_source;
stream_close( &input_source );
}
+ $log( $info, {"Generating threads header"} );
if( _threads.using )
{
struct stream thread_header, thread_source;
stream_close( &thread_source );
}
+ $log( $info, {"Generating console header"} );
if( _console.using )
{
struct stream console_header, console_source;
stream_close( &console_source );
}
+ $log( $info, {"Generating hooks header"} );
+ if( _hooks.using )
+ {
+ struct stream header, source;
+ ASSERT_CRITICAL( stream_open_file( &header, "generated/hooks.h", k_stream_write ) );
+ ASSERT_CRITICAL( stream_open_file( &source, "generated/hooks.c", k_stream_write ) );
+
+ for( u32 i=0; i<stretchy_count( &_hooks.events ); i ++ )
+ {
+ struct hook_event *event = stretchy_get( &_hooks.events, i );
+
+ const c8 *returns = string_get( &event->returns ),
+ *alias = string_get( &event->alias ),
+ *prototype = string_get( &event->proto );
+
+ $v_string( &header, {"extern "}, {returns}, {" (*_event_"}, {alias}, {"_subscribers[])( "}, {prototype}, {" );\n"} );
+
+ for( u32 j=0; j<stretchy_count( &event->users ); j ++ )
+ {
+ struct hook_user *user = stretchy_get( &event->users, j );
+ $v_string( &source, {returns}, {" "}, {string_get(&user->function_name)}, {"( "}, {prototype}, {" );\n"} );
+ }
+ $v_string( &source, {"\n"} );
+
+ $v_string( &source, {returns}, {" (*_event_"}, {alias}, {"_subscribers[])( "}, {prototype}, {" ) = \n{\n"} );
+ for( u32 j=0; j<stretchy_count( &event->users ); j ++ )
+ {
+ struct hook_user *user = stretchy_get( &event->users, j );
+ $v_string( &source, {" "}, {string_get(&user->function_name)}, {",\n"} );
+ }
+ $v_string( &source, {" NULL\n};\n\n"} );
+ }
+
+ stream_close( &source );
+ stream_close( &header );
+
+ $v_string( &_metacompiler.source_list, {" generated/hooks.c \\\n"} );
+ }
+
+ $log( $info, {"Generating shader header"} );
+ if( _shaders.using )
+ {
+ struct stream header, source;
+ ASSERT_CRITICAL( stream_open_file( &header, "generated/shaders.h", k_stream_write ) );
+ ASSERT_CRITICAL( stream_open_file( &source, "generated/shaders.c", k_stream_write ) );
+
+ $v_string( &header, {"enum shader_id\n{\n"}, {string_get( &_shaders.shader_enum )}, {" k_shader_count\n};\n"} );
+ $v_string( &header, {"enum shader_uniform_id\n{\n"}, {string_get( &_shaders.uniform_enum )}, {" k_shader_uniform_count\n};\n"} );
+ $v_string( &header, {string_get( &_shaders.uniform_func_protos )} );
+
+ $v_string( &source, {"const c8 *_uniform_aliases[] = \n{\n"}, {string_get( &_shaders.uniform_aliases )}, {"};\n"} );
+
+ $v_string( &source, {"struct shader _shader_definitions[] = {\n"} );
+ for( u32 i=0; i<stretchy_count( &_shaders.shaders ); i ++ )
+ {
+ struct shader *shader = stretchy_get( &_shaders.shaders, i );
+ $v_string( &source, {" [k_shader_"}, {string_get( &shader->name )}, {"] = \n {\n"} );
+ $v_string( &source, {" .name = \""}, {string_get( &shader->name )}, {"\",\n"} );
+ $v_string( &source, {" .subshader_count = "}, $unsigned( shader->subshader_count ), {",\n"} );
+ $v_string( &source, {" .uniform_start = "}, $unsigned( shader->uniform_start ), {",\n"} );
+ $v_string( &source, {" .uniform_count = "}, $unsigned( shader->uniform_count ), {",\n"} );
+ $v_string( &source, {" .subshaders = (struct subshader[])\n {\n"} );
+
+ for( u32 j=0; j<shader->subshader_count; j ++ )
+ {
+ struct subshader *subshader = &shader->subshaders[ j ];
+ $v_string( &source, {" {\n"} );
+ $v_string( &source, {" .type = "},
+ { (const c8 *[]){ [k_subshader_vertex] = "k_subshader_vertex",
+ [k_subshader_fragment] = "k_subshader_fragment",
+ [k_subshader_geometry] = "k_subshader_geometry" }[ subshader->type ] }, {",\n"} );
+ $v_string( &source, {" .source_count = "}, $unsigned( subshader->debug_source_count ), {",\n"} );
+ $v_string( &source, {" .source_list = (const c8 *[])\n"} );
+ $v_string( &source, {" {\n"} );
+ $v_string( &source, {string_get( &subshader->debug_source_list )} );
+ $v_string( &source, {" },\n"} );
+ $v_string( &source, {" .source_static = \""} );
+
+ for( u32 k=0; k<subshader->static_source.offset; k ++ )
+ {
+ c8 c = subshader->static_source.buffer_read[ k ];
+ if( c == '\n' ) $v_string( &source, {"\\n"} );
+ else string_append_c8( &source, c );
+ }
+ $v_string( &source, {"\",\n"} );
+ $v_string( &source, {" .source_uniforms = \""}, {string_get( &subshader->uniform_source )}, {"\",\n"} );
+ $v_string( &source, {" },\n"} );
+ }
+
+ $v_string( &source, {" },\n"} );
+ $v_string( &source, {" },\n"} );
+ }
+ $v_string( &source, {"};\n"} );
+ $v_string( &source, {string_get( &_shaders.uniform_funcs )} );
+
+ stream_close( &source );
+ stream_close( &header );
+ }
+
/* main */
u32 temp_frame = _start_temporary_frame();
{