Fixed a long-standing issue with the chained hashmap (finally)
This commit is contained in:
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user