VEFontCache : Finished porting the LRU
This commit is contained in:
parent
f99157aae5
commit
26ad2d1e49
@ -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
|
||||
}
|
||||
|
@ -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 )
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -12,3 +12,7 @@ ShaperInfo :: struct {
|
||||
font : harfbuzz.Font,
|
||||
}
|
||||
|
||||
shaper_init :: proc( ctx : ^ShaperContext )
|
||||
{
|
||||
ctx.hb_buffer = harfbuzz.buffer_create()
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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
2
thirdparty/harfbuzz
vendored
@ -1 +1 @@
|
||||
Subproject commit 6781256b374ddafc42eed265e7b8377099b36919
|
||||
Subproject commit 5112039d627bf5f6a683b7aca5bb360e53570d97
|
Loading…
Reference in New Issue
Block a user