VEFontCache : Finished porting the LRU

This commit is contained in:
Edward R. Gonzalez 2024-06-03 01:04:24 -04:00
parent f99157aae5
commit 26ad2d1e49
8 changed files with 201 additions and 20 deletions

View File

@ -1,10 +1,10 @@
package VEFontCache
/*
The choice was made to keep the LUR cache implementation as close to the original as possible.
The choice was made to keep the LRU cache implementation as close to the original as possible.
*/
PoolListIter :: u32
PoolListIter :: i32
PoolListValue :: u64
PoolListItem :: struct {
@ -27,13 +27,15 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32 )
error : AllocatorError
pool.items, error = make( Array( PoolListItem ), u64(capacity) )
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array")
array_resize( & pool.items, u64(capacity) )
pool.free_list, error = make( Array( PoolListIter ), u64(capacity) )
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array")
array_resize( & pool.items, u64(capacity) )
pool.capacity = i32(capacity)
for id in 0 ..< capacity do pool.free_list.data[id] = id
for id in 0 ..< capacity do pool.free_list.data[id] = i32(id)
}
pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
@ -44,6 +46,60 @@ pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
assert( free_list.num == u64(capacity - size) )
id := array_back( free_list )
array_pop( free_list )
items.data[ id ].prev = -1
items.data[ id ].next = front
items.data[ id ].value = value
if front != -1 do items.data[ front ].prev = id
if back != -1 do back = id
front = id
size += 1
}
pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
{
using pool
if size <= 0 do return
assert( iter >= 0 && iter < capacity )
assert( free_list.num == u64(capacity - size) )
iter_node := & items.data[ iter ]
prev := iter_node.prev
next := iter_node.next
if iter_node.prev != -1 do items.data[ prev ].next = iter_node.next
if iter_node.next != -1 do items.data[ next ].prev = iter_node.prev
if front == iter do front = iter_node.next
if back == iter do back = iter_node.prev
iter_node.prev = -1
iter_node.next = -1
iter_node.value = 0
append( & free_list, iter )
size -= 1
if size == 0 {
back = -1
front = -1
}
}
pool_list_peek_back :: proc ( pool : ^PoolList ) -> PoolListValue {
assert( pool.back != - 1 )
value := pool.items.data[ pool.back ].value
return value
}
pool_list_pop_back :: proc( pool : ^PoolList ) -> PoolListValue {
using pool
if size <= 0 do return 0
assert( back != -1 )
value := items.data[ back ].value
pool_list_erase( pool, back )
return value
}
LRU_Link :: struct {
@ -57,13 +113,72 @@ LRU_Cache :: struct {
key_queue : PoolList,
}
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 )
{
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 ) {
error : AllocatorError
cache.capacity = i32(capacity)
cache.table, error = make( HMapChained(LRU_Link), 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 )
}
LRU_find :: proc( cache : ^LRU_Cache, value : u64 ) -> ^LRU_Link {
bytes := transmute( [8]byte ) value
key := fnv64a( bytes[:] )
link := get( cache.table, key )
return link
}
LRU_get :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 {
iter := LRU_find( cache, key )
if iter == nil {
return -1
}
LRU_refresh( cache, key )
return iter.value
}
LRU_get_next_evicted :: proc( cache : ^LRU_Cache ) -> u64
{
if cache.key_queue.size >= cache.capacity {
evict := pool_list_peek_back( & cache.key_queue )
return evict
}
return 0xFFFFFFFFFFFFFFFF
}
LRU_peek :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 {
iter := LRU_find( cache, key )
if iter == nil {
return -1
}
return iter.value
}
LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 {
iter := LRU_find( cache, key )
if iter != nil {
LRU_refresh( cache, key )
iter.value = value
return key
}
evict := key
if cache.key_queue.size >= cache.capacity {
evict = pool_list_pop_back( & cache.key_queue )
hmap_chained_remove( cache.table, evict )
}
pool_list_push_front( & cache.key_queue, key )
link := LRU_find( cache, key )
link.value = value
link.ptr = cache.key_queue.front
return evict
}
LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
link := LRU_find( cache, key )
pool_list_erase( & cache.key_queue, link.ptr )
pool_list_push_front( & cache.key_queue, key )
link.ptr = cache.key_queue.front
}

View File

@ -37,8 +37,8 @@ Vertex :: struct {
// }
ShapedText :: struct {
Glyphs : Array(Glyph),
Positions : Array(Vec2),
glyphs : Array(Glyph),
positions : Array(Vec2),
end_cursor_pos : Vec2,
}
@ -94,7 +94,6 @@ Module_Ctx :: Context
InitAtlasRegionParams :: struct {
width : u32,
height : u32,
offset : Vec2i,
}
InitAtlasParams :: struct {
@ -113,7 +112,22 @@ InitAtlasParams_Default :: InitAtlasParams {
height = 2 * Kilobyte,
glyph_padding = 1,
region_a = {
width = 32,
height = 32,
},
region_b = {
width = 32,
height = 64,
},
region_c = {
width = 64,
height = 64,
},
region_d = {
width = 128,
height = 128,
}
}
InitGlyphDrawParams :: struct {
@ -162,7 +176,7 @@ init :: proc( ctx : ^Context,
temp_path, error = make( Array(Vec2), u64(temp_path_reserve) )
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
temp_codepoint_seen, error = make( HMapChained(bool), uint(temp_codepoint_seen_reserve) )
temp_codepoint_seen, error = make( HMapChained(bool), hmap_closest_prime( uint(temp_codepoint_seen_reserve)) )
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
draw_list.vertices, error = make( Array(Vertex), 4 * Kilobyte )
@ -188,7 +202,6 @@ init :: proc( ctx : ^Context,
size.x / width,
size.y / height,
}
offset = region_params.offset
error : AllocatorError
// state.cache, error = make( HMapChained(LRU_Link), uint(capacity.x * capacity.y) )
@ -200,5 +213,42 @@ init :: proc( ctx : ^Context,
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c )
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d )
atlas.region_b.offset.y = atlas.region_a.size.y
atlas.region_c.offset.x = atlas.region_a.size.x
atlas.region_d.offset.x = atlas.width / 2
LRU_init( & shape_cache.state, shape_cache_params.capacity )
for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 {
stroage_entry := & shape_cache.storage.data[idx]
using stroage_entry
glyphs, error = make( Array(Glyph), cast(u64) shape_cache_params.reserve_length )
assert( error != .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" )
positions, error = make( Array(Vec2), cast(u64) shape_cache_params.reserve_length )
assert( error != .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" )
}
// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing!
{
using atlas
draw_list.calls, error = make( Array(DrawCall), cast(u64) glyph_draw_params.buffer_batch * 2 )
assert( error != .None, "VEFontCache.init : Failed to allocate calls for draw_list" )
draw_list.indices, error = make( Array(u32), cast(u64) glyph_draw_params.buffer_batch * 2 * 6 )
assert( error != .None, "VEFontCache.init : Failed to allocate indices array for draw_list" )
draw_list.vertices, error = make( Array(Vertex), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 )
assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" )
clear_draw_list.calls, error = make( Array(DrawCall), cast(u64) glyph_draw_params.buffer_batch * 2 )
assert( error != .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" )
clear_draw_list.indices, error = make( Array(u32), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 )
assert( error != .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" )
clear_draw_list.vertices, error = make( Array(Vertex), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 )
assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" )
}
shaper_init( & shaper_ctx )
}

View File

@ -1,5 +1,8 @@
package VEFontCache
import "core:hash"
fnv64a :: hash.fnv64a
import "core:mem"
Kilobyte :: mem.Kilobyte
@ -24,13 +27,18 @@ array_back :: grime.array_back
array_clear :: grime.array_clear
array_free :: grime.array_free
array_remove_at :: grime.array_remove_at
array_pop :: grime.array_pop
array_resize :: grime.array_resize
array_to_slice :: grime.array_to_slice
array_to_slice_cpacity :: grime.array_to_slice_capacity
array_underlying_slice :: grime.array_underlying_slice
HMapChained :: grime.HMapChained
hmap_chained_init :: grime.hmap_chained_init
hmap_chained_init :: grime.hmap_chained_init
hmap_chained_get :: grime.hmap_chained_get
hmap_chained_remove :: grime.hmap_chained_remove
hmap_closest_prime :: grime.hmap_closest_prime
// Pool :: grime.Pool
@ -64,6 +72,10 @@ delete :: proc {
array_free,
}
get :: proc {
hmap_chained_get,
}
make :: proc {
array_init,
hmap_chained_init,

View File

@ -12,3 +12,7 @@ ShaperInfo :: struct {
font : harfbuzz.Font,
}
shaper_init :: proc( ctx : ^ShaperContext )
{
ctx.hb_buffer = harfbuzz.buffer_create()
}

View File

@ -232,9 +232,9 @@ array_grow :: proc( using self : ^Array( $ Type ), min_capacity : u64 ) -> Alloc
return array_set_capacity( self, new_capacity )
}
array_pop :: proc( using self : Array( $ Type ) ) {
verify( num != 0, "Attempted to pop an array with no elements" )
num -= 1
array_pop :: proc( self : Array( $ Type ) ) {
verify( self.num != 0, "Attempted to pop an array with no elements" )
self.num -= 1
}
array_remove_at :: proc( using self : Array( $ Type ), id : u64 )

View File

@ -139,7 +139,7 @@ hmap_chained_reload :: proc( self : HMapChained($Type), allocator : Allocator )
// Entries already found to be vacant will not return true
hmap_chained_remove :: proc( self : HMapChained($Type), key : u64 ) -> b32
{
surface_slot := lookup[hmap_chained_lookup_id(self, key)]
surface_slot := self.lookup[hmap_chained_lookup_id(self, key)]
if surface_slot == nil {
return false
@ -150,7 +150,7 @@ hmap_chained_remove :: proc( self : HMapChained($Type), key : u64 ) -> b32
return true
}
for slot := surface_slot.next; slot != nil; slot.next
for slot := surface_slot.next; slot != nil; slot = slot.next
{
if slot.occupied && slot.key == key {
slot.occupied = false

View File

@ -32,7 +32,7 @@ import c "core:c/libc"
import "core:dynlib"
import "core:hash"
crc32 :: hash.crc32
crc32 :: hash.crc32
import "core:hash/xxhash"
xxh32 :: xxhash.XXH32

2
thirdparty/harfbuzz vendored

@ -1 +1 @@
Subproject commit 6781256b374ddafc42eed265e7b8377099b36919
Subproject commit 5112039d627bf5f6a683b7aca5bb360e53570d97