From f230481ad0b530761c616568c655cac96bc3cfaf Mon Sep 17 00:00:00 2001 From: hgn Date: Wed, 22 Oct 2025 03:10:34 +0100 Subject: [PATCH] MAKE METACOMPILER GOOD --- bootstrap.sh | 2 +- source/async/async.kv | 12 +- source/console/console_system.h | 4 +- source/engine/array_file.h | 4 +- source/engine/engine.kv | 182 ++-- source/engine/model_entity.h | 6 +- source/engine/vg_engine.c | 2 + source/engine/vg_engine.h | 4 + source/foundation/foundation.h | 3 + source/foundation/foundation.kv | 38 +- source/foundation/keyvalues.c | 24 + source/maths/common_maths.h | 11 +- source/tools/metacompiler.c | 1396 ++++++++++++++----------------- 13 files changed, 789 insertions(+), 899 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 38805e5..e1ac54a 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,5 +1,5 @@ mkdir -p bin/vg.metacompiler-linux-x86_64-zig-cc/ && -taskset -c 0-1 zig cc -I. -I./include/ -fsanitize=address -lasan -Wall -Wno-unused-function -O0 -ggdb \ +taskset -c 0-1 zig cc -I. -I./include/ -fsanitize=address -lasan -Wall -Wno-unused-function -O1 -ggdb \ -std=c11 -D_DEFAULT_SOURCE \ -include "source/types.h" \ -I./source/foundation/ \ diff --git a/source/async/async.kv b/source/async/async.kv index dfab494..bb3a9a8 100644 --- a/source/async/async.kv +++ b/source/async/async.kv @@ -1,8 +1,16 @@ include "" +test_feature { - if SDL - add sdl_async.c + feature SDL + yes + { + add sdl_async.c + } + no + { + + } } hook diff --git a/source/console/console_system.h b/source/console/console_system.h index f2b2329..a996f80 100644 --- a/source/console/console_system.h +++ b/source/console/console_system.h @@ -1,5 +1,3 @@ -#include "generated/console.h" - void _console_init(void); void _console_execute( const c8 *command, b8 silent, b8 cheat_allowed ); @@ -9,3 +7,5 @@ struct console_arguments u32 count; }; const c8 *console_get_argument( struct console_arguments *args, u32 index ); + +#include "generated/console.h" diff --git a/source/engine/array_file.h b/source/engine/array_file.h index 68f9739..5abd164 100644 --- a/source/engine/array_file.h +++ b/source/engine/array_file.h @@ -47,8 +47,8 @@ const c8 *af_str( const void *packed_strings, u32 pstr ); u32 af_str_hash( const void *packed_strings, u32 pstr ); b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash ); -#define AF_STR_EQ( CTX, PSTR, CONSTR ) \ - af_str_eq( CTX, PSTR, CONSTR, vg_strdjb2( CONSTR ) ) +#define AF_STR_EQ( PACKED, PSTR, CONSTR ) \ + af_str_eq( PACKED, PSTR, CONSTR, buffer_djb2( CONSTR, 0 ) ) /* COmpiler * ------------------------------------ */ diff --git a/source/engine/engine.kv b/source/engine/engine.kv index 0c66c16..e81bb62 100644 --- a/source/engine/engine.kv +++ b/source/engine/engine.kv @@ -8,127 +8,115 @@ add ../../dep/glad.4.3/glad.c enable SDL append ../foundation/foundation.kv +add vg_engine.c +hook { - add vg_engine.c - hook - { - event OPTIONS - function _engine_options - } - event - { - name ENGINE_NEW_FRAME - prototype "void" - } - event - { - name ENGINE_FIXED_UPDATE - prototype "void" - } - event - { - name ENGINE_RENDER - prototype "void" - } - event - { - name ENGINE_UI - prototype "void" - } - event - { - name ENGINE_WINDOW_RESIZE - prototype "void" - } + event OPTIONS + function _engine_options +} +event +{ + name ENGINE_NEW_FRAME + prototype "void" +} +event +{ + name ENGINE_FIXED_UPDATE + prototype "void" +} +event +{ + name ENGINE_RENDER + prototype "void" +} +event +{ + name ENGINE_UI + prototype "void" +} +event +{ + name ENGINE_WINDOW_RESIZE + prototype "void" } +add vg_ui.c +hook { - add vg_ui.c - hook - { - event START - function _engine_ui_init - } + event START + function _engine_ui_init } +add vg_input.c +hook { - add vg_input.c - hook + event START + function _input_init +} +hook +{ + event ENGINE_NEW_FRAME + function _input_update +} +ccmd +{ + name bind + function _input_bind_ccmd + description "Bind device input to a button, action or axis" + + parameter { - event START - function _input_init + description "Device input alias" } - hook + + parameter { - event ENGINE_NEW_FRAME - function _input_update + description "button,action,axis name" } - ccmd - { - name bind - function _input_bind_ccmd - description "Bind device input to a button, action or axis" - - parameter - { - description "Device input alias" - } +} +ccmd +{ + name unbind + description "Unbind all bindings that match" + function _input_unbind_ccmd - parameter - { - description "button,action,axis name" - } - } - ccmd + parameter { - name unbind - description "Unbind all bindings that match" - function _input_unbind_ccmd - - parameter - { - description "binding" - } + description "binding" } } append ../console/console_system.kv +add console.c +hook { - add console.c - hook - { - event START - function _engine_console_init - } - hook - { - event ENGINE_NEW_FRAME - function _engine_console_update - } - hook - { - event ENGINE_UI - function _engine_console_ui - affinity 9999 - } + event START + function _engine_console_init +} +hook +{ + event ENGINE_NEW_FRAME + function _engine_console_update +} +hook +{ + event ENGINE_UI + function _engine_console_ui + affinity 9999 } +add vg_framebuffer.c +hook { - add vg_framebuffer.c - hook - { - event ENGINE_WINDOW_RESIZE - function _framebuffer_resize - } + event ENGINE_WINDOW_RESIZE + function _framebuffer_resize } +add vg_render.c +hook { - add vg_render.c - hook - { - event START - function _vg_render_init - } + event START + function _vg_render_init } add vg_shader.c diff --git a/source/engine/model_entity.h b/source/engine/model_entity.h index 65e26af..513d02a 100644 --- a/source/engine/model_entity.h +++ b/source/engine/model_entity.h @@ -39,10 +39,14 @@ enum entity_alias k_mdl_mesh = 33, k_editer_property = 34, k_editer_item = 35, + + k_ent_flame = 200, + k_ent_waterfall = 201, k_ent_max }; -const char *_entity_alias_str[] = +// FIXME +static const char *_entity_alias_str[] = { [k_ent_none] = "none/null", [k_ent_gate] = "ent_gate", diff --git a/source/engine/vg_engine.c b/source/engine/vg_engine.c index 925c293..6d14fac 100644 --- a/source/engine/vg_engine.c +++ b/source/engine/vg_engine.c @@ -213,6 +213,8 @@ i32 main( i32 argc, const c8 *argv[] ) /* ------------- */ + mt_random_seed( &_engine.random, 887765 ); + EVENT_CALL( START ); SDL_CreateThread( async_thread, "ASync thread", NULL ); ASSERT_CRITICAL( async_thread ); diff --git a/source/engine/vg_engine.h b/source/engine/vg_engine.h index 091cc2f..8f6e563 100644 --- a/source/engine/vg_engine.h +++ b/source/engine/vg_engine.h @@ -1,3 +1,5 @@ +#include "random.h" + struct _engine { b8 vsync; @@ -17,5 +19,7 @@ struct _engine i32 w, h; i32 native_fbo; + + struct mt_random random; } extern _engine; diff --git a/source/foundation/foundation.h b/source/foundation/foundation.h index b682a41..33d2fc2 100644 --- a/source/foundation/foundation.h +++ b/source/foundation/foundation.h @@ -308,6 +308,7 @@ struct keyvalues void keyvalues_init( struct keyvalues *kvs, struct stack_allocator *stack ); void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stream *in_stream ); void keyvalues_write_stream( struct keyvalues *kvs, struct stream *out_stream, u32 node, u32 depth ); +u32 keyvalues_current_offset( struct keyvalues *kvs ); b8 keyvalues_read_file( struct keyvalues *kvs, const char *path, struct stack_allocator *stack ); b8 keyvalues_write_file( struct keyvalues *kvs, const char *path ); @@ -327,6 +328,8 @@ u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, b8 rel u32 keyvalues_get_next( struct keyvalues *kvs, u32 kv_offset ); u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index ); +b8 keyvalues_foreach( struct keyvalues *kvs, u32 *kv, u32 block, const c8 *alias ); + const c8 *keyvalues_read_string( struct keyvalues *kvs, u32 root_offset, const c8 *key, const c8 *default_value ); b8 keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *out_values, u32 len ); b8 keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, u32 *default_values, u32 *out_values, u32 len ); diff --git a/source/foundation/foundation.kv b/source/foundation/foundation.kv index 83ff4ea..a4fb6e6 100644 --- a/source/foundation/foundation.kv +++ b/source/foundation/foundation.kv @@ -29,24 +29,30 @@ add keyvalues.c add buffer_operations.c add temporary.c +test_feature { - if linux - add io.c - add exit.c + feature linux + yes + { + add io.c + add exit.c + } + no + { + add io_windows.c + add exit_windows.c + } } +test_feature { - if windows - add io_windows.c - add exit_windows.c -} - -{ - if SDL - add threads_sdl.c -} - -{ - if !SDL - add threads_c11.c + feature SDL + yes + { + add threads_sdl.c + } + no + { + add threads_c11.c + } } diff --git a/source/foundation/keyvalues.c b/source/foundation/keyvalues.c index 5710f81..179adea 100644 --- a/source/foundation/keyvalues.c +++ b/source/foundation/keyvalues.c @@ -38,6 +38,11 @@ static struct keyvalue *keyvalues_new( struct keyvalues *kvs ) return kv; } +u32 keyvalues_current_offset( struct keyvalues *kvs ) +{ + return kvs->kv_page_offset + kvs->kv_page_count*sizeof(struct keyvalue); +} + void keyvalues_init( struct keyvalues *kvs, struct stack_allocator *stack ) { zero_buffer( kvs, sizeof(struct keyvalues) ); @@ -204,6 +209,25 @@ u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, b8 rel return 0; } +b8 keyvalues_foreach( struct keyvalues *kvs, u32 *kv, u32 block, const c8 *alias ) +{ + if( *kv == 0 ) + { + if( alias ) + *kv = keyvalues_get( kvs, block, alias, 0 ); + else + *kv = keyvalues_get_child( kvs, block, 0 ); + } + else + { + if( alias ) + *kv = keyvalues_get( kvs, *kv, alias, 1 ); + else + *kv = keyvalues_get_next( kvs, *kv ); + } + return (*kv) != 0; +} + b8 keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *out_values, u32 len ) { b8 good = 1; diff --git a/source/maths/common_maths.h b/source/maths/common_maths.h index 9945016..7fc07f1 100644 --- a/source/maths/common_maths.h +++ b/source/maths/common_maths.h @@ -18,7 +18,7 @@ static inline f32 f32_friction( f32 velocity, f32 F ) } static inline f32 f32_lerp( f32 a, f32 b, f32 t ) { return a + t*(b-a); } static inline f64 f64_lerp( f64 a, f64 b, f64 t ) { return a + t*(b-a); } -static inline void vg_slewf( f32 *a, f32 b, f32 speed ) +static inline void f32_slew( f32 *a, f32 b, f32 speed ) { f32 d = f32_sign( b-*a ), c = *a + d*speed; @@ -1403,3 +1403,12 @@ static inline b8 segment_segment_intersect_2d( f32 a0[2], f32 a1[2], f32 b0[2], return 1; } +static inline f32 closest_point_on_segment_2d( f32 co[2], f32 a[2], f32 b[2], f32 d[2] ) +{ + f32 v0[2], v1[2]; + v2_sub( b, a, v0 ); + v2_sub( co, a, v1 ); + f32 t = f32_clamp( v2_dot( v0, v1 ) / v2_dot( v0, v0 ), 0.0f, 1.0f ); + v2_muladds( a, v0, t, d ); + return t; +} diff --git a/source/tools/metacompiler.c b/source/tools/metacompiler.c index 16baa93..e925b44 100644 --- a/source/tools/metacompiler.c +++ b/source/tools/metacompiler.c @@ -1,18 +1,16 @@ #include #include "foundation.h" -struct stream folder_string; -struct stream tripple_string; -b8 _setup_done = 0; - -const c8 *target_file_path = NULL; - -b8 use_tsan = 0; -b8 use_asan = 0; -b8 shared = 0; -b8 no_pdb = 0; - -u32 optimise = 0; +const c8 *_option_file_path = NULL; +u64 _option_processors = 4; +b8 _option_use_tsan = 0; +b8 _option_use_asan = 0; +b8 _option_no_pdb = 0; +b8 _option_shared = 0; +b8 _option_assemble_only = 0; +u32 _option_optimise = 0; +struct stream _assemble_file; +struct keyvalues _assembly; enum libc_version { @@ -20,7 +18,7 @@ enum libc_version k_libc_version_2_23, k_libc_version_count, } -libc = k_libc_version_native; +_option_libc = k_libc_version_native; const c8 *libc_names[] = { [k_libc_version_native] = "", @@ -34,7 +32,7 @@ enum architecture k_architecture_x86_64, k_architecture_count, } -arch = k_architecture_x86_64; +_option_arch = k_architecture_x86_64; const c8 *architecture_names[] = { [k_architecture_native] = "native", @@ -49,583 +47,133 @@ enum platform k_platform_linux, k_platform_count, } -platform = k_platform_linux; +_option_platform = k_platform_linux; const c8 *platform_names[] = { [k_platform_native] = "native", [k_platform_windows] = "windows", [k_platform_linux] = "linux", }; -u64 processors = 4; - -void system_call( const c8 *call ) -{ - $log( $shell, {call} ); - ASSERT_CRITICAL( system(call) == 0 ); -} - struct { - c8 project_name[ 128 ]; - struct stream source_list, include_path_list, define_list, configuration; + struct stream name, tripple, folder; const c8 *enabled_features[ 64 ]; } static _metacompiler; -struct -{ - b8 using; - struct stream layer_enums; - struct stream input_enums, input_structures; -} -static _input; - -struct -{ - b8 using; - struct stream enums, structures; -} -static _threads; - -struct hook_event -{ - struct stream alias, returns, proto; - struct stretchy_allocator users; -}; -struct hook_user +struct shell_command { - struct stream function_name; - i32 affinity; + u32 temp_frame; + struct stream line; }; -struct -{ - b8 using; - struct stretchy_allocator events; -} -static _hooks; -struct +struct shell_command *_shell_command(void) { - b8 using; - struct stream cvar_header, cvar_definitions, command_prototypes, command_definitions; - u32 command_count; + u32 temp_frame = _start_temporary_frame(); + struct shell_command *command = _temporary_allocate( sizeof(struct shell_command), 8 ); + command->temp_frame = temp_frame; + stream_open_stack( &command->line, _temporary_stack_allocator(), k_stream_null_terminate ); + return command; } -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 +void _shell_run( struct shell_command *command ) { - b8 using; - struct stream shader_enum, uniform_enum, uniform_aliases, uniform_func_protos, uniform_funcs; - u32 uniform_count; - struct stretchy_allocator shaders; + $log( $shell, {string_get( &command->line )} ); + ASSERT_CRITICAL( system( string_get( &command->line ) ) == 0 ); + _end_temporary_frame( command->temp_frame ); } -static _shaders; -struct shell_command -{ - struct stream command; -}; -struct +u32 _assembly_kv_file_context( u32 kv ) { - struct stretchy_allocator shell; -} -static _commands; + u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 ); + ASSERT_CRITICAL( vgc_block ); -struct block_context -{ - enum target_type + u32 narrowest = 0; + u32 it = 0; + while( keyvalues_foreach( &_assembly, &it, vgc_block, "file_context" ) ) { - k_target_main, - k_target_input_layer, - k_target_input, - k_target_shader, - k_target_cvar, - k_target_ccmd, - k_target_thread, - k_target_hook, - k_target_subshader + u32 min = 0, max = 0; + keyvalues_read_u32s( &_assembly, it, "kv_start", NULL, &min, 1 ); + keyvalues_read_u32s( &_assembly, it, "kv_end", NULL, &max, 1 ); + + if( (kv >= min) && (kv <= max) ) + narrowest = it; } - target; - u32 feature_count; - const c8 *folder; -}; + return narrowest; +} -void _append_kv_list( const c8 *path, struct block_context context ); -void _parse_kv_block( struct keyvalues *kvs, u32 block, struct block_context context ) +void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count ); +void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 assembly_block, u32 feature_count ) { - const c8 *block_key = keyvalues_key( kvs, block, NULL ); - if( block_key != NULL ) + u32 it = 0; + while( keyvalues_foreach( kvs, &it, block, NULL ) ) { - if( context.target == k_target_shader ) + const c8 *it_k = keyvalues_key( kvs, it, NULL ); + if( keyvalues_type( kvs, it ) == k_keyvalue_type_frame ) { - if( compare_buffers( block_key, 0, "subshader", 0 )) + if( compare_buffers( it_k, 0, "test_feature", 0 ) ) { - context.target = k_target_subshader; + const c8 *test_feature = keyvalues_read_string( kvs, it, "feature", NULL ); + ASSERT_CRITICAL( test_feature ); - struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 ); - struct subshader *subshader = &shader->subshaders[ shader->subshader_count ++ ]; + b8 result = 0; + for( u32 i=0; itype = 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 ) - { - struct shader *shader = stretchy_get( &_shaders.shaders, stretchy_count( &_shaders.shaders ) -1 ); - struct subshader *subshader = &shader->subshaders[ shader->subshader_count -1 ]; - - 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", "f32 v[2]", "glUniform2fv", "1,v" }, - { "vec3", "f32 v[3]", "glUniform3fv", "1,v" }, - { "vec4", "f32 v[4]", "glUniform4fv", "1,v" }, - { "bool", "i32 b", "glUniform1i", "b" }, - { "mat2", "f32 m[2][2]", "glUniformMatrix2fv", "1,GL_FALSE,(f32*)m" }, - { "mat3", "f32 m[3][3]", "glUniformMatrix3fv", "1,GL_FALSE,(f32*)m" }, - { "mat4", "f32 m[4][4]", "glUniformMatrix4fv", "1,GL_FALSE,(f32*)m" }, - { "float", "f32 f", "glUniform1f", "f" }, - { "mat4x3", "f32 m[4][3]", "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; iname)}, {"_"}, {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 ++; + u32 pass_block = keyvalues_get( kvs, it, result? "yes": "no", 0 ); + if( pass_block ) + _parse_kv_block( kvs, pass_block, file_context, assembly_block, feature_count ); } else { - $log( $warning, {"We don't have a compiler:subshader definition for block '"}, {block_key}, {"'"} ); - return; + /* Add the block title to the assembly if it's not a pre-processor thing */ + _parse_kv_block( kvs, it, file_context, keyvalues_append_frame( &_assembly, assembly_block, it_k ), feature_count ); } } else { - 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 *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"} ); - - if( layer_mask ) - $v_string( &_input.input_structures, {" .layer_mask = 1<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"} ); - - 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 flag_kv = keyvalues_get( kvs, block, "flag", 0 ); - if( flag_kv ) - $v_string( &_threads.structures, {" .flags = "} ); - - u32 flag_count = 0; - while( flag_kv ) - { - const c8 *flag_name = keyvalues_value( kvs, flag_kv, NULL ); - $v_string( &_threads.structures, {flag_count? "|": ""}, {"THREAD_FLAG_"}, {flag_name} ); - - flag_kv = keyvalues_get( kvs, flag_kv, "flag", 1 ); - flag_count ++; - } - if( flag_count ) - $v_string( &_threads.structures, {",\n"} ); - - $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 ); - - i32 affinity = 0; - keyvalues_read_i32s( kvs, block, "affinity", NULL, &affinity, 1 ); - - b8 found = 0; - - for( u32 i=0; ialias ), 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} ); - user->affinity = affinity; - found = 1; - break; - } - } - - if( !found ) - { - $log( $fatal, {"We don't have an event registered called '"}, {event_alias}, {"'"} ); - _fatal_exit(); - } - } - else if( compare_buffers( block_key, 0, "symlink", 0 ) ) - { - const c8 *source = keyvalues_read_string( kvs, block, "source", NULL ); - const c8 *name = keyvalues_read_string( kvs, block, "name", NULL ); - ASSERT_CRITICAL( source ); - - struct shell_command *command = stretchy_append( &_commands.shell ); - stream_open_auto( &command->command, k_stream_null_terminate ); - $v_string( &command->command, {"ln -snf "}, {context.folder}, {"/"}, {source}, {" "}, {string_get( &folder_string )}, {"/"}, { name? name: source } ); - } - else - { - $log( $warning, {"We don't have a compiler definition for block '"}, {block_key}, {"'"} ); - return; - } - } - } - - u32 kv = keyvalues_get_child( kvs, block, 0 ); - while( kv ) - { - if( keyvalues_type( kvs, kv ) == k_keyvalue_type_frame ) - { - _parse_kv_block( kvs, kv, context ); - kv = keyvalues_get_next( kvs, kv ); - continue; - } - - const c8 *key = keyvalues_key( kvs, kv, NULL ); - const c8 *value = keyvalues_value( kvs, kv, NULL ); - - /* MUST COME FIRST */ - if( _setup_done == 0 ) - { - if( compare_buffers( key, 0, "name", 0 ) ) - { - buffer_copy( value, 0, _metacompiler.project_name, sizeof(_metacompiler.project_name) ); - - c8 *output_folder = realpath( target_file_path, NULL ); - if( !output_folder ) - { - $log( $fatal, {"'"}, {target_file_path}, {"' "}, $errno() ); - _fatal_exit(); - } - - i32 s = buffer_last_index( output_folder, '/', 0 ); - if( s != -1 ) - output_folder[s] = '\0'; - - - stream_open_auto( &tripple_string, k_stream_null_terminate ); - $v_string( &tripple_string, {architecture_names[arch]}, {"-"}, {platform_names[platform]} ); - if( platform == k_platform_linux ) - { - $v_string( &tripple_string, {"-gnu"} ); - if( libc != k_libc_version_native ) - $v_string( &tripple_string, {"."}, {libc_names[libc]} ); - } - - stream_open_auto( &folder_string, k_stream_null_terminate ); - $v_string( &folder_string, {output_folder}, {"/bin/"}, {_metacompiler.project_name}, {"-"}, {string_get(&tripple_string)} ); - - free(output_folder); - - u32 temp_frame = _start_temporary_frame(); - { - struct stream command_string; - stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate ); - $v_string( &command_string, {"mkdir -p "}, {string_get( &folder_string )} ); - system_call( string_get( &command_string ) ); - } - _end_temporary_frame( temp_frame ); - - _setup_done = 1; - } - } - - ASSERT_CRITICAL( _setup_done ); - - /* availible in all targets */ - if( compare_buffers( key, 0, "enable", 0 ) ) - _metacompiler.enabled_features[ context.feature_count ++ ] = keyvalues_value( kvs, kv, NULL ); - - if( compare_buffers( key, 0, "if", 0 ) ) - { - b8 invert = 0; - if( value[0] == '!' ) - { - value ++; - invert = 1; - } - - b8 result = 0 ^ invert; - for( u32 i=0; isubshaders[ shader->subshader_count -1 ]; + /* ENABLE FEATURE */ + if( compare_buffers( it_k, 0, "enable", 0 ) ) + _metacompiler.enabled_features[ feature_count ++ ] = it_v; - if( compare_buffers( key, 0, "add", 0 ) ) + /* APPEND */ + else if( compare_buffers( it_k, 0, "append", 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; - } + + const c8 *folder = keyvalues_read_string( &_assembly, file_context, "folder", NULL ); + ASSERT_CRITICAL( folder ); - stream_close( &source ); - $v_string( &subshader->debug_source_list, {" \""}, {string_get(&path_string)}, {"\",\n"} ); - subshader->debug_source_count ++; + $v_string( &path_string, {folder}, {"/"}, {it_v} ); + _assemble_kv_file( string_get( &path_string ), assembly_block, feature_count ); } _end_temporary_frame( temp_frame ); } - } - kv = keyvalues_get_next( kvs, kv ); + /* NORMAL KV */ + else + keyvalues_append_string( &_assembly, assembly_block, it_k, it_v ); + } } } -void _append_kv_list( const c8 *path, struct block_context context ) +void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count ) { - u32 temp_frame = _start_temporary_frame(); + /* Append context block */ + u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 ); + ASSERT_CRITICAL( vgc_block ); + u32 context_block = keyvalues_append_frame( &_assembly, vgc_block, "file_context" ); + u32 start_offset = keyvalues_current_offset( &_assembly ); + + u32 temp_frame = _start_temporary_frame(); c8 *folder = realpath( path, NULL ); if( !folder ) { @@ -635,17 +183,19 @@ void _append_kv_list( const c8 *path, struct block_context context ) i32 s = buffer_last_index( folder, '/', 0 ); if( s != -1 ) - { folder[s] = '\0'; - } - context.folder = folder; + keyvalues_append_string( &_assembly, context_block, "folder", folder ); + free( folder ); - struct keyvalues list; - ASSERT_CRITICAL( keyvalues_read_file( &list, path, _temporary_stack_allocator() ) ); - _parse_kv_block( &list, 0, context ); + /* read file and pre-process it into the assembly */ + struct keyvalues kvs; + ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) ); + _parse_kv_block( &kvs, 0, context_block, assembly_block, feature_count ); - free( folder ); + u32 end_offset = keyvalues_current_offset( &_assembly ); + keyvalues_append_u32s( &_assembly, context_block, "kv_start", &start_offset, 1 ); + keyvalues_append_u32s( &_assembly, context_block, "kv_end", &end_offset, 1 ); _end_temporary_frame( temp_frame ); } @@ -667,42 +217,48 @@ void _metacompiler_options(void) { const c8 *arg; if( _option_long( "tsan", "Build using thread sanitizer" ) ) - use_tsan = 1; + _option_use_tsan = 1; if( _option_long( "asan", "Build using address sanitizer" ) ) - use_asan = 1; + _option_use_asan = 1; - if( use_tsan || use_asan ) - ASSERT_CRITICAL( use_tsan != use_asan ); + if( _option_use_tsan || _option_use_asan ) + ASSERT_CRITICAL( _option_use_tsan != _option_use_asan ); if( _option_long( "shared", "Create a shared object, instead of executable" ) ) - shared = 1; + _option_shared = 1; if( _option_long( "no-pdb", "Prevent compiler from outputing pdb files" ) ) - no_pdb = 1; + _option_no_pdb = 1; if( (arg = _option_long_argument( "libc", "native, 2.23 (recommended)" )) ) - libc = enum_read( libc_names, k_libc_version_count, arg ); + _option_libc = enum_read( libc_names, k_libc_version_count, arg ); if( (arg = _option_long_argument( "arch", "native, i386, x86_64" )) ) - arch = enum_read( architecture_names, k_architecture_count, arg ); - ASSERT_CRITICAL( arch == k_architecture_x86_64 ); + _option_arch = enum_read( architecture_names, k_architecture_count, arg ); + ASSERT_CRITICAL( _option_arch == k_architecture_x86_64 ); if( (arg = _option_long_argument( "platform", "native, linux, windows" )) ) - platform = enum_read( platform_names, k_platform_count, arg ); + _option_platform = enum_read( platform_names, k_platform_count, arg ); if( (arg = _option_argument( 'j', "CPU count <0-8>" )) ) { struct stream arg_stream; stream_open_buffer_read( &arg_stream, (void*)arg, buffer_last_index(arg,0,0)+1, 0 ); - ASSERT_CRITICAL( string_parse_u64( &arg_stream, &processors ) == k_string_parse_ok ); + ASSERT_CRITICAL( string_parse_u64( &arg_stream, &_option_processors ) == k_string_parse_ok ); } if( _option_flag( 'O', "Run optimisers" ) ) - optimise = 3; + _option_optimise = 3; + + if( (arg = _option_argument( 'E', "Assemble target KV's only, and output it to file" )) ) + { + _option_assemble_only = 1; + ASSERT_CRITICAL( stream_open_file( &_assemble_file, arg, k_stream_write ) ); + } - target_file_path = _option(); - ASSERT_CRITICAL( target_file_path ); + _option_file_path = _option(); + ASSERT_CRITICAL( _option_file_path ); } void (*_event_OPTIONS_subscribers[])( void ) = @@ -711,281 +267,567 @@ void (*_event_OPTIONS_subscribers[])( void ) = NULL }; -i32 main( i32 argc, const c8 *argv[] ) +i32 compar( const void *a, const void *b ) { - VG_PRE_MAIN; + return ((struct sort_index *)a)->value - ((struct sort_index *)b)->value; +} - 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 ) ); - stretchy_init( &_commands.shell, sizeof( struct shell_command ) ); - - if( platform == k_platform_linux ) _metacompiler.enabled_features[0] = "linux"; - if( platform == k_platform_windows ) _metacompiler.enabled_features[0] = "windows"; - - struct block_context context = {0}; - context.feature_count = 1; - _append_kv_list( target_file_path, context ); - - ASSERT_CRITICAL( _setup_done ); - - system_call( "mkdir -p generated" ); - - $log( $info, {"Generating input header"} ); - if( _input.using ) - { - struct stream input_header, input_source; - ASSERT_CRITICAL( stream_open_file( &input_header, "generated/input.h", k_stream_write ) ); - $v_string( &input_header, {"enum input_layer_id\n{\n"}, {string_get( &_input.layer_enums )}, {" k_input_layer_count\n};\n"} ); - $v_string( &input_header, {"enum input_id\n{\n"}, {string_get( &_input.input_enums )}, {" k_input_count\n};\n"} ); - stream_close( &input_header ); - - ASSERT_CRITICAL( stream_open_file( &input_source, "generated/input.c", k_stream_write ) ); - $v_string( &input_source, {"struct input_info _input_infos[k_input_count] = \n{\n"}, - {string_get( &_input.input_structures )}, {"};\n"} ); - stream_close( &input_source ); - } +void index_sort( struct sort_index *indices, u32 indice_count ) +{ + qsort( indices, indice_count, sizeof(struct sort_index), compar ); +} - $log( $info, {"Generating threads header"} ); - if( _threads.using ) +void _codegen_hooks(void) +{ + $log( $info, {"[CODEGEN] hooks.h/hooks.c"} ); + struct stream hooks_c, hooks_h; + stream_open_file( &hooks_h, "generated/hooks.h", k_stream_write ); + stream_open_file( &hooks_c, "generated/hooks.c", k_stream_write ); + + u32 it = 0; + while( keyvalues_foreach( &_assembly, &it, 0, "event" ) ) { - struct stream thread_header, thread_source; - ASSERT_CRITICAL( stream_open_file( &thread_header, "generated/threads.h", k_stream_write ) ); - $v_string( &thread_header, {"enum thread_id\n{\n"}, - {string_get( &_threads.enums )}, - {" k_thread_count\n};\n"} ); - stream_close( &thread_header ); - - ASSERT_CRITICAL( stream_open_file( &thread_source, "generated/threads.c", k_stream_write ) ); - $v_string( &thread_source, {"struct thread_info _thread_infos[k_thread_count] = \n{\n"}, - {string_get( &_threads.structures )}, {"};\n"} ); - stream_close( &thread_source ); + const c8 *name = keyvalues_read_string( &_assembly, it, "name", NULL ); + const c8 *prototype = keyvalues_read_string( &_assembly, it, "prototype", NULL ); + const c8 *returns = keyvalues_read_string( &_assembly, it, "returns", "void" ); + ASSERT_CRITICAL( name && prototype && returns ); + + $v_string( &hooks_h, {"extern "}, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" );\n"} ); + + struct sort_index indices[ 128 ]; + u32 indice_count = 0; + + u32 user_it = 0; + while( keyvalues_foreach( &_assembly, &user_it, 0, "hook" ) ) + { + if( compare_buffers( keyvalues_read_string( &_assembly, user_it, "event", NULL ), 0, name, 0 ) ) + { + i32 affinity = 0; + keyvalues_read_i32s( &_assembly, user_it, "affinity", NULL, &affinity, 1 ); + + ASSERT_CRITICAL( indice_count < ARRAY_COUNT( indices ) ); + indices[ indice_count ].index = user_it; + indices[ indice_count ++ ].value = affinity; + + const c8 *function = keyvalues_read_string( &_assembly, user_it, "function", NULL ); + ASSERT_CRITICAL( function ); + $v_string( &hooks_c, {returns}, {" "}, {function}, {"( "}, {prototype}, {" );\n"} ); + } + } + + index_sort( indices, indice_count ); + + $v_string( &hooks_c, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" ) = \n{\n"} ); + for( u32 i=0; ireturns ), - *alias = string_get( &event->alias ), - *prototype = string_get( &event->proto ); + const c8 *shader_name = keyvalues_read_string( &_assembly, it, "name", NULL ); + ASSERT_CRITICAL( shader_name ); + $v_string( &H, {" k_shader_"},{shader_name},{",\n"} ); + $v_string( &C, {" [k_shader_"},{shader_name},{"] = \n {\n"} ); + $v_string( &C, {" .name = \""}, {shader_name}, {"\",\n"} ); + $v_string( &C, {" .uniform_start = "}, $unsigned( uniform_start ), {",\n"} ); - $v_string( &header, {"extern "}, {returns}, {" (*_event_"}, {alias}, {"_subscribers[])( "}, {prototype}, {" );\n"} ); + $v_string( &C, {" .subshaders = (struct subshader[])\n {\n"} ); - for( u32 j=0; jusers ); j ++ ) + u32 subshader_count = 0; + u32 subshader_it = 0; + while( keyvalues_foreach( &_assembly, &subshader_it, it, "subshader" ) ) + { + const c8 *subshader_type = keyvalues_read_string( &_assembly, subshader_it, "type", NULL ); + ASSERT_CRITICAL( subshader_type ); + + $v_string( &C, {" {\n"} ); + $v_string( &C, {" .type = k_subshader_"}, {subshader_type}, {",\n"} ); + $v_string( &C, {" .source_list = (const c8 *[])\n"} ); + $v_string( &C, {" {\n"} ); + + u32 sources[ 32 ]; + u32 source_count = 0; + u32 source_it = 0; + while( keyvalues_foreach( &_assembly, &source_it, subshader_it, "add" ) ) { - struct hook_user *user = stretchy_get( &event->users, j ); - $v_string( &source, {returns}, {" "}, {string_get(&user->function_name)}, {"( "}, {prototype}, {" );\n"} ); + ASSERT_CRITICAL( source_count < ARRAY_COUNT( sources ) ); + + /* FIXME: EXPAND PATH TO FULL PATH LIKE SOURCE ADDS */ + const c8 *path = keyvalues_value( &_assembly, source_it, NULL ); + $v_string( &C, {" \""}, {path}, {"\",\n"} ); + sources[ source_count ++ ] = source_it; } - $v_string( &source, {"\n"} ); - b8 sorted = 0; - while( !sorted ) + $v_string( &C, {" },\n"} ); + $v_string( &C, {" .source_count = "}, $unsigned( source_count ), {",\n"} ); + $v_string( &C, {" .source_static = \""} ); + + for( u32 i=0; iusers ); j ++ ) + u32 context_block = _assembly_kv_file_context( sources[i] ); + u32 temp_frame = _start_temporary_frame(); { - struct hook_user *j1 = stretchy_get( &event->users, j ), - *j0 = stretchy_get( &event->users, j-1 ); - if( j1->affinity < j0->affinity ) + struct stream path_string; + stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate ); + $v_string( &path_string, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, {"/"}, + {keyvalues_value( &_assembly, sources[i], NULL )} ); + + struct stream source_file; + ASSERT_CRITICAL( stream_open_file( &source_file, string_get( &path_string ), k_stream_read ) ); + u32 l; + const c8 *source = stream_read_all( &source_file, _temporary_stack_allocator(), 4, &l ); + for( u32 k=0; kusers ); j ++ ) + $v_string( &C, {"\",\n"} ); + $v_string( &C, {" .source_uniforms = \""} ); + + u32 uniform_it = 0; + while( keyvalues_foreach( &_assembly, &uniform_it, subshader_it, "uniform" ) ) { - struct hook_user *user = stretchy_get( &event->users, j ); - $v_string( &source, {" "}, {string_get(&user->function_name)}, {",\n"} ); + struct uniform_info *uniform = stretchy_append( &uniforms ); + uniform->kv = uniform_it; + uniform->glsl_index = -1; + uniform->type = keyvalues_read_string( &_assembly, uniform_it, "type", NULL ); + uniform->alias = keyvalues_read_string( &_assembly, uniform_it, "alias", NULL ); + uniform->shader_name = shader_name; + ASSERT_CRITICAL( uniform->type && uniform->alias ); + + for( i32 i=0; itype, 0 ) ) + { + uniform->glsl_index = i; + break; + } + } + ASSERT_CRITICAL( uniform->glsl_index != -1 ); + uniform_count ++; + + $v_string( &C, {"uniform "}, {uniform->type}, {" "}, {uniform->alias}, {";\\n"} ); } - $v_string( &source, {" NULL\n};\n\n"} ); + $v_string( &C, {"\",\n"}, {" },\n"} ); + subshader_count ++; } - stream_close( &source ); - stream_close( &header ); + $v_string( &C, {" },\n"} ); + $v_string( &C, {" .uniform_count = "}, $unsigned( uniform_count ), {",\n"} ); + $v_string( &C, {" .subshader_count = "}, $unsigned( subshader_count ), {",\n"} ); + $v_string( &C, {" },\n"} ); + } + $v_string( &C, {"};\n"} ); + $v_string( &H, {" k_shader_count\n};\n\n"} ); + - $v_string( &_metacompiler.source_list, {" generated/hooks.c \\\n"} ); + for( u32 i=0; iglsl_index ]; + $v_string( &H, {"void _shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {"( "},{trans->args},{" );\n"} ); + $v_string( &C, {"void _shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {"( "},{trans->args},{" )"}, + {"{ "},{trans->call},{"( _uniform_locations["}, {"k_shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, + {"], "},{trans->end},{" ); }\n"}); } - $log( $info, {"Generating shader header"} ); - if( _shaders.using ) + $v_string( &C, {"const c8 *_uniform_aliases[] = \n{\n"} ); + $v_string( &H, {"enum shader_uniform_id\n{\n"} ); + for( u32 i=0; ishader_name}, {"_"}, {uniform->alias}, {",\n"} ); + $v_string( &C, {" [k_shader_"}, {uniform->shader_name}, {"_"}, {uniform->alias}, {"] = \""}, {uniform->alias},{"\",\n"} ); + } + $v_string( &H, {" k_shader_uniform_count\n};\n\n"} ); + $v_string( &C, {"};\n"} ); - $v_string( &source, {"const c8 *_uniform_aliases[] = \n{\n"}, {string_get( &_shaders.uniform_aliases )}, {"};\n"} ); + stream_close( &H ); + stream_close( &C ); - $v_string( &source, {"struct shader _shader_definitions[] = {\n"} ); - for( u32 i=0; iname )}, {"] = \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; jsubshader_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; kstatic_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"} ); - } + stretchy_free( &uniforms ); +} - $v_string( &source, {" },\n"} ); - $v_string( &source, {" },\n"} ); - } - $v_string( &source, {"};\n"} ); - $v_string( &source, {string_get( &_shaders.uniform_funcs )} ); +void _codegen_inputs(void) +{ + $log( $info, {"[CODEGEN] input.h/input.c"} ); + struct stream C, H; + stream_open_file( &H, "generated/input.h", k_stream_write ); + stream_open_file( &C, "generated/input.c", k_stream_write ); + + $v_string( &H, {"enum input_layer_id\n{\n"} ); + u32 layer_it = 0; + while( keyvalues_foreach( &_assembly, &layer_it, 0, "input_layer" ) ) + { + const c8 *layer_name = keyvalues_read_string( &_assembly, layer_it, "name", NULL ); + ASSERT_CRITICAL( layer_name ); + $v_string( &H, {" k_input_layer_"}, {layer_name}, {",\n"} ); + } + $v_string( &H, {" k_input_layer_count\n};\n"} ); + + $v_string( &H, {"enum input_id\n{\n"} ); + $v_string( &C, {"struct input_info _input_infos[k_input_count] = \n{\n"} ); + u32 input_it = 0; + while( keyvalues_foreach( &_assembly, &input_it, 0, "input" ) ) + { + const c8 *name = keyvalues_read_string( &_assembly, input_it, "name", NULL ); + const c8 *type = keyvalues_read_string( &_assembly, input_it, "type", NULL ); + const c8 *layer_mask = keyvalues_read_string( &_assembly, input_it, "layer_mask", NULL ); + ASSERT_CRITICAL( name && type ); + + $v_string( &H, {" k_input_"},{type},{"_"}, {name}, {",\n"} ); + $v_string( &C, {" [k_input_"},{type},{"_"},{name},{"]=\n {\n"} ); + $v_string( &C, {" .name = \""},{name},{"\",\n"} ); + $v_string( &C, {" .type = k_input_type_"},{type},{",\n"} ); + if( layer_mask ) + $v_string( &C, {" .layer_mask = 1<line, {"mkdir -p "}, {string_get( &_metacompiler.folder )}, {"/cfg"} ); + _shell_run( cmd ); + u32 temp_frame = _start_temporary_frame(); { - struct stream command_string; - stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate ); - $v_string( &command_string, {"taskset -c 0-"}, $unsigned(processors), - {" zig cc -Wall -Wno-unused-function -std=gnu99 -D_REENTRANT -lm \\\n"}, - {" -include \"source/types.h\" \\\n"} ); + struct stream config_path; + stream_open_stack( &config_path, _temporary_stack_allocator(), k_stream_null_terminate ); + $v_string( &config_path, {string_get( &_metacompiler.folder )}, {"/cfg/default.cfg"} ); + + struct stream config_file; + ASSERT_CRITICAL( stream_open_file( &config_file, string_get( &config_path ), k_stream_write ) ); + + u32 config_it = 0; + while( keyvalues_foreach( &_assembly, &config_it, 0, "config" ) ) + $v_string( &config_file, {keyvalues_value( &_assembly, config_it, NULL ) }, {"\n"} ); + + stream_close( &config_file ); + } + _end_temporary_frame( temp_frame ); +} + +i32 main( i32 argc, const c8 *argv[] ) +{ + VG_PRE_MAIN; + + /* Get name of target and do a few checks */ + i32 name_start = buffer_last_index( _option_file_path, '/', 0 ); + if( name_start == -1 ) name_start = 0; + i32 name_end = buffer_last_index( _option_file_path+name_start, '.', 0 ); + + b8 valid_kv = 1; + if( name_end == -1 ) + valid_kv = 0; + else if( !compare_buffers( ".kv", 0, _option_file_path+name_start+name_end, 0 ) ) + valid_kv = 0; + + if( !valid_kv ) + { + $log( $fatal, {"Target file must be a text KV file"} ); + _fatal_exit(); + } + + struct stream open_test; + if( stream_open_file( &open_test, _option_file_path, k_stream_read ) ) + stream_close( &open_test ); + else + { + $log( $fatal, {"Can't open for reading: '"}, {_option_file_path}, {"'"} ); + _fatal_exit(); + } + + stream_open_auto( &_metacompiler.name, k_stream_null_terminate ); + string_append( &_metacompiler.name, _option_file_path+name_start, name_end ); + + /* Create tripple string from configuration */ + stream_open_auto( &_metacompiler.tripple, k_stream_null_terminate ); + $v_string( &_metacompiler.tripple, {architecture_names[_option_arch]}, {"-"}, {platform_names[_option_platform]} ); + if( _option_platform == k_platform_linux ) + { + $v_string( &_metacompiler.tripple, {"-gnu"} ); + if( _option_libc != k_libc_version_native ) + $v_string( &_metacompiler.tripple, {"."}, {libc_names[_option_libc]} ); + } + + /* Create output folder (relative to target file) from tripple + name */ + c8 *output_folder = realpath( _option_file_path, NULL ); + if( !output_folder ) + { + $log( $fatal, {"While making output folder, realpath of '"}, {_option_file_path}, {"' failed. "}, $errno() ); + _fatal_exit(); + } + + i32 s = buffer_last_index( output_folder, '/', 0 ); + if( s != -1 ) + output_folder[s] = '\0'; + + stream_open_auto( &_metacompiler.folder, k_stream_null_terminate ); + $v_string( &_metacompiler.folder, {output_folder}, {"/bin/"}, + {string_get( &_metacompiler.name )}, {"-"}, {string_get( &_metacompiler.tripple )} ); + + free(output_folder); + $log( $info, {"Target: "}, {string_get( &_metacompiler.name )}, {"\n"}, + {" configuration: "}, {string_get( &_metacompiler.tripple )}, {_option_shared?" SO":" EXE"}, {"\n"}, + {" output: "}, {string_get( &_metacompiler.folder )}, {"\n"}, + {" options: "}, + {_option_use_tsan? "TSAN ": ""}, + {_option_use_asan? "ASAN ": ""}, + {"O"}, $unsigned( _option_optimise ), {" "}, + {_option_no_pdb? "NO-PDB ": ""} ); + + /* Assemble target into main assembly */ + struct stack_allocator stack; + stack_init( &stack, NULL, BYTES_MB(16), "Assembly KV" ); + keyvalues_init( &_assembly, &stack ); + + u32 info_block = keyvalues_append_frame( &_assembly, 0, "vgc" ); + keyvalues_append_string( &_assembly, info_block, "version", "0.2" ); + keyvalues_append_string( &_assembly, info_block, "name", string_get( &_metacompiler.name ) ); + keyvalues_append_string( &_assembly, info_block, "tripple", string_get( &_metacompiler.tripple ) ); + keyvalues_append_string( &_assembly, info_block, "folder", string_get( &_metacompiler.folder ) ); + + if( _option_platform == k_platform_linux ) _metacompiler.enabled_features[0] = "linux"; + if( _option_platform == k_platform_windows ) _metacompiler.enabled_features[0] = "windows"; + _assemble_kv_file( _option_file_path, 0, 1 ); + + if( _option_assemble_only ) + { + keyvalues_write_stream( &_assembly, &_assemble_file, 0, 0 ); + stream_close( &_assemble_file ); + _normal_exit(); + } + + { + struct shell_command *cmd = _shell_command(); + $v_string( &cmd->line, {"mkdir -p "}, {string_get( &_metacompiler.folder )} ); + _shell_run( cmd ); + } + + { + struct shell_command *cmd = _shell_command(); + $v_string( &cmd->line, {"mkdir -p generated"} ); + _shell_run( cmd ); + } + + _codegen_hooks(); + _codegen_shaders(); + _codegen_inputs(); + _codegen_console(); + _codegen_threads(); + + { + struct shell_command *cmd = _shell_command(); + $v_string( &cmd->line, {"taskset -c 0-"}, $unsigned(_option_processors), + {" zig cc -Wall -Wno-unused-function -std=gnu99 -D_REENTRANT -lm \\\n"}, + {" -include \"vg/source/types.h\" \\\n"} ); #if 0 - if( use_tsan ) $v_string( &command_string, {" -fsanitize=thread -lasan -L/lib/x86_64-linux-gnu \\\n"} ); - if( use_asan ) $v_string( &command_string, {" -fsanitize=address -lasan -L/lib/x86_64-linux-gnu \\\n"} ); + if( _option_use_tsan ) $v_string( &cmd->line, {" -fsanitize=thread -lasan -L/lib/x86_64-linux-gnu \\\n"} ); + if( _option_use_asan ) $v_string( &cmd->line, {" -fsanitize=address -lasan -L/lib/x86_64-linux-gnu \\\n"} ); #else - if( use_tsan ) $v_string( &command_string, {" -fsanitize=thread -lasan -L/usr/lib \\\n"} ); - if( use_asan ) $v_string( &command_string, {" -fsanitize=address -lasan -L/usr/lib \\\n"} ); + if( _option_use_tsan ) $v_string( &cmd->line, {" -fsanitize=thread -lasan -L/usr/lib \\\n"} ); + if( _option_use_asan ) $v_string( &cmd->line, {" -fsanitize=address -lasan -L/usr/lib \\\n"} ); #endif - $v_string( &command_string, { " -O" }, $unsigned( optimise ) ); - if( optimise == 0 ) $v_string( &command_string, { " -ggdb" } ); - $v_string( &command_string, {" \\\n"} ); + $v_string( &cmd->line, { " -O" }, $unsigned( _option_optimise ) ); + if( _option_optimise == 0 ) $v_string( &cmd->line, { " -ggdb" } ); + $v_string( &cmd->line, {" \\\n"} ); - $v_string( &command_string, {shared? " -shared -fPIC ": " "} ); - if( platform == k_platform_windows ) + $v_string( &cmd->line, {_option_shared? " -shared -fPIC ": " "} ); + if( _option_platform == k_platform_windows ) { - $v_string( &command_string, {" -target "}, {string_get(&tripple_string)}, {" \\\n"} ); - if( !shared ) - $v_string( &command_string, {"-Wl,--subsystem=windows "} ); + $v_string( &cmd->line, {" -target "}, {string_get(&_metacompiler.tripple)}, {" \\\n"} ); + if( !_option_shared ) + $v_string( &cmd->line, {"-Wl,--subsystem=windows "} ); - if( no_pdb ) - $v_string( &command_string, {"-Wl,/pdb:/dev/null "} ); + if( _option_no_pdb ) + $v_string( &cmd->line, {"-Wl,/pdb:/dev/null "} ); } - $v_string( &command_string, {"\\\n"} ); - - $v_string( &command_string, {" -fno-sanitize=undefined \\\n"} ); - $v_string( &command_string, {" -fkeep-static-consts -fkeep-persistent-storage-variables \\\n"} ); - - $v_string( &command_string, {" -I. \\\n"} ); - $v_string( &command_string, {string_get( &_metacompiler.include_path_list )} ); - $v_string( &command_string, {string_get( &_metacompiler.define_list )} ); - $v_string( &command_string, {string_get( &_metacompiler.source_list )} ); + $v_string( &cmd->line, {"\\\n"} ); - $v_string( &command_string, {"-o "}, {string_get( &folder_string )}, {"/"}, {_metacompiler.project_name} ); - if( platform == k_platform_windows ) $v_string( &command_string, {shared? ".dll ": ".exe "} ); - else $v_string( &command_string, {shared? ".so ": " "} ); + $v_string( &cmd->line, {" -fno-sanitize=undefined \\\n"} ); + $v_string( &cmd->line, {" -fkeep-static-consts -fkeep-persistent-storage-variables \\\n"} ); - system_call( string_get( &command_string ) ); + $v_string( &cmd->line, {" -I. \\\n"} ); - if( _console.using ) + u32 compile_it = 0; + while( keyvalues_foreach( &_assembly, &compile_it, 0, NULL ) ) { - u32 temp_frame2 = _start_temporary_frame(); - struct stream command_string; - stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate ); - $v_string( &command_string, {"mkdir -p "}, {string_get( &folder_string )}, {"/cfg"} ); - system_call( string_get( &command_string ) ); - - struct stream config_path; - stream_open_stack( &config_path, _temporary_stack_allocator(), k_stream_null_terminate ); - $v_string( &config_path, {string_get( &folder_string )}, {"/cfg/default.cfg"} ); - - struct stream config_file; - ASSERT_CRITICAL( stream_open_file( &config_file, string_get( &config_path ), k_stream_write ) ); - $v_string( &config_file, {string_get( &_metacompiler.configuration )} ); - stream_close( &config_file ); - - _end_temporary_frame( temp_frame2 ); + const c8 *key = keyvalues_key( &_assembly, compile_it, NULL ); + b8 include = compare_buffers( key, 0, "include", 0 ), + link = compare_buffers( key, 0, "link", 0 ), + link_path = compare_buffers( key, 0, "link_folder", 0 ), + define = compare_buffers( key, 0, "define", 0 ), + add = compare_buffers( key, 0, "add", 0 ); + + if( include || link_path || add ) + { + u32 context_block = _assembly_kv_file_context( compile_it ); + if( include ) $v_string( &cmd->line, {" -I"} ); + if( link_path ) $v_string( &cmd->line, {" -L"} ); + if( add ) $v_string( &cmd->line, {" "} ); + $v_string( &cmd->line, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, + {"/"}, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} ); + } + else if( define || link ) + { + if( define ) $v_string( &cmd->line, {" -D"} ); + if( link ) $v_string( &cmd->line, {" -l"} ); + $v_string( &cmd->line, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} ); + } } + $v_string( &cmd->line, {" generated/hooks.c \\\n"} ); - for( u32 i=0; i< stretchy_count( &_commands.shell ); i ++ ) - { - struct shell_command *command = stretchy_get( &_commands.shell, i ); - system_call( string_get( &command->command ) ); - } + $v_string( &cmd->line, {"-o "}, {string_get( &_metacompiler.folder )}, {"/"}, {string_get(&_metacompiler.name)} ); + if( _option_platform == k_platform_windows ) $v_string( &cmd->line, {_option_shared? ".dll ": ".exe "} ); + else $v_string( &cmd->line, {_option_shared? ".so ": " "} ); + _shell_run( cmd ); } - _end_temporary_frame( temp_frame ); + + _default_config_gen(); return 0; } -- 2.25.1