Converted string cache table to use HMapChained, initial impl for ui_box_traverse_next_layer_based

This commit is contained in:
Edward R. Gonzalez 2024-06-21 16:33:15 -04:00
parent a560222d5d
commit 2b83b69745
7 changed files with 122 additions and 25 deletions

View File

@ -72,6 +72,9 @@ pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
assert( free_list.num == u64(capacity - size) )
id := array_back( free_list )
if pool.dbg_name != "" {
logf("pool_list: back %v", id)
}
array_pop( free_list )
items.data[ id ].prev = -1
items.data[ id ].next = front

View File

@ -15,6 +15,7 @@ package grime
import "base:runtime"
import "core:mem"
import "core:strings"
HTable_Minimum_Capacity :: 4 * Kilobyte
@ -55,7 +56,7 @@ hmap_closest_prime :: proc( capacity : uint ) -> uint
hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_capacity : uint,
allocator := context.allocator,
pool_bucket_cap : uint = 1 * Kilo,
pool_bucket_cap : uint = 0,
pool_bucket_reserve_num : uint = 0,
pool_alignment : uint = mem.DEFAULT_ALIGNMENT,
dbg_name : string = "",
@ -69,6 +70,11 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_
raw_mem, error = alloc( size, allocator = allocator )
if error != AllocatorError.None do return
pool_bucket_cap := pool_bucket_cap
if pool_bucket_cap == 0 {
pool_bucket_cap = cast(uint) int(lookup_capacity) * size_of( HMapChainedSlot(Type))
}
table.header = cast( ^HMapChainedHeader(Type)) raw_mem
table.pool, error = pool_init(
should_zero_buckets = false,
@ -77,7 +83,7 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_
bucket_reserve_num = pool_bucket_reserve_num,
alignment = pool_alignment,
allocator = allocator,
dbg_name = str_intern(str_fmt("%v: pool", dbg_name)).str,
dbg_name = strings.clone(str_fmt_tmp("%v: pool", dbg_name), allocator = allocator),
enable_mem_tracking = enable_mem_tracking,
)
data := transmute(^^HMapChainedSlot(Type)) memory_after_header(table.header)

View File

@ -31,7 +31,7 @@ to_str_runes_pair_via_runes :: #force_inline proc ( content : []rune ) -> StrRu
StringCache :: struct {
slab : Slab,
table : HMapZPL(StrRunesPair),
table : HMapChained(StrRunesPair),
}
// This is the default string cache for the runtime module.
@ -70,25 +70,25 @@ str_cache_init :: proc( table_allocator, slabs_allocator : Allocator ) -> (cache
cache.slab, alloc_error = slab_init( & policy, allocator = slabs_allocator, dbg_name = dbg_name )
verify(alloc_error == .None, "Failed to initialize the string cache" )
cache.table, alloc_error = make( HMapZPL(StrRunesPair), 4 * Megabyte, table_allocator, dbg_name )
cache.table, alloc_error = make( HMapChained(StrRunesPair), 1 * Kilo, table_allocator, dbg_name = dbg_name )
return
}
str_cache_reload :: #force_inline proc ( cache : ^StringCache, table_allocator, slabs_allocator : Allocator ) {
slab_reload( cache.slab, table_allocator )
hmap_zpl_reload( & cache.table, slabs_allocator )
hmap_chained_reload( cache.table, slabs_allocator )
}
str_cache_set_module_ctx :: #force_inline proc "contextless" ( cache : ^StringCache ) { Module_String_Cache = cache }
str_intern_key :: #force_inline proc( content : string ) -> StringKey { return cast(StringKey) crc32( transmute([]byte) content ) }
str_intern_lookup :: #force_inline proc( key : StringKey ) -> (^StrRunesPair) { return hmap_zpl_get( & Module_String_Cache.table, transmute(u64) key ) }
str_intern_lookup :: #force_inline proc( key : StringKey ) -> (^StrRunesPair) { return hmap_chained_get( Module_String_Cache.table, transmute(u64) key ) }
str_intern :: proc( content : string ) -> StrRunesPair
{
// profile(#procedure)
cache := Module_String_Cache
key := str_intern_key(content)
result := hmap_zpl_get( & cache.table, transmute(u64) key )
result := hmap_chained_get( cache.table, transmute(u64) key )
if result != nil {
return (result ^)
}
@ -104,7 +104,7 @@ str_intern :: proc( content : string ) -> StrRunesPair
verify( alloc_error == .None, "String cache had a backing allocator error" )
// slab_validate_pools( cache.slab.backing )
result, alloc_error = hmap_zpl_set( & cache.table, transmute(u64) key, StrRunesPair { transmute(string) str_mem, runes } )
result, alloc_error = hmap_chained_set( cache.table, transmute(u64) key, StrRunesPair { transmute(string) str_mem, runes } )
verify( alloc_error == .None, "String cache had a backing allocator error" )
// slab_validate_pools( cache.slab.backing )

View File

@ -73,7 +73,7 @@ font_provider_startup :: proc()
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_allocator() )
ve.init( & provider_data.ve_font_cache, .STB_TrueType, allocator = persistent_slab_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) )

View File

@ -65,6 +65,11 @@ UI_Parent_Stack_Size :: 512
// UI_Built_Boxes_Array_Size :: 8
UI_Built_Boxes_Array_Size :: 128 * Kilobyte
UI_RenderQueueEntry :: struct {
using links : DLL_NodePN(UI_RenderQueueEntry),
box : ^UI_Box,
}
// TODO(Ed): Rename to UI_Context
UI_State :: struct {
// TODO(Ed) : Use these?
@ -77,7 +82,9 @@ UI_State :: struct {
prev_cache : ^HMapZPL( UI_Box ),
curr_cache : ^HMapZPL( UI_Box ),
render_queue : Array(UI_RenderBoxInfo),
// render_queue_builder : SubArena,
render_queue : DLL_NodeFL(UI_RenderQueueEntry),
render_list : Array(UI_RenderBoxInfo),
null_box : ^UI_Box, // This was used with the Linked list interface...
root : ^UI_Box,
@ -120,7 +127,7 @@ ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator /* , cache_rese
ui.prev_cache = (& ui.caches[0])
allocation_error : AllocatorError
ui.render_queue, allocation_error = make( Array(UI_RenderBoxInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
ui.render_list, allocation_error = make( Array(UI_RenderBoxInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
verify( allocation_error == AllocatorError.None, "Failed to allocate render queue" )
log("ui_startup completed")
@ -132,7 +139,7 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator )
for & cache in ui.caches {
hmap_zpl_reload( & cache, cache_allocator)
}
ui.render_queue.backing = cache_allocator
ui.render_list.backing = cache_allocator
}
// TODO(Ed) : Is this even needed?
@ -150,7 +157,7 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
stack_clear( & layout_combo_stack )
stack_clear( & style_combo_stack )
array_clear( render_queue )
array_clear( render_list )
curr_cache, prev_cache = swap( curr_cache, prev_cache )
@ -187,21 +194,72 @@ ui_graph_build_end :: proc( ui : ^UI_State )
}
computed.content = computed.bounds
}
for current := root.first; current != nil; current = ui_box_tranverse_next( current )
{
if ! current.computed.fresh {
ui_box_compute_layout( current )
}
// Enqueue for rendering
array_append( & ui.render_queue, UI_RenderBoxInfo {
current.computed,
current.style,
current.text,
current.layout.font_size,
current.layout.border_width,
})
}
// Render order is "layer-based" and thus needs a new traversal done
// ui.render_queue.first = new(UI_RenderQueueEntry)
// ui.render_queue.first.box = root.first
// for current := root.first.next; current != nil; current = ui_box_traverse_next_layer_based( current )
// {
// entry := new(UI_RenderQueueEntry)
// entry.box = current
// // Enqueue box to render
// // Would be best to use a linked list tied to a sub-arena
// dll_push_back( & ui.render_queue.first, entry )
// ui.render_queue.last = entry
// }
// enqueued_id : i32 = 0
// for enqueued := ui.render_queue.first; enqueued != nil; enqueued = enqueued.next
// {
// /*
// For each enqueue box:
// 1. Check to see if any overlap (traverse all Queued Entries and check to see if their bounds overlap)
// 2. If they do, the box & its children must be popped off the current ancestry layer and moved to the start of the next
// (This could problably be an iteration where we just pop and push_back until we reach the adjacent box of the same layer)
// */
// // other_id : i32 = 0
// for other := enqueued.next; other != nil; other = other.next
// {
// if intersects_range2( enqueued.box.computed.bounds, other.box.computed.bounds)
// {
// // Intersection occured, other's box is on top, it and its children must be deferred to the next layer
// next_layer_start := other.next
// for ; next_layer_start != nil; next_layer_start = next_layer_start.next
// {
// if next_layer_start.box == other.box.first do break
// }
// other.next = next_layer_start
// other.prev = next_layer_start.prev
// next_layer_start.prev = other
// enqueued.next = other.prev
// break
// }
// }
// }
// Queue should be optimized now to generate the final draw list
// for enqueued := ui.render_queue.first; enqueued != nil; enqueued = enqueued.next
// {
// using enqueued.box
// // Enqueue for rendering
// array_append( & ui.render_list, UI_RenderBoxInfo {
// computed,
// style,
// text,
// layout.font_size,
// layout.border_width,
// })
// }
}
get_state().ui_context = nil

View File

@ -124,6 +124,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hmap_zpl_get( ui_context().prev_cache, cast(u64) box.key ) }
// Traveral pritorizes immeidate children
ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
{
using state := get_state()
@ -132,14 +133,13 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
{
// Check to make sure parent is present on the screen, if its not don't bother.
is_app_ui := ui_context == & screen_ui
if intersects_range2( ui_view_bounds(), box.computed.bounds)
{
if intersects_range2( ui_view_bounds(), box.computed.bounds) {
return box.first
}
}
if box.next != nil do return box.next
// There is no more adjacent nodes
// There are no more adjacent nodes
parent := box.parent
// Attempt to find a parent with a next, otherwise we just return a parent with nil
@ -154,3 +154,33 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
// Lift back up to parent, and set it to its next.
return parent.next
}
// Traveral pritorizes traversing a "anestry layer"
ui_box_traverse_next_layer_based :: proc "contextless" ( box : ^UI_Box, skip_intersection_test := false ) -> (^UI_Box)
{
using state := get_state()
parent := box.parent
if parent != nil
{
if parent.last != box
{
if box.next != nil do return box.next
// There are no more adjacent nodes
}
}
// Either is root OR
// Parent children exhausted, this should be the last box of this ancestry
// Do children if they are available.
if box.first != nil
{
// Check to make sure parent is present on the screen, if its not don't bother.
if ! skip_intersection_test && intersects_range2( ui_view_bounds(), box.computed.bounds) {
return box.first
}
}
// We should have exhausted the list if we're on the last box of teh last parent
return nil
}

Binary file not shown.