SectrPrototype/code/host/host.odin

215 lines
6.7 KiB
Odin
Raw Normal View History

2024-01-21 20:38:02 -08:00
package host
import "core:dynlib"
import "core:io"
import "core:fmt"
import "core:log"
import "core:mem"
import "core:mem/virtual"
Byte :: 1
Kilobyte :: 1024 * Byte
Megabyte :: 1024 * Kilobyte
Gigabyte :: 1024 * Megabyte
Terabyte :: 1024 * Gigabyte
Petabyte :: 1024 * Terabyte
Exabyte :: 1024 * Petabyte
import "core:os"
import "core:runtime"
import "core:strings"
2024-01-22 00:47:53 -08:00
import "core:time"
2024-01-21 20:38:02 -08:00
import rl "vendor:raylib"
import sectr "../."
RuntimeState :: struct {
2024-01-22 00:47:53 -08:00
running : b32,
memory : VMemChunk,
2024-01-21 20:38:02 -08:00
sectr_api : sectr.ModuleAPI,
}
VMemChunk :: struct {
sarena : virtual.Arena,
eng_persistent : mem.Arena,
eng_transient : mem.Arena,
env_persistent : mem.Arena,
env_transient : mem.Arena,
env_temp : mem.Arena
2024-01-21 20:38:02 -08:00
}
setup_engine_memory :: proc () -> VMemChunk
{
2024-01-22 00:47:53 -08:00
memory : VMemChunk; using memory
2024-01-21 20:38:02 -08:00
arena_init :: mem.arena_init
ptr_offset :: mem.ptr_offset
slice_ptr :: mem.slice_ptr
// Setup the static arena for the entire application
if result := virtual.arena_init_static( & sarena, Gigabyte * 2, Gigabyte * 2 );
result != runtime.Allocator_Error.None
{
// TODO(Ed) : Setup a proper logging interface
fmt. printf( "Failed to allocate memory for the engine" )
runtime.debug_trap()
os. exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
// For now I'm making persistent sections each 128 meg and transient sections w/e is left over / 2 (one for engine the other for the env)
2024-01-22 00:47:53 -08:00
persistent_size :: Megabyte * 128 * 2
transient_size :: (Gigabyte * 2 - persistent_size * 2) / 2
eng_persistent_size :: persistent_size / 4
eng_transient_size :: transient_size / 4
env_persistent_size :: persistent_size - eng_persistent_size
env_trans_temp_size :: (transient_size - eng_transient_size) / 2
2024-01-21 20:38:02 -08:00
block := memory.sarena.curr_block
// Try to get a slice for each segment
2024-01-22 00:47:53 -08:00
eng_persistent_slice := slice_ptr( block.base, eng_persistent_size)
eng_transient_slice := slice_ptr( & eng_persistent_slice[ eng_persistent_size - 1], eng_transient_size)
env_persistent_slice := slice_ptr( & eng_transient_slice [ eng_transient_size - 1], env_persistent_size)
env_transient_slice := slice_ptr( & env_persistent_slice[ env_persistent_size - 1], env_trans_temp_size)
env_temp_slice := slice_ptr( & env_transient_slice [ env_trans_temp_size - 1], env_trans_temp_size)
2024-01-21 20:38:02 -08:00
arena_init( & eng_persistent, eng_persistent_slice )
arena_init( & eng_transient, eng_transient_slice )
arena_init( & env_persistent, env_persistent_slice )
arena_init( & env_transient, env_transient_slice )
arena_init( & env_temp, env_temp_slice )
2024-01-21 20:38:02 -08:00
return memory;
}
load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI
{
loaded_module : sectr.ModuleAPI
2024-01-22 00:47:53 -08:00
write_time,
2024-01-21 20:38:02 -08:00
result := os.last_write_time_by_name("sectr.dll")
if result != os.ERROR_NONE {
fmt. println("Could not resolve the last write time for sectr.dll")
runtime.debug_trap()
return {}
}
lock_file := fmt.tprintf( "sectr_{0}_locked.dll", version_id )
sectr.copy_file_sync( "sectr.dll", lock_file )
lib, load_result := dynlib.load_library( lock_file )
if ! load_result {
fmt. println( "Failed to load the sectr module." )
runtime.debug_trap()
return {}
}
2024-01-22 00:47:53 -08:00
startup := cast( type_of( sectr.startup )) dynlib.symbol_address( lib, "startup" )
shutdown := cast( type_of( sectr.sectr_shutdown )) dynlib.symbol_address( lib, "sectr_shutdown" )
reload := cast( type_of( sectr.reload )) dynlib.symbol_address( lib, "reload" )
update := cast( type_of( sectr.update )) dynlib.symbol_address( lib, "update" )
render := cast( type_of( sectr.render )) dynlib.symbol_address( lib, "render" )
missing_symbol : b32 = false
if startup == nil do fmt.println("Failed to load sectr.startup symbol")
if shutdown == nil do fmt.println("Failed to load sectr.shutdown symbol")
if reload == nil do fmt.println("Failed to load sectr.reload symbol")
if update == nil do fmt.println("Failed to load sectr.update symbol")
if render == nil do fmt.println("Failed to load sectr.render symbol")
if missing_symbol {
runtime.debug_trap()
return {}
}
2024-01-21 20:38:02 -08:00
loaded_module = {
lib = lib,
2024-01-22 00:47:53 -08:00
write_time = write_time,
2024-01-21 20:38:02 -08:00
lib_version = version_id,
2024-01-22 00:47:53 -08:00
startup = startup,
shutdown = shutdown,
reload = reload,
update = update,
render = render
2024-01-21 20:38:02 -08:00
}
return loaded_module
}
2024-01-22 00:47:53 -08:00
unload_sectr_api :: proc ( module : ^ sectr.ModuleAPI )
{
lock_file := fmt.tprintf( "sectr_{0}_locked.dll", module.lib_version )
dynlib.unload_library( module.lib )
// os.remove( lock_file )
module^ = {}
}
2024-01-21 20:38:02 -08:00
main :: proc()
{
fmt.println("Hellope!")
2024-01-22 00:47:53 -08:00
state : RuntimeState
using state
2024-01-21 20:38:02 -08:00
// Basic Giant VMem Block
{
// By default odin uses a growing arena for the runtime context
// We're going to make it static for the prototype and separate it from the 'project' memory.
// Then shove the context allocator for the engine to it.
// The project's context will use its own subsection arena allocator.
memory = setup_engine_memory()
context.allocator = mem.arena_allocator( & memory.eng_persistent )
context.temp_allocator = mem.arena_allocator( & memory.eng_transient )
}
// Load the Enviornment API for the first-time
{
sectr_api = load_sectr_api( 1 )
if sectr_api.lib_version == 0 {
fmt. println( "Failed to initially load the sectr module" )
runtime.debug_trap()
os. exit( -1 )
}
}
2024-01-22 00:47:53 -08:00
running = true;
memory = memory
sectr_api = sectr_api
sectr_api.startup( & memory.env_persistent, & memory.env_transient, & memory.env_temp )
2024-01-21 20:38:02 -08:00
2024-01-22 00:47:53 -08:00
// TODO(Ed) : This should have an end status so that we know the reason the engine stopped.
for ; running ;
2024-01-21 20:38:02 -08:00
{
// Hot-Reload
2024-01-22 00:47:53 -08:00
if write_time, result := os.last_write_time_by_name("sectr.dll");
result == os.ERROR_NONE && sectr_api.write_time != write_time
2024-01-21 20:38:02 -08:00
{
2024-01-22 00:47:53 -08:00
version_id := sectr_api.lib_version + 1
unload_sectr_api( & sectr_api )
// Wait for pdb to unlock (linker may still be writting)
for ; sectr.is_file_locked( "sectr.pdb" ); {
}
time.sleep( time.Millisecond )
sectr_api = load_sectr_api( version_id )
if sectr_api.lib_version == 0 {
fmt.println("Failed to hot-reload the sectr module")
runtime.debug_trap()
os.exit(-1)
}
sectr_api.reload( & memory.env_persistent, & memory.env_transient, & memory.env_temp )
2024-01-21 20:38:02 -08:00
}
2024-01-22 00:47:53 -08:00
running = sectr_api.update()
sectr_api.render()
2024-01-21 20:38:02 -08:00
2024-01-22 00:47:53 -08:00
free_all( mem.arena_allocator( & memory.env_temp ) )
// free_all( mem.arena_allocator( & memory.env_transient ) )
2024-01-21 20:38:02 -08:00
}
// Determine how the run_cyle completed, if it failed due to an error,
// fallback the env to a failsafe state and reload the run_cycle.
{
// TODO(Ed): Implement this.
}
2024-01-22 00:47:53 -08:00
sectr_api.shutdown()
unload_sectr_api( & sectr_api )
2024-01-21 20:38:02 -08:00
}