From 714d60ee347fc4c9b3b49c82d7ee249ccbfcf99a Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 20 Mar 2024 13:34:47 -0400 Subject: [PATCH] Trying to figure out this bug with pools... --- code/grime.odin | 4 +++ code/grime_array.odin | 2 +- code/grime_pool_allocator.odin | 60 ++++++++++++++++++++++++-------- code/grime_slab_allocator.odin | 29 +++++++++++---- code/grime_string_interning.odin | 8 +++-- code/math.odin | 10 ++++++ code/parser_whitespace.odin | 6 ++-- code/ui.odin | 2 +- code/ui_layout.odin | 11 +++--- toolchain/Odin | 2 +- 10 files changed, 101 insertions(+), 33 deletions(-) diff --git a/code/grime.odin b/code/grime.odin index 26eed5d..3b2a35b 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -165,6 +165,10 @@ released :: proc { btn_released, } +sub :: proc { + sub_range2, +} + to_rl_rect :: proc { range2_to_rl_rect, } diff --git a/code/grime_array.odin b/code/grime_array.odin index 4039ae1..d9233f4 100644 --- a/code/grime_array.odin +++ b/code/grime_array.odin @@ -60,7 +60,7 @@ array_init_reserve :: proc raw_mem : rawptr raw_mem, alloc_error = alloc( array_size, allocator = allocator ) - log( str_fmt_tmp("array reserved: %d", header_size + int(capacity) * size_of(Type) )) + // log( str_fmt_tmp("array reserved: %d", header_size + int(capacity) * size_of(Type) )) if alloc_error != AllocatorError.None do return result.header = cast( ^ArrayHeader(Type)) raw_mem; diff --git a/code/grime_pool_allocator.odin b/code/grime_pool_allocator.odin index fb88609..07d3558 100644 --- a/code/grime_pool_allocator.odin +++ b/code/grime_pool_allocator.odin @@ -13,6 +13,8 @@ The pool doesn't allocate any buckets on initialization unless the user specifes */ package sectr +import "base:intrinsics" +import "base:runtime" import "core:mem" import "core:slice" @@ -29,13 +31,13 @@ PoolHeader :: struct { free_list_head : ^Pool_FreeBlock, current_bucket : ^PoolBucket, - bucket_list : DLL_NodeFL( PoolBucket) + bucket_list : DLL_NodeFL( PoolBucket), } PoolBucket :: struct { using nodes : DLL_NodePN( PoolBucket), next_block : uint, - blocks : [^]byte + blocks : [^]byte, } Pool_FreeBlock :: struct { @@ -88,21 +90,23 @@ pool_destroy :: proc ( using self : Pool ) free( self.header, backing ) } -pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> AllocatorError +pool_allocate_buckets :: proc( pool : Pool, num_buckets : uint ) -> AllocatorError { profile(#procedure) - pool := self if num_buckets == 0 { return .Invalid_Argument } - header_size := cast(uint) align_forward_int( size_of(PoolBucket), int(alignment)) - bucket_size := header_size + bucket_capacity + header_size := cast(uint) align_forward_int( size_of(PoolBucket), int(pool.alignment)) + bucket_size := header_size + pool.bucket_capacity to_allocate := cast(int) (bucket_size * num_buckets) - bucket_memory, alloc_error := alloc_bytes_non_zeroed( to_allocate, int(alignment), backing ) + pool_validate( pool ) + bucket_memory, alloc_error := alloc_bytes_non_zeroed( to_allocate, int(pool.alignment), pool.backing ) + pool_validate( pool ) if alloc_error != .None { return alloc_error } + verify( bucket_memory != nil, "Bucket memory is null") next_bucket_ptr := cast( [^]byte) raw_data(bucket_memory) for index in 0 ..< num_buckets @@ -110,15 +114,22 @@ pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> Alloca bucket := cast( ^PoolBucket) next_bucket_ptr bucket.blocks = memory_after_header(bucket) bucket.next_block = 0 - log( str_fmt_tmp("Pool (%d) allocated bucket: %p capacity: %d", self.block_size, raw_data(bucket_memory), bucket_capacity / self.block_size)) + log( str_fmt_tmp("Pool (%d) allocated bucket: %p capacity: %d", + pool.block_size, + raw_data(bucket_memory), + pool.bucket_capacity / pool.block_size + )) + if bucket == cast(rawptr) uintptr(0x100017740D0) { + runtime.debug_trap() + } - if self.bucket_list.first == nil { - self.bucket_list.first = bucket - self.bucket_list.last = bucket + if pool.bucket_list.first == nil { + pool.bucket_list.first = bucket + pool.bucket_list.last = bucket } else { - dll_push_back( & self.bucket_list.last, bucket ) + dll_push_back( & pool.bucket_list.last, bucket ) } // log( str_fmt_tmp("Bucket List First: %p", self.bucket_list.first)) @@ -130,6 +141,10 @@ pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> Alloca pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, alloc_error : AllocatorError ) { pool := pool + if pool.current_bucket != nil { + verify( pool.current_bucket.blocks != nil, str_fmt_tmp("current_bucket was wiped %p", pool.current_bucket) ) + } + // profile(#procedure) alloc_error = .None @@ -150,6 +165,7 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo if zero_memory { slice.zero(block) } + verify(false, "WE SHOULD NEVER BE HERE") return } @@ -172,8 +188,8 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo next := uintptr(pool.current_bucket.blocks) + uintptr(pool.current_bucket.next_block) end := uintptr(pool.current_bucket.blocks) + uintptr(pool.bucket_capacity) - blocks_left := end - next - if blocks_left == 0 + blocks_left, overflow_signal := intrinsics.overflow_sub( end, next ) + if blocks_left == 0 || overflow_signal { // Compiler Bug // if current_bucket.next != nil { @@ -181,6 +197,7 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo // current_bucket = current_bucket.next // log( str_fmt_tmp("Bucket %p exhausted using %p", pool.current_bucket, pool.current_bucket.next)) pool.current_bucket = pool.current_bucket.next + verify( pool.current_bucket.blocks != nil, "Next's blocks are null?" ) } else { @@ -191,9 +208,12 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo return } pool.current_bucket = pool.current_bucket.next + verify( pool.current_bucket.blocks != nil, "Next's blocks are null (Post new bucket alloc)?" ) } } + verify( pool.current_bucket != nil, "Attempted to grab a block from a null bucket reference" ) + // Compiler Bug // block = slice_ptr( current_bucket.blocks[ current_bucket.next_block:], int(block_size) ) // self.current_bucket.next_block += block_size @@ -246,6 +266,18 @@ pool_reset :: proc( using pool : Pool ) pool.current_bucket = bucket_list.first } +pool_validate :: proc( pool : Pool ) +{ + pool := pool + // Make sure all buckets don't show any indication of corruption + bucket : ^PoolBucket = pool.bucket_list.first + // Compiler bug ^^ same as pool_reset + for ; bucket != nil; bucket = bucket.next + { + verify( bucket.blocks != nil, "Found corrupted bucket" ) + } +} + pool_validate_ownership :: proc( using self : Pool, block : [] byte ) -> b32 { profile(#procedure) diff --git a/code/grime_slab_allocator.odin b/code/grime_slab_allocator.odin index fb89365..cd28bb0 100644 --- a/code/grime_slab_allocator.odin +++ b/code/grime_slab_allocator.odin @@ -106,7 +106,7 @@ slab_destroy :: proc( using self : Slab ) free( self.header, backing ) } -slab_alloc :: proc( using self : Slab, +slab_alloc :: proc( self : Slab, size : uint, alignment : uint, zero_memory := true, @@ -116,24 +116,26 @@ slab_alloc :: proc( using self : Slab, // profile(#procedure) pool : Pool id : u32 = 0 - for ; id < pools.idx; id += 1 { - pool = pools.items[id] + for ; id < self.pools.idx; id += 1 { + pool = self.pools.items[id] if pool.block_size >= size && pool.alignment >= alignment { break } } - verify( id < pools.idx, "There is not a size class in the slab's policy to satisfy the requested allocation" ) + verify( id < self.pools.idx, "There is not a size class in the slab's policy to satisfy the requested allocation" ) verify( pool.header != nil, "Requested alloc not supported by the slab allocator", location = loc ) block : []byte + slab_validate_pools( self ) block, alloc_error = pool_grab(pool) - if alloc_error != .None { + slab_validate_pools( self ) + if block == nil || alloc_error != .None { ensure(false, "Bad block from pool") return nil, alloc_error } - // log( str_fmt_tmp("%v: Retrieved block: %p %d", dbg_name, raw_data(block), len(block) )) + log( str_fmt_tmp("%v: Retrieved block: %p %d", self.dbg_name, raw_data(block), len(block) )) data = byte_slice(raw_data(block), size) if zero_memory { @@ -196,7 +198,9 @@ slab_resize :: proc( using self : Slab, if zero_memory && new_size > old_size { to_zero := byte_slice( new_data_ptr, int(new_size - old_size) ) + slab_validate_pools( self ) slice.zero( to_zero ) + slab_validate_pools( self ) log( str_fmt_tmp("Zeroed memory - Range(%p to %p)", new_data_ptr, cast(rawptr) (uintptr(new_data_ptr) + uintptr(new_size - old_size)))) } return @@ -204,7 +208,9 @@ slab_resize :: proc( using self : Slab, // We'll need to provide an entirely new block, so the data will need to be copied over. new_block : []byte + slab_validate_pools( self ) new_block, alloc_error = pool_grab( pool_resize ) + slab_validate_pools( self ) if new_block == nil { ensure(false, "Retreived a null block") return @@ -223,7 +229,7 @@ slab_resize :: proc( using self : Slab, pool_release( pool_old, data ) } - new_data = byte_slice( raw_data(new_block), int(old_size) ) + new_data = new_block[ :new_size] return } @@ -235,6 +241,15 @@ slab_reset :: proc( slab : Slab ) } } +slab_validate_pools :: proc( slab : Slab ) +{ + slab := slab + for id in 0 ..< slab.pools.idx { + pool := slab.pools.items[id] + pool_validate( pool ) + } +} + slab_allocator_proc :: proc( allocator_data : rawptr, mode : AllocatorMode, diff --git a/code/grime_string_interning.odin b/code/grime_string_interning.odin index ef593db..495142f 100644 --- a/code/grime_string_interning.odin +++ b/code/grime_string_interning.odin @@ -58,7 +58,7 @@ str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) { cache.slab, alloc_error = slab_init( & policy, allocator = persistent_allocator(), dbg_name = dbg_name ) verify(alloc_error == .None, "Failed to initialize the string cache" ) - cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, persistent_slab_allocator(), 64 * Kilobyte ) + cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, persistent_slab_allocator(), 8 ) return } @@ -69,7 +69,7 @@ str_intern :: proc( ) -> StringCached { // profile(#procedure) - cache := get_state().string_cache + cache := & get_state().string_cache key := u64( crc32( transmute([]byte) content )) result := zpl_hmap_get( & cache.table, key ) @@ -92,9 +92,13 @@ str_intern :: proc( runes, alloc_error = to_runes( content, slab_allocator(cache.slab) ) verify( alloc_error == .None, "String cache had a backing allocator error" ) + slab_validate_pools( get_state().persistent_slab ) + // result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) byte_slice(str_mem, length), runes } ) result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) str_mem, runes } ) verify( alloc_error == .None, "String cache had a backing allocator error" ) + + slab_validate_pools( get_state().persistent_slab ) } // profile_end() diff --git a/code/math.odin b/code/math.odin index 835e3c4..4d09566 100644 --- a/code/math.odin +++ b/code/math.odin @@ -45,6 +45,10 @@ Range2 :: struct #raw_union{ x0, y0 : f32, x1, y1 : f32, }, + + // TODO(Ed) : Test these + array : [4]f32, + mat : matrix[2, 2] f32, } range2 :: #force_inline proc "contextless" ( a, b : Vec2 ) -> Range2 { @@ -60,6 +64,12 @@ add_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> Range2 { return result } +sub_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> Range2 { + // result := Range2 { array = a.array - b.array } + result := Range2 { mat = a.mat - b.mat } + return result +} + equal_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> b32 { result := a.p0 == b.p0 && a.p1 == b.p1 return b32(result) diff --git a/code/parser_whitespace.odin b/code/parser_whitespace.odin index 0b9470f..e031338 100644 --- a/code/parser_whitespace.odin +++ b/code/parser_whitespace.odin @@ -89,9 +89,9 @@ PWS_ParseError :: struct { } PWS_ParseError_Max :: 32 -PWS_TokenArray_ReserveSize :: Kilobyte * 64 -PWS_NodeArray_ReserveSize :: Kilobyte * 64 -PWS_LineArray_ReserveSize :: Kilobyte * 64 +PWS_TokenArray_ReserveSize :: 8 +PWS_NodeArray_ReserveSize :: 8 +PWS_LineArray_ReserveSize :: 8 // TODO(Ed) : The ast arrays should be handled by a slab allocator dedicated to PWS_ASTs // This can grow in undeterministic ways, persistent will get very polluted otherwise. diff --git a/code/ui.odin b/code/ui.odin index cd4b82b..09ba704 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -274,7 +274,7 @@ UI_Box :: struct { UI_Layout_Stack_Size :: 512 UI_Style_Stack_Size :: 512 UI_Parent_Stack_Size :: 512 -UI_Built_Boxes_Array_Size :: 512 +UI_Built_Boxes_Array_Size :: 8 * Kilobyte UI_State :: struct { // TODO(Ed) : Use these diff --git a/code/ui_layout.odin b/code/ui_layout.odin index 737d71f..ae5c8ea 100644 --- a/code/ui_layout.odin +++ b/code/ui_layout.odin @@ -76,7 +76,7 @@ ui_compute_layout :: proc() parent_content.min + parent_content_size * anchor.min, parent_content.max - parent_content_size * anchor.max, ) - anchored_bounds_origin := (anchored_bounds.min + anchored_bounds.max) * 0.5 + // anchored_bounds_origin := (anchored_bounds.min + anchored_bounds.max) * 0.5 // 2. Apply Margins margins := range2( @@ -106,7 +106,12 @@ ui_compute_layout :: proc() } text_size : Vec2 - text_size = cast(Vec2) measure_text_size( current.text.str, style.font, style.font_size, 0 ) + if style.font_size == computed.text_size.y { + text_size = computed.text_size + } + else { + text_size = cast(Vec2) measure_text_size( current.text.str, style.font, style.font_size, 0 ) + } if size_to_text { adjusted_size = text_size @@ -158,8 +163,6 @@ ui_compute_layout :: proc() computed.padding = padding_bounds computed.content = content_bounds - // TODO(Ed): Needs a rework based on changes to rest of layout above being changed - // Text if len(current.text.str) > 0 { content_size := content_bounds.max - content_bounds.min diff --git a/toolchain/Odin b/toolchain/Odin index d9e318a..fe500e2 160000 --- a/toolchain/Odin +++ b/toolchain/Odin @@ -1 +1 @@ -Subproject commit d9e318a22076638f91aa6c168c8c7f5f47f2ed6f +Subproject commit fe500e29040ef741cd507c4ce250d4183fa51852