WIP: fleshing out cod2 for once a bit more

Planning to try out a flavor of Ryan's multi-threaded laned procs with the some extra threads hooked up separately to a job system.
Will most likely do 2 threads main/helper on live lanes, then 2 others on job queue loops
This commit is contained in:
2025-10-11 19:17:29 -04:00
parent 7219b780fc
commit 05e979907a
20 changed files with 387 additions and 37 deletions

7
code2/grime/Readme.md Normal file
View File

@@ -0,0 +1,7 @@
# Grime
This is a top-level package to adjust odin to my personalized usage.
I curate all usage of odin's provided package definitons through here. The client and host packages should never directly import them.
There are no implicit static allocations in Grime. Ideally there are also none from the base/core packages but some probably leak.

1
code2/grime/arenas.odin Normal file
View File

@@ -0,0 +1 @@
package grime

8
code2/grime/context.odin Normal file
View File

@@ -0,0 +1,8 @@
package grime
// Context :: struct {
// }
// context_usr :: #force_inline proc( $ Type : typeid ) -> (^Type) {
// return cast(^Type) context.user_ptr
// }

View File

@@ -0,0 +1,20 @@
package grime
sll_stack_push_n :: proc "contextless" (curr, n, n_link: ^^$Type) {
(n_link ^) = (curr ^)
(curr ^) = (n ^)
}
sll_queue_push_nz :: proc "contextless" (first: ^$ParentType, last, n: ^^$Type, nil_val: ^Type) {
if (first ^) == nil_val {
(first ^) = n^
(last ^) = n^
n^.next = nil_val
}
else {
(last ^).next = n^
(last ^) = n^
n^.next = nil_val
}
}
sll_queue_push_n :: #force_inline proc "contextless" (first: $ParentType, last, n: ^^$Type) { sll_queue_push_nz(first, last, n, nil) }

226
code2/grime/memory.odin Normal file
View File

@@ -0,0 +1,226 @@
package grime
Kilo :: 1024
Mega :: Kilo * 1024
Giga :: Mega * 1024
Tera :: Giga * 1024
ptr_cursor :: #force_inline proc "contextless" (ptr: ^$Type) -> [^]Type { return transmute([^]Type) ptr }
align_pow2 :: proc(x: int, b: int) -> int {
assert(b != 0)
assert((b & (b - 1)) == 0) // Check power of 2
return ((x + b - 1) & ~(b - 1))
}
memory_zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
mem_zero_volatile(data, len) // Use the volatile mem_zero
atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data
}
memory_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
mem_copy(dst, src, len)
return dst
}
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)] }
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
}
@(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 }
//region Allocator Interface
AllocatorOp :: enum u32 {
Alloc_NoZero = 0, // If Alloc exist, so must No_Zero
Alloc,
Free,
Reset,
Grow_NoZero,
Grow,
Shrink,
Rewind,
SavePoint,
Query, // Must always be implemented
}
AllocatorQueryFlag :: enum u64 {
Alloc,
Free,
Reset, // Wipe the allocator's state
Shrink,
Grow,
Resize, // Supports both grow and shrink
Rewind, // Ability to rewind to a save point (ex: arenas, stack), must also be able to save such a point
// Actually_Resize,
// Is_This_Yours,
Hint_Fast_Bump,
Hint_General_Heap,
Hint_Per_Frame_Temporary,
Hint_Debug_Support,
}
AllocatorQueryFlags :: bit_set[AllocatorQueryFlag; u64]
AllocatorSP :: struct {
type_sig: AllocatorProc,
slot: int,
}
AllocatorProc :: #type proc (input: AllocatorProc_In, out: ^AllocatorProc_Out)
AllocatorProc_In :: struct {
data: rawptr,
requested_size: int,
alignment: int,
using _ : struct #raw_union {
old_allocation: []byte,
save_point : AllocatorSP,
},
op: AllocatorOp,
}
AllocatorProc_Out :: struct {
using _ : struct #raw_union {
allocation: []byte,
save_point: AllocatorSP,
},
features: AllocatorQueryFlags,
left: int,
max_alloc: int,
min_alloc: int,
continuity_break: b32,
}
AllocatorQueryInfo :: struct {
save_point: AllocatorSP,
features: AllocatorQueryFlags,
left: int,
max_alloc: int,
min_alloc: int,
continuity_break: b32,
}
AllocatorInfo :: struct {
procedure: AllocatorProc,
data: rawptr,
}
// #assert(size_of(AllocatorQueryInfo) == size_of(AllocatorProc_Out))
MEMORY_ALIGNMENT_DEFAULT :: 2 * size_of(rawptr)
allocator_query :: proc(ainfo := context.allocator) -> AllocatorQueryInfo {
assert(ainfo.procedure != nil)
out: AllocatorQueryInfo; (cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Query}, transmute(^AllocatorProc_Out) & out)
return out
}
mem_free :: proc(mem: []byte, ainfo := context.allocator) {
assert(ainfo.procedure != nil)
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Free, old_allocation = mem}, & {})
}
mem_reset :: proc(ainfo := context.allocator) {
assert(ainfo.procedure != nil)
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Reset}, &{})
}
mem_rewind :: proc(ainfo := context.allocator, save_point: AllocatorSP) {
assert(ainfo.procedure != nil)
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Rewind, save_point = save_point}, & {})
}
mem_save_point :: proc(ainfo := context.allocator) -> AllocatorSP {
assert(ainfo.procedure != nil)
out: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .SavePoint}, & out)
return out.save_point
}
mem_alloc :: proc(size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
assert(ainfo.procedure != nil)
input := AllocatorProc_In {
data = ainfo.data,
op = no_zero ? .Alloc_NoZero : .Alloc,
requested_size = size,
alignment = alignment,
}
output: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)(input, & output)
return output.allocation
}
mem_grow :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
assert(ainfo.procedure != nil)
input := AllocatorProc_In {
data = ainfo.data,
op = no_zero ? .Grow_NoZero : .Grow,
requested_size = size,
alignment = alignment,
old_allocation = mem,
}
output: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)(input, & output)
return output.allocation
}
mem_resize :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
assert(ainfo.procedure != nil)
input := AllocatorProc_In {
data = ainfo.data,
op = len(mem) < size ? .Shrink : no_zero ? .Grow_NoZero : .Grow,
requested_size = size,
alignment = alignment,
old_allocation = mem,
}
output: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)(input, & output)
return output.allocation
}
mem_shrink :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
assert(ainfo.procedure != nil)
input := AllocatorProc_In {
data = ainfo.data,
op = .Shrink,
requested_size = size,
alignment = alignment,
old_allocation = mem,
}
output: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)(input, & output)
return output.allocation
}
alloc_type :: proc($Type: typeid, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> ^Type {
assert(ainfo.procedure != nil)
input := AllocatorProc_In {
data = ainfo.data,
op = no_zero ? .Alloc_NoZero : .Alloc,
requested_size = size_of(Type),
alignment = alignment,
}
output: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)(input, & output)
return transmute(^Type) raw_data(output.allocation)
}
alloc_slice :: proc($SliceType: typeid / []$Type, num : int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []Type {
assert(ainfo.procedure != nil)
input := AllocatorProc_In {
data = ainfo.data,
op = no_zero ? .Alloc_NoZero : .Alloc,
requested_size = size_of(Type) * num,
alignment = alignment,
}
output: AllocatorProc_Out
(cast(AllocatorProc)ainfo.procedure)(input, & output)
return transmute([]Type) slice(raw_data(output.allocation), num)
}
//endregion Allocator Interface

1
code2/grime/os.odin Normal file
View File

@@ -0,0 +1 @@
package grime

View File

@@ -0,0 +1,23 @@
package grime
import "base:builtin"
Odin_OS_Type :: type_of(ODIN_OS)
import "base:intrinsics"
atomic_thread_fence :: intrinsics.atomic_thread_fence
mem_zero :: intrinsics.mem_zero
mem_zero_volatile :: intrinsics.mem_zero_volatile
mem_copy :: intrinsics.mem_copy_non_overlapping
mem_copy_overlapping :: intrinsics.mem_copy
import "base:runtime"
Assertion_Failure_Proc :: runtime.Assertion_Failure_Proc
Logger :: runtime.Logger
Random_Generator :: runtime.Random_Generator
slice_copy_overlapping :: runtime.copy_slice
import core_os "core:os"
// ODIN_OS :: core_os.ODIN_OS
import "core:slice"
slice_zero :: slice.zero