diff --git a/code/api.odin b/code/api.odin index 136a00e..b1e5b4c 100644 --- a/code/api.odin +++ b/code/api.odin @@ -37,6 +37,9 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 ) using memory; block := live_mem.curr_block + live = live_mem + snapshot = snapshot_mem + persistent_slice := slice_ptr( block.base, memory_persistent_size ) transient_slice := slice_ptr( memory_after( persistent_slice), memory_trans_temp_size ) temp_slice := slice_ptr( memory_after( transient_slice), memory_trans_temp_size ) @@ -91,7 +94,7 @@ sectr_shutdown :: proc() // Replay { - os.close( state.replay.active_file ) + os.close( memory.replay.active_file ) } // Raylib @@ -107,6 +110,9 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 ) using memory; block := live_mem.curr_block + live = live_mem + snapshot = snapshot_mem + persistent_slice := slice_ptr( block.base, memory_persistent_size ) transient_slice := slice_ptr( memory_after( persistent_slice), memory_trans_temp_size ) temp_slice := slice_ptr( memory_after( transient_slice), memory_trans_temp_size ) @@ -114,8 +120,6 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8 ) persistent = cast( ^TrackedAllocator ) & persistent_slice[0] transient = cast( ^TrackedAllocator ) & transient_slice[0] temp = cast( ^TrackedAllocator ) & temp_slice[0] - - snapshot = snapshot_mem } // TODO(Ed) : This lang really not have a fucking swap? @@ -126,12 +130,13 @@ swap :: proc( a, b : ^ $Type ) -> ( ^ Type, ^ Type ) { @export update :: proc() -> b32 { - state := get_state(); using state + state := get_state(); using state + replay := & memory.replay state.input, state.input_prev = swap( state.input, state.input_prev ) poll_input( state.input_prev, state.input ) - debug_actions : DebugActions + debug_actions : DebugActions = {} poll_debug_actions( & debug_actions, state.input ) // Input Replay @@ -147,30 +152,40 @@ update :: proc() -> b32 } }} + DO_NOT_CONTINUE : b32 = false + if debug_actions.play_replay { switch replay.mode { case ReplayMode.Off : { - replay_playback_begin( Path_Input_Replay ) + if ! file_exists( Path_Input_Replay ) { + save_snapshot( & memory.snapshot[0] ) + replay_recording_begin( Path_Input_Replay ) + break + } + else { + load_snapshot( & memory.snapshot[0] ) + replay_playback_begin( Path_Input_Replay ) + break + } } case ReplayMode.Playback : { replay_playback_end() load_snapshot( & memory.snapshot[0] ) + break } case ReplayMode.Record : { - replay_recording_end( ) + replay_recording_end() load_snapshot( & memory.snapshot[0] ) replay_playback_begin( Path_Input_Replay ) + break } }} - if replay.loop_active - { - if replay.mode == ReplayMode.Record { - record_input( replay.active_file, input ) - } - else if replay.mode == ReplayMode.Playback { - play_input( replay.active_file, input ) - } + if replay.mode == ReplayMode.Record { + record_input( replay.active_file, input ) + } + else if replay.mode == ReplayMode.Playback { + play_input( replay.active_file, input ) } } @@ -178,6 +193,8 @@ update :: proc() -> b32 debug.mouse_vis = !debug.mouse_vis } + debug.mouse_pos.basis = { input.mouse.X, input.mouse.Y } + should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() return should_shutdown } @@ -185,7 +202,8 @@ update :: proc() -> b32 @export render :: proc() { - state := get_state(); using state + state := get_state(); using state + replay := & memory.replay rl.BeginDrawing() rl.ClearBackground( Color_BG ) @@ -213,17 +231,19 @@ render :: proc() draw_text( "Screen Width : %v", rl.GetScreenWidth () ) draw_text( "Screen Height: %v", rl.GetScreenHeight() ) - if pressed( input.keyboard.M ) { - draw_text( "M Prssed" ) + if replay.mode == ReplayMode.Record { + draw_text( "Recording Input") } - if pressed( input.keyboard.right_alt ) { - draw_text( "Alt Pressed") + if replay.mode == ReplayMode.Playback { + draw_text( "Replaying Input") } if debug.mouse_vis { width : f32 = 32 pos := debug.mouse_pos + draw_text( "Position: %v", rl.GetMousePosition() ) + mouse_rect : rl.Rectangle mouse_rect.x = pos.x - width/2 mouse_rect.y = pos.y - width/2 diff --git a/code/env.odin b/code/env.odin index 9c5a208..d974de8 100644 --- a/code/env.odin +++ b/code/env.odin @@ -19,7 +19,9 @@ Memory :: struct { snapshot : []u8, persistent : ^ TrackedAllocator, transient : ^ TrackedAllocator, - temp : ^ TrackedAllocator + temp : ^ TrackedAllocator, + + replay : ReplayState } State :: struct { @@ -28,7 +30,6 @@ State :: struct { input_prev : ^ InputState, input : ^ InputState, - replay : ReplayState, debug : DebugData, project : Project, @@ -66,7 +67,7 @@ DebugData :: struct { draw_debug_text_y : f32, mouse_vis : b32, - mouse_pos : vec3, + mouse_pos : vec2, } DebugActions :: struct { @@ -84,11 +85,11 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) using actions using input - base_replay_bind := pressed(keyboard.right_alt) && pressed( keyboard.L) - record_replay = base_replay_bind && pressed(keyboard.right_shift) - play_replay = base_replay_bind + base_replay_bind := keyboard.right_alt.ended_down && pressed( keyboard.L) + record_replay = base_replay_bind && keyboard.right_shift.ended_down + play_replay = base_replay_bind && ! keyboard.right_shift.ended_down - show_mouse_pos = pressed(keyboard.right_alt) && pressed(keyboard.M) + show_mouse_pos = keyboard.right_alt.ended_down && pressed(keyboard.M) } save_snapshot :: proc( snapshot : [^]u8 ) { @@ -115,14 +116,16 @@ ReplayState :: struct { replay_recording_begin :: proc( path : string ) { - result := os.remove( path ) - if ( result != os.ERROR_NONE ) - { - // TODO(Ed) : Setup a proper logging interface - fmt. printf( "Failed to delete replay file before beginning a new one" ) - runtime.debug_trap() - os. exit( -1 ) - // TODO(Ed) : Figure out the error code enums.. + if file_exists( path ) { + result := os.remove( path ) + if ( result != os.ERROR_NONE ) + { + // TODO(Ed) : Setup a proper logging interface + fmt. printf( "Failed to delete replay file before beginning a new one" ) + runtime.debug_trap() + os. exit( -1 ) + // TODO(Ed) : Figure out the error code enums.. + } } replay_file, open_error := os.open( path, os.O_RDWR | os.O_CREATE ) @@ -134,15 +137,18 @@ replay_recording_begin :: proc( path : string ) os. exit( -1 ) // TODO(Ed) : Figure out the error code enums.. } + os.seek( replay_file, 0, 0 ) - state := get_state(); using state + replay := & memory.replay replay.active_file = replay_file replay.mode = ReplayMode.Record } replay_recording_end :: proc() { - state := get_state(); using state + replay := & memory.replay replay.mode = ReplayMode.Off + + os.seek( replay.active_file, 0, 0 ) os.close( replay.active_file ) } @@ -167,14 +173,17 @@ replay_playback_begin :: proc( path : string ) // TODO(Ed) : Figure out the error code enums.. } // TODO(Ed): WE need to wrap any actions that can throw a fatal like this. Files need a grime wrap. + os.seek( replay_file, 0, 0 ) - state := get_state(); using state + replay := & memory.replay replay.active_file = replay_file replay.mode = ReplayMode.Playback } replay_playback_end :: proc() { - state := get_state(); using state + input := get_state().input + replay := & memory.replay replay.mode = ReplayMode.Off + os.seek( replay.active_file, 0, 0 ) os.close( replay.active_file ) } diff --git a/code/filesystem.odin b/code/filesystem.odin index d618c39..2517622 100644 --- a/code/filesystem.odin +++ b/code/filesystem.odin @@ -52,3 +52,14 @@ is_file_locked :: proc( file_path : string ) -> b32 { os.close(handle) return false } + +rewind :: proc ( file : os.Handle ) { + os.seek( file, 0, 0 ) +} + +read_looped :: proc ( file : os.Handle, data : []byte ) { + total_read, result_code := os.read( file, data ) + if result_code == os.ERROR_HANDLE_EOF { + rewind( file ) + } +} diff --git a/code/host/host.odin b/code/host/host.odin index 70370b8..fbf6f9f 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -62,7 +62,9 @@ setup_memory :: proc () -> VMemChunk // Setup the static arena for the entire application { - result := virtual.arena_init_static( & sectr_live, sectr.memory_chunk_size, sectr.memory_chunk_size ) + 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 @@ -86,7 +88,12 @@ setup_memory :: proc () -> VMemChunk os. exit( -1 ) // TODO(Ed) : Figure out the error code enums.. } - file_resize( snapshot_file, sectr.memory_chunk_size ) + 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 } ) diff --git a/code/host/memory_windows.odin b/code/host/memory_windows.odin new file mode 100644 index 0000000..30ef446 --- /dev/null +++ b/code/host/memory_windows.odin @@ -0,0 +1,126 @@ +// TODO(Ed): Move this to the grime module when its made +// This was made becaause odin didn't expose the base_address param that virtual alloc allows. +package host + +import "core:mem" +import "core:mem/virtual" + +import win32 "core:sys/windows" + +@(private="file") +virtual_Platform_Memory_Block :: struct { + block: virtual.Memory_Block, + committed: uint, + reserved: uint, +} + +@(private="file", require_results) +align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint { + result := size + align-1 + return result - result%align +} + +@(private="file") +win32_reserve :: proc "contextless" (base_address : rawptr, size: uint) -> (data: []byte, err: virtual.Allocator_Error) { + result := win32.VirtualAlloc(base_address, size, win32.MEM_RESERVE, win32.PAGE_READWRITE) + if result == nil { + err = .Out_Of_Memory + return + } + data = ([^]byte)(result)[:size] + return +} + +@(private="file") +platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint, base_address : rawptr) -> + (block: ^virtual_Platform_Memory_Block, err: virtual.Allocator_Error) +{ + to_commit, to_reserve := to_commit, to_reserve + to_reserve = max(to_commit, to_reserve) + + total_to_reserved := max(to_reserve, size_of( virtual_Platform_Memory_Block)) + to_commit = clamp(to_commit, size_of( virtual_Platform_Memory_Block), total_to_reserved) + + data := win32_reserve(base_address, total_to_reserved) or_return + virtual.commit(raw_data(data), to_commit) + + block = (^virtual_Platform_Memory_Block)(raw_data(data)) + block.committed = to_commit + block.reserved = to_reserve + return +} + +@(private="file") +platform_memory_commit :: proc "contextless" (block: ^virtual_Platform_Memory_Block, to_commit: uint) -> (err: virtual.Allocator_Error) { + if to_commit < block.committed { + return nil + } + if to_commit > block.reserved { + return .Out_Of_Memory + } + + virtual.commit(block, to_commit) or_return + block.committed = to_commit + return nil +} + +@(private="file", require_results) +memory_block_alloc :: proc(committed, reserved: uint, base_address : rawptr, + alignment : uint = 0, + flags : virtual.Memory_Block_Flags = {} +) -> (block: ^virtual.Memory_Block, err: virtual.Allocator_Error) +{ + page_size := virtual.DEFAULT_PAGE_SIZE + assert(mem.is_power_of_two(uintptr(page_size))) + + committed := committed + reserved := reserved + + committed = align_formula(committed, page_size) + reserved = align_formula(reserved, page_size) + committed = clamp(committed, 0, reserved) + + total_size := uint(reserved + max(alignment, size_of( virtual_Platform_Memory_Block))) + base_offset := uintptr(max(alignment, size_of( virtual_Platform_Memory_Block))) + protect_offset := uintptr(0) + + do_protection := false + if .Overflow_Protection in flags { // overflow protection + rounded_size := reserved + total_size = uint(rounded_size + 2*page_size) + base_offset = uintptr(page_size + rounded_size - uint(reserved)) + protect_offset = uintptr(page_size + rounded_size) + do_protection = true + } + + pmblock := platform_memory_alloc(0, total_size, base_address) or_return + + pmblock.block.base = ([^]byte)(pmblock)[base_offset:] + platform_memory_commit(pmblock, uint(base_offset) + committed) or_return + + // Should be zeroed + assert(pmblock.block.used == 0) + assert(pmblock.block.prev == nil) + if do_protection { + virtual.protect(([^]byte)(pmblock)[protect_offset:], page_size, virtual.Protect_No_Access) + } + + pmblock.block.committed = committed + pmblock.block.reserved = reserved + + return &pmblock.block, nil +} + +// This is the same as odin's virtual library, except I use my own allocation implementation to set the address space base. +@(require_results) +arena_init_static :: proc(arena: ^virtual.Arena, base_address : rawptr, + reserved : uint = virtual.DEFAULT_ARENA_STATIC_RESERVE_SIZE, + commit_size : uint = virtual.DEFAULT_ARENA_STATIC_COMMIT_SIZE +) -> (err: virtual.Allocator_Error) +{ + arena.kind = .Static + arena.curr_block = memory_block_alloc(commit_size, reserved, base_address, {}) or_return + arena.total_used = 0 + arena.total_reserved = arena.curr_block.reserved + return +} diff --git a/code/input.odin b/code/input.odin index e3e748a..6230177 100644 --- a/code/input.odin +++ b/code/input.odin @@ -299,7 +299,8 @@ poll_input :: proc( old, new : ^ InputState ) { check_range :: proc( old, new : ^ InputState, start, end : i32 ) { - for id := start; id < end; id += 1 { + for id := start; id < end; id += 1 + { // TODO(Ed) : LOOK OVER THIS... entry_old := & old.keyboard.keys[id - 1] entry_new := & new.keyboard.keys[id - 1] @@ -329,7 +330,8 @@ poll_input :: proc( old, new : ^ InputState ) // Mouse { // Process Buttons - for id : i32 = 0; id < i32(MouseBtn.count); id += 1 { + for id : i32 = 0; id < i32(MouseBtn.count); id += 1 + { old_btn := & old.mouse.btns[id] new_btn := & new.mouse.btns[id] @@ -353,7 +355,11 @@ record_input :: proc( replay_file : os.Handle, input : ^ InputState ) { play_input :: proc( replay_file : os.Handle, input : ^ InputState ) { raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) ) - os.read( replay_file, raw_data ) + total_read, result_code := os.read( replay_file, raw_data ) + if result_code == os.ERROR_HANDLE_EOF { + rewind( replay_file ) + load_snapshot( & memory.snapshot[0] ) + } } to_raylib_key :: proc ( key : i32 ) -> rl.KeyboardKey { diff --git a/code/math.odin b/code/math.odin index 6c6f79f..98e82dd 100644 --- a/code/math.odin +++ b/code/math.odin @@ -2,6 +2,14 @@ package sectr // TODO(Ed) : Evaluate if this is needed +vec2 :: vec2_f32 +vec2_f32 :: struct #raw_union { + basis : [2] f32, + using components : struct { + x, y : f32 + } +} + vec3 :: vec3_f32 vec3_f32 :: struct #raw_union { basis : [3] f32,