Ed_
84d9675a27
Doesn't work across app runs, unlike handmade the crux here is raylib. Even if we did raylibs features ourselves there would still be an issue of restoring the gpu memory state. So in order to have replays work across app runs is proper state serialization. I'll leave that for later and focus on the next core features.
260 lines
7.8 KiB
Odin
260 lines
7.8 KiB
Odin
package host
|
|
|
|
import "base:runtime"
|
|
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:strings"
|
|
import "core:time"
|
|
import rl "vendor:raylib"
|
|
import sectr "../."
|
|
|
|
TrackedAllocator :: sectr.TrackedAllocator
|
|
tracked_allocator :: sectr.tracked_allocator
|
|
tracked_allocator_init :: sectr.tracked_allocator_init
|
|
|
|
path_snapshot :: "VMemChunk_1.snapshot"
|
|
when ODIN_OS == runtime.Odin_OS_Type.Windows
|
|
{
|
|
path_sectr_module :: "sectr.dll"
|
|
path_sectr_live_module :: "sectr_live.dll"
|
|
path_sectr_debug_symbols :: "sectr.pdb"
|
|
}
|
|
|
|
RuntimeState :: struct {
|
|
running : b32,
|
|
memory : VMemChunk,
|
|
sectr_api : sectr.ModuleAPI,
|
|
}
|
|
|
|
VMemChunk :: struct {
|
|
og_allocator : mem.Allocator,
|
|
og_temp_allocator : mem.Allocator,
|
|
host_persistent : TrackedAllocator,
|
|
host_transient : TrackedAllocator,
|
|
sectr_live : virtual.Arena,
|
|
sectr_snapshot : []u8
|
|
}
|
|
|
|
setup_memory :: proc () -> VMemChunk
|
|
{
|
|
Arena :: mem.Arena
|
|
Tracking_Allocator :: mem.Tracking_Allocator
|
|
memory : VMemChunk; using memory
|
|
|
|
host_persistent_size :: 32 * Megabyte
|
|
host_transient_size :: 96 * Megabyte
|
|
internals_size :: 4 * Megabyte
|
|
|
|
host_persistent = tracked_allocator_init( host_persistent_size, internals_size )
|
|
host_transient = tracked_allocator_init( host_transient_size, internals_size )
|
|
|
|
// Setup the static arena for the entire application
|
|
{
|
|
base_address : rawptr = transmute( rawptr) u64(Terabyte * 2)
|
|
|
|
result := arena_init_static( & sectr_live, base_address, sectr.memory_chunk_size, sectr.memory_chunk_size )
|
|
if result != runtime.Allocator_Error.None
|
|
{
|
|
// TODO(Ed) : Setup a proper logging interface
|
|
fmt. printf( "Failed to allocate live memory for the sectr module" )
|
|
runtime.debug_trap()
|
|
os. exit( -1 )
|
|
// TODO(Ed) : Figure out the error code enums..
|
|
}
|
|
}
|
|
|
|
// Setup memory mapped io for snapshots
|
|
{
|
|
file_resize :: os.ftruncate
|
|
|
|
snapshot_file, open_error := os.open( path_snapshot, os.O_RDWR | os.O_CREATE )
|
|
if ( open_error != os.ERROR_NONE )
|
|
{
|
|
// TODO(Ed) : Setup a proper logging interface
|
|
fmt. printf( "Failed to open snapshot file for the sectr module" )
|
|
runtime.debug_trap()
|
|
os. exit( -1 )
|
|
// TODO(Ed) : Figure out the error code enums..
|
|
}
|
|
file_info, stat_code := os.stat( path_snapshot )
|
|
{
|
|
if file_info.size != sectr.memory_chunk_size {
|
|
file_resize( snapshot_file, sectr.memory_chunk_size )
|
|
}
|
|
}
|
|
|
|
map_error : virtual.Map_File_Error
|
|
sectr_snapshot, map_error = virtual.map_file_from_file_descriptor( uintptr(snapshot_file), { virtual.Map_File_Flag.Read, virtual.Map_File_Flag.Write } )
|
|
if map_error != virtual.Map_File_Error.None
|
|
{
|
|
// TODO(Ed) : Setup a proper logging interface
|
|
fmt. printf( "Failed to allocate snapshot memory for the sectr module" )
|
|
runtime.debug_trap()
|
|
os. exit( -1 )
|
|
// TODO(Ed) : Figure out the error code enums..
|
|
}
|
|
|
|
os.close(snapshot_file)
|
|
}
|
|
|
|
// Reassign default allocators for host
|
|
memory.og_allocator = context.allocator
|
|
memory.og_temp_allocator = context.temp_allocator
|
|
context.allocator = tracked_allocator( & memory.host_persistent )
|
|
context.temp_allocator = tracked_allocator( & memory.host_transient )
|
|
return memory;
|
|
}
|
|
|
|
load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI
|
|
{
|
|
loaded_module : sectr.ModuleAPI
|
|
|
|
write_time,
|
|
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 {}
|
|
}
|
|
|
|
live_file := path_sectr_live_module
|
|
sectr.copy_file_sync( path_sectr_module, live_file )
|
|
|
|
lib, load_result := dynlib.load_library( live_file )
|
|
if ! load_result {
|
|
// TODO(Ed) : Setup a proper logging interface
|
|
fmt. println( "Failed to load the sectr module." )
|
|
runtime.debug_trap()
|
|
return {}
|
|
}
|
|
|
|
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" )
|
|
clean_temp := cast( type_of( sectr.clean_temp )) dynlib.symbol_address( lib, "clean_temp" )
|
|
|
|
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 clean_temp == nil do fmt.println("Failed to load sector.clean_temp symbol")
|
|
if missing_symbol {
|
|
runtime.debug_trap()
|
|
return {}
|
|
}
|
|
|
|
loaded_module = {
|
|
lib = lib,
|
|
write_time = write_time,
|
|
lib_version = version_id,
|
|
|
|
startup = startup,
|
|
shutdown = shutdown,
|
|
reload = reload,
|
|
update = update,
|
|
render = render,
|
|
clean_temp = clean_temp,
|
|
}
|
|
return loaded_module
|
|
}
|
|
|
|
unload_sectr_api :: proc ( module : ^ sectr.ModuleAPI )
|
|
{
|
|
dynlib.unload_library( module.lib )
|
|
os.remove( path_sectr_live_module )
|
|
module^ = {}
|
|
}
|
|
|
|
sync_sectr_api :: proc ( sectr_api : ^ sectr.ModuleAPI, memory : ^ VMemChunk )
|
|
{
|
|
if write_time, result := os.last_write_time_by_name( path_sectr_module );
|
|
result == os.ERROR_NONE && sectr_api.write_time != write_time
|
|
{
|
|
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( path_sectr_debug_symbols ) && sectr.is_file_locked( path_sectr_live_module ); {}
|
|
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)
|
|
// TODO(Ed) : Figure out the error code enums..
|
|
}
|
|
sectr_api.reload( memory.sectr_live, memory.sectr_snapshot )
|
|
}
|
|
}
|
|
|
|
main :: proc()
|
|
{
|
|
fmt.println("Hellope!")
|
|
|
|
state : RuntimeState
|
|
using state
|
|
|
|
// 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_memory()
|
|
}
|
|
|
|
// Load the Enviornment API for the first-time
|
|
{
|
|
sectr_api = load_sectr_api( 1 )
|
|
if sectr_api.lib_version == 0 {
|
|
// TODO(Ed) : Setup a proper logging interface
|
|
fmt. println( "Failed to initially load the sectr module" )
|
|
runtime.debug_trap()
|
|
os. exit( -1 )
|
|
// TODO(Ed) : Figure out the error code enums..
|
|
}
|
|
}
|
|
|
|
running = true;
|
|
memory = memory
|
|
sectr_api = sectr_api
|
|
sectr_api.startup( memory.sectr_live, memory.sectr_snapshot )
|
|
|
|
// TODO(Ed) : This should have an end status so that we know the reason the engine stopped.
|
|
for ; running ;
|
|
{
|
|
// Hot-Reload
|
|
sync_sectr_api( & sectr_api, & memory )
|
|
|
|
running = sectr_api.update()
|
|
sectr_api.render()
|
|
sectr_api.clean_temp()
|
|
}
|
|
|
|
// 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.
|
|
}
|
|
|
|
sectr_api.shutdown()
|
|
unload_sectr_api( & sectr_api )
|
|
}
|