diff --git a/src/base/base_arena.c b/src/base/base_arena.c new file mode 100644 index 0000000..2da64a9 --- /dev/null +++ b/src/base/base_arena.c @@ -0,0 +1,127 @@ +internal Arena *arena_alloc_(ArenaParams *params) { + u64 reserve_size = params->reserve_size; + u64 commit_size = params->commit_size; + + if (params->flags & ArenaFlag_LargePages) { + reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size); + commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size); + } else { + reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size); + commit_size = AlignPow2(commit_size, os_get_system_info()->page_size); + } + + // reserve/commit initial block of memory + void *base = params->optional_backing_buffer; + if (base == 0) { + if (params->flags & ArenaFlag_LargePages) { + base = os_reserve_large(reserve_size); + os_commit_large(base, commit_size); + } else { + base = os_reserve(reserve_size); + os_commit(base, commit_size); + } + } + + Arena *arena = (Arena *)base; + arena->current = arena; // Current arena in the chain + arena->flags = params->flags; + arena->commit_size = (u32)params->commit_size; + arena->reserve_size = params->reserve_size; + arena->base_position = 0; + arena->position = ARENA_HEADER_SIZE; + arena->commit = commit_size; + arena->reserve = reserve_size; + return arena; +} + +internal void arena_release(Arena *arena) { + for (Arena *current_arena = arena->current, *previous_arena = 0; current_arena != 0; current_arena = previous_arena) { + previous_arena = current_arena->previous; + os_release(current_arena, current_arena->reserve); + } +} + +internal void *arena_push(Arena *arena, u64 size_to_push, u64 align) { + Arena *current_arena = arena->current; + u64 position_pre_push = AlignPow2(current_arena->position, align); // position of arena before push + u64 position_post_push = position_pre_push + size_to_push; // position of arena after push + + // ??(tijani): chain, if needed + if (current_arena->reserve < position_post_push && !(arena->flags & ArenaFlag_NoChain)) { + u64 new_reserve_size = current_arena->reserve_size; + u64 new_commit_size = current_arena->commit_size; + + if (size_to_push > new_reserve_size) { + new_reserve_size = size_to_push + ARENA_HEADER_SIZE; + new_commit_size = size_to_push + ARENA_HEADER_SIZE; + } + Arena *new_memory_block = + arena_alloc(.reserve_size = new_reserve_size, .commit_size = new_commit_size, .flags = current_arena->flags); + new_memory_block->base_position = current_arena->base_position + current_arena->reserve; + SLLStackPush_N(arena->current, new_memory_block, previous); // `previous` refers to `previous` in `Arena` struct + current_arena = new_memory_block; + position_pre_push = AlignPow2(current_arena->position, align); + position_post_push = position_pre_push + size_to_push; + } + + // ??(tijani): commit new page, if needed + if (current_arena->commit < position_post_push && !(current_arena->flags & ArenaFlag_LargePages)) { + u64 commit_post_aligned = AlignPow2(position_post_push, current_arena->commit_size); + u64 commit_post_clamped = ClampTop(commit_post_aligned, current_arena->reserve); + u64 new_commit_size = commit_post_clamped - current_arena->commit; + os_commit((u8 *)current_arena + current_arena->commit, new_commit_size); + } + + // push onto the current block + void *memory = 0; + if (current_arena->commit >= position_post_push) { + memory = (u8 *)current_arena + position_pre_push; + current_arena->position = position_post_push; + // ??(tijani): why need to Asan Unpoision memory + } + return memory; +} + +internal u64 arena_position(Arena *arena) { + Arena *current_arena = arena->current; + u64 current_position = current_arena->base_position + current_arena->position; + return current_position; +} + +internal void arena_pop_to(Arena *arena, u64 position) { + u64 big_position = ClampBot(ARENA_HEADER_SIZE, position); + Arena *current_arena = arena->current; + for (Arena *previous_arena = 0; current_arena->base_position >= big_position; current_arena = previous_arena) { + previous_arena = current_arena->previous; + os_release(current_arena, current_arena->reserve); + } + arena->current = current_arena; + u64 new_position = big_position - current_arena->base_position; + // ??(tijani): why need to ASSERT and AsanPoision memory region. + current_arena->position = new_position; +} + +internal void arena_clear(Arena *arena) { arena_pop_to(arena, 0); } + +// Pops the amount of memory passed off the passed in arena. +// The new position of the passed in arena will be the same +// memory block minus the amount passed in. +internal void arena_pop_off(Arena *arena, u64 amount) { + u64 old_position = arena_position(arena); + u64 new_position = old_position; + + if (amount < old_position) { + new_position = old_position - amount; + } + + arena_pop_to(arena, new_position); +} + +// Temporary memory +internal Temp temp_begin(Arena *arena) { + u64 position = arena_position(arena); + Temp temp = {arena, position}; + return temp; +} + +internal void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.position); } diff --git a/src/base/base_arena.h b/src/base/base_arena.h new file mode 100644 index 0000000..9069892 --- /dev/null +++ b/src/base/base_arena.h @@ -0,0 +1,78 @@ +#ifndef BASE_ARENA_H +#define BASE_ARENA_H + +// Arena allocator simulates how stack memory works. +// This allows us the ability to allocate a reasonableamount of memory +// at startup and only use that allocated amount of memory throughout +// the lifetime of the application. Obviously this still uses +// "malloc, free, VirtualAlloc, VirtualFree" but instead of having to +// request and freeing memory during runtime, we create an environment +// where we have as much memory we could ever need and them portion it +// out in small chunk as needed. This has multiple benefits that would +// make itself glaring throughout the codebase but the gist of it is that +// we have one large block of memory throughout the lifetime, +// then application specific code that could need memory grab a chunck +// memory from the big block of memory we have reserved with the operating +// system at startup do whatever operation it needs to do, then return +// that block of memory back to the arena. + +// Constants +#define ARENA_HEADER_SIZE 64 + +// Flags +// ArenaFlag_NoChain - Arenas are chained by default, this flag tells it otherwise. +// ArenaFlag_LargePages - Use large pages. +typedef u32 ArenaFlags; +enum { + ArenaFlag_NoChain = (1 << 0), + ArenaFlag_LargePages = (1 << 1), +}; + +typedef struct ArenaParams ArenaParams; +struct ArenaParams { + ArenaFlags flags; + u64 reserve_size; + u64 commit_size; + void *optional_backing_buffer; // NOTE(tijani): ?? +}; + +typedef struct Arena Arena; +struct Arena { + Arena *previous; // Previous arena in the chain + Arena *current; // Current arena in the chain + ArenaFlags flags; + u32 commit_size; + u64 reserve_size; + u64 base_position; + u64 position; + u64 commit; + u64 reserve; +}; + +typedef struct Temp Temp; +struct Temp { + Arena *arena; + u64 position; +}; + +// creation, destruction +internal Arena *arena_alloc_(ArenaParams *params); +#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = MB(64), .commit_size = KB(64), __VA_ARGS__}) +internal void arena_release(Arena *arena); + +// push, pop operations +internal void *arena_push(Arena *arena, u64 size_to_push, u64 align); +internal u64 arena_position(Arena *arena); +internal void arena_pop_to(Arena *arena, u64 position); +internal void arena_clear(Arena *arena); +internal void arena_pop_off(Arena *arena, u64 amount); +#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T) * (c), (align)) +#define push_array_aligned(a, T, c, align) (T *)MemoryZero(push_array_no_zero_aligned(a, T, c, align), sizeof(T) * (c)) +#define push_array_no_zero(arena, Type, count) push_array_no_zero_aligned(arena, Type, count, Max(8, Align_Of(Type))) +#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, Align_Of(T))) + +// Temp +internal Temp temp_begin(Arena *arena); +internal void temp_end(Temp temp); + +#endif // BASE_ARENA_H diff --git a/src/base/base_inc.c b/src/base/base_inc.c new file mode 100644 index 0000000..2d2667c --- /dev/null +++ b/src/base/base_inc.c @@ -0,0 +1,4 @@ +// clang-format off + + +// clang-format on diff --git a/src/base/base_inc.h b/src/base/base_inc.h new file mode 100644 index 0000000..c2b0213 --- /dev/null +++ b/src/base/base_inc.h @@ -0,0 +1,11 @@ +#ifndef BASE_INC_H +#define BASE_INC_H + +// clang-format off +#include "base_context_switching.h" +#include "base_core.h" +#include "base_math.h" + +// clang-format on + +#endif // BASE_INC_H diff --git a/src/base/base_math.h b/src/base/base_math.h new file mode 100644 index 0000000..056d36c --- /dev/null +++ b/src/base/base_math.h @@ -0,0 +1,23 @@ +#ifndef BASE_MATH_H +#define BASE_MATH_H + +typedef struct Vec2F32 Vec2F32; +union Vec2F32 { + struct { + f32 x; + f32 y; + }; + f32 v[2]; +}; + +typedef struct Vec3F32 Vec3F32; +union Vec3F32 { + struct { + f32 x; + f32 y; + f32 z; + }; + f32 v[2]; +}; + +#endif // BASE_MATH_H diff --git a/src/os/core/os_core.h b/src/os/core/os_core.h new file mode 100644 index 0000000..2acb4be --- /dev/null +++ b/src/os/core/os_core.h @@ -0,0 +1,16 @@ +#ifndef OS_CORE_H +#define OS_CORE_H + +// %os_hooks(implemented per-os) Memory allocation + +// NOTE(tijani): For normal page size +internal b32 os_commit(void *ptr, u64 size); +internal void *os_reserve(u64 size); +internal void os_decommit(void *ptr, u64 size); +internal void os_release(void *ptr, u64 size); + +// NOTE(tijani): For large page size +internal void *os_reserve_large(u64 size); +internal b32 os_commit_large(void *ptr, u64 size); + +#endif // OS_CORE_H diff --git a/src/os/core/win32/os_core_win32.c b/src/os/core/win32/os_core_win32.c new file mode 100644 index 0000000..e1a557c --- /dev/null +++ b/src/os/core/win32/os_core_win32.c @@ -0,0 +1,33 @@ +// %os_hooks(implemented per-os) Memory allocation + +// NOTE(tijani): For normal page size +internal b32 os_commit(void *ptr, u64 size) { + b32 l_memory = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + return l_memory; +} + +internal void *os_reserve(u64 size) { + void *l_memory = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return l_memory; +} + +internal void os_decommit(void *ptr, u64 size) { VirtualFree(ptr, size, MEM_DECOMMIT); } + +internal void os_release(void *ptr, u64 size) { + // NOTE(tijani): size must be 0 on windows hence it will fail. + VirtualFree(ptr, size, MEM_RELEASE); +} + +// NOTE(tijani): For large page size +internal void *os_reserve_large(u64 size) { + void *l_memory = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + return l_memory; +} + +// NOTE(tijani): From my understanding, on windows you cannot +// commit large pages as this has to be done during reservation. +// see `os_reserve_large` and WIN32 docs on large page support. +internal b32 os_commit_large(void *ptr, u64 size) { return -1; } + +// %os_hooks(implemented per-os) Aborting +internal void os_abort(s32 exit_code) { ExitProcess(exit_code); } diff --git a/src/os/core/win32/os_core_win32.h b/src/os/core/win32/os_core_win32.h new file mode 100644 index 0000000..33d5be9 --- /dev/null +++ b/src/os/core/win32/os_core_win32.h @@ -0,0 +1,11 @@ +#ifndef OS_CORE_WIN32_H +#define OS_CORE_WIN32_H + +// clang-format off + +#define WIN32_LEAN_AND_MEAN +#include + +// clang-format on + +#endif // OS_CORE_WIN32_H diff --git a/src/os/os_inc.c b/src/os/os_inc.c new file mode 100644 index 0000000..d32032f --- /dev/null +++ b/src/os/os_inc.c @@ -0,0 +1,6 @@ +// clang-format off + +#include "core/os_core.c" +#include "core/win32/os_core_win32.c" + +// clang-format on diff --git a/src/os/os_inc.h b/src/os/os_inc.h new file mode 100644 index 0000000..48469cd --- /dev/null +++ b/src/os/os_inc.h @@ -0,0 +1,11 @@ +#ifndef OS_INC_H +#define OS_INC_H + +// clang-format off + +#include "core/os_core.h" +#include "core/win32/os_core_win32.h" + +// clang-format on + +#endif // OS_INC_H