General codebase refactor & cleanup

Renamed HashTable to HMapZPL, with procs having the zpl_ namespace prefix.
(I want to eventually get away from using it)

Started to use the grime pattern for library aliasing better.
This commit is contained in:
Edward R. Gonzalez 2024-02-27 07:50:57 -05:00
parent c9dc5fe54a
commit 4deee942a8
28 changed files with 752 additions and 613 deletions

View File

@ -4,5 +4,6 @@
"thirdparty/**" : false,
"**/thirdparty" : false,
},
"godot_tools.scene_file_config": "c:\\projects\\SectrPrototype\\code"
"godot_tools.scene_file_config": "c:\\projects\\SectrPrototype\\code",
"autoHide.autoHidePanel": false
}

View File

@ -17,7 +17,7 @@ UI_State :: struct {
// TODO(ED) : Put this in its own struct?
first_free_box : UI_Box,
box_table_size : u64,
box_table : ^ UI_BoxHashSlot, // TODO(Ed) : Can the cache use HashTable?
box_table : ^ UI_BoxHashSlot, // TODO(Ed) : Can the cache use HMapZPL?
// Note(rjf) : Build phase output
// TODO(ED) : Put this in its own struct?

View File

@ -3,7 +3,6 @@ package sectr
import "base:runtime"
import c "core:c/libc"
import "core:dynlib"
import "core:fmt"
import "core:mem"
import "core:mem/virtual"
import "core:os"
@ -16,7 +15,7 @@ Path_Input_Replay :: "scratch.sectr_replay"
ModuleAPI :: struct {
lib : dynlib.Library,
write_time : os.File_Time,
write_time : FileTime,
lib_version : i32,
startup : type_of( startup ),
@ -29,23 +28,23 @@ ModuleAPI :: struct {
@export
startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ Logger )
{
init( & memory.logger, "Sectr", host_logger.file_path, host_logger.file )
context.logger = to_odin_logger( & memory.logger )
logger_init( & Memory_App.logger, "Sectr", host_logger.file_path, host_logger.file )
context.logger = to_odin_logger( & Memory_App.logger )
// Setup memory for the first time
{
arena_size :: size_of( mem.Arena)
arena_size :: size_of( Arena)
internals_size :: 4 * Megabyte
using memory;
using Memory_App;
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 )
persistent_slice := slice_ptr( block.base, Memory_Persistent_Size )
transient_slice := slice_ptr( memory_after( persistent_slice), Memory_Trans_Temp_Szie )
temp_slice := slice_ptr( memory_after( transient_slice), Memory_Trans_Temp_Szie )
when Use_TrackingAllocator {
// We assign the beginning of the block to be the host's persistent memory's arena.
@ -95,7 +94,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^
monitor_id = rl.GetCurrentMonitor()
monitor_refresh_hz = rl.GetMonitorRefreshRate( monitor_id )
rl.SetTargetFPS( monitor_refresh_hz )
log( fmt.tprintf( "Set target FPS to: %v", monitor_refresh_hz ) )
log( str_fmt_tmp( "Set target FPS to: %v", monitor_refresh_hz ) )
// Basic Font Setup
{
@ -153,14 +152,14 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^
@export
sectr_shutdown :: proc()
{
if memory.persistent == nil {
if Memory_App.persistent == nil {
return
}
state := get_state()
// Replay
{
os.close( memory.replay.active_file )
os.close( Memory_App.replay.active_file )
}
font_provider_shutdown()
@ -171,27 +170,30 @@ sectr_shutdown :: proc()
@export
reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ Logger )
{
using memory;
using Memory_App;
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 )
// This is no longer necessary as we have proper base address setting
when false
{
persistent_slice := slice_ptr( block.base, Memory_Persistent_Size )
transient_slice := slice_ptr( memory_after( persistent_slice), Memory_Trans_Temp_Szie )
temp_slice := slice_ptr( memory_after( transient_slice), Memory_Trans_Temp_Szie )
when Use_TrackingAllocator {
persistent = cast( ^ TrackedAllocator ) & persistent_slice[0]
transient = cast( ^ TrackedAllocator ) & transient_slice[0]
temp = cast( ^ TrackedAllocator ) & temp_slice[0]
when Use_TrackingAllocator {
persistent = cast( ^ TrackedAllocator ) & persistent_slice[0]
transient = cast( ^ TrackedAllocator ) & transient_slice[0]
temp = cast( ^ TrackedAllocator ) & temp_slice[0]
}
else {
persistent = cast( ^ Arena ) & persistent_slice[0]
transient = cast( ^ Arena ) & transient_slice[0]
temp = cast( ^ Arena ) & temp_slice[0]
}
}
else {
persistent = cast( ^ Arena ) & persistent_slice[0]
transient = cast( ^ Arena ) & transient_slice[0]
temp = cast( ^ Arena ) & temp_slice[0]
}
context.allocator = transient_allocator()
context.temp_allocator = temp_allocator()
@ -203,7 +205,6 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ L
// font_provider_data := & get_state().font_provider_data
// font_provider_data.font_cache.allocator = arena_allocator( & font_provider_data.font_arena )
// Have to reload allocators for all dynamic allocating data-structures.
ui_reload( & get_state().project.workspace.ui, persistent_allocator() )
log("Module reloaded")
@ -228,7 +229,7 @@ tick :: proc( delta_time : f64 ) -> b32
@export
clean_temp :: proc() {
when Use_TrackingAllocator {
mem.tracking_allocator_clear( & memory.temp.tracker )
mem.tracking_allocator_clear( & Memory_App.temp.tracker )
}
else {
free_all( temp_allocator() )

View File

@ -1,4 +1,4 @@
package sectr
package sectr
import "base:runtime"
import "core:os"

View File

@ -2,15 +2,14 @@ package sectr
import rl "vendor:raylib"
Color :: rl.Color
Color :: rl.Color
Color_White :: rl.WHITE
Color_Transparent :: Color { 0, 0, 0, 0 }
Color_BG :: Color { 41, 41, 45, 255 }
Color_BG_TextBox :: Color { 32, 32, 32, 255 }
Color_BG_TextBox_Green :: Color { 102, 102, 110, 255 }
Color_Frame_Hover :: Color { 122, 122, 125, 255 }
Color_Frame_Select :: Color { 188, 188, 188, 255 }
Color_GreyRed :: Color { 220, 100, 100, 125 }
Color_White_A125 :: Color { 255, 255, 255, 125 }
Color_Transparent :: Color { 0, 0, 0, 0 }
Color_BG :: Color { 41, 41, 45, 255 }
Color_BG_TextBox :: Color { 32, 32, 32, 255 }
Color_BG_TextBox_Green :: Color { 102, 102, 110, 255 }
Color_Frame_Hover :: Color { 122, 122, 125, 255 }
Color_Frame_Select :: Color { 188, 188, 188, 255 }
Color_GreyRed :: Color { 220, 100, 100, 125 }
Color_White_A125 :: Color { 255, 255, 255, 125 }

View File

@ -8,11 +8,12 @@ import "core:os"
import rl "vendor:raylib"
memory : Memory
Memory_App : Memory
memory_chunk_size :: 2 * Gigabyte
memory_persistent_size :: 256 * Megabyte
memory_trans_temp_size :: (memory_chunk_size - memory_persistent_size ) / 2
Memory_Base_Address :: Terabyte * 1
Memory_Chunk_Size :: 2 * Gigabyte
Memory_Persistent_Size :: 256 * Megabyte
Memory_Trans_Temp_Szie :: (Memory_Chunk_Size - Memory_Persistent_Size ) / 2
// TODO(Ed): There is an issue with mutex locks on the tracking allocator..
Use_TrackingAllocator :: false
@ -48,39 +49,39 @@ else
persistent_allocator :: proc() -> Allocator {
when Use_TrackingAllocator {
return tracked_allocator( memory.persistent )
return tracked_allocator( Memory_App.persistent )
}
else {
return arena_allocator( memory.persistent )
return arena_allocator( Memory_App.persistent )
}
}
transient_allocator :: proc() -> Allocator {
when Use_TrackingAllocator {
return tracked_allocator( memory.transient )
return tracked_allocator( Memory_App.transient )
}
else {
return arena_allocator( memory.transient )
return arena_allocator( Memory_App.transient )
}
}
temp_allocator :: proc() -> Allocator {
when Use_TrackingAllocator {
return tracked_allocator( memory.temp )
return tracked_allocator( Memory_App.temp )
}
else {
return arena_allocator( memory.temp )
return arena_allocator( Memory_App.temp )
}
}
save_snapshot :: proc( snapshot : [^]u8 ) {
live_ptr := cast( ^ rawptr ) memory.live.curr_block.base
mem.copy_non_overlapping( & snapshot[0], live_ptr, memory_chunk_size )
live_ptr := cast( ^ rawptr ) Memory_App.live.curr_block.base
mem.copy_non_overlapping( & snapshot[0], live_ptr, Memory_Chunk_Size )
}
load_snapshot :: proc( snapshot : [^]u8 ) {
live_ptr := cast( ^ rawptr ) memory.live.curr_block.base
mem.copy_non_overlapping( live_ptr, snapshot, memory_chunk_size )
live_ptr := cast( ^ rawptr ) Memory_App.live.curr_block.base
mem.copy_non_overlapping( live_ptr, snapshot, Memory_Chunk_Size )
}
AppConfig :: struct {
@ -124,10 +125,10 @@ State :: struct {
get_state :: proc "contextless" () -> ^ State {
when Use_TrackingAllocator {
return cast( ^ State ) raw_data( memory.persistent.backing.data )
return cast( ^ State ) raw_data( Memory_App.persistent.backing.data )
}
else {
return cast( ^ State ) raw_data( memory.persistent. data )
return cast( ^ State ) raw_data( Memory_App.persistent. data )
}
}

View File

@ -56,7 +56,7 @@ FontProviderData :: struct {
//TODO(Ed) : There is an issue with hot-reload and map allocations that I can't figure out right now..
// font_cache : ^ map [FontID](FontDef),
// font_cache : HashTable(FontDef),
// font_cache : HMapZPL(FontDef),
font_cache : [10] FontDef,
open_id : i32
}
@ -101,7 +101,7 @@ font_load :: proc( path_file : string,
font_provider_data := & get_state().font_provider_data; using font_provider_data
font_data, read_succeded : = os.read_entire_file( path_file )
verify( b32(read_succeded), fmt.tprintf("Failed to read font file for: %v", path_file) )
verify( b32(read_succeded), str_fmt_tmp("Failed to read font file for: %v", path_file) )
font_data_size := cast(i32) len(font_data)
desired_id := desired_id
@ -141,7 +141,7 @@ font_load :: proc( path_file : string,
codepoints = nil,
codepointCount = count,
type = rl.FontType.DEFAULT )
verify( glyphs != nil, fmt.tprintf("Failed to load glyphs for font: %v at desired size: %v", desired_id, size ) )
verify( glyphs != nil, str_fmt_tmp("Failed to load glyphs for font: %v at desired size: %v", desired_id, size ) )
atlas := rl.GenImageFontAtlas( glyphs, & recs, count, size, padding, i32(Font_Atlas_Packing_Method.Raylib_Basic) )
texture = rl.LoadTextureFromImage( atlas )

26
code/girme_stack.odin Normal file
View File

@ -0,0 +1,26 @@
package sectr
Stack :: struct ( $ Type : typeid, $ Size : i32 ) {
idx : i32,
items : [ Size ] Type,
}
stack_push :: proc( using stack : ^ Stack( $ Type, $ Size ), value : Type ) {
verify( idx < len( items ), "Attempted to push on a full stack" )
items[ idx ] = value
idx += 1
}
stack_pop :: proc( using stack : ^ Stack( $ Type, $ Size ) ) {
verify( idx > 0, "Attempted to pop an empty stack" )
idx -= 1
if idx == 0 {
items[idx] = {}
}
}
stack_peek :: proc( using stack : ^ Stack( $ Type, $ Size ) ) -> ^ Type {
return & items[idx]
}

View File

@ -1,92 +1,88 @@
package sectr
// At least its less than C/C++ ...
import "base:builtin"
copy :: builtin.copy
import "base:runtime"
Byte :: runtime.Byte
Kilobyte :: runtime.Kilobyte
Megabyte :: runtime.Megabyte
Gigabyte :: runtime.Gigabyte
Terabyte :: runtime.Terabyte
Petabyte :: runtime.Petabyte
Exabyte :: runtime.Exabyte
import c "core:c/libc"
import "core:dynlib"
import "core:hash"
crc32 :: hash.crc32
import fmt_io "core:fmt"
str_fmt :: fmt_io.printf
str_fmt_tmp :: fmt_io.tprintf
str_fmt_builder :: fmt_io.sbprintf
str_fmt_buffer :: fmt_io.bprintf
str_to_file_ln :: fmt_io.fprintln
str_tmp_from_any :: fmt_io.tprint
import "core:mem"
Allocator :: mem.Allocator
AllocatorError :: mem.Allocator_Error
alloc :: mem.alloc
alloc_bytes :: mem.alloc_bytes
Arena :: mem.Arena
arena_allocator :: mem.arena_allocator
arena_init :: mem.arena_init
free :: mem.free
ptr_offset :: mem.ptr_offset
slice_ptr :: mem.slice_ptr
TrackingAllocator :: mem.Tracking_Allocator
tracking_allocator :: mem.tracking_allocator
tracking_allocator_init :: mem.tracking_allocator_init
import "core:mem/virtual"
import "core:os"
FileFlag_Create :: os.O_CREATE
FileFlag_ReadWrite :: os.O_RDWR
FileTime :: os.File_Time
file_close :: os.close
file_open :: os.open
file_read :: os.read
file_remove :: os.remove
file_seek :: os.seek
file_status :: os.stat
file_write :: os.write
import "core:path/filepath"
file_name_from_path :: filepath.short_stem
import str "core:strings"
str_builder_to_string :: str.to_string
import "core:unicode/utf8"
import c "core:c/libc"
to_runes :: proc {
utf8.string_to_runes,
}
Byte :: 1
Kilobyte :: 1024 * Byte
Megabyte :: 1024 * Kilobyte
Gigabyte :: 1024 * Megabyte
Terabyte :: 1024 * Gigabyte
Petabyte :: 1024 * Terabyte
Exabyte :: 1024 * Petabyte
to_string :: proc {
str_builder_to_string,
}
kilobytes :: proc( kb : $ integer_type ) -> integer_type {
OS_Type :: type_of(ODIN_OS)
kilobytes :: #force_inline proc "contextless" ( kb : $ integer_type ) -> integer_type {
return kb * Kilobyte
}
megabytes :: proc( mb : $ integer_type ) -> integer_type {
megabytes :: #force_inline proc "contextless" ( mb : $ integer_type ) -> integer_type {
return mb * Megabyte
}
gigabyte :: proc( gb : $ integer_type ) -> integer_type {
gigabytes :: #force_inline proc "contextless" ( gb : $ integer_type ) -> integer_type {
return gb * Gigabyte
}
terabyte :: proc( tb : $ integer_type ) -> integer_type {
terabytes :: #force_inline proc "contextless" ( tb : $ integer_type ) -> integer_type {
return tb * Terabyte
}
copy :: builtin.copy
crc32 :: hash.crc32
Allocator :: mem.Allocator
AllocatorError :: mem.Allocator_Error
alloc :: mem.alloc
alloc_bytes :: mem.alloc_bytes
Arena :: mem.Arena
arena_allocator :: mem.arena_allocator
arena_init :: mem.arena_init
free :: mem.free
ptr_offset :: mem.ptr_offset
slice_ptr :: mem.slice_ptr
Tracking_Allocator :: mem.Tracking_Allocator
tracking_allocator :: mem.tracking_allocator
tracking_allocator_init :: mem.tracking_allocator_init
file_name_from_path :: filepath.short_stem
OS_Type :: type_of(ODIN_OS)
get_bounds :: proc {
box_get_bounds,
view_get_bounds,
}
//region Stack - Basic fixed-size stack container
Stack :: struct ( $ Type : typeid, $ Size : i32 ) {
idx : i32,
items : [ Size ] Type,
}
stack_push :: proc( stack : ^ $ StackType / Stack( $ Type, $ Size ), value : Type ) {
using stack
verify( idx < len( items ), "Attempted to push on a full stack" )
items[ idx ] = value
idx += 1
}
stack_pop :: proc( stack : ^ $ StackType / Stack( $ Type, $ Size ) ) {
using stack
verify( idx > 0, "Attempted to pop an empty stack" )
idx -= 1
if idx == 0 {
items[idx] = {}
}
}
stack_peek :: proc( stack : ^ Stack( $ Type, $ Size ) ) -> ^ Type {
using stack
return & items[idx]
}
//endregion Stack
// TODO(Ed): Review
//region Doubly Linked List generic procs (verbose)
dbl_linked_list_push_back :: proc(first: ^(^ $ Type), last: ^(^ Type), new_node: ^ Type)
@ -109,104 +105,3 @@ dbl_linked_list_push_back :: proc(first: ^(^ $ Type), last: ^(^ Type), new_node:
}
//endregion
// TODO(Ed) : This is extremely jank, Raylib requires a 'heap' allocator with the way it works.
// We do not have persistent segmented in such a way for this. Eventually we might just want to segment vmem and just shove a heap allocator on a segment of it.
when false {
RL_MALLOC :: proc "c" ( size : c.size_t ) -> rawptr
{
allocator : Allocator
when Use_TrackingAllocator {
allocator = Allocator {
data = & memory.persistent.tracker,
procedure = mem.tracking_allocator_proc,
}
}
else {
allocator = Allocator {
data = & memory.persistent,
procedure = mem.arena_allocator_proc,
}
}
result, error_code := allocator.procedure( allocator.data, mem.Allocator_Mode.Alloc_Non_Zeroed, cast(int) size, mem.DEFAULT_ALIGNMENT, nil, 0, auto_cast {} )
if error_code != AllocatorError.None {
runtime.debug_trap()
os.exit( -1 )
}
return raw_data(result)
}
RL_CALLOC :: proc "c" ( count : c.size_t, size : c.size_t ) -> rawptr
{
allocator : Allocator
when Use_TrackingAllocator {
allocator = Allocator {
data = & memory.persistent.tracker,
procedure = mem.tracking_allocator_proc,
}
}
else {
allocator = Allocator {
data = & memory.persistent,
procedure = mem.arena_allocator_proc,
}
}
result, error_code := allocator.procedure( allocator.data, mem.Allocator_Mode.Alloc, cast(int) size, mem.DEFAULT_ALIGNMENT, nil, 0, auto_cast {} )
if error_code != AllocatorError.None {
runtime.debug_trap()
os.exit( -1 )
}
return raw_data(result)
}
RL_REALLOC :: proc "c" ( block : rawptr, size : c.size_t ) -> rawptr
{
allocator : Allocator
when Use_TrackingAllocator {
allocator = Allocator {
data = & memory.persistent.tracker,
procedure = mem.tracking_allocator_proc,
}
}
else {
allocator = Allocator {
data = & memory.persistent,
procedure = mem.arena_allocator_proc,
}
}
result, error_code := allocator.procedure( allocator.data, mem.Allocator_Mode.Resize_Non_Zeroed, cast(int) size, mem.DEFAULT_ALIGNMENT, block, 0, auto_cast {} )
if error_code != AllocatorError.None {
runtime.debug_trap()
os.exit( -1 )
}
return raw_data(result)
}
RL_FREE :: proc "c" ( block : rawptr )
{
allocator : Allocator
when Use_TrackingAllocator {
allocator = Allocator {
data = & memory.persistent.tracker,
procedure = mem.tracking_allocator_proc,
}
}
else {
allocator = Allocator {
data = & memory.persistent,
procedure = mem.arena_allocator_proc,
}
}
result, error_code := allocator.procedure( allocator.data, mem.Allocator_Mode.Free, 0, 0, block, 0, auto_cast {} )
if error_code != AllocatorError.None {
runtime.debug_trap()
os.exit( -1 )
}
}
}

View File

@ -1,6 +1,6 @@
// Based on gencpp's and thus zpl's Array implementation
// Made becasue of the map issue with fonts during hot-reload.
// I didn't want to make the HashTable impl with the [dynamic] array for now to isolate
// I didn't want to make the HMapZPL impl with the [dynamic] array for now to isolate
// what in the world is going on with the memory...
package sectr
@ -15,8 +15,11 @@ Array :: struct ( $ Type : typeid ) {
data : [^]Type,
}
array_to_slice :: proc( arr : Array( $ Type) ) -> []Type {
using arr; return slice_ptr( data, num )
// @(private=file)
// Array :: Array_ZPL
array_to_slice :: proc( using self : Array( $ Type) ) -> []Type {
return slice_ptr( data, num )
}
array_grow_formula :: proc( value : u64 ) -> u64 {
@ -30,19 +33,18 @@ array_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( Array(Type), A
array_init_reserve :: proc( $ Type : typeid, allocator : Allocator, capacity : u64 ) -> ( Array(Type), AllocatorError )
{
raw_data, result_code := alloc( int(capacity) * size_of(Type), allocator = allocator )
result : Array( Type)
result : Array( Type);
result.data = cast( [^] Type ) raw_data
result.allocator = allocator
result.capacity = capacity
return result, result_code
}
array_append :: proc( array : ^ Array( $ Type), value : Type ) -> AllocatorError
array_append :: proc( using self : ^ Array( $ Type), value : Type ) -> AllocatorError
{
using array
if num == capacity
{
grow_result := array_grow( array, capacity )
grow_result := array_grow( self, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
@ -53,12 +55,11 @@ array_append :: proc( array : ^ Array( $ Type), value : Type ) -> AllocatorError
return AllocatorError.None
}
array_append_slice :: proc( array : ^ Array( $ Type ), items : []Type ) -> AllocatorError
array_append_slice :: proc( using self : ^ Array( $ Type ), items : []Type ) -> AllocatorError
{
using array
if num + len(items) > capacity
{
grow_result := array_grow( array, capacity )
grow_result := array_grow( self, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
@ -75,11 +76,9 @@ array_append_slice :: proc( array : ^ Array( $ Type ), items : []Type ) -> Alloc
return AllocatorError.None
}
array_append_at :: proc( array : ^ Array( $ Type ), item : Type, id : u64 ) -> AllocatorError
array_append_at :: proc( using self : ^ Array( $ Type ), item : Type, id : u64 ) -> AllocatorError
{
id := id
using array
if id >= num {
id = num - 1
}
@ -89,7 +88,7 @@ array_append_at :: proc( array : ^ Array( $ Type ), item : Type, id : u64 ) -> A
if capacity < num + 1
{
grow_result := array_grow( array, capacity )
grow_result := array_grow( self, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
@ -109,17 +108,15 @@ array_append_at :: proc( array : ^ Array( $ Type ), item : Type, id : u64 ) -> A
return AllocatorError.None
}
array_append_at_slice :: proc( array : ^ Array( $ Type ), items : []Type, id : u64 ) -> AllocatorError
array_append_at_slice :: proc( using self : ^ Array( $ Type ), items : []Type, id : u64 ) -> AllocatorError
{
id := id
using array
if id >= num {
return array_append_slice( items )
}
if len(items) > capacity
{
grow_result := array_grow( array, capacity )
grow_result := array_grow( self, capacity )
if grow_result != AllocatorError.None {
return grow_result
}
@ -142,18 +139,19 @@ array_append_at_slice :: proc( array : ^ Array( $ Type ), items : []Type, id : u
return AllocatorError.None
}
array_back :: proc( array : ^ Array( $ Type ) ) -> ^ Type {
using array; return & data[ num - 1 ]
array_back :: proc( using self : ^ Array( $ Type ) ) -> ^ Type {
return & data[ num - 1 ]
}
array_clear :: proc( array : ^ Array( $ Type ) ) {
array.num = 0
array_clear :: proc( using self : ^ Array( $ Type ), zero_data : b32 ) {
if zero_data {
mem.set( raw_data( data ), 0, num )
}
num = 0
}
array_fill :: proc( array : ^ Array( $ Type ), begin, end : u64, value : Type ) -> b32
array_fill :: proc( using self : ^ Array( $ Type ), begin, end : u64, value : Type ) -> b32
{
using array
if begin < 0 || end >= num {
return false
}
@ -168,32 +166,28 @@ array_fill :: proc( array : ^ Array( $ Type ), begin, end : u64, value : Type )
return true
}
array_free :: proc( array : ^ Array( $ Type ) ) {
using array
array_free :: proc( using self : ^ Array( $ Type ) ) {
free( data, allocator )
data = nil
}
array_grow :: proc( array : ^ Array( $ Type ), min_capacity : u64 ) -> AllocatorError
array_grow :: proc( using self : ^ Array( $ Type ), min_capacity : u64 ) -> AllocatorError
{
using array
new_capacity := array_grow_formula( capacity )
if new_capacity < min_capacity {
new_capacity = min_capacity
}
return array_set_capacity( array, new_capacity )
return array_set_capacity( self, new_capacity )
}
array_pop :: proc( array : ^ Array( $ Type ) ) {
verify( array.num != 0, "Attempted to pop an array with no elements" )
array.num -= 1
array_pop :: proc( using self : ^ Array( $ Type ) ) {
verify( num != 0, "Attempted to pop an array with no elements" )
num -= 1
}
array_remove_at :: proc( array : ^ Array( $ Type ), id : u64 )
array_remove_at :: proc( using self : ^ Array( $ Type ), id : u64 )
{
using array
verify( id >= num, "Attempted to remove from an index larger than the array" )
left = slice_ptr( data, id )
@ -203,11 +197,10 @@ array_remove_at :: proc( array : ^ Array( $ Type ), id : u64 )
num -= 1
}
array_reserve :: proc( array : ^ Array( $ Type ), new_capacity : u64 ) -> AllocatorError
array_reserve :: proc( using self : ^ Array( $ Type ), new_capacity : u64 ) -> AllocatorError
{
using array
if capacity < new_capacity {
return array_set_capacity( array, new_capacity )
return array_set_capacity( self, new_capacity )
}
return AllocatorError.None
}
@ -226,9 +219,8 @@ array_resize :: proc( array : ^ Array( $ Type ), num : u64 ) -> AllocatorError
return AllocatorError.None
}
array_set_capacity :: proc( array : ^ Array( $ Type ), new_capacity : u64 ) -> AllocatorError
array_set_capacity :: proc( using self : ^ Array( $ Type ), new_capacity : u64 ) -> AllocatorError
{
using array
if new_capacity == capacity {
return AllocatorError.None
}

View File

@ -5,11 +5,11 @@ import "core:fmt"
import "core:os"
import "core:runtime"
copy_file_sync :: proc( path_src, path_dst: string ) -> b32
file_copy_sync :: proc( path_src, path_dst: string ) -> b32
{
file_size : i64
{
path_info, result := os.stat( path_src, context.temp_allocator )
path_info, result := file_status( path_src, context.temp_allocator )
if result != os.ERROR_NONE {
logf("Could not get file info: %v", result, LogLevel.Error )
return false
@ -34,32 +34,32 @@ copy_file_sync :: proc( path_src, path_dst: string ) -> b32
}
file_exists :: proc( file_path : string ) -> b32 {
path_info, result := os.stat( file_path, context.temp_allocator )
path_info, result := file_status( file_path, context.temp_allocator )
if result != os.ERROR_NONE {
return false
}
return true;
}
is_file_locked :: proc( file_path : string ) -> b32 {
handle, err := os.open(file_path, os.O_RDONLY)
file_is_locked :: proc( file_path : string ) -> b32 {
handle, err := file_open(file_path, os.O_RDONLY)
if err != os.ERROR_NONE {
// If the error indicates the file is in use, return true.
return true
}
// If the file opens successfully, close it and return false.
os.close(handle)
file_close(handle)
return false
}
rewind :: proc( file : os.Handle ) {
os.seek( file, 0, 0 )
file_rewind :: proc( file : os.Handle ) {
file_seek( file, 0, 0 )
}
read_looped :: proc( file : os.Handle, data : []byte ) {
total_read, result_code := os.read( file, data )
file_read_looped :: proc( file : os.Handle, data : []byte ) {
total_read, result_code := file_read( file, data )
if result_code == os.ERROR_HANDLE_EOF {
rewind( file )
file_rewind( file )
}
}

4
code/grime_hashmap.odin Normal file
View File

@ -0,0 +1,4 @@
// TODO(Ed) : Make your own hashmap implementation (open-addressing, round-robin possibly)
package sectr

View File

@ -1,6 +1,10 @@
// This is an alternative to Odin's default map type.
// The only reason I may need this is due to issues with allocator callbacks or something else going on
// with hot-reloads...
// This implementation uses two ZPL-Based Arrays to hold entires and the actual hash table.
// Its algorithim isn't that great, removal of elements is very expensive.
// To the point where if thats done quite a bit another implementation should be looked at.
package sectr
import "core:slice"
@ -9,33 +13,35 @@ import "core:slice"
// Note(Ed) : See core:hash for hasing procs.
// This might be problematic...
HT_MapProc :: #type proc( $ Type : typeid, key : u64, value : Type )
HT_MapMutProc :: #type proc( $ Type : typeid, key : u64, value : ^ Type )
HMapZPL_MapProc :: #type proc( $ Type : typeid, key : u64, value : Type )
HMapZPL_MapMutProc :: #type proc( $ Type : typeid, key : u64, value : ^ Type )
HT_FindResult :: struct {
HMapZPL_CritialLoadScale :: 0.70
HMapZPL_FindResult :: struct {
hash_index : i64,
prev_index : i64,
entry_index : i64,
}
HashTable_Entry :: struct ( $ Type : typeid) {
HMapZPL_Entry :: struct ( $ Type : typeid) {
key : u64,
next : i64,
value : Type,
}
HashTable :: struct ( $ Type : typeid ) {
HMapZPL :: struct ( $ Type : typeid ) {
hashes : Array( i64 ),
entries : Array( HashTable_Entry(Type) ),
entries : Array( HMapZPL_Entry(Type) ),
}
hashtable_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HashTable( Type), AllocatorError ) {
return hashtable_init_reserve( Type, allocator )
zpl_hmap_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HMapZPL( Type), AllocatorError ) {
return zpl_hmap_init_reserve( Type, allocator )
}
hashtable_init_reserve :: proc( $ Type : typeid, allocator : Allocator, num : u64 ) -> ( HashTable( Type), AllocatorError )
zpl_hmap_init_reserve :: proc( $ Type : typeid, allocator : Allocator, num : u64 ) -> ( HMapZPL( Type), AllocatorError )
{
result : HashTable(Type)
result : HMapZPL(Type)
hashes_result, entries_result : AllocatorError
result.hashes, hashes_result = array_init_reserve( i64, allocator, num )
@ -46,7 +52,7 @@ hashtable_init_reserve :: proc( $ Type : typeid, allocator : Allocator, num : u6
array_resize( & result.hashes, num )
slice.fill( slice_ptr( result.hashes.data, cast(int) result.hashes.num), -1 )
result.entries, entries_result = array_init_reserve( HashTable_Entry(Type), allocator, num )
result.entries, entries_result = array_init_reserve( HMapZPL_Entry(Type), allocator, num )
if entries_result != AllocatorError.None {
ensure( false, "Failed to allocate entries array" )
return result, entries_result
@ -54,8 +60,7 @@ hashtable_init_reserve :: proc( $ Type : typeid, allocator : Allocator, num : u6
return result, AllocatorError.None
}
hashtable_clear :: proc( ht : ^ HashTable( $ Type ) ) {
using ht
zpl_hmap_clear :: proc( using self : ^ HMapZPL( $ Type ) ) {
for id := 0; id < hashes.num; id += 1 {
hashes[id] = -1
}
@ -64,18 +69,16 @@ hashtable_clear :: proc( ht : ^ HashTable( $ Type ) ) {
array_clear( entries )
}
hashtable_destroy :: proc( using ht : ^ HashTable( $ Type ) ) {
zpl_hmap_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) {
if hashes.data != nil && hashes.capacity > 0 {
array_free( & hashes )
array_free( & entries )
}
}
hashtable_get :: proc( ht : ^ HashTable( $ Type ), key : u64 ) -> ^ Type
zpl_hmap_get :: proc( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type
{
using ht
id := hashtable_find( ht, key ).entry_index
id := zpl_hmap_find( self, key ).entry_index
if id >= 0 {
return & entries.data[id].value
}
@ -83,44 +86,41 @@ hashtable_get :: proc( ht : ^ HashTable( $ Type ), key : u64 ) -> ^ Type
return nil
}
hashtable_map :: proc( ht : ^ HashTable( $ Type), map_proc : HT_MapProc ) {
using ht
zpl_hmap_map :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapProc ) {
ensure( map_proc != nil, "Mapping procedure must not be null" )
for id := 0; id < entries.num; id += 1 {
map_proc( Type, entries[id].key, entries[id].value )
}
}
hashtable_map_mut :: proc( ht : ^ HashTable( $ Type), map_proc : HT_MapMutProc ) {
using ht
zpl_hmap_map_mut :: proc( using self : ^ HMapZPL( $ Type), map_proc : HMapZPL_MapMutProc ) {
ensure( map_proc != nil, "Mapping procedure must not be null" )
for id := 0; id < entries.num; id += 1 {
map_proc( Type, entries[id].key, & entries[id].value )
}
}
hashtable_grow :: proc( ht : ^ HashTable( $ Type ) ) -> AllocatorError {
using ht
zpl_hmap_grow :: proc( using self : ^ HMapZPL( $ Type ) ) -> AllocatorError {
new_num := array_grow_formula( entries.num )
return hashtable_rehash( ht, new_num )
return zpl_hmap_rehash( self, new_num )
}
hashtable_rehash :: proc( ht : ^ HashTable( $ Type ), new_num : u64 ) -> AllocatorError
zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorError
{
last_added_index : i64
new_ht, init_result := hashtable_init_reserve( Type, ht.hashes.allocator, new_num )
new_ht, init_result := zpl_hmap_init_reserve( Type, ht.hashes.allocator, new_num )
if init_result != AllocatorError.None {
ensure( false, "New hashtable failed to allocate" )
ensure( false, "New zpl_hmap failed to allocate" )
return init_result
}
for id : u64 = 0; id < ht.entries.num; id += 1 {
find_result : HT_FindResult
find_result : HMapZPL_FindResult
entry := & ht.entries.data[id]
find_result = hashtable_find( & new_ht, entry.key )
last_added_index = hashtable_add_entry( & new_ht, entry.key )
find_result = zpl_hmap_find( & new_ht, entry.key )
last_added_index = zpl_hmap_add_entry( & new_ht, entry.key )
if find_result.prev_index < 0 {
new_ht.hashes.data[ find_result.hash_index ] = last_added_index
@ -133,15 +133,14 @@ hashtable_rehash :: proc( ht : ^ HashTable( $ Type ), new_num : u64 ) -> Allocat
new_ht.entries.data[ last_added_index ].value = entry.value
}
hashtable_destroy( ht )
zpl_hmap_destroy( ht )
(ht ^) = new_ht
return AllocatorError.None
}
hashtable_rehash_fast :: proc( ht : ^ HashTable( $ Type ) )
zpl_hmap_rehash_fast :: proc( using self : ^ HMapZPL( $ Type ) )
{
using ht
for id := 0; id < entries.num; id += 1 {
entries[id].Next = -1;
}
@ -150,7 +149,7 @@ hashtable_rehash_fast :: proc( ht : ^ HashTable( $ Type ) )
}
for id := 0; id < entries.num; id += 1 {
entry := & entries[id]
find_result := hashtable_find( entry.key )
find_result := zpl_hmap_find( entry.key )
if find_result.prev_index < 0 {
hashes[ find_result.hash_index ] = id
@ -161,42 +160,39 @@ hashtable_rehash_fast :: proc( ht : ^ HashTable( $ Type ) )
}
}
hashtable_remove :: proc( ht : ^ HashTable( $ Type ), key : u64 ) {
using ht
find_result := hashtable_find( key )
zpl_hmap_remove :: proc( self : ^ HMapZPL( $ Type ), key : u64 ) {
find_result := zpl_hmap_find( key )
if find_result.entry_index >= 0 {
array_remove_at( & ht.entries, find_result.entry_index )
hashtable_rehash_fast( ht )
array_remove_at( & entries, find_result.entry_index )
zpl_hmap_rehash_fast( self )
}
}
hashtable_remove_entry :: proc( ht : ^ HashTable( $ Type ), id : i64 ) {
array_remove_at( & ht.entries, id )
zpl_hmap_remove_entry :: proc( using self : ^ HMapZPL( $ Type ), id : i64 ) {
array_remove_at( & entries, id )
}
hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
zpl_hmap_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
{
using ht
id : i64 = 0
find_result : HT_FindResult
find_result : HMapZPL_FindResult
if hashtable_full( ht )
if zpl_hmap_full( self )
{
grow_result := hashtable_grow(ht)
grow_result := zpl_hmap_grow( self )
if grow_result != AllocatorError.None {
return nil, grow_result
}
}
find_result = hashtable_find( ht, key )
find_result = zpl_hmap_find( self, key )
if find_result.entry_index >= 0 {
id = find_result.entry_index
}
else
{
id = hashtable_add_entry( ht, key )
id = zpl_hmap_add_entry( self, key )
if find_result.prev_index >= 0 {
entries.data[ find_result.prev_index ].next = id
}
@ -207,15 +203,14 @@ hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> (
entries.data[id].value = value
if hashtable_full( ht ) {
return & entries.data[id].value, hashtable_grow( ht )
if zpl_hmap_full( self ) {
return & entries.data[id].value, zpl_hmap_grow( self )
}
return & entries.data[id].value, AllocatorError.None
}
hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
using ht
zpl_hmap_slot :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 {
for id : i64 = 0; id < hashes.num; id += 1 {
if hashes.data[id] == key {
return id
@ -224,36 +219,35 @@ hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
return -1
}
hashtable_add_entry :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
using ht
entry : HashTable_Entry(Type) = { key, -1, {} }
zpl_hmap_add_entry :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64 {
entry : HMapZPL_Entry(Type) = { key, -1, {} }
id := cast(i64) entries.num
array_append( & entries, entry )
return id
}
hashtable_find :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> HT_FindResult
zpl_hmap_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_FindResult
{
using ht
result : HT_FindResult = { -1, -1, -1 }
result : HMapZPL_FindResult = { -1, -1, -1 }
if hashes.num > 0 {
result.hash_index = cast(i64)( key % hashes.num )
result.entry_index = hashes.data[ result.hash_index ]
for ; result.entry_index >= 0; {
if entries.data[ result.entry_index ].key == key {
entry := & entries.data[ result.entry_index ]
if entry.key == key {
break
}
result.prev_index = result.entry_index
result.entry_index = entries.data[ result.entry_index ].next
result.entry_index = entry.next
}
}
return result
}
hashtable_full :: proc( using ht : ^ HashTable( $ Type) ) -> b32 {
result : b32 = entries.num > u64(0.75 * cast(f64) hashes.num)
zpl_hmap_full :: proc( using self : ^ HMapZPL( $ Type) ) -> b32 {
result : b32 = entries.num > u64(HMapZPL_CritialLoadScale * cast(f64) hashes.num)
return result
}

View File

@ -27,7 +27,7 @@ memory_after :: proc( slice : []byte ) -> ( ^ byte) {
TrackedAllocator :: struct {
backing : Arena,
internals : Arena,
tracker : Tracking_Allocator,
tracker : TrackingAllocator,
}
tracked_allocator :: proc( self : ^ TrackedAllocator ) -> Allocator {
@ -45,7 +45,7 @@ tracked_allocator_init :: proc( size, internals_size : int, allocator := context
raw_size := backing_size + internals_size
raw_mem, raw_mem_code := alloc( raw_size, mem.DEFAULT_ALIGNMENT, allocator )
verify( raw_mem_code == mem.Allocator_Error.None, "Failed to allocate memory for the TrackingAllocator" )
verify( raw_mem_code == AllocatorError.None, "Failed to allocate memory for the TrackingAllocator" )
backing_slice := slice_ptr( cast( ^ byte) raw_mem, backing_size )
internals_slice := slice_ptr( memory_after( backing_slice), internals_size )
@ -67,7 +67,7 @@ tracked_allocator_init :: proc( size, internals_size : int, allocator := context
tracked_allocator_init_vmem :: proc( vmem : [] byte, internals_size : int ) -> ^ TrackedAllocator
{
arena_size :: size_of( Arena)
tracking_allocator_size :: size_of( Tracking_Allocator )
tracking_allocator_size :: size_of( TrackingAllocator )
backing_size := len(vmem) - internals_size
raw_size := backing_size + internals_size

View File

@ -1,42 +1,84 @@
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"
file_resize :: os.ftruncate
import "core:strings"
import "core:time"
//region Grime & Dependencies
import "base:runtime"
Byte :: runtime.Byte
Kilobyte :: runtime.Kilobyte
Megabyte :: runtime.Megabyte
Gigabyte :: runtime.Gigabyte
Terabyte :: runtime.Terabyte
Petabyte :: runtime.Petabyte
Exabyte :: runtime.Exabyte
import "core:dynlib"
os_lib_load :: dynlib.load_library
os_lib_unload :: dynlib.unload_library
os_lib_get_proc :: dynlib.symbol_address
import "core:io"
import fmt_io "core:fmt"
str_fmt :: fmt_io.printf
str_fmt_tmp :: fmt_io.tprintf
str_fmt_builder :: fmt_io.sbprintf
import "core:log"
import "core:mem"
Allocator :: mem.Allocator
TrackingAllocator :: mem.Tracking_Allocator
import "core:mem/virtual"
Arena :: virtual.Arena
MapFileError :: virtual.Map_File_Error
MapFileFlag :: virtual.Map_File_Flag
MapFileFlags :: virtual.Map_File_Flags
import "core:os"
FileFlag_Create :: os.O_CREATE
FileFlag_ReadWrite :: os.O_RDWR
file_open :: os.open
file_close :: os.close
file_rename :: os.rename
file_remove :: os.remove
file_resize :: os.ftruncate
file_status_via_handle :: os.fstat
file_status_via_path :: os.stat
import "core:strings"
builder_to_string :: strings.to_string
str_clone :: strings.clone
str_builder_from_bytes :: strings.builder_from_bytes
import "core:time"
Millisecond :: time.Millisecond
Second :: time.Second
Duration :: time.Duration
duration_seconds :: time.duration_seconds
thread_sleep :: time.sleep
import rl "vendor:raylib"
import sectr "../."
fatal :: sectr.fatal
file_is_locked :: sectr.file_is_locked
file_copy_sync :: sectr.file_copy_sync
Logger :: sectr.Logger
logger_init :: sectr.logger_init
LogLevel :: sectr.LogLevel
log :: sectr.log
to_odin_logger :: sectr.to_odin_logger
TrackedAllocator :: sectr.TrackedAllocator
tracked_allocator :: sectr.tracked_allocator
tracked_allocator_init :: sectr.tracked_allocator_init
verify :: sectr.verify
TrackedAllocator :: sectr.TrackedAllocator
tracked_allocator :: sectr.tracked_allocator
tracked_allocator_init :: sectr.tracked_allocator_init
file_status :: proc {
file_status_via_handle,
file_status_via_path,
}
LogLevel :: sectr.LogLevel
log :: sectr.log
fatal :: sectr.fatal
verify :: sectr.verify
to_str :: proc {
builder_to_string,
}
//endregion Grime & Dependencies
path_snapshot :: "VMemChunk_1.snapshot"
Path_Snapshot :: "VMemChunk_1.snapshot"
Path_Logs :: "../logs"
when ODIN_OS == runtime.Odin_OS_Type.Windows
{
path_logs :: "../logs"
path_sectr_module :: "sectr.dll"
path_sectr_live_module :: "sectr_live.dll"
path_sectr_debug_symbols :: "sectr.pdb"
Path_Sectr_Module :: "sectr.dll"
Path_Sectr_Live_Module :: "sectr_live.dll"
Path_Sectr_Debug_Symbols :: "sectr.pdb"
}
RuntimeState :: struct {
@ -46,53 +88,48 @@ RuntimeState :: struct {
}
VMemChunk :: struct {
og_allocator : mem.Allocator,
og_temp_allocator : mem.Allocator,
og_allocator : Allocator,
og_temp_allocator : Allocator,
host_persistent : TrackedAllocator,
host_transient : TrackedAllocator,
sectr_live : virtual.Arena,
sectr_live : 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_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 )
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 * 1)
result := arena_init_static( & sectr_live, base_address, sectr.memory_chunk_size, sectr.memory_chunk_size )
base_address : rawptr = transmute( rawptr) u64(sectr.Memory_Base_Address)
result := arena_init_static( & sectr_live, base_address, sectr.Memory_Chunk_Size, sectr.Memory_Chunk_Size )
verify( result == runtime.Allocator_Error.None, "Failed to allocate live memory for the sectr module" )
}
// Setup memory mapped io for snapshots
{
snapshot_file, open_error := os.open( path_snapshot, os.O_RDWR | os.O_CREATE )
snapshot_file, open_error := file_open( Path_Snapshot, FileFlag_ReadWrite | FileFlag_Create )
verify( open_error == os.ERROR_NONE, "Failed to open snapshot file for the sectr module" )
file_info, stat_code := os.stat( path_snapshot )
file_info, stat_code := file_status( snapshot_file )
{
if file_info.size != sectr.memory_chunk_size {
file_resize( snapshot_file, sectr.memory_chunk_size )
if file_info.size != sectr.Memory_Chunk_Size {
file_resize( snapshot_file, sectr.Memory_Chunk_Size )
}
}
map_error : virtual.Map_File_Error
map_flags : virtual.Map_File_Flags = { virtual.Map_File_Flag.Read, virtual.Map_File_Flag.Write }
map_error : MapFileError
map_flags : MapFileFlags = { MapFileFlag.Read, MapFileFlag.Write }
sectr_snapshot, map_error = virtual.map_file_from_file_descriptor( uintptr(snapshot_file), map_flags )
verify( map_error == virtual.Map_File_Error.None, "Failed to allocate snapshot memory for the sectr module" )
os.close(snapshot_file)
verify( map_error == MapFileError.None, "Failed to allocate snapshot memory for the sectr module" )
file_close(snapshot_file)
}
// Reassign default allocators for host
@ -113,28 +150,28 @@ load_sectr_api :: proc( version_id : i32 ) -> sectr.ModuleAPI
return {}
}
live_file := path_sectr_live_module
sectr.copy_file_sync( path_sectr_module, live_file )
live_file := Path_Sectr_Live_Module
file_copy_sync( Path_Sectr_Module, live_file )
lib, load_result := dynlib.load_library( live_file )
lib, load_result := os_lib_load( live_file )
if ! load_result {
log( "Failed to load the sectr module.", LogLevel.Warning )
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" )
tick := cast( type_of( sectr.tick )) dynlib.symbol_address( lib, "tick" )
clean_temp := cast( type_of( sectr.clean_temp )) dynlib.symbol_address( lib, "clean_temp" )
startup := cast( type_of( sectr.startup )) os_lib_get_proc( lib, "startup" )
shutdown := cast( type_of( sectr.sectr_shutdown )) os_lib_get_proc( lib, "sectr_shutdown" )
reload := cast( type_of( sectr.reload )) os_lib_get_proc( lib, "reload" )
tick := cast( type_of( sectr.tick )) os_lib_get_proc( lib, "tick" )
clean_temp := cast( type_of( sectr.clean_temp )) os_lib_get_proc( 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 tick == nil do fmt.println("Failed to load sectr.tick symbol")
if clean_temp == nil do fmt.println("Failed to load sector.clean_temp symbol")
if startup == nil do log("Failed to load sectr.startup symbol", LogLevel.Warning )
if shutdown == nil do log("Failed to load sectr.shutdown symbol", LogLevel.Warning )
if reload == nil do log("Failed to load sectr.reload symbol", LogLevel.Warning )
if tick == nil do log("Failed to load sectr.tick symbol", LogLevel.Warning )
if clean_temp == nil do log("Failed to load sector.clean_temp symbol", LogLevel.Warning )
if missing_symbol {
runtime.debug_trap()
return {}
@ -157,23 +194,23 @@ load_sectr_api :: proc( version_id : i32 ) -> sectr.ModuleAPI
unload_sectr_api :: proc( module : ^ sectr.ModuleAPI )
{
dynlib.unload_library( module.lib )
os.remove( path_sectr_live_module )
os_lib_unload( module.lib )
file_remove( Path_Sectr_Live_Module )
module^ = {}
log("Unloaded sectr API")
}
sync_sectr_api :: proc( sectr_api : ^ sectr.ModuleAPI, memory : ^ VMemChunk, logger : ^ sectr.Logger )
sync_sectr_api :: proc( sectr_api : ^ sectr.ModuleAPI, memory : ^ VMemChunk, logger : ^ Logger )
{
if write_time, result := os.last_write_time_by_name( path_sectr_module );
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 )
for ; file_is_locked( Path_Sectr_Debug_Symbols ) && file_is_locked( Path_Sectr_Live_Module ); {}
thread_sleep( Millisecond * 50 )
sectr_api ^ = load_sectr_api( version_id )
verify( sectr_api.lib_version != 0, "Failed to hot-reload the sectr module" )
@ -187,30 +224,32 @@ main :: proc()
state : RuntimeState
using state
// Generating the logger's name, it will be used when the app is shutting down.
path_logger_finalized : string
{
startup_time := time.now()
year, month, day := time.date( startup_time)
hour, min, sec := time.clock_from_time( startup_time)
if ! os.is_dir( path_logs ) {
os.make_directory( path_logs )
if ! os.is_dir( Path_Logs ) {
os.make_directory( Path_Logs )
}
timestamp := fmt.tprintf("%04d-%02d-%02d_%02d-%02d-%02d", year, month, day, hour, min, sec)
path_logger_finalized = strings.clone( fmt.tprintf( "%s/sectr_%v.log", path_logs, timestamp) )
timestamp := str_fmt_tmp("%04d-%02d-%02d_%02d-%02d-%02d", year, month, day, hour, min, sec)
path_logger_finalized = str_clone( str_fmt_tmp( "%s/sectr_%v.log", Path_Logs, timestamp) )
}
logger : sectr.Logger
sectr.init( & logger, "Sectr Host", fmt.tprintf( "%s/sectr.log", path_logs ) )
context.logger = sectr.to_odin_logger( & logger )
logger_init( & logger, "Sectr Host", str_fmt_tmp( "%s/sectr.log", Path_Logs ) )
context.logger = to_odin_logger( & logger )
{
// Log System Context
backing_builder : [16 * Kilobyte] u8
builder := strings.builder_from_bytes( backing_builder[:] )
fmt.sbprintf( & builder, "Core Count: %v, ", os.processor_core_count() )
fmt.sbprintf( & builder, "Page Size: %v", os.get_page_size() )
builder := str_builder_from_bytes( backing_builder[:] )
str_fmt_builder( & builder, "Core Count: %v, ", os.processor_core_count() )
str_fmt_builder( & builder, "Page Size: %v", os.get_page_size() )
sectr.log( strings.to_string(builder) )
log( to_str(builder) )
}
// Basic Giant VMem Block
@ -232,12 +271,11 @@ main :: proc()
verify( sectr_api.lib_version != 0, "Failed to initially load the sectr module" )
}
running = true;
memory = memory
sectr_api = sectr_api
running = true;
sectr_api = sectr_api
sectr_api.startup( memory.sectr_live, memory.sectr_snapshot, & logger )
delta_ns : time.Duration
delta_ns : Duration
// TODO(Ed) : This should have an end status so that we know the reason the engine stopped.
for ; running ;
@ -247,7 +285,7 @@ main :: proc()
// Hot-Reload
sync_sectr_api( & sectr_api, & memory, & logger )
running = sectr_api.tick( time.duration_seconds( delta_ns ) )
running = sectr_api.tick( duration_seconds( delta_ns ) )
sectr_api.clean_temp()
delta_ns = time.tick_lap_time( & start_tick )
@ -263,6 +301,6 @@ main :: proc()
unload_sectr_api( & sectr_api )
log("Succesfuly closed")
os.close( logger.file )
os.rename( logger.file_path, path_logger_finalized )
file_close( logger.file )
file_rename( logger.file_path, path_logger_finalized )
}

View File

@ -23,7 +23,7 @@ align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint {
}
@(private="file")
win32_reserve :: proc "contextless" (base_address : rawptr, size: uint) -> (data: []byte, err: virtual.Allocator_Error) {
win32_reserve_with_base_address :: 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
@ -43,7 +43,7 @@ platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint, base_a
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
data := win32_reserve_with_base_address(base_address, total_to_reserved) or_return
virtual.commit(raw_data(data), to_commit)
block = (^virtual_Platform_Memory_Block)(raw_data(data))
@ -126,13 +126,10 @@ arena_init_static :: proc(arena: ^virtual.Arena, base_address : rawptr,
arena.total_reserved = arena.curr_block.reserved
return
}
// END WINDOWS CHECK WRAP
}
/* END OF: when ODIN_OS == runtime.Odin_OS_Type.Windows */ }
else
{
// Fallback to regular init_static impl for other platforms for now.
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

21
code/hot_reload.odin Normal file
View File

@ -0,0 +1,21 @@
package sectr
// TODO(Ed): Do this if we got over 25 bindings we're dealing with in persistent.
// The purpose of this is to manage anything we need to help mitigate the difficulties with hot-reloading.
// ProcAddressEntry :: struct {
// addresses : [2] rawptr
// }
// ProcAddressTracker :: struct {
// table : HMapZPL( ProcAddressEntry ),
// current :
// }
// reload_report_callback :: proc( binding : ^Type ) {
// }

View File

@ -346,15 +346,15 @@ poll_input :: proc( old, new : ^ InputState )
record_input :: proc( replay_file : os.Handle, input : ^ InputState ) {
raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) )
os.write( replay_file, raw_data )
file_write( replay_file, raw_data )
}
play_input :: proc( replay_file : os.Handle, input : ^ InputState ) {
raw_data := slice_ptr( transmute(^ byte) input, size_of(InputState) )
total_read, result_code := os.read( replay_file, raw_data )
total_read, result_code := file_read( replay_file, raw_data )
if result_code == os.ERROR_HANDLE_EOF {
rewind( replay_file )
load_snapshot( & memory.snapshot[0] )
file_rewind( replay_file )
load_snapshot( & Memory_App.snapshot[0] )
}
}

View File

@ -4,7 +4,7 @@ import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:os"
import "core:strings"
import str "core:strings"
import "core:time"
import core_log "core:log"
@ -22,15 +22,15 @@ to_odin_logger :: proc( logger : ^ Logger ) -> core_log.Logger {
return { logger_interface, logger, core_log.Level.Debug, core_log.Default_File_Logger_Opts }
}
init :: proc( logger : ^ Logger, id : string, file_path : string, file := os.INVALID_HANDLE )
logger_init :: proc( logger : ^ Logger, id : string, file_path : string, file := os.INVALID_HANDLE )
{
if file == os.INVALID_HANDLE
{
logger_file, result_code := os.open( file_path, os.O_RDWR | os.O_CREATE )
logger_file, result_code := file_open( file_path, os.O_RDWR | os.O_CREATE )
if result_code != os.ERROR_NONE {
// Log failures are fatal and must never occur at runtime (there is no logging)
runtime.debug_trap()
os. exit( -1 )
os.exit( -1 )
// TODO(Ed) : Figure out the error code enums..
}
logger.file = logger_file
@ -60,37 +60,37 @@ logger_interface :: proc(
@static builder_backing : [16 * Kilobyte] byte; {
mem.set( raw_data( builder_backing[:] ), 0, len(builder_backing) )
}
builder := strings.builder_from_bytes( builder_backing[:] )
builder := str.builder_from_bytes( builder_backing[:] )
first_line_length := len(text) > Max_Logger_Message_Width ? Max_Logger_Message_Width : len(text)
first_line := transmute(string) text[ 0 : first_line_length ]
fmt.sbprintf( & builder, "%-*s ", Max_Logger_Message_Width, first_line )
str_fmt_builder( & builder, "%-*s ", Max_Logger_Message_Width, first_line )
// Signature
{
when time.IS_SUPPORTED
{
if core_log.Full_Timestamp_Opts & options != nil {
fmt.sbprint( & builder, "[")
str_fmt_builder( & builder, "[")
t := time.now()
y, m, d := time.date(t)
h, min, s := time.clock(t)
year, month, day := time.date(t)
hour, minute, second := time.clock(t)
if .Date in options {
fmt.sbprintf( & builder, "%d-%02d-%02d ", y, m, d )
str_fmt_builder( & builder, "%d-%02d-%02d ", year, month, day )
}
if .Time in options {
fmt.sbprintf( & builder, "%02d:%02d:%02d", h, min, s)
str_fmt_builder( & builder, "%02d:%02d:%02d", hour, minute, second)
}
fmt.sbprint( & builder, "] ")
str_fmt_builder( & builder, "] ")
}
}
core_log.do_level_header( options, level, & builder )
if logger.id != "" {
fmt.sbprintf( & builder, "[%s] ", logger.id )
str_fmt_builder( & builder, "[%s] ", logger.id )
}
core_log.do_location_header( options, & builder, location )
}
@ -99,21 +99,21 @@ logger_interface :: proc(
if len(text) > Max_Logger_Message_Width
{
offset := Max_Logger_Message_Width
bytes := transmute([]u8) text
bytes := transmute( []u8 ) text
for left := len(bytes) - Max_Logger_Message_Width; left > 0; left -= Max_Logger_Message_Width
{
fmt.sbprintf( & builder, "\n" )
str_fmt_builder( & builder, "\n" )
subset_length := len(text) - offset
if subset_length > Max_Logger_Message_Width {
subset_length = Max_Logger_Message_Width
}
subset := slice_ptr( ptr_offset( raw_data(bytes), offset), subset_length )
fmt.sbprintf( & builder, "%s", transmute(string)subset )
str_fmt_builder( & builder, "%s", transmute(string) subset )
offset += Max_Logger_Message_Width
}
}
fmt.fprintln( logger.file, strings.to_string(builder) )
str_to_file_ln( logger.file, to_string(builder) )
}
log :: proc( msg : string, level := LogLevel.Info, loc := #caller_location ) {

View File

@ -17,46 +17,46 @@ ReplayState :: struct {
replay_recording_begin :: proc( path : string )
{
if file_exists( path ) {
result := os.remove( path )
result := file_remove( path )
verify( result != os.ERROR_NONE, "Failed to delete replay file before beginning a new one" )
}
replay_file, open_error := os.open( path, os.O_RDWR | os.O_CREATE )
replay_file, open_error := file_open( path, FileFlag_ReadWrite | FileFlag_Create )
verify( open_error != os.ERROR_NONE, "Failed to create or open the replay file" )
os.seek( replay_file, 0, 0 )
file_seek( replay_file, 0, 0 )
replay := & memory.replay
replay := & Memory_App.replay
replay.active_file = replay_file
replay.mode = ReplayMode.Record
}
replay_recording_end :: proc() {
replay := & memory.replay
replay := & Memory_App.replay
replay.mode = ReplayMode.Off
os.seek( replay.active_file, 0, 0 )
os.close( replay.active_file )
file_seek( replay.active_file, 0, 0 )
file_close( replay.active_file )
}
replay_playback_begin :: proc( path : string )
{
verify( ! file_exists( path ), "Failed to find replay file" )
replay_file, open_error := os.open( path, os.O_RDWR | os.O_CREATE )
replay_file, open_error := file_open( path, FileFlag_ReadWrite | FileFlag_Create )
verify( open_error != os.ERROR_NONE, "Failed to create or open the replay file" )
os.seek( replay_file, 0, 0 )
file_seek( replay_file, 0, 0 )
replay := & memory.replay
replay := & Memory_App.replay
replay.active_file = replay_file
replay.mode = ReplayMode.Playback
}
replay_playback_end :: proc() {
input := get_state().input
replay := & memory.replay
replay := & Memory_App.replay
replay.mode = ReplayMode.Off
os.seek( replay.active_file, 0, 0 )
os.close( replay.active_file )
file_seek( replay.active_file, 0, 0 )
file_close( replay.active_file )
}

View File

@ -168,7 +168,7 @@ project_save :: proc( project : ^ Project, archive : ^ ArchiveData = nil )
verify( cast(b32) os.is_dir( project.path ), "Failed to create project path for saving" )
}
os.write_entire_file( fmt.tprint( project.path, project.name, ".sectr_proj", sep = ""), archive.data )
os.write_entire_file( str_tmp_from_any( project.path, project.name, ".sectr_proj", sep = ""), archive.data )
}
project_load :: proc( path : string, project : ^ Project, archive : ^ ArchiveData = nil )

11
code/string_format.odin Normal file
View File

@ -0,0 +1,11 @@
// This provides a string generator using a token replacement approach instead of a %<id> verb-syntax to parse.
// This was done just for preference as I personally don't like the c-printf-like syntax.
package sectr
// str_format :: proc ( format : string, tokens : ..args ) {
// }

View File

@ -1,7 +1,7 @@
package sectr
import "core:math"
import "core:unicode/utf8"
import "core:unicode/utf8"
import rl "vendor:raylib"
debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
@ -11,7 +11,7 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co
if len( content ) == 0 {
return
}
runes := utf8.string_to_runes( content, context.temp_allocator )
runes := to_runes( content, context.temp_allocator )
font := font
if font == 0 {
@ -38,7 +38,7 @@ debug_draw_text_world :: proc( content : string, pos : Vec2, size : f32, color :
if len( content ) == 0 {
return
}
runes := utf8.string_to_runes( content, context.temp_allocator )
runes := to_runes( content, context.temp_allocator )
font := font
if font == 0 {

View File

@ -7,7 +7,7 @@ import rl "vendor:raylib"
render :: proc()
{
state := get_state(); using state
replay := & memory.replay
replay := & Memory_App.replay
cam := & project.workspace.cam
win_extent := state.app_window.extent
@ -21,7 +21,7 @@ render :: proc()
render_mode_2d()
//region Render Screenspace
{
fps_msg := fmt.tprint( "FPS:", rl.GetFPS() )
fps_msg := str_fmt_tmp( "FPS:", rl.GetFPS() )
fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 }
debug_draw_text( fps_msg, fps_msg_pos, 16.0, color = rl.GREEN )
@ -42,7 +42,7 @@ render :: proc()
position.x -= 200
position.y += debug.draw_debug_text_y
content := fmt.bprintf( draw_text_scratch[:], format, ..args )
content := str_fmt_buffer( draw_text_scratch[:], format, ..args )
debug_draw_text( content, position, 16.0 )
debug.draw_debug_text_y += 16
@ -50,8 +50,8 @@ render :: proc()
// Debug Text
{
// debug_text( "Screen Width : %v", rl.GetScreenWidth () )
// debug_text( "Screen Height: %v", rl.GetScreenHeight() )
debug_text( "Screen Width : %v", rl.GetScreenWidth () )
debug_text( "Screen Height: %v", rl.GetScreenHeight() )
if replay.mode == ReplayMode.Record {
debug_text( "Recording Input")
}

View File

@ -2,7 +2,6 @@ package sectr
import "base:runtime"
import "core:math"
import "core:fmt"
import rl "vendor:raylib"
@ -60,7 +59,7 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState )
update :: proc( delta_time : f64 ) -> b32
{
state := get_state(); using state
replay := & memory.replay
replay := & Memory_App.replay
if rl.IsWindowResized() {
window := & state.app_window
@ -82,7 +81,7 @@ update :: proc( delta_time : f64 ) -> b32
project_save( & project )
}
if debug_actions.load_project {
project_load( fmt.tprint( project.path, project.name, ".sectr_proj", sep = "" ), & project )
project_load( str_tmp_from_any( project.path, project.name, ".sectr_proj", sep = "" ), & project )
}
}
@ -91,7 +90,7 @@ update :: proc( delta_time : f64 ) -> b32
if debug_actions.record_replay { #partial switch replay.mode
{
case ReplayMode.Off : {
save_snapshot( & memory.snapshot[0] )
save_snapshot( & Memory_App.snapshot[0] )
replay_recording_begin( Path_Input_Replay )
}
case ReplayMode.Record : {
@ -103,21 +102,21 @@ update :: proc( delta_time : f64 ) -> b32
{
case ReplayMode.Off : {
if ! file_exists( Path_Input_Replay ) {
save_snapshot( & memory.snapshot[0] )
save_snapshot( & Memory_App.snapshot[0] )
replay_recording_begin( Path_Input_Replay )
}
else {
load_snapshot( & memory.snapshot[0] )
load_snapshot( & Memory_App.snapshot[0] )
replay_playback_begin( Path_Input_Replay )
}
}
case ReplayMode.Playback : {
replay_playback_end()
load_snapshot( & memory.snapshot[0] )
load_snapshot( & Memory_App.snapshot[0] )
}
case ReplayMode.Record : {
replay_recording_end()
load_snapshot( & memory.snapshot[0] )
load_snapshot( & Memory_App.snapshot[0] )
replay_playback_begin( Path_Input_Replay )
}
}}
@ -175,15 +174,16 @@ update :: proc( delta_time : f64 ) -> b32
//endregion
//region Imgui Tick
{
ui_context := & state.project.workspace.ui
// Creates the root box node, set its as the first parent.
ui_graph_build( & state.project.workspace.ui )
// Build Graph (Determines if layout is dirty)
ui_graph_build_begin( ui_context )
ui_style({ bg_color = Color_BG_TextBox })
ui_set_layout({ size = { 200, 200 }})
// Regnerate compute if layout is dirty.
first_flags : UI_BoxFlags = { .Mouse_Clickable, .Focusable, .Click_To_Focus }
ui_box_make( first_flags, "FIRST BOX BOIS" )
}
// endregion

View File

@ -138,30 +138,34 @@ UI_Layout :: struct {
corner_radii : [Corner.Count]f32,
// TODO(Ed) I problably won't use this as I can determine
// the size of content manually when needed and make helper procs...
// size_to_content : b32,
// TODO(Ed) : Add support for this
size_to_content : b32,
size : Vec2,
}
size : Vec2,
UI_BoxState :: enum {
Disabled,
Default,
Hovered,
Focused,
}
UI_Style :: struct {
bg_color : Color,
overlay_color : Color,
border_color : Color,
bg_color : Color,
border_color : Color,
// blur_size : f32,
blur_size : f32,
font : FontID,
font_size : f32,
text_color : Color,
text_alignment : UI_TextAlign,
// text_wrap_width_pixels : f32,
// text_wrap_width_percent : f32,
font : FontID,
font_size : f32,
text_color : Color,
text_alignment : UI_TextAlign,
// cursors : [CursorKind.Count]UI_Cursor,
// active_cursor : ^UI_Cursor,
// hover_cursor : ^ UI_Cursor,
cursor : UI_Cursor,
layout : UI_Layout,
transition_time : f32,
}
UI_TextAlign :: enum u32 {
@ -171,124 +175,239 @@ UI_TextAlign :: enum u32 {
Count
}
UI_Box :: struct {
first, last, prev, next : ^ UI_Box,
num_children : i32,
flags : UI_BoxFlags,
key : UI_Key,
label : string,
computed : UI_Computed,
layout : UI_Layout,
style : UI_Style,
// Persistent Data
UI_InteractState :: struct {
hot_time : f32,
active_time : f32,
disabled_time : f32,
}
Layout_Stack_Size :: 512
Style_Stack_Size :: 512
UI_Box :: struct {
// Cache ID
key : UI_Key,
label : string,
// Regenerated per frame.
first, last, prev, next, parent : ^ UI_Box,
num_children : i32,
flags : UI_BoxFlags,
computed : UI_Computed,
style : UI_Style,
// Persistent Data
// prev_computed : UI_Computed,
// prev_style : UI_Style,
mouse : UI_InteractState,
keyboard : UI_InteractState,
}
UI_Layout_Stack_Size :: 512
UI_Style_Stack_Size :: 512
UI_Parent_Stack_Size :: 1024
UI_Built_Boxes_Array_Size :: Kilobyte * 8
UI_FramePassKind :: enum {
Generate,
Compute,
Logical,
}
UI_State :: struct {
box_cache : HashTable( UI_Box ),
// TODO(Ed) : Use these
build_arenas : [2]Arena,
build_arena : ^ Arena,
built_box_count : i32,
caches : [2] HMapZPL( UI_Box ),
prev_cache : ^ HMapZPL( UI_Box ),
curr_cache : ^ HMapZPL( UI_Box ),
root : ^ UI_Box,
// Do we need to recompute the layout?
layout_dirty : b32,
layout_stack : Stack( UI_Layout, Layout_Stack_Size ),
style_stack : Stack( UI_Style, Style_Stack_Size ),
hot : UI_Key,
active : UI_Key,
clipboard_copy_key : UI_Key,
// TODO(Ed) : Look into using a build arena like Ryan does for these possibly (and thus have a linked-list stack)
style_stack : Stack( UI_Style, UI_Style_Stack_Size ),
parent_stack : Stack( ^ UI_Box, UI_Parent_Stack_Size ),
hot : UI_Key,
active : UI_Key,
clipboard_copy : UI_Key,
last_clicked : UI_Key,
drag_start_mouse : Vec2,
// drag_state_arena : ^ Arena,
// drag_state data : string,
}
ui_key_from_string :: proc ( value : string ) -> UI_Key {
ui_key_from_string :: proc( value : string ) -> UI_Key {
key := cast(UI_Key) crc32( transmute([]byte) value )
return key
}
ui_box_equal :: proc ( a, b : ^ UI_Box ) -> b32 {
ui_box_equal :: proc( a, b : ^ UI_Box ) -> b32 {
BoxSize :: size_of(UI_Box)
hash_a := crc32( transmute([]u8) slice_ptr( a, BoxSize ) )
hash_b := crc32( transmute([]u8) slice_ptr( b, BoxSize ) )
result : b32 = hash_a == hash_b
result : b32 = true
result &= a.key == b.key // We assume for now the label is the same as the key, if not something is terribly wrong.
result &= a.flags == b.flags
return result
}
ui_startup :: proc ( ui : ^ UI_State, cache_allocator : Allocator ) {
ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator )
{
ui := ui
ui^ = {}
box_cache, allocation_error := hashtable_init_reserve( UI_Box, cache_allocator, Kilobyte * 8 )
verify( allocation_error != AllocatorError.None, "Failed to allocate box cache" )
ui.box_cache = box_cache
for cache in (& ui.caches) {
box_cache, allocation_error := zpl_hmap_init_reserve( UI_Box, cache_allocator, UI_Built_Boxes_Array_Size )
verify( allocation_error == AllocatorError.None, "Failed to allocate box cache" )
cache = box_cache
}
ui.curr_cache = & ui.caches[1]
ui.prev_cache = & ui.caches[0]
}
ui_reload :: proc ( ui : ^ UI_State, cache_allocator : Allocator ) {
ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator )
{
// We need to repopulate Allocator references
ui.box_cache.entries.allocator = cache_allocator
ui.box_cache.hashes.allocator = cache_allocator
for cache in & ui.caches {
cache.entries.allocator = cache_allocator
cache.hashes.allocator = cache_allocator
}
}
// TODO(Ed) : Is this even needed?
ui_shutdown :: proc () {
ui_shutdown :: proc() {
}
ui_graph_build_begin :: proc ( ui : ^ UI_State )
ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
{
ui_context := & get_state().ui_context
ui_context = ui
using ui_context
get_state().ui_context = ui
using get_state().ui_context
box : UI_Box = {}
box.label = "root#001"
box.key = ui_key_from_string( box.label )
box.layout = stack_peek( & layout_stack ) ^
box.style = stack_peek( & style_stack ) ^
swap( & curr_cache, & prev_cache )
cached_box := hashtable_get( & box_cache, cast(u64) box.key )
root = ui_box_make( {}, "root#001" )
ui_parent_push(root)
if cached_box != nil {
layout_dirty &= ! ui_box_equal( & box, cached_box )
log("BUILD GRAPH BEGIN")
}
// TODO(Ed) :: Is this even needed?
ui_graph_build_end :: proc()
{
ui_parent_pop()
// Regenerate the computed layout if dirty
// ui_compute_layout()
get_state().ui_context = nil
log("BUILD GRAPH END")
}
@(deferred_none = ui_graph_build_end)
ui_graph_build :: proc( ui : ^ UI_State ) {
ui_graph_build_begin( ui )
}
ui_parent_push :: proc( ui : ^ UI_Box ) {
stack := & get_state().ui_context.parent_stack
stack_push( & get_state().ui_context.parent_stack, ui )
}
ui_parent_pop :: proc() {
// If size_to_content is set, we need to compute the layout now.
// Check to make sure that the parent's children are the same for this frame,
// if its not we need to mark the layout as dirty.
stack_pop( & get_state().ui_context.parent_stack )
}
@(deferred_none = ui_parent_pop)
ui_parent :: proc( ui : ^ UI_Box) {
ui_parent_push( ui )
}
ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
{
using get_state().ui_context
key := ui_key_from_string( label )
curr_box : (^ UI_Box)
prev_box := zpl_hmap_get( prev_cache, cast(u64) key )
{
set_result : ^ UI_Box
set_error : AllocatorError
if prev_box != nil {
// Previous history was found, copy over previous state.
set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, (prev_box ^) )
}
else {
box : UI_Box
box.key = key
box.label = label
set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, box )
}
verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" )
curr_box = set_result
}
if prev_box != nil {
layout_dirty &= ! ui_box_equal( curr_box, prev_box )
}
else {
layout_dirty = true
}
set_result, set_error: = hashtable_set( & box_cache, cast(u64) box.key, box )
verify( set_error != AllocatorError.None, "Failed to set hashtable due to allocator error" )
root = set_result
curr_box.flags = flags
curr_box.style = ( stack_peek( & style_stack ) ^ )
curr_box.parent = ( stack_peek( & parent_stack ) ^ )
// If there is a parent, setup the relevant references
if curr_box.parent != nil
{
// dbl_linked_list_push_back( box.parent, nil, box )
curr_box.parent.last = curr_box
if curr_box.parent.first == nil {
curr_box.parent.first = curr_box
}
}
return curr_box
}
ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
{
return nil
ui_set_layout :: proc ( layout : UI_Layout ) {
log("LAYOUT SET")
}
ui_layout_push :: proc ( preset : UI_Layout ) {
ui_compute_layout :: proc() {
// TODO(Ed) : This generates the bounds for each box.
}
ui_layout_pop :: proc () {
ui_layout_set_size :: proc( size : Vec2 ) {
}
ui_layout_push_size :: proc( size : Vec2 ) {
ui_style_push :: proc( preset : UI_Style ) {
log("STYLE PUSH")
stack_push( & get_state().ui_context.style_stack, preset )
}
ui_style_push :: proc ( preset : UI_Style ) {
ui_style_pop :: proc() {
log("STYLE POP")
stack_pop( & get_state().ui_context.style_stack )
}
@(deferred_none = ui_style_pop)
ui_style :: proc( preset : UI_Style ) {
ui_style_push( preset )
}

View File

@ -18,7 +18,7 @@
"path": "C:/projects/SectrPrototype/thirdparty/ini"
}
],
"odin_command": "C:/projects/SectrPrototype/thirdparty/Odin",
"odin_command": "C:/projects/SectrPrototype/thirdparty/Odin/odin.exe",
"enable_document_symbols": true,
"enable_fake_methods": true,
"enable_format": false,

View File

@ -10,14 +10,34 @@ $path_scripts = join-path $path_root 'scripts'
$path_thirdparty = join-path $path_root 'thirdparty'
$path_odin = join-path $path_thirdparty 'odin'
if ( $IsWindows ) {
$CPU_Info = Get-CimInstance ClassName Win32_Processor | Select-Object -Property NumberOfCores, NumberOfLogicalProcessors
$CoreCount_Physical, $CoreCount_Logical = $CPU_Info.NumberOfCores, $CPU_Info.NumberOfLogicalProcessors
}
# Odin Compiler Flags
$flag_build = 'build'
$flag_run = 'run'
$flag_check = 'check'
$flag_query = 'query'
$flag_report = 'report'
$command_build = 'build'
$command_check = 'check'
$command_query = 'query'
$command_report = 'report'
$command_run = 'run'
$flag_build_mode = '-build-mode:'
$flag_build_mode_dll = '-build-mode:dll'
$flag_collection = '-collection:'
$flag_debug = '-debug'
$flag_define = '-define:'
$flag_disable_assert = '-disable-assert'
$flag_extra_assembler_flags = '-extra_assembler-flags:'
$flag_extra_linker_flags = '-extra-linker-flags:'
$flag_ignore_unknown_attributes = '-ignore-unknown-attributes'
$flag_keep_temp_files = '-keep-temp-files'
$flag_no_bounds_check = '-no-bounds-check'
$flag_no_crt = '-no-crt'
$flag_no_entrypoint = '-no-entry-point'
$flag_no_thread_local = '-no-thread-local'
$flag_no_thread_checker = '-no-thread-checker'
$flag_output_path = '-out='
$flag_optimization_level = '-opt:'
$flag_optimize_none = '-o:none'
@ -25,40 +45,31 @@ $flag_optimize_minimal = '-o:minimal'
$flag_optimize_size = '-o:size'
$flag_optimize_speed = '-o:speed'
$falg_optimize_aggressive = '-o:aggressive'
$flag_pdb_name = '-pdb-name:'
$flag_sanitize = '-sanitize:'
$flag_subsystem = '-subsystem:'
$flag_show_timings = '-show-timings'
$flag_show_more_timings = '-show-more-timings'
$flag_show_system_calls = '-show-system-calls'
$flag_target = '-target:'
$flag_thread_count = '-thread-count:'
$flag_collection = '-collection:'
$flag_build_mode = '-build-mode:'
$flag_build_mode_dll = '-build-mode:dll'
$flag_no_bounds_check = '-no-bounds-check'
$flag_disable_assert = '-disable-assert'
$flag_no_thread_local = '-no-thread-local'
$flag_no_thread_checker = '-no-thread-checker'
$flag_use_lld = '-lld'
$flag_use_separate_modules = '-use-separate-modules'
$flag_vet_all = '-vet'
$flag_vet_unused_entities = '-vet-unused'
$flag_vet_semicolon = '-vet-semicolon'
$flag_vet_shadow_vars = '-vet-shadowing'
$flag_vet_using_stmt = '-vet-using-stmt'
$flag_use_separate_modules = '-use-separate-modules'
$flag_define = '-define:'
$flag_extra_assembler_flags = '-extra_assembler-flags:'
$flag_extra_linker_flags = '-extra-linker-flags:'
$flag_ignore_unknown_attributes = '-ignore-unknown-attributes'
$flag_keep_temp_files = '-keep-temp-files'
$flag_no_crt = '-no-crt'
$flag_no_entrypoint = '-no-entry-point'
$flag_pdb_name = '-pdb-name:'
$flag_sanitize = '-sanitize:'
$flag_subsystem = '-subsystem:'
$flag_target = '-target:'
$flag_use_lld = '-lld'
$flag_msvc_link_disable_dynamic_base = '/DYNAMICBASE:NO'
$flag_msvc_link_base_address = '/BASE:'
$flag_msvc_link_fixed_base_address = '/FIXED'
$msvc_link_default_base_address = 0x180000000
push-location $path_root
$update_deps = join-path $path_scripts 'update_deps.ps1'
$odin = join-path $path_odin 'odin.exe'
$update_deps = join-path $path_scripts 'update_deps.ps1'
$odin_compiler = join-path $path_odin 'odin.exe'
if ( -not( test-path 'build') ) {
new-item -ItemType Directory -Path 'build'
}
@ -68,12 +79,19 @@ push-location $path_root
push-location $path_code
$project_name = 'sectr'
write-host "`nBuilding Sectr Prototype"
write-host "`nBuilding Sectr Prototype`n"
$module_host = join-path $path_code 'host'
$module_sectr = $path_code
& $update_deps
$pkg_collection_thirdparty = 'thirdparty=' + $path_thirdparty
$host_process_active = Get-Process | Where-Object {$_.Name -like 'sectr_host*'}
if ( -not $host_process_active ) {
& $update_deps
write-host
}
function build-sectr
{
@ -86,17 +104,30 @@ push-location $path_root
$module_dll = join-path $path_build ( $project_name + '.dll' )
$pdb = join-path $path_build ( $project_name + '.pdb' )
$linker_args = ""
$linker_args += ( $flag_msvc_link_disable_dynamic_base + ' ' )
$linker_args += ( $flag_msvc_link_fixed_base_address + ' ' )
$linker_args += ( $flag_msvc_link_base_address + '0x20000000000' )
# $linker_args += ( $flag_msvc_link_base_address + '0x200000000' )
$build_args = @()
$build_args += $flag_build
$build_args += $command_build
$build_args += '.'
$build_args += $flag_build_mode_dll
$build_args += $flag_output_path + $module_dll
$build_args += ($flag_collection + $pkg_collection_thirdparty)
$build_args += $flag_use_separate_modules
$build_args += $flag_optimize_none
$build_args += $flag_debug
$build_args += $flag_pdb_name + $pdb
$build_args += ($flag_extra_linker_flags + $linker_args )
$build_args += $flag_subsystem + 'windows'
# $build_args += $flag_show_system_calls
# $build_args += $flag_show_timings
write-host 'Building Sectr Module'
& $odin $build_args
& $odin_compiler $build_args
write-host
}
build-sectr
@ -105,7 +136,6 @@ push-location $path_root
$executable = join-path $path_build ($project_name + '_host.exe')
$pdb = join-path $path_build ($project_name + '_host.pdb')
$host_process_active = Get-Process | Where-Object {$_.Name -like 'sectr_host*'}
if ( $host_process_active ) {
write-host 'Skipping sectr_host build, process is active'
return
@ -117,21 +147,31 @@ push-location $path_root
return
}
$linker_args = ""
$linker_args += ( $flag_msvc_link_disable_dynamic_base + ' ' )
$build_args = @()
$build_args += $flag_build
$build_args += $command_build
$build_args += './host'
$build_args += $flag_output_path + $executable
$build_args += ($flag_collection + $pkg_collection_thirdparty)
$build_args += $flag_use_separate_modules
$build_args += $flag_thread_count + $CoreCount_Physical
$build_args += $flag_optimize_none
$build_args += $flag_debug
$build_args += $flag_pdb_name + $pdb
$build_args += ($flag_extra_linker_flags + $linker_args )
$build_args += $flag_subsystem + 'windows'
# $build_args += $flag_show_system_call
# $build_args += $flag_show_timings
write-host 'Building Host Module'
& $odin $build_args
& $odin_compiler $build_args
write-host
}
build-host
Pop-Location
Pop-Location # path_code
}
build-prototype
pop-location
pop-location # path_root