diff --git a/code/font/vefontcache/LRU.odin b/code/font/vefontcache/LRU.odin index dab054e..a11a8f7 100644 --- a/code/font/vefontcache/LRU.odin +++ b/code/font/vefontcache/LRU.odin @@ -10,6 +10,8 @@ package vefontcache There was an attempt at an optimization pass but the directives done here (other than force_inline) are marginal changes at best. + + TODO(Ed): Odin's map rehashes integer values. Maybe bring in a custom KeyTable? */ // 16-bit hashing was attempted, however it seems to get collisions with djb8_hash_16 diff --git a/code/font/vefontcache/kt1cx.odin b/code/font/vefontcache/kt1cx.odin new file mode 100644 index 0000000..881081d --- /dev/null +++ b/code/font/vefontcache/kt1cx.odin @@ -0,0 +1,3 @@ +package vefontcache + +// TODO(Ed): Bring in KT1CX and see how it performs. diff --git a/code/font/vefontcache/vefontcache.odin b/code/font/vefontcache/vefontcache.odin index 5abc9fc..95a15b0 100644 --- a/code/font/vefontcache/vefontcache.odin +++ b/code/font/vefontcache/vefontcache.odin @@ -3,6 +3,8 @@ See: https://github.com/Ed94/VEFontCache-Odin */ package vefontcache +// TODO(Ed): Provide a way to give the user a memory footprint query for all dyanmic allocations. + // See: mappings.odin for profiling hookup DISABLE_PROFILING :: true ENABLE_OVERSIZED_GLYPHS :: true diff --git a/code/grime/hashing.odin b/code/grime/hashing.odin index 715bcb8..03f4d2e 100644 --- a/code/grime/hashing.odin +++ b/code/grime/hashing.odin @@ -4,6 +4,6 @@ djb8_hash_32 :: #force_inline proc "contextless" ( hash : ^u32, bytes : []byte ) for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u32(value) } -djb8_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { +hash64_djb8 :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u64(value) } diff --git a/code/grime/hashmap_kt1l.odin b/code/grime/hashmap_kt1l.odin index 83926e2..ea4a83c 100644 --- a/code/grime/hashmap_kt1l.odin +++ b/code/grime/hashmap_kt1l.odin @@ -1,48 +1,72 @@ package grime -when (false) { +// Key Table 1-Layer Linear (KT1L) -KT1L_Slot :: struct($Type: typeid) { - key: u64, - value: Type, +KT1L_Use_TypeErased :: true + +KT1L_Slot :: struct($KeyType, $ValueType: typeid) { + key: KeyType, + value: ValueType, } KT1L_Meta :: struct { slot_size: uintptr, kt_value_offset: uintptr, type_width: uintptr, - type_name: string, + type: typeid, } -kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: Allocator = context.allocator, values: []byte, num_values: int, m: KT1L_Meta) { +kt1l_64_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: Allocator, values: []byte, num_values: int, m: KT1L_Meta) { assert(kt != nil) if num_values == 0 { return } table_size_bytes := num_values * int(m.slot_size) - err : AllocatorError - kt^, err = alloc_bytes(table_size_bytes, allocator = backing) + alloc_error: AllocatorError + kt^, alloc_error = alloc_bytes(table_size_bytes, allocator = backing) + assert(alloc_error == .None) slice_assert(kt ^) - kt_raw : Raw_Slice = transmute(Raw_Slice) kt^ - for cursor in 0 ..< cast(uintptr) num_values { - slot_offset := cursor * m.slot_size - slot_cursor := uintptr(kt_raw.data) + slot_offset - slot_key := cast(^u64) slot_cursor - slot_value := transmute([]byte) Raw_Slice { cast([^]byte) (slot_cursor + m.kt_value_offset), int(m.type_width)} - a2_offset := cursor * m.type_width * 2 - a2_cursor := uintptr(& values[a2_offset]) - a2_key := (transmute(^[]byte) a2_cursor) ^ - a2_value := transmute([]byte) Raw_Slice { rawptr(a2_cursor + m.type_width), int(m.type_width) } - copy(slot_value, a2_value) - slot_key^ = 0; hash64_djb8(slot_key, a2_key) + kt_raw : SliceByte = transmute(SliceByte) kt^ + for id in 0 ..< cast(uintptr) num_values { + slot_offset := id * m.slot_size // slot id + slot_cursor := kt_raw.data[slot_offset:] // slots[id] type: KT1L_ + slot_key := cast(^u64) slot_cursor // slots[id].key type: U64 + slot_value := slice(slot_cursor[m.kt_value_offset:], m.type_width) // slots[id].value type: + a2_offset := id * m.type_width * 2 // a2 entry id + a2_cursor := cursor(values)[a2_offset:] // a2_entries[id] type: A2_ + a2_key := (transmute(^[]byte) a2_cursor) ^ // a2_entries[id].key type: + a2_value := slice(a2_cursor[m.type_width:], m.type_width) // a2_entries[id].value type: + copy(slot_value, a2_value) // slots[id].value = a2_entries[id].value + slot_key^ = 0; hash64_djb8(slot_key, a2_key) // slots[id].key = hash64_djb8(a2_entries[id].key) } kt_raw.len = num_values } -kt1l_populate_slice_a2 :: proc($Type: typeid, kt: ^[]KT1L_Slot(Type), backing: AllocatorInfo, values: [][2]Type) { - assert(kt != nil) - values_bytes := transmute([]byte) Raw_Slice{data = raw_data(values), len = len(values) * size_of([2]Type)} - kt1l_populate_slice_a2_Slice_Byte(transmute(^[]byte) kt, backing, values_bytes, len(values), { - slot_size = size_of(KT1L_Slot(Type)), - kt_value_offset = offset_of(KT1L_Slot(Type), KT1L_Slot(Type).value), - type_width = size_of(Type), - type_name = #type_string(Type), - }) +when KT1L_Use_TypeErased +{ + kt1l_64_populate_slice_a2 :: proc(kt: ^[]KT1L_Slot(u64, $Type), values: [][2]Type, backing:= context.allocator) { + assert(kt != nil) + values_bytes := slice(transmute([^]u8) raw_data(values), len(values) * size_of([2]Type)) + kt1l_64_populate_slice_a2_Slice_Byte(transmute(^[]byte) kt, backing, values_bytes, len(values), { + slot_size = size_of(KT1L_Slot(Type)), + kt_value_offset = offset_of(KT1L_Slot(Type), value), + type_width = size_of(Type), + type = Type, + }) + } +} +else +{ + kt1l_64_populate_slice_a2 :: proc(kt: ^[]KT1L_Slot(u64, $Type), values: [][2]Type, backing:= context.allocator) { + assert(kt != nil) + if len(values) == 0 { retrurn } + kt, alloc_error = make_slice([]KT1L_Slot(u64, Type), len(values), allocator = backing) + assert(alloc_error == .None) + slice_assert(kt) + for id in 0 ..< len(values) { + slot := & kt[id] + hash64_djb8(& slot.key, values[id][0]) + slot.value = values[id][1] + } + } } +// TODO(Ed): Move ot package mappings +kt1l_populate_slice_a2 :: proc { + kt1l_64_populate_slice_a2, } diff --git a/code/grime/hot_reload.odin b/code/grime/hot_reload.odin index 89c063c..9c32b63 100644 --- a/code/grime/hot_reload.odin +++ b/code/grime/hot_reload.odin @@ -2,17 +2,17 @@ package grime import "base:runtime" -reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) { +reload_array :: proc(self: ^[dynamic]$Type, allocator: Allocator) { raw := transmute( ^runtime.Raw_Dynamic_Array) self raw.allocator = allocator } -reload_queue :: proc( self : ^Queue($Type), allocator : Allocator ) { +reload_queue :: proc(self: ^Queue($Type), allocator: Allocator) { raw_array := transmute( ^runtime.Raw_Dynamic_Array) self.data raw_array.allocator = allocator } -reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) { +reload_map :: proc(self: ^map [$KeyType] $EntryType, allocator: Allocator) { raw := transmute( ^runtime.Raw_Map) self raw.allocator = allocator } diff --git a/code/grime/memory.odin b/code/grime/memory.odin index 5c543f9..4a1ef70 100644 --- a/code/grime/memory.odin +++ b/code/grime/memory.odin @@ -99,3 +99,5 @@ swap :: #force_inline proc "contextless" ( a, b : ^ $Type ) -> ( ^ Type, ^ Type to_bytes :: #force_inline proc "contextless" ( typed_block : ^$Type ) -> []byte { return slice_ptr( transmute(^byte) typed_block, size_of(Type) ) } + +ptr_cursor :: #force_inline proc "contextless" (ptr: ^$Type) -> [^]Type { return transmute([^]Type) ptr } diff --git a/code/grime/pkg_mappings.odin b/code/grime/pkg_mappings.odin index a5018ec..609aa85 100644 --- a/code/grime/pkg_mappings.odin +++ b/code/grime/pkg_mappings.odin @@ -6,10 +6,12 @@ import "base:builtin" copy :: builtin.copy import "base:intrinsics" - mem_zero :: intrinsics.mem_zero - ptr_sub :: intrinsics.ptr_sub - type_has_field :: intrinsics.type_has_field - type_elem_type :: intrinsics.type_elem_type + mem_zero :: intrinsics.mem_zero + mem_copy_non_overlapping :: intrinsics.mem_copy_non_overlapping + mem_copy :: intrinsics.mem_copy + ptr_sub :: intrinsics.ptr_sub + type_has_field :: intrinsics.type_has_field + type_elem_type :: intrinsics.type_elem_type import "base:runtime" Byte :: runtime.Byte @@ -144,6 +146,12 @@ array_append_at :: proc { array_append_at_value, } +cursor :: proc { + ptr_cursor, + slice_cursor, + string_cursor, +} + is_power_of_two :: proc { is_power_of_two_u32, is_power_of_two_uintptr, diff --git a/code/grime/slice.odin b/code/grime/slice.odin new file mode 100644 index 0000000..49bfc63 --- /dev/null +++ b/code/grime/slice.odin @@ -0,0 +1,36 @@ +package grime + +SliceByte :: struct { + data: [^]byte, + len: int +} +SliceRaw :: struct ($Type: typeid) { + data: [^]Type, + len: int, +} +slice :: #force_inline proc "contextless" (s: [^] $Type, num: $Some_Integer) -> [ ]Type { return transmute([]Type) SliceRaw(Type) { s, cast(int) num } } +slice_cursor :: #force_inline proc "contextless" (s: []$Type) -> [^]Type { return transmute([^]Type) raw_data(s) } +slice_assert :: #force_inline proc (s: $SliceType / []$Type) { + assert(len(s) > 0) + assert(s != nil) +} +slice_end :: #force_inline proc "contextless" (s : $SliceType / []$Type) -> ^Type { return & cursor(s)[len(s)] } + +@(require_results) slice_to_bytes :: proc "contextless" (s: []$Type) -> []byte { return ([^]byte)(raw_data(s))[:len(s) * size_of(Type)] } +@(require_results) slice_raw :: proc "contextless" (s: []$Type) -> SliceRaw(Type) { return transmute(SliceRaw(Type)) s } + +slice_zero :: proc "contextless" (data: $SliceType / []$Type) { memory_zero(raw_data(data), size_of(Type) * len(data)) } +slice_copy :: proc "contextless" (dst, src: $SliceType / []$Type) -> int { + n := max(0, min(len(dst), len(src))) + if n > 0 { + mem_copy(raw_data(dst), raw_data(src), n * size_of(Type)) + } + return n +} +slice_copy_non_overlapping :: proc "contextless" (dst, src: $SliceType / []$Type) -> int { + n := max(0, min(len(dst), len(src))) + if n > 0 { + mem_copy_non_overlapping(raw_data(dst), raw_data(src), n * size_of(Type)) + } + return n +} diff --git a/code/grime/strings.odin b/code/grime/strings.odin new file mode 100644 index 0000000..a1cc419 --- /dev/null +++ b/code/grime/strings.odin @@ -0,0 +1,10 @@ +package grime + +Raw_String :: struct { + data: [^]byte, + len: int, +} +string_cursor :: proc(s: string) -> [^]u8 { return slice_cursor(transmute([]byte) s) } +string_copy :: proc(dst, src: string) { slice_copy (transmute([]byte) dst, transmute([]byte) src) } +string_end :: proc(s: string) -> ^u8 { return slice_end (transmute([]byte) s) } +string_assert :: proc(s: string) { slice_assert(transmute([]byte) s) } diff --git a/code/sectr/grime/pkg_mappings.odin b/code/sectr/grime/pkg_mappings.odin index 4520615..da6ec63 100644 --- a/code/sectr/grime/pkg_mappings.odin +++ b/code/sectr/grime/pkg_mappings.odin @@ -270,7 +270,7 @@ import "codebase:grime" file_rewind :: grime.file_rewind // hashing - djb8_hash :: grime.djb8_hash + hash64_djb8 :: grime.hash64_djb8 // linked lists SLL_Node :: grime.SLL_Node @@ -664,7 +664,7 @@ vec2 :: proc { vec2_from_scalar, // vec2_64_from_vec2, vec2_from_vec2i, - vec2i_from_vec2 + vec2i_from_vec2, } vec3 :: proc { diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 4c56122..b0d706b 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -29,6 +29,7 @@ if ( $args ) { $args | ForEach-Object { switch ($_){ "force" { $force = $true } "clang" { $clang = $true } + "full" { $full = $true } } }} #endregion Arguments @@ -165,7 +166,7 @@ push-location $path_root $pkg_collection_thirdparty = 'thirdparty=' + $path_thirdparty $host_process_active = Get-Process | Where-Object {$_.Name -like 'sectr_host*'} - if ( -not $host_process_active ) { + if ( -not $host_process_active -and $full -eq $true) { # We cannot update thidparty dependencies during hot-reload. & $update_deps write-host