Got this thing to compile and seem operable at runtime

There was quite a few errors with the hashtable (not suprised). I need to use it more to see if it fails to work properly.

For now it should be fine enough for prototyping
This commit is contained in:
Edward R. Gonzalez 2024-02-22 23:15:29 -05:00
parent 7332644515
commit 9b3bc6fd68
7 changed files with 208 additions and 67 deletions

View File

@ -143,13 +143,8 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^
// projection = rl.CameraProjection.ORTHOGRAPHIC,
// }
frame_1.color = Color_BG_TextBox
box_set_size( & frame_1, { 100, 50 } * CM_Per_Point )
frame_2.color = Color_BG_TextBox_Green
box_set_size( & frame_2, { 60, 100 } * CM_Per_Point )
// Setup workspace UI state
ui_startup( & workspace.ui, persistent_allocator() )
}
}
}
@ -208,6 +203,9 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ L
// font_provider_data := & get_state().font_provider_data
// font_provider_data.font_cache.allocator = arena_allocator( & font_provider_data.font_arena )
// Have to reload allocators for all dynamic allocating data-structures.
ui_reload( & get_state().project.workspace.ui, persistent_allocator() )
log("Module reloaded")
}

View File

@ -115,6 +115,11 @@ State :: struct {
font_squidgy_slimes : FontID,
font_rec_mono_semicasual_reg : FontID,
default_font : FontID,
// There are two potential UI contextes for this prototype so far,
// the screen-space UI and the current workspace UI.
// This is used so that the ui api doesn't need to have the user pass the context every single time.
ui_context : UI_State,
}
get_state :: proc "contextless" () -> ^ State {

View File

@ -4,12 +4,14 @@ package sectr
import "base:builtin"
import "base:runtime"
import c "core:c/libc"
import "core:hash"
import "core:mem"
import "core:mem/virtual"
import "core:os"
import "core:path/filepath"
import c "core:c/libc"
Byte :: 1
Kilobyte :: 1024 * Byte
Megabyte :: 1024 * Kilobyte
@ -32,6 +34,7 @@ terabyte :: proc ( tb : $ integer_type ) -> integer_type {
}
copy :: builtin.copy
crc32 :: hash.crc32
Allocator :: mem.Allocator
AllocatorError :: mem.Allocator_Error
alloc :: mem.alloc
@ -53,6 +56,39 @@ get_bounds :: proc {
view_get_bounds,
}
//region Stack - Basic fixed-size stack container
Stack :: struct ( $ Type : typeid, $ Size : i32 ) {
idx : i32,
items : [ Size ] Type,
}
stack_push :: proc( stack : ^ $ StackType / Stack( $ Type, $ Size ), value : Type ) {
using stack
verify( idx < len( items ), "Attempted to push on a full stack" )
items[ idx ] = value
idx += 1
}
stack_pop :: proc( stack : ^ $ StackType / Stack( $ Type, $ Size ) ) {
using stack
verify( idx > 0, "Attempted to pop an empty stack" )
idx -= 1
}
stack_peek :: proc ( stack : ^ Stack( $ Type, $ Size ) ) -> ^ Type {
using stack
return & items[idx]
}
//endregion Stack
// TODO(Ed) : This is extremely jank, Raylib requires a 'heap' allocator with the way it works.

View File

@ -6,6 +6,7 @@ package sectr
import "core:c/libc"
import "core:mem"
import "core:slice"
Array :: struct ( $ Type : typeid ) {
allocator : Allocator,
@ -28,7 +29,7 @@ array_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( Array(Type), A
array_init_reserve :: proc( $ Type : typeid, allocator : Allocator, capacity : u64 ) -> ( Array(Type), AllocatorError )
{
raw_data, result_code = alloc( capacity * size_of(Type), allocator = allocator )
raw_data, result_code := alloc( int(capacity) * size_of(Type), allocator = allocator )
result : Array( Type)
result.data = cast( [^] Type ) raw_data
result.allocator = allocator
@ -157,6 +158,10 @@ array_fill :: proc ( array : ^ Array( $ Type ), begin, end : u64, value : Type )
return false
}
// TODO(Ed) : Bench this?
// data_slice := slice_ptr( ptr_offset( data, begin ), end - begin )
// slice.fill( data_slice, cast(int) value )
for id := begin; id < end; id += 1 {
data[ id ] = value
}
@ -172,7 +177,7 @@ array_free :: proc( array : ^ Array( $ Type ) ) {
array_grow :: proc( array : ^ Array( $ Type ), min_capacity : u64 ) -> AllocatorError
{
using array
new_capacity = grow_formula( capacity )
new_capacity := array_grow_formula( capacity )
if new_capacity < min_capacity {
new_capacity = min_capacity
@ -211,7 +216,7 @@ array_resize :: proc ( array : ^ Array( $ Type ), num : u64 ) -> AllocatorError
{
if array.capacity < num
{
grow_result := array_grow( array, capacity )
grow_result := array_grow( array, array.capacity )
if grow_result != AllocatorError.None {
return grow_result
}
@ -225,14 +230,14 @@ array_set_capacity :: proc( array : ^ Array( $ Type ), new_capacity : u64 ) -> A
{
using array
if new_capacity == capacity {
return true
return AllocatorError.None
}
if new_capacity < num {
num = new_capacity
return true
return AllocatorError.None
}
raw_data, result_code = alloc( new_capacity * size_of(Type), allocator = allocator )
raw_data, result_code := alloc( cast(int) new_capacity * size_of(Type), allocator = allocator )
data = cast( [^] Type ) raw_data
capacity = new_capacity
return result_code

View File

@ -3,6 +3,9 @@
// with hot-reloads...
package sectr
import "core:slice"
// Note(Ed) : See core:hash for hasing procs.
// This might be problematic...
@ -17,11 +20,11 @@ HT_FindResult :: struct {
HashTable_Entry :: struct ( $ Type : typeid) {
key : u64,
next : u64,
next : i64,
value : Type,
}
HashTable :: struct ( $ Type : typeid) {
HashTable :: struct ( $ Type : typeid ) {
hashes : Array( i64 ),
entries : Array( HashTable_Entry(Type) ),
}
@ -30,7 +33,7 @@ hashtable_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HashTable(
return hashtable_init_reserve( Type, allocator )
}
hashtable_init_reserve :: proc ( $ Type : typeid, allcoator : Allocator, num : u64 ) -> ( HashTable( Type), AllocatorError )
hashtable_init_reserve :: proc ( $ Type : typeid, allocator : Allocator, num : u64 ) -> ( HashTable( Type), AllocatorError )
{
result : HashTable(Type)
hashes_result, entries_result : AllocatorError
@ -40,8 +43,11 @@ hashtable_init_reserve :: proc ( $ Type : typeid, allcoator : Allocator, num : u
ensure( false, "Failed to allocate hashes array" )
return result, hashes_result
}
array_resize( & result.hashes, num )
slice.fill( slice_ptr( result.hashes.data, cast(int) result.hashes.num), -1 )
// array_fill( result.hashes, 0, num - 1, -1 )
result.entries, entries_result = array_init_reserve( allocator, num )
result.entries, entries_result = array_init_reserve( HashTable_Entry(Type), allocator, num )
if entries_result != AllocatorError.None {
ensure( false, "Failed to allocate entries array" )
return result, entries_result
@ -59,10 +65,10 @@ hashtable_clear :: proc( ht : ^ HashTable( $ Type ) ) {
array_clear( entries )
}
hashtable_destroy :: proc( ht : ^ HashTable( $ Type ) ) {
if hashes.data && hashes.capacity {
array_free( hashes )
array_free( entries )
hashtable_destroy :: proc( using ht : ^ HashTable( $ Type ) ) {
if hashes.data != nil && hashes.capacity > 0 {
array_free( & hashes )
array_free( & entries )
}
}
@ -70,9 +76,9 @@ hashtable_get :: proc( ht : ^ HashTable( $ Type ), key : u64 ) -> ^ Type
{
using ht
id := hashtable_find( key ).entry_index
id := hashtable_find( ht, key ).entry_index
if id >= 0 {
return & entries[id].value
return & entries.data[id].value
}
return nil
@ -95,11 +101,12 @@ hashtable_map_mut :: proc( ht : ^ HashTable( $ Type), map_proc : HT_MapMutProc )
}
hashtable_grow :: proc( ht : ^ HashTable( $ Type ) ) -> AllocatorError {
using ht
new_num := array_grow_formula( entries.num )
return rehash( ht, new_num )
return hashtable_rehash( ht, new_num )
}
hashtable_rehash :: proc ( ht : ^ HashTable( $ Type ), new_num : i64 ) -> AllocatorError
hashtable_rehash :: proc ( ht : ^ HashTable( $ Type ), new_num : u64 ) -> AllocatorError
{
last_added_index : i64
@ -109,30 +116,27 @@ hashtable_rehash :: proc ( ht : ^ HashTable( $ Type ), new_num : i64 ) -> Alloca
return init_result
}
for id := 0; id < new_ht.hashes.num; id += 1 {
new_ht.hashes[id] = -1
}
// for id : u64 = 0; id < new_ht.hashes.num; id += 1 {
// new_ht.hashes.data[id] = -1
// }
slice.fill( slice_ptr( new_ht.hashes.data, cast(int) new_ht.hashes.num ), -1 )
for id := 0; id < ht.entries.num; id += 1 {
for id : u64 = 0; id < ht.entries.num; id += 1 {
find_result : HT_FindResult
if new_ht.hashes.num == 0 {
hashtable_grow( new_ht )
}
entry = & entries[id]
entry := & ht.entries.data[id]
find_result = hashtable_find( & new_ht, entry.key )
last_added_index = hashtable_add_entry( & new_ht, entry.key )
if find_result.prev_index < 0 {
new_ht.hashes[ find_result.hash_index ] = last_added_index
new_ht.hashes.data[ find_result.hash_index ] = last_added_index
}
else {
new_ht.hashes[ find_result.prev_index ].next = last_added_index
new_ht.entries.data[ find_result.prev_index ].next = last_added_index
}
new_ht.entries[ last_added_index ].next = find_result.entry_index
new_ht.entries[ last_added_index ].value = entry.value
new_ht.entries.data[ last_added_index ].next = find_result.entry_index
new_ht.entries.data[ last_added_index ].value = entry.value
}
hashtable_destroy( ht )
@ -177,22 +181,22 @@ hashtable_remove_entry :: proc( ht : ^ HashTable( $ Type ), id : i64 ) {
array_remove_at( & ht.entries, id )
}
hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> AllocatorError
hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
{
using ht
id := 0
id : i64 = 0
find_result : HT_FindResult
if hashes.num == 0
if hashtable_full( ht )
{
grow_result := hashtable_grow( ht )
grow_result := hashtable_grow(ht)
if grow_result != AllocatorError.None {
return grow_result
return nil, grow_result
}
}
find_result = hashtable_find( key )
find_result = hashtable_find( ht, key )
if find_result.entry_index >= 0 {
id = find_result.entry_index
}
@ -200,26 +204,26 @@ hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> A
{
id = hashtable_add_entry( ht, key )
if find_result.prev_index >= 0 {
entries[ find_result.prev_index ].next = id
entries.data[ find_result.prev_index ].next = id
}
else {
hashes[ find_result.hash_index ] = id
hashes.data[ find_result.hash_index ] = id
}
}
entries[id].value = value
entries.data[id].value = value
if hashtable_full( ht ) {
return hashtable_grow( ht )
return & entries.data[id].value, hashtable_grow( ht )
}
return AllocatorError.None
return & entries.data[id].value, AllocatorError.None
}
hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
using ht
for id := 0; id < hashes.num; id += 1 {
if hashes[id] == key {
for id : i64 = 0; id < hashes.num; id += 1 {
if hashes.data[id] == key {
return id
}
}
@ -228,33 +232,34 @@ hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
hashtable_add_entry :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
using ht
entry : HashTable_Entry = { key, -1 }
id := entries.num
array_append( entries, entry )
entry : HashTable_Entry(Type) = { key, -1, {} }
id := cast(i64) entries.num
array_append( & entries, entry )
return id
}
hashtable_find :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> HT_FindResult
{
using ht
find_result : HT_FindResult = { -1, -1, -1 }
result : HT_FindResult = { -1, -1, -1 }
if hashes.num > 0 {
result.hash_index = key % hash.num
result.entry_index = hashes[ result.hash_index ]
result.hash_index = cast(i64)( key % hashes.num )
result.entry_index = hashes.data[ result.hash_index ]
for ; result.entry_index >= 0; {
if entries[ result.entry_index ].key == key {
for ; result.entry_index >= 0; {
if entries.data[ result.entry_index ].key == key {
break
}
result.prev_index = result.entry_index
result.entry_index = entries[ result.entry_index ].next
result.entry_index = entries.data[ result.entry_index ].next
}
}
return result
}
hashtable_full :: proc( ht : ^ HashTable( $ Type) ) -> b32 {
return 0.75 * hashes.num < entries.num
hashtable_full :: proc( using ht : ^ HashTable( $ Type) ) -> b32 {
result : b32 = entries.num > u64(0.75 * cast(f64) hashes.num)
return result
}

View File

@ -176,8 +176,14 @@ update :: proc( delta_time : f64 ) -> b32
//region Imgui Tick
{
// Layout
ui_context := & state.project.workspace.ui
// Build Graph (Determines if layout is dirty)
ui_graph_build_begin( ui_context )
// Regnerate compute if layout is dirty.
}
// endregion

View File

@ -138,7 +138,11 @@ UI_Layout :: struct {
corner_radii : [Corner.Count]f32,
size : UI_ScalarConstraint,
// TODO(Ed) I problably won't use this as I can determine
// the size of content manually when needed and make helper procs...
// size_to_content : b32,
size : Vec2,
}
UI_Style :: struct {
@ -187,11 +191,17 @@ UI_Box :: struct {
disabled_time : f32,
}
Layout_Stack_Size :: 512
Style_Stack_Size :: 512
UI_State :: struct {
box_cache : HashTable( UI_Box ),
box_tree_dirty : b32,
root : ^ UI_Box,
root : ^ UI_Box,
layout_dirty : b32,
layout_stack : Stack( UI_Layout, Layout_Stack_Size ),
style_stack : Stack( UI_Style, Style_Stack_Size ),
hot : UI_Key,
active : UI_Key,
@ -202,7 +212,83 @@ UI_State :: struct {
// drag_state data : string,
}
ui_key_from_string :: proc ( value : string ) -> UI_Key {
key := cast(UI_Key) crc32( transmute([]byte) value )
return key
}
ui_box_equal :: proc ( a, b : ^ UI_Box ) -> b32 {
BoxSize :: size_of(UI_Box)
hash_a := crc32( transmute([]u8) slice_ptr( a, BoxSize ) )
hash_b := crc32( transmute([]u8) slice_ptr( b, BoxSize ) )
result : b32 = hash_a == hash_b
return result
}
ui_startup :: proc ( ui : ^ UI_State, cache_allocator : Allocator ) {
ui := ui
ui^ = {}
box_cache, allocation_error := hashtable_init_reserve( UI_Box, cache_allocator, Kilobyte * 8 )
verify( allocation_error != AllocatorError.None, "Failed to allocate box cache" )
ui.box_cache = box_cache
}
ui_reload :: proc ( ui : ^ UI_State, cache_allocator : Allocator ) {
// We need to repopulate Allocator references
ui.box_cache.entries.allocator = cache_allocator
ui.box_cache.hashes.allocator = cache_allocator
}
// TODO(Ed) : Is this even needed?
ui_shutdown :: proc () {
}
ui_graph_build_begin :: proc ( ui : ^ UI_State )
{
ui_context := & get_state().ui_context
ui_context = ui
using ui_context
box : UI_Box = {}
box.label = "root#001"
box.key = ui_key_from_string( box.label )
box.layout = stack_peek( & layout_stack ) ^
box.style = stack_peek( & style_stack ) ^
cached_box := hashtable_get( & box_cache, cast(u64) box.key )
if cached_box != nil {
layout_dirty &= ! ui_box_equal( & box, cached_box )
}
else {
layout_dirty = true
}
set_result, set_error: = hashtable_set( & box_cache, cast(u64) box.key, box )
verify( set_error != AllocatorError.None, "Failed to set hashtable due to allocator error" )
root = set_result
}
ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
{
return nil
}
ui_layout_push :: proc ( preset : UI_Layout ) {
}
ui_layout_pop :: proc () {
}
ui_layout_push_size :: proc( size : Vec2 ) {
}
ui_style_push :: proc ( preset : UI_Style ) {
}