diff --git a/code/font/VEFontCache/LRU.odin b/code/font/VEFontCache/LRU.odin index 9518bd3..8de34fe 100644 --- a/code/font/VEFontCache/LRU.odin +++ b/code/font/VEFontCache/LRU.odin @@ -4,6 +4,8 @@ package VEFontCache The choice was made to keep the LRU cache implementation as close to the original as possible. */ +import "base:runtime" + PoolListIter :: i32 PoolListValue :: u64 @@ -20,9 +22,10 @@ PoolList :: struct { back : PoolListIter, size : u32, capacity : u32, + dbg_name : string, } -pool_list_init :: proc( pool : ^PoolList, capacity : u32 ) +pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" ) { error : AllocatorError pool.items, error = make( Array( PoolListItem ), u64(capacity) ) @@ -35,6 +38,7 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32 ) pool.capacity = capacity + pool.dbg_name = dbg_name using pool for id in 0 ..< capacity { @@ -72,6 +76,9 @@ pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue ) items.data[ id ].prev = -1 items.data[ id ].next = front items.data[ id ].value = value + if pool.dbg_name != "" { + logf("pool_list: pushed %v into id %v", value, id) + } if front != -1 do items.data[ front ].prev = id if back == -1 do back = id @@ -98,6 +105,9 @@ pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter ) iter_node.prev = -1 iter_node.next = -1 + // if pool.dbg_name != "" { + // logf("pool_list: erased %v, at id %v", iter_node.value, iter) + // } iter_node.value = 0 append( & free_list, iter ) @@ -115,34 +125,35 @@ pool_list_peek_back :: proc ( pool : ^PoolList ) -> PoolListValue { } pool_list_pop_back :: proc( pool : ^PoolList ) -> PoolListValue { - using pool - if size <= 0 do return 0 - assert( back != -1 ) + if pool.size <= 0 do return 0 + assert( pool.back != -1 ) - value := items.data[ back ].value - pool_list_erase( pool, back ) + value := pool.items.data[ pool.back ].value + pool_list_erase( pool, pool.back ) return value } LRU_Link :: struct { + pad_top : u64, value : i32, ptr : PoolListIter, + pad_bottom : u64, } LRU_Cache :: struct { capacity : u32, num : u32, - table : HMapZPL(LRU_Link), + table : HMapChained(LRU_Link), key_queue : PoolList, } -LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 ) { +LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) { error : AllocatorError cache.capacity = capacity - cache.table, error = hmap_zpl_init( HMapZPL(LRU_Link), u64( hmap_closest_prime( uint(capacity))) ) + cache.table, error = make( HMapChained(LRU_Link), hmap_closest_prime( uint(capacity)) ) assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table") - pool_list_init( & cache.key_queue, capacity ) + pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name ) } LRU_free :: proc( cache : ^LRU_Cache ) @@ -152,7 +163,7 @@ LRU_free :: proc( cache : ^LRU_Cache ) LRU_reload :: proc( cache : ^LRU_Cache, allocator : Allocator ) { - hmap_zpl_reload( & cache.table, allocator ) + hmap_chained_reload( cache.table, allocator ) pool_list_reload( & cache.key_queue, allocator ) } @@ -162,9 +173,13 @@ LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) { return } -LRU_find :: proc( cache : ^LRU_Cache, key : u64 ) -> ^LRU_Link { +LRU_find :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> ^LRU_Link { hash := LRU_hash_key( key ) - link := get( & cache.table, hash ) + link := get( cache.table, hash ) + // if link == nil && must_find { + // runtime.debug_trap() + // link = get( cache.table, hash ) + // } return link } @@ -186,8 +201,8 @@ LRU_get_next_evicted :: proc( cache : ^LRU_Cache ) -> u64 return 0xFFFFFFFFFFFFFFFF } -LRU_peek :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 { - iter := LRU_find( cache, key ) +LRU_peek :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 { + iter := LRU_find( cache, key, must_find ) if iter == nil { return -1 } @@ -197,7 +212,7 @@ LRU_peek :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 { LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 { hash_key := LRU_hash_key( key ) - iter := get( & cache.table, hash_key ) + iter := get( cache.table, hash_key ) if iter != nil { LRU_refresh( cache, key ) iter.value = value @@ -209,14 +224,19 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 evict = pool_list_pop_back( & cache.key_queue ) evict_hash := LRU_hash_key( evict ) - hmap_zpl_remove( & cache.table, evict_hash ) + // if cache.table.dbg_name != "" { + // logf("%v: Evicted %v with hash: %v", cache.table.dbg_name, evict, evict_hash) + // } + hmap_chained_remove( cache.table, evict_hash ) cache.num -= 1 } pool_list_push_front( & cache.key_queue, key ) + // if cache.table.dbg_name != "" { + // logf("%v: Pushed %v with hash: %v", cache.table.dbg_name, key, hash_key ) + // } - // set( cache.table, hash_key, LRU_Link { - set( & cache.table, hash_key, LRU_Link { + set( cache.table, hash_key, LRU_Link { value = value, ptr = cache.key_queue.front }) @@ -227,6 +247,9 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) { link := LRU_find( cache, key ) + // if cache.table.dbg_name != "" { + // logf("%v: Refreshed %v", cache.table.dbg_name, key) + // } pool_list_erase( & cache.key_queue, link.ptr ) pool_list_push_front( & cache.key_queue, key ) link.ptr = cache.key_queue.front diff --git a/code/font/VEFontCache/VEFontCache.odin b/code/font/VEFontCache/VEFontCache.odin index 49416b1..167f014 100644 --- a/code/font/VEFontCache/VEFontCache.odin +++ b/code/font/VEFontCache/VEFontCache.odin @@ -15,6 +15,7 @@ Changes: */ package VEFontCache +import "base:runtime" import "core:math" import "core:mem" @@ -625,7 +626,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph next_evict_codepoint := LRU_get_next_evicted( & region.state ) assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF ) - atlas_index = LRU_peek( & region.state, next_evict_codepoint ) + atlas_index = LRU_peek( & region.state, next_evict_codepoint, must_find = true ) assert( atlas_index != -1 ) evicted := LRU_put( & region.state, lru_code, atlas_index ) @@ -763,16 +764,17 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) - if shape_cache_idx == -1 { if shape_cache.next_cache_id < i32(state.capacity) { - shape_cache_idx = shape_cache.next_cache_id - LRU_put( state, hash, shape_cache_idx ) + shape_cache_idx = shape_cache.next_cache_id shape_cache.next_cache_id += 1 + evicted := LRU_put( state, hash, shape_cache_idx ) + assert( evicted == hash ) } else { next_evict_idx := LRU_get_next_evicted( state ) assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF ) - shape_cache_idx = LRU_peek( state, next_evict_idx ) + shape_cache_idx = LRU_peek( state, next_evict_idx, must_find = true ) assert( shape_cache_idx != - 1 ) LRU_put( state, hash, shape_cache_idx ) diff --git a/code/font/VEFontCache/mappings.odin b/code/font/VEFontCache/mappings.odin index fb31d57..e1a3a92 100644 --- a/code/font/VEFontCache/mappings.odin +++ b/code/font/VEFontCache/mappings.odin @@ -8,6 +8,8 @@ import "core:mem" Kilobyte :: mem.Kilobyte +slice_ptr :: mem.slice_ptr + Arena :: mem.Arena arena_allocator :: mem.arena_allocator @@ -46,6 +48,7 @@ hmap_chained_destroy :: grime.hmap_chained_destroy hmap_chained_init :: grime.hmap_chained_init hmap_chained_get :: grime.hmap_chained_get hmap_chained_remove :: grime.hmap_chained_remove +hmap_chained_reload :: grime.hmap_chained_reload hmap_chained_set :: grime.hmap_chained_set hmap_closest_prime :: grime.hmap_closest_prime diff --git a/code/grime/hashmap_chained.odin b/code/grime/hashmap_chained.odin index c1f1835..1eafc03 100644 --- a/code/grime/hashmap_chained.odin +++ b/code/grime/hashmap_chained.odin @@ -13,8 +13,8 @@ and direct pointers are kept across the codebase instead of a key to the slot. */ package grime -import "core:mem" import "base:runtime" +import "core:mem" HTable_Minimum_Capacity :: 4 * Kilobyte @@ -26,6 +26,7 @@ HMapChainedSlot :: struct( $Type : typeid ) { } HMapChainedHeader :: struct( $ Type : typeid ) { + tracker : MemoryTracker, pool : Pool, lookup : [] ^HMapChainedSlot(Type), dbg_name : string, @@ -62,7 +63,7 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_ ) -> (table : HMapChained(Type), error : AllocatorError) { header_size := size_of(HMapChainedHeader(Type)) - size := header_size + int(lookup_capacity) * size_of( ^HMapChainedSlot(Type)) + size_of(int) + size := header_size + int(lookup_capacity) * size_of( ^HMapChainedSlot(Type)) raw_mem : rawptr raw_mem, error = alloc( size, allocator = allocator ) @@ -79,9 +80,14 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_ dbg_name = str_intern(str_fmt("%v: pool", dbg_name)).str, enable_mem_tracking = enable_mem_tracking, ) - data := transmute([^] ^HMapChainedSlot(Type)) (transmute( [^]HMapChainedHeader(Type)) table.header)[1:] + data := transmute(^^HMapChainedSlot(Type)) memory_after_header(table.header) table.lookup = slice_ptr( data, int(lookup_capacity) ) table.dbg_name = dbg_name + + if Track_Memory && enable_mem_tracking { + memtracker_init( & table.tracker, allocator, Kilobyte * 16, dbg_name ) + } + return } @@ -115,9 +121,6 @@ hmap_chained_get :: proc( using self : HMapChained($Type), key : u64) -> ^Type { // profile(#procedure) hash_index := hmap_chained_lookup_id(self, key) - // if hash_index == 565 { - // runtime.debug_trap() - // } surface_slot := lookup[hash_index] if surface_slot == nil { @@ -128,9 +131,13 @@ hmap_chained_get :: proc( using self : HMapChained($Type), key : u64) -> ^Type return & surface_slot.value } - for slot := surface_slot.next; slot != nil; slot = slot.next { + for slot := surface_slot.next; slot != nil; slot = slot.next + { if slot.occupied && slot.key == key { - return & surface_slot.value + if self.dbg_name != "" && self.tracker.entries.header != nil { + logf( "%v: Retrieved %v in lookup[%v] which shows key as %v", self.dbg_name, key, hash_index, slot.key ) + } + return & slot.value } } @@ -139,14 +146,15 @@ hmap_chained_get :: proc( using self : HMapChained($Type), key : u64) -> ^Type hmap_chained_reload :: proc( self : HMapChained($Type), allocator : Allocator ) { - pool_reload(self.pool, allocator) + // pool_reload(self.pool, allocator) } // Returns true if an slot was actually found and marked as vacant // Entries already found to be vacant will not return true hmap_chained_remove :: proc( self : HMapChained($Type), key : u64 ) -> b32 { - surface_slot := self.lookup[hmap_chained_lookup_id(self, key)] + hash_index := hmap_chained_lookup_id(self, key) + surface_slot := self.lookup[hash_index] if surface_slot == nil { return false @@ -154,13 +162,24 @@ hmap_chained_remove :: proc( self : HMapChained($Type), key : u64 ) -> b32 if surface_slot.occupied && surface_slot.key == key { surface_slot.occupied = false + surface_slot.value = {} + surface_slot.key = {} + if self.dbg_name != "" && self.tracker.entries.header != nil { + logf( "%v: Removed %v in lookup[%v]", self.dbg_name, key, hash_index ) + } return true } - for slot := surface_slot.next; slot != nil; slot = slot.next + nest_id : i32 = 1 + for slot := surface_slot.next; slot != nil; slot = slot.next { if slot.occupied && slot.key == key { slot.occupied = false + slot.value = {} + slot.key = {} + if self.dbg_name != "" && self.tracker.entries.header != nil { + logf( "%v: Removed %v in lookup[%v] nest_id: %v", self.dbg_name, key, hash_index, nest_id ) + } return true } } @@ -176,62 +195,69 @@ hmap_chained_set :: proc( self : HMapChained($Type), key : u64, value : Type ) - using self hash_index := hmap_chained_lookup_id(self, key) surface_slot := lookup[hash_index] - set_slot :: #force_inline proc( using self : HMapChained(Type), - slot : ^HMapChainedSlot(Type), - key : u64, - value : Type - ) -> (^ Type, AllocatorError ) - { - error := AllocatorError.None - if slot.next == nil { - block : []byte - block, error = pool_grab(pool, false) - next := transmute( ^HMapChainedSlot(Type)) & block[0] - next^ = {} - slot.next = next - next.prev = slot - } - slot.key = key - slot.value = value - slot.occupied = true - return & slot.value, error - } - if surface_slot == nil { - block, error := pool_grab(pool, false) - surface_slot := transmute( ^HMapChainedSlot(Type)) & block[0] + slot_size := size_of(HMapChainedSlot(Type)) + if surface_slot == nil + { + block, error := pool_grab(pool, false) + surface_slot := transmute( ^HMapChainedSlot(Type)) raw_data(block) surface_slot^ = {} + surface_slot.key = key surface_slot.value = value surface_slot.occupied = true - if error != AllocatorError.None { - ensure(error != AllocatorError.None, "Allocation failure for chained slot in hash table") - return nil, error - } + lookup[hash_index] = surface_slot - block, error = pool_grab(pool, false) - next := transmute( ^HMapChainedSlot(Type)) & block[0] - next^ = {} - surface_slot.next = next - next.prev = surface_slot + if Track_Memory && tracker.entries.header != nil { + memtracker_register_auto_name_slice( & self.tracker, block) + } return & surface_slot.value, error } if ! surface_slot.occupied { - result, error := set_slot( self, surface_slot, key, value) - return result, error + surface_slot.key = key + surface_slot.value = value + surface_slot.occupied = true + if dbg_name != "" && tracker.entries.header != nil { + logf( "%v: Set %v in lookup[%v]", self.dbg_name, key, hash_index ) + } + + return & surface_slot.value, .None } - slot : ^HMapChainedSlot(Type) = surface_slot.next - for ; slot != nil; slot = slot.next + slot : ^HMapChainedSlot(Type) = surface_slot + nest_id : i32 = 1 + for ;; slot = slot.next { - if !slot.occupied + error : AllocatorError + if slot.next == nil { - result, error := set_slot( self, slot, key, value) - return result, error + block : []byte + block, error = pool_grab(pool, false) + next := transmute( ^HMapChainedSlot(Type)) raw_data(block) + + slot.next = next + slot.next^ = {} + slot.next.prev = slot + if Track_Memory && tracker.entries.header != nil { + memtracker_register_auto_name_slice( & self.tracker, block) + } } + + if ! slot.next.occupied + { + slot.next.key = key + slot.next.value = value + slot.next.occupied = true + if dbg_name != "" && tracker.entries.header != nil { + logf( "%v: Set %v in lookup[%v] nest_id: %v", self.dbg_name, key, hash_index, nest_id ) + } + return & slot.next.value, .None + } + + nest_id += 1 } ensure(false, "Somehow got to a null slot that wasn't preemptively allocated from a previus set") return nil, AllocatorError.None diff --git a/code/grime/virtual_arena.odin b/code/grime/virtual_arena.odin index a734b8d..3539642 100644 --- a/code/grime/virtual_arena.odin +++ b/code/grime/virtual_arena.odin @@ -276,7 +276,7 @@ varena_allocator_proc :: proc( data = new_region // log( str_fmt_tmp("varena resize (new): old: %p %v new: %p %v", old_memory, old_size, (& data[0]), size)) - when Track_Memory { + if Track_Memory && arena.tracker.entries.header != nil { memtracker_register_auto_name( & arena.tracker, & data[0], & data[len(data) - 1] ) } return diff --git a/code/host/host.odin b/code/host/host.odin index 73e6480..216bf28 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -128,9 +128,9 @@ setup_memory :: proc( profiler : ^SpallProfiler ) -> ClientMemory sectr.Memory_Reserve_Persistent, sectr.Memory_Commit_Initial_Persistent, growth_policy = nil, - allow_any_resize = false, + allow_any_resize = true, dbg_name = "persistent", - enable_mem_tracking = true ) + enable_mem_tracking = false ) verify( alloc_error == .None, "Failed to allocate persistent virtual arena for the sectr module") frame, alloc_error = varena_init( diff --git a/code/sectr/engine/client_api.odin b/code/sectr/engine/client_api.odin index 64f2e79..dd02443 100644 --- a/code/sectr/engine/client_api.odin +++ b/code/sectr/engine/client_api.odin @@ -92,7 +92,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem // push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment }) alloc_error : AllocatorError - persistent_slab, alloc_error = slab_init( policy_ptr, allocator = persistent_allocator(), dbg_name = Persistent_Slab_DBG_Name, enable_mem_tracking = true ) + persistent_slab, alloc_error = slab_init( policy_ptr, allocator = persistent_allocator(), dbg_name = Persistent_Slab_DBG_Name, enable_mem_tracking = false ) verify( alloc_error == .None, "Failed to allocate the persistent slab" ) transient_slab, alloc_error = slab_init( & default_slab_policy, allocator = transient_allocator(), dbg_name = Transient_Slab_DBG_Name ) @@ -254,23 +254,23 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem if true { font_provider_startup() - // path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" }) - // font_rec_mono_semicasual_reg = font_load( path_rec_mono_semicasual_reg, 24.0, "RecMonoSemiCasual_Regular" ) + path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" }) + font_rec_mono_semicasual_reg = font_load( path_rec_mono_semicasual_reg, 24.0, "RecMonoSemiCasual_Regular" ) - // path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } ) - // font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" ) + path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } ) + font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" ) path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" } ) font_firacode = font_load( path_firacode, 24.0, "FiraCode" ) - // path_open_sans := strings.concatenate( { Path_Assets, "OpenSans-Regular.ttf" } ) - // font_open_sans = font_load( path_open_sans, 24.0, "OpenSans" ) + path_open_sans := strings.concatenate( { Path_Assets, "OpenSans-Regular.ttf" } ) + font_open_sans = font_load( path_open_sans, 24.0, "OpenSans" ) - // path_noto_sans := strings.concatenate( { Path_Assets, "NotoSans-Regular.ttf" } ) - // font_noto_sans = font_load( path_noto_sans, 24.0, "NotoSans" ) + path_noto_sans := strings.concatenate( { Path_Assets, "NotoSans-Regular.ttf" } ) + font_noto_sans = font_load( path_noto_sans, 24.0, "NotoSans" ) - // path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } ) - // font_arial_unicode_ms = font_load( path_arial_unicode_ms, 24.0, "Arial_Unicode_MS" ) + path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } ) + font_arial_unicode_ms = font_load( path_arial_unicode_ms, 24.0, "Arial_Unicode_MS" ) default_font = font_firacode log( "Default font loaded" ) diff --git a/code/sectr/engine/render.odin b/code/sectr/engine/render.odin index ea8ecfa..cd9cc9e 100644 --- a/code/sectr/engine/render.odin +++ b/code/sectr/engine/render.odin @@ -125,7 +125,7 @@ render_mode_screenspace :: proc() debug.debug_text_vis = true if debug.debug_text_vis { - fps_msg := str_fmt( "FPS: %0.2f", fps_avg) + fps_msg := str_fmt( "FPS: %d", frame) fps_msg_width := measure_text_size( fps_msg, default_font, 12.0, 0.0 ).x fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } - { 5, 5 } debug_draw_text( fps_msg, fps_msg_pos, 38.0, color = Color_Red ) @@ -133,7 +133,7 @@ render_mode_screenspace :: proc() // debug_text( "Screen Width : %v", rl.GetScreenWidth () ) // debug_text( "Screen Height: %v", rl.GetScreenHeight() ) // debug_text( "frametime_target_ms : %f ms", frametime_target_ms ) - debug_text( "frametime : %f ms", frametime_delta_ms ) + debug_text( "frametime : %d ms", frame ) // debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms ) if replay.mode == ReplayMode.Record { debug_text( "Recording Input") diff --git a/code/sectr/font/provider.odin b/code/sectr/font/provider.odin index dbee9ea..72dfcf4 100644 --- a/code/sectr/font/provider.odin +++ b/code/sectr/font/provider.odin @@ -70,10 +70,10 @@ font_provider_startup :: proc() provider_data := & state.font_provider_data; using provider_data error : AllocatorError - font_cache, error = make( HMapChained(FontDef), hmap_closest_prime(1 * Kilo), persistent_allocator() /*dbg_name = "font_cache"*/ ) + font_cache, error = make( HMapChained(FontDef), hmap_closest_prime(1 * Kilo), persistent_allocator(), dbg_name = "font_cache" ) verify( error == AllocatorError.None, "Failed to allocate font_cache" ) - ve.init( & provider_data.ve_font_cache, .STB_TrueType, allocator = persistent_slab_allocator() ) + ve.init( & provider_data.ve_font_cache, .STB_TrueType, allocator = persistent_allocator() ) log("VEFontCached initialized") ve.configure_snap( & provider_data.ve_font_cache, u32(state.app_window.extent.x * 2.0), u32(state.app_window.extent.y * 2.0) ) @@ -552,7 +552,7 @@ font_load :: proc(path_file : string, for font_size : i32 = Font_Size_Interval; font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval { - logf("Loading at size %v", font_size) + // logf("Loading at size %v", font_size) id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval) ve_id := & def.size_table[id - 1] ve_id^ = ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, 14.0 )