diff --git a/code/font/VEFontCache/LRU.odin b/code/font/VEFontCache/LRU.odin index 8de34fe..cbd0012 100644 --- a/code/font/VEFontCache/LRU.odin +++ b/code/font/VEFontCache/LRU.odin @@ -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 diff --git a/code/grime/hashmap_chained.odin b/code/grime/hashmap_chained.odin index 1eafc03..3594933 100644 --- a/code/grime/hashmap_chained.odin +++ b/code/grime/hashmap_chained.odin @@ -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) diff --git a/code/grime/string_cache.odin b/code/grime/string_cache.odin index 3dfa842..406cb3b 100644 --- a/code/grime/string_cache.odin +++ b/code/grime/string_cache.odin @@ -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 ) diff --git a/code/sectr/font/provider.odin b/code/sectr/font/provider.odin index 72dfcf4..c022767 100644 --- a/code/sectr/font/provider.odin +++ b/code/sectr/font/provider.odin @@ -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) ) diff --git a/code/sectr/ui/core/base.odin b/code/sectr/ui/core/base.odin index d2c6b54..5093287 100644 --- a/code/sectr/ui/core/base.odin +++ b/code/sectr/ui/core/base.odin @@ -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 diff --git a/code/sectr/ui/core/box.odin b/code/sectr/ui/core/box.odin index b10f72d..abeeb32 100644 --- a/code/sectr/ui/core/box.odin +++ b/code/sectr/ui/core/box.odin @@ -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 +} diff --git a/docs/memory design.pur b/docs/memory design.pur index c20815e..83aea6e 100644 Binary files a/docs/memory design.pur and b/docs/memory design.pur differ