diff --git a/code2/grime/Readme.md b/code2/grime/Readme.md index 6da0bad..672008a 100644 --- a/code2/grime/Readme.md +++ b/code2/grime/Readme.md @@ -2,6 +2,5 @@ This is a top-level package to adjust odin to my personalized usage. -I curate all usage of odin's provided package definitons through here. The client and host packages should never directly import them. +There is only one definition with static allocations in Grime. Ideally there are also none, but spall profiler needs a context. -There are no implicit static allocations in Grime. Ideally there are also none from the base/core packages but some probably leak. diff --git a/code2/grime/assert.odin b/code2/grime/assert.odin index 43bc8ea..e4918a0 100644 --- a/code2/grime/assert.odin +++ b/code2/grime/assert.odin @@ -1,32 +1,21 @@ package grime -import "core:os" - // Below should be defined per-package -ensure :: #force_inline proc( condition : b32, msg : string, location := #caller_location ) -{ - if condition { - return - } +ensure :: #force_inline proc( condition : b32, msg : string, location := #caller_location ) { + if condition do return log_print( msg, LoggerLevel.Warning, location ) debug_trap() } - // TODO(Ed) : Setup exit codes! -fatal :: #force_inline proc( msg : string, exit_code : int = -1, location := #caller_location ) -{ +fatal :: #force_inline proc( msg : string, exit_code : int = -1, location := #caller_location ) { log_print( msg, LoggerLevel.Fatal, location ) debug_trap() process_exit( exit_code ) } - // TODO(Ed) : Setup exit codes! -verify :: #force_inline proc( condition : b32, msg : string, exit_code : int = -1, location := #caller_location ) -{ - if condition { - return - } +verify :: #force_inline proc( condition : b32, msg : string, exit_code : int = -1, location := #caller_location ) { + if condition do return log_print( msg, LoggerLevel.Fatal, location ) debug_trap() process_exit( exit_code ) diff --git a/code2/grime/context.odin b/code2/grime/context.odin index e728da5..c76a7b3 100644 --- a/code2/grime/context.odin +++ b/code2/grime/context.odin @@ -1,6 +1,6 @@ package grime -Context :: struct { +OdinContext :: struct { allocator: AllocatorInfo, temp_allocator: AllocatorInfo, assertion_failure_proc: Assertion_Failure_Proc, @@ -14,6 +14,6 @@ Context :: struct { _internal: rawptr, } -context_usr :: #force_inline proc( $ Type : typeid ) -> (^Type) { +context_user :: #force_inline proc( $ Type : typeid ) -> (^Type) { return cast(^Type) context.user_ptr } diff --git a/code2/grime/memory.odin b/code2/grime/memory.odin index 0baceff..20183bb 100644 --- a/code2/grime/memory.odin +++ b/code2/grime/memory.odin @@ -119,7 +119,6 @@ memory_aign_forward :: #force_inline proc( address, alignment : uintptr) -> uint return aligned_address } - // align_up :: proc(address: uintptr, alignment: uintptr) -> uintptr { // return (address + alignment - 1) & ~(alignment - 1) // } diff --git a/code2/grime/pkg_mappings.odin b/code2/grime/pkg_mappings.odin index 81bf018..ca1f393 100644 --- a/code2/grime/pkg_mappings.odin +++ b/code2/grime/pkg_mappings.odin @@ -78,8 +78,12 @@ import "core:os" file_truncate :: os.truncate file_write :: os.write - file_read_entire :: os.read_entire_file - file_write_entire :: os.write_entire_file + file_read_entire_from_filename :: #force_inline proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { return os.read_entire_file_from_filename(name, resolve_odin_allocator(allocator), loc) } + file_write_entire :: os.write_entire_file + + file_read_entire :: proc { + file_read_entire_from_filename, + } import "core:strings" StrBuilder :: strings.Builder diff --git a/code2/grime/profiler.odin b/code2/grime/profiler.odin index 7e11c7d..9d160ef 100644 --- a/code2/grime/profiler.odin +++ b/code2/grime/profiler.odin @@ -12,26 +12,21 @@ SpallProfiler :: struct { buffer : spall.Buffer, } -// @(private) -// Module_Context : ^SpallProfiler - -// set_profiler_module_context :: #force_inline proc "contextless" ( ctx : ^SpallProfiler ) { -// Module_Context = ctx -// } +set_profiler_module_context :: #force_inline proc "contextless" ( profiler : ^SpallProfiler ) { + static_memory.spall_profiler = profiler +} DISABLE_PROFILING :: true @(deferred_none = profile_end, disabled = DISABLE_PROFILING) profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { - // spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc ) + spall._buffer_begin( & static_memory.spall_profiler.ctx, & static_memory.spall_profiler.buffer, name, "", loc ) } - @(disabled = DISABLE_PROFILING) profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { - // spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc ) + spall._buffer_begin( & static_memory.spall_profiler.ctx, & static_memory.spall_profiler.buffer, name, "", loc ) } - @(disabled = DISABLE_PROFILING) profile_end :: #force_inline proc "contextless" () { - // spall._buffer_end( & Module_Context.ctx, & Module_Context.buffer) + spall._buffer_end( & static_memory.spall_profiler.ctx, & static_memory.spall_profiler.buffer) } diff --git a/code2/grime/static_memory.odin b/code2/grime/static_memory.odin new file mode 100644 index 0000000..c8fea22 --- /dev/null +++ b/code2/grime/static_memory.odin @@ -0,0 +1,9 @@ +package grime + +//region STATIC MEMORY +static_memory: StaticMemory +//endregion STATIC MEMORY + +StaticMemory :: struct { + spall_profiler: ^SpallProfiler, +} \ No newline at end of file diff --git a/code2/gui_code/Readme.md b/code2/gui_code/Readme.md new file mode 100644 index 0000000..4a71acc --- /dev/null +++ b/code2/gui_code/Readme.md @@ -0,0 +1,3 @@ +# gui_code + +This is the UI package used by sectr. It's meant to be optimial for composing complex code visualizations in soft real-time. diff --git a/code2/host/host.odin b/code2/host/host.odin index 0089ff3..4b62d5d 100644 --- a/code2/host/host.odin +++ b/code2/host/host.odin @@ -1,59 +1,39 @@ package host -import "core:thread" -import "core:sync" +//region STATIC MEMORY +// All program defined process memory here. (There will still be artifacts from the OS CRT and third-party pacakges) + host_memory: ProcessMemory +@(thread_local) thread_memory: ThreadMemory -Path_Logs :: "../logs" -when ODIN_OS == .Windows -{ - Path_Sectr_Module :: "sectr.dll" - Path_Sectr_Live_Module :: "sectr_live.dll" - Path_Sectr_Debug_Symbols :: "sectr.pdb" - Path_Sectr_Spall_Record :: "sectr.spall" -} +//endregion STATIC MEMORY -// Only static memory host has. -host_memory: ProcessMemory +//region HOST RUNTIME -@(thread_local) -thread_memory: ThreadMemory - -load_client_api :: proc(version_id: int) -> (loaded_module: Client_API) -{ - write_time, result := file_last_write_time_by_name("sectr.dll") - if result != OS_ERROR_NONE { +load_client_api :: proc(version_id: int) -> (loaded_module: Client_API) { + using loaded_module + // Make sure we have a dll to work with + file_io_err: OS_Error; write_time, file_io_err = file_last_write_time_by_name("sectr.dll") + if file_io_err != OS_ERROR_NONE { panic_contextless( "Could not resolve the last write time for sectr") } - + //TODO(Ed): Lets try to minimize this... thread_sleep( Millisecond * 100 ) - + // Get the live dll loaded up live_file := Path_Sectr_Live_Module file_copy_sync( Path_Sectr_Module, live_file, allocator = context.temp_allocator ) - - lib, load_result := os_lib_load( live_file ) - if ! load_result { - panic( "Failed to load the sectr module." ) - } - - startup := cast( type_of( host_memory.client_api.startup)) os_lib_get_proc(lib, "startup") - tick_lane_startup := cast( type_of( host_memory.client_api.tick_lane_startup)) os_lib_get_proc(lib, "tick_lane_startup") - hot_reload := cast( type_of( host_memory.client_api.hot_reload)) os_lib_get_proc(lib, "hot_reload") - tick_lane := cast( type_of( host_memory.client_api.tick_lane)) os_lib_get_proc(lib, "tick_lane") - clean_frame := cast( type_of( host_memory.client_api.clean_frame)) os_lib_get_proc(lib, "clean_frame") + did_load: bool; lib, did_load = os_lib_load( live_file ) + if ! did_load do panic( "Failed to load the sectr module.") + startup = cast( type_of( host_memory.client_api.startup)) os_lib_get_proc(lib, "startup") + tick_lane_startup = cast( type_of( host_memory.client_api.tick_lane_startup)) os_lib_get_proc(lib, "tick_lane_startup") + hot_reload = cast( type_of( host_memory.client_api.hot_reload)) os_lib_get_proc(lib, "hot_reload") + tick_lane = cast( type_of( host_memory.client_api.tick_lane)) os_lib_get_proc(lib, "tick_lane") + clean_frame = cast( type_of( host_memory.client_api.clean_frame)) os_lib_get_proc(lib, "clean_frame") if startup == nil do panic("Failed to load sectr.startup symbol" ) if tick_lane_startup == nil do panic("Failed to load sectr.tick_lane_startup symbol" ) if hot_reload == nil do panic("Failed to load sectr.hot_reload symbol" ) if tick_lane == nil do panic("Failed to load sectr.tick_lane symbol" ) if clean_frame == nil do panic("Failed to load sectr.clean_frmae symbol" ) - - loaded_module.lib = lib - loaded_module.write_time = write_time - loaded_module.lib_version = version_id - loaded_module.startup = startup - loaded_module.tick_lane_startup = tick_lane_startup - loaded_module.hot_reload = hot_reload - loaded_module.tick_lane = tick_lane - loaded_module.clean_frame = clean_frame + lib_version = version_id return } @@ -65,8 +45,9 @@ main :: proc() arena_init(& host_memory.host_scratch, host_memory.host_scratch_buf[:]) context.allocator = arena_allocator(& host_memory.host_persist) context.temp_allocator = arena_allocator(& host_memory.host_scratch) - // Setup the profiler + when SHOULD_SETUP_PROFILERS { + // Setup profilers buffer_backing := make([]u8, SPALL_BUFFER_DEFAULT_SIZE * 4) host_memory.spall_profiler.ctx = spall_context_create(Path_Sectr_Spall_Record) host_memory.spall_profiler.buffer = spall_buffer_create(buffer_backing) @@ -120,43 +101,32 @@ main :: proc() host_memory.client_api = load_client_api( 1 ) verify( host_memory.client_api.lib_version != 0, "Failed to initially load the sectr module" ) } - // Client API Startup - host_memory.host_api.sync_client_module = sync_client_api - host_memory.host_api.launch_tick_lane_thread = launch_tick_lane_thread host_memory.client_api.startup(& host_memory, & thread_memory) - // Start the tick lanes - thread_wide_startup() -} - -thread_wide_startup :: proc() -{ - assert(thread_memory.id == .Master_Prepper) - if THREAD_TICK_LANES > 1 { - launch_tick_lane_thread(.Atomic_Accountant) - sync.barrier_init(& host_memory.client_api_sync_lock, THREAD_TICK_LANES) + /*thread_wide_startup() :: proc()*/ { + assert(thread_memory.id == .Master_Prepper) + if THREAD_TICK_LANES > 1 { + launch_tick_lane_thread(.Atomic_Accountant) + barrier_init(& host_memory.client_api_sync_lock, THREAD_TICK_LANES) + } + host_tick_lane_startup(thread_memory.system_ctx) } - host_tick_lane_startup(thread_memory.system_ctx) } - -@export launch_tick_lane_thread :: proc(id : WorkerID) { assert_contextless(thread_memory.id == .Master_Prepper) // TODO(Ed): We need to make our own version of this that doesn't allocate memory. - lane_thread := thread.create(host_tick_lane_startup, .High) + lane_thread := thread_create(host_tick_lane_startup, .High) lane_thread.user_index = int(id) - thread.start(lane_thread) + thread_start(lane_thread) } host_tick_lane_startup :: proc(lane_thread: ^SysThread) { thread_memory.system_ctx = lane_thread thread_memory.id = cast(WorkerID) lane_thread.user_index host_memory.client_api.tick_lane_startup(& thread_memory) - host_tick_lane() } - host_tick_lane :: proc() { delta_ns: Duration @@ -167,6 +137,7 @@ host_tick_lane :: proc() for ; running ; { profile("Host Tick") + leader := barrier_wait(& host_memory.client_api_sync_lock) sync_client_api() running = host_memory.client_api.tick_lane( duration_seconds(delta_ns), delta_ns ) @@ -180,41 +151,40 @@ host_tick_lane :: proc() @export sync_client_api :: proc() { - leader := sync.barrier_wait(& host_memory.client_api_sync_lock) - free_all(context.temp_allocator) profile(#procedure) - if thread_memory.id == .Master_Prepper { - write_time, result := file_last_write_time_by_name( Path_Sectr_Module ); - if result == OS_ERROR_NONE && host_memory.client_api.write_time != write_time + if thread_memory.id == .Master_Prepper { - cache_coherent_store(& host_memory.client_api_hot_reloaded, true) - - version_id := host_memory.client_api.lib_version + 1 - unload_client_api( & host_memory.client_api ) - - // Wait for pdb to unlock (linker may still be writting) - for ; file_is_locked( Path_Sectr_Debug_Symbols ) && file_is_locked( Path_Sectr_Live_Module ); {} - thread_sleep( Millisecond * 100 ) - - host_memory.client_api = load_client_api( version_id ) - verify( host_memory.client_api.lib_version != 0, "Failed to hot-reload the sectr module" ) + profile("Master_Prepper: Reloading client module") + write_time, result := file_last_write_time_by_name( Path_Sectr_Module ); + if result == OS_ERROR_NONE && host_memory.client_api.write_time != write_time + { + sync_store(& host_memory.client_api_hot_reloaded, true, .Release) + version_id := host_memory.client_api.lib_version + 1 + unload_client_api( & host_memory.client_api ) + // Wait for pdb to unlock (linker may still be writting) + for ; file_is_locked( Path_Sectr_Debug_Symbols ) && file_is_locked( Path_Sectr_Live_Module ); {} + thread_sleep( Millisecond * 100 ) + host_memory.client_api = load_client_api( version_id ) + verify( host_memory.client_api.lib_version != 0, "Failed to hot-reload the sectr module" ) + } } } - leader = sync.barrier_wait(& host_memory.client_api_sync_lock) - if cache_coherent_load(& host_memory.client_api_hot_reloaded) + leader := barrier_wait(& host_memory.client_api_sync_lock) + if sync_load(& host_memory.client_api_hot_reloaded, .Acquire) { host_memory.client_api.hot_reload(& host_memory, & thread_memory) if thread_memory.id == .Master_Prepper { - cache_coherent_store(& host_memory.client_api_hot_reloaded, false) + sync_store(& host_memory.client_api_hot_reloaded, false, .Release) } } } - unload_client_api :: proc( module : ^Client_API ) { os_lib_unload( module.lib ) file_remove( Path_Sectr_Live_Module ) module^ = {} - log_print("Unloaded sectr API") + log_print("Unloaded client API") } + +//endregion HOST RUNTIME diff --git a/code2/host/pkg_mappings.odin b/code2/host/pkg_mappings.odin index abdb903..b90ba08 100644 --- a/code2/host/pkg_mappings.odin +++ b/code2/host/pkg_mappings.odin @@ -1,15 +1,5 @@ package host -// import "base:builtin" - // Odin_OS_Type :: type_of(ODIN_OS) - -// import "base:intrinsics" - // atomic_thread_fence :: intrinsics.atomic_thread_fence - // mem_zero :: intrinsics.mem_zero - // mem_zero_volatile :: intrinsics.mem_zero_volatile - // mem_copy :: intrinsics.mem_copy_non_overlapping - // mem_copy_overlapping :: intrinsics.mem_copy - import "base:runtime" debug_trap :: runtime.debug_trap @@ -31,10 +21,11 @@ import "core:mem" arena_init :: mem.arena_init import "core:os" + OS_ERROR_NONE :: os.ERROR_NONE + OS_Error :: os.Error FileTime :: os.File_Time file_last_write_time_by_name :: os.last_write_time_by_name file_remove :: os.remove - OS_ERROR_NONE :: os.ERROR_NONE os_is_directory :: os.is_dir os_make_directory :: os.make_directory os_core_count :: os.processor_core_count @@ -51,9 +42,12 @@ import "core:strings" builder_to_str :: strings.to_string import "core:sync" + Barrier :: sync.Barrier + barrier_init :: sync.barrier_init + barrier_wait :: sync.barrier_wait thread_current_id :: sync.current_thread_id - cache_coherent_load :: sync.atomic_load - cache_coherent_store :: sync.atomic_store + sync_load :: sync.atomic_load_explicit + sync_store :: sync.atomic_store_explicit import "core:time" Millisecond :: time.Millisecond @@ -68,79 +62,74 @@ import "core:time" time_tick_lap_time :: time.tick_lap_time import "core:thread" - SysThread :: thread.Thread + SysThread :: thread.Thread + thread_create :: thread.create + thread_start :: thread.start import grime "codebase:grime" - DISABLE_PROFILING :: grime.DISABLE_PROFILING + DISABLE_GRIME_PROFILING :: grime.DISABLE_PROFILING + file_copy_sync :: grime.file_copy_sync file_is_locked :: grime.file_is_locked logger_init :: grime.logger_init to_odin_logger :: grime.to_odin_logger import "codebase:sectr" - MAX_THREADS :: sectr.MAX_THREADS - THREAD_TICK_LANES :: sectr.THREAD_TICK_LANES + DISABLE_HOST_PROFILING :: sectr.DISABLE_HOST_PROFILING + DISABLE_CLIENT_PROFILING :: sectr.DISABLE_CLIENT_PROFILING + + Path_Logs :: sectr.Path_Logs + Path_Sectr_Debug_Symbols :: sectr.Path_Debug_Symbols + Path_Sectr_Live_Module :: sectr.Path_Live_Module + Path_Sectr_Module :: sectr.Path_Module + Path_Sectr_Spall_Record :: sectr.Path_Spall_Record + MAX_THREADS :: sectr.MAX_THREADS + THREAD_TICK_LANES :: sectr.THREAD_TICK_LANES + Client_API :: sectr.ModuleAPI ProcessMemory :: sectr.ProcessMemory ThreadMemory :: sectr.ThreadMemory WorkerID :: sectr.WorkerID SpallProfiler :: sectr.SpallProfiler -ensure :: #force_inline proc( condition : b32, msg : string, location := #caller_location ) -{ - if condition { - return - } +ensure :: #force_inline proc( condition : b32, msg : string, location := #caller_location ) { + if condition do return log_print( msg, LoggerLevel.Warning, location ) debug_trap() } - // TODO(Ed) : Setup exit codes! -fatal :: #force_inline proc( msg : string, exit_code : int = -1, location := #caller_location ) -{ +fatal :: #force_inline proc( msg : string, exit_code : int = -1, location := #caller_location ) { log_print( msg, LoggerLevel.Fatal, location ) debug_trap() process_exit( exit_code ) } - // TODO(Ed) : Setup exit codes! -verify :: #force_inline proc( condition : b32, msg : string, exit_code : int = -1, location := #caller_location ) -{ - if condition { - return - } +verify :: #force_inline proc( condition : b32, msg : string, exit_code : int = -1, location := #caller_location ) { + if condition do return log_print( msg, LoggerLevel.Fatal, location ) debug_trap() process_exit( exit_code ) } - log_print :: proc( msg : string, level := LoggerLevel.Info, loc := #caller_location ) { context.allocator = arena_allocator(& host_memory.host_scratch) context.temp_allocator = arena_allocator(& host_memory.host_scratch) log.log( level, msg, location = loc ) } - log_print_fmt :: proc( fmt : string, args : ..any, level := LoggerLevel.Info, loc := #caller_location ) { context.allocator = arena_allocator(& host_memory.host_scratch) context.temp_allocator = arena_allocator(& host_memory.host_scratch) log.logf( level, fmt, ..args, location = loc ) } -@(deferred_none = profile_end, disabled = DISABLE_PROFILING) -profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { - spall._buffer_begin( & host_memory.spall_profiler.ctx, & host_memory.spall_profiler.buffer, name, "", loc ) -} +@(deferred_none = profile_end, disabled = DISABLE_HOST_PROFILING) profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { spall._buffer_begin( & host_memory.spall_profiler.ctx, & host_memory.spall_profiler.buffer, name, "", loc ) } +@( disabled = DISABLE_HOST_PROFILING) profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { spall._buffer_begin( & host_memory.spall_profiler.ctx, & host_memory.spall_profiler.buffer, name, "", loc ) } +@( disabled = DISABLE_HOST_PROFILING) profile_end :: #force_inline proc "contextless" () { spall._buffer_end ( & host_memory.spall_profiler.ctx, & host_memory.spall_profiler.buffer) } -@(disabled = DISABLE_PROFILING) -profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { - spall._buffer_begin( & host_memory.spall_profiler.ctx, & host_memory.spall_profiler.buffer, name, "", loc ) -} - -@(disabled = DISABLE_PROFILING) -profile_end :: #force_inline proc "contextless" () { - spall._buffer_end( & host_memory.spall_profiler.ctx, & host_memory.spall_profiler.buffer) -} +SHOULD_SETUP_PROFILERS :: \ + DISABLE_GRIME_PROFILING == false || + DISABLE_CLIENT_PROFILING == false || + DISABLE_HOST_PROFILING == false Kilo :: 1024 Mega :: Kilo * 1024 diff --git a/code2/sectr/config_compile_time.odin b/code2/sectr/config_compile_time.odin new file mode 100644 index 0000000..3a2ce6c --- /dev/null +++ b/code2/sectr/config_compile_time.odin @@ -0,0 +1,21 @@ +package sectr + +Path_Assets :: "../assets/" +Path_Shaders :: "../shaders/" +Path_Input_Replay :: "input.sectr_replay" + + +Path_Logs :: "../logs" +when ODIN_OS == .Windows +{ + Path_Module :: "sectr.dll" + Path_Live_Module :: "sectr_live.dll" + Path_Debug_Symbols :: "sectr.pdb" + Path_Spall_Record :: "sectr.spall" +} + +DISABLE_CLIENT_PROFILING :: false +DISABLE_HOST_PROFILING :: false + +// TODO(Ed): We can technically hot-reload this (spin up or down lanes on reloads) +THREAD_TICK_LANES :: 2 diff --git a/code2/sectr/engine/client_api.odin b/code2/sectr/engine/client_api.odin index b8b4727..af9111e 100644 --- a/code2/sectr/engine/client_api.odin +++ b/code2/sectr/engine/client_api.odin @@ -1,14 +1,18 @@ package sectr -import "core:dynlib" -import "core:sync" +// Sokol should only be used here and in the client_api_sokol_callbacks.odin -Path_Assets :: "../assets/" -Path_Shaders :: "../shaders/" -Path_Input_Replay :: "input.sectr_replay" +import sokol_app "thirdparty:sokol/app" +import sokol_gfx "thirdparty:sokol/gfx" +import sokol_glue "thirdparty:sokol/glue" +import sokol_gp "thirdparty:sokol/gp" + +/* +This definies the client interface for the host process to call into +*/ ModuleAPI :: struct { - lib: dynlib.Library, + lib: DynLibrary, write_time: FileTime, lib_version : int, @@ -19,8 +23,6 @@ ModuleAPI :: struct { clean_frame: type_of( clean_frame), } -StartupContext :: struct {} - /* Called by host.main when it completes its setup. @@ -34,44 +36,63 @@ startup :: proc(host_mem: ^ProcessMemory, thread_mem: ^ThreadMemory) } /* -Called by sync_client_api when the client module has be reloaded. +Called by host.sync_client_api when the client module has be reloaded. Threads will eventually return to their tick_lane upon completion. */ @export hot_reload :: proc(host_mem: ^ProcessMemory, thread_mem: ^ThreadMemory) { - thread_ctx = thread_mem - if thread_ctx.id == .Master_Prepper { - cache_coherent_store(& memory, host_mem, .Release) + thread = thread_mem + if thread.id == .Master_Prepper { + sync_store(& memory, host_mem, .Release) } } /* - Called by host_tick_lane_startup - Used for lane specific startup operations - - The lane tick cannot be handled it, its call must be done by the host module. - (We need threads to not be within a client callstack in the even of a hot-reload) +Called by host_tick_lane_startup +Used for lane specific startup operations + +The lane tick cannot be handled it, its call must be done by the host module. +(We need threads to not be within a client callstack in the even of a hot-reload) */ @export tick_lane_startup :: proc(thread_mem: ^ThreadMemory) { - thread_ctx = thread_mem - thread_ctx.live_lanes = THREAD_TICK_LANES + thread = thread_mem + thread.live_lanes = THREAD_TICK_LANES } +/* + +*/ @export tick_lane :: proc(host_delta_time_ms: f64, host_delta_ns: Duration) -> (should_close: b64) { - @thread_local dummy: int = 0; - dummy += 2 + profile_begin("sokol_app: pre_client_tick") + // should_close |= cast(b64) sokol_app.pre_client_frame() + profile_end() + + profile_begin("Client Tick") + profile_end() + + profile_begin("sokol_app: post_client_tick") + profile_end() + + tick_lane_frametime() return true } +tick_lane_frametime :: proc() +{ + +} + @export clean_frame :: proc() { - @thread_local dummy: int = 0; - dummy += 1 + if thread.id == .Master_Prepper + { + + } return } diff --git a/code2/sectr/engine/host_api.odin b/code2/sectr/engine/host_api.odin index 772f41d..20d4bfd 100644 --- a/code2/sectr/engine/host_api.odin +++ b/code2/sectr/engine/host_api.odin @@ -5,6 +5,12 @@ import "core:sync" /* Everything defined for the host module within the client module so that the client module has full awareness of relevant host definitions + +Client interaction with host is very minimal, +host will only provide the base runtime for client's tick lanes and job system workers. + +Host is has all statically (data/bss) defined memory for the application, it will not mess with +client_memory however. */ ProcessMemory :: struct { @@ -19,8 +25,7 @@ ProcessMemory :: struct { logger: Logger, // Profiling - spall_profiler: SpallProfiler, - // TODO(Ed): Try Superluminal! + spall_profiler: ^SpallProfiler, // Multi-threading threads: [MAX_THREADS](SysThread), @@ -34,12 +39,8 @@ ProcessMemory :: struct { } Host_API :: struct { - launch_tick_lane_thread: #type proc(WorkerID), - request_virtual_memory: #type proc(), request_virtual_mapped_io: #type proc(), - - sync_client_module : #type proc(), } ThreadMemory :: struct { diff --git a/code2/sectr/pkg_mappings.odin b/code2/sectr/pkg_mappings.odin index 944d9ce..8561582 100644 --- a/code2/sectr/pkg_mappings.odin +++ b/code2/sectr/pkg_mappings.odin @@ -1,14 +1,36 @@ package sectr +/* +All direct non-codebase package symbols should do zero allocations. +Any symbol that does must be mapped from the Grime package to properly tirage its allocator to odin's ideomatic interface. + + +*/ + +import "base:intrinsics" + debug_trap :: intrinsics.debug_trap + +import "core:dynlib" + // Only referenced in ModuleAPI + DynLibrary :: dynlib.Library + +import "core:log" + LoggerLevel :: log.Level + import "core:mem" - Odin_Arena :: mem.Arena + // Used strickly for the logger + Odin_Arena :: mem.Arena + odin_arena_allocator :: mem.arena_allocator import "core:os" - FileTime :: os.File_Time + FileTime :: os.File_Time + process_exit :: os.exit + +import "core:prof/spall" import "core:sync" AtomicMutex :: sync.Atomic_Mutex - cache_coherent_store :: sync.atomic_store_explicit + sync_store :: sync.atomic_store_explicit import "core:thread" SysThread :: thread.Thread @@ -17,10 +39,44 @@ import "core:time" Duration :: time.Duration import "codebase:grime" - Logger :: grime.Logger - SpallProfiler :: grime.SpallProfiler + Logger :: grime.Logger + SpallProfiler :: grime.SpallProfiler Kilo :: 1024 Mega :: Kilo * 1024 Giga :: Mega * 1024 Tera :: Giga * 1024 + +ensure :: #force_inline proc( condition : b32, msg : string, location := #caller_location ) { + if condition do return + log_print( msg, LoggerLevel.Warning, location ) + debug_trap() +} +// TODO(Ed) : Setup exit codes! +fatal :: #force_inline proc( msg : string, exit_code : int = -1, location := #caller_location ) { + log_print( msg, LoggerLevel.Fatal, location ) + debug_trap() + process_exit( exit_code ) +} +// TODO(Ed) : Setup exit codes! +verify :: #force_inline proc( condition : b32, msg : string, exit_code : int = -1, location := #caller_location ) { + if condition do return + log_print( msg, LoggerLevel.Fatal, location ) + debug_trap() + process_exit( exit_code ) +} + +log_print :: proc( msg : string, level := LoggerLevel.Info, loc := #caller_location ) { + context.allocator = odin_arena_allocator(& memory.host_scratch) + context.temp_allocator = odin_arena_allocator(& memory.host_scratch) + log.log( level, msg, location = loc ) +} +log_print_fmt :: proc( fmt : string, args : ..any, level := LoggerLevel.Info, loc := #caller_location ) { + context.allocator = odin_arena_allocator(& memory.host_scratch) + context.temp_allocator = odin_arena_allocator(& memory.host_scratch) + log.logf( level, fmt, ..args, location = loc ) +} + +@(deferred_none = profile_end, disabled = DISABLE_CLIENT_PROFILING) profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { spall._buffer_begin( & memory.spall_profiler.ctx, & memory.spall_profiler.buffer, name, "", loc ) } +@( disabled = DISABLE_CLIENT_PROFILING) profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { spall._buffer_begin( & memory.spall_profiler.ctx, & memory.spall_profiler.buffer, name, "", loc ) } +@( disabled = DISABLE_CLIENT_PROFILING) profile_end :: #force_inline proc "contextless" () { spall._buffer_end ( & memory.spall_profiler.ctx, & memory.spall_profiler.buffer) } diff --git a/code2/sectr/state.odin b/code2/sectr/state.odin index fa19a62..3ff528c 100644 --- a/code2/sectr/state.odin +++ b/code2/sectr/state.odin @@ -1,12 +1,10 @@ package sectr +//region STATIC MEMORY // This should be the only global on client module side. -memory: ^ProcessMemory - -@(thread_local) -thread_ctx: ^ThreadMemory - -THREAD_TICK_LANES :: 2 + memory: ^ProcessMemory +@(thread_local) thread: ^ThreadMemory +//endregion STATIC MEMORy State :: struct { job_system: JobSystemContext,