Added memory arena

Fixed frames in flight validation error in vulkan
Added some utility macros
Added function decorator macros
Added some consolidation headers/code include files
This commit is contained in:
2025-10-04 00:46:26 +01:00
parent 9f2ef576b9
commit 5f07239374
13 changed files with 429 additions and 12 deletions

91
code/core/arena.h Normal file
View File

@@ -0,0 +1,91 @@
#if !defined(LD_CORE_ARENA_H_)
#define LD_CORE_ARENA_H_
#define AlignUp(x, a) (((x) + ~((a) - 1)) & ~((a) - 1))
#define AlignDown(x, a) (((x)) & ~((a) - 1))
#define KB(x) ((U64) (x) << 10)
#define MB(x) ((U64) (x) << 20)
#define GB(x) ((U64) (x) << 30)
#if !defined(M_ARENA_CHAIN_RESERVE)
#define M_ARENA_CHAIN_RESERVE MB(4)
#endif
function void *M_ZeroSize(void *base, U64 size);
function void *M_CopySize(void *dst, void *src, U64 size);
typedef U32 M_ArenaFlags;
enum {
// Arena is fixed size, so will not chain
M_ARENA_FIXED_SIZE = (1 << 0),
// Don't clear allocation
M_ARENA_NO_ZERO = (1 << 1)
};
typedef union M_Arena M_Arena;
union M_Arena {
struct {
M_Arena *current;
M_Arena *prev;
U64 base;
U64 limit;
U64 offset;
U64 committed;
U64 increment;
M_ArenaFlags flags;
};
U8 __pad[64];
};
typedef struct M_ArenaOpts M_ArenaOpts;
struct M_ArenaOpts {
U64 initial;
U64 increment;
M_ArenaFlags flags;
};
typedef struct M_ArenaPushOpts M_ArenaPushOpts;
struct M_ArenaPushOpts {
U64 count;
U64 align;
M_ArenaFlags flags;
};
function M_Arena *_M_ArenaAlloc(U64 limit, M_ArenaOpts *opts);
function void *_M_ArenaPush(M_Arena *arena, U64 esize, M_ArenaPushOpts *opts);
function void *_M_ArenaPushCopy(M_Arena *arena, void *src, U64 esize, M_ArenaPushOpts *opts);
#define M_ArenaAlloc(limit, ...) _M_ArenaAlloc(limit, &(M_ArenaOpts) { .initial = MB(1), .increment = KB(64), ##__VA_ARGS__ })
#define M_ArenaPush(arena, T, ...) (T *) _M_ArenaPush(arena, sizeof(T), &(M_ArenaOpts) { .count = 1, .align = Alignof(T), ##__VA_ARGS__ })
#define M_ArenaPushCopy(arena, T, src, ...) (T *) _M_ArenaPush(arena, src, sizeof(T), &(M_ArenaOpts) { .count = 1, .align = Alignof(T), ##__VA_ARGS__ })
function void M_ArenaReset(M_Arena *arena);
function void M_ArenaRelease(M_Arena *arena);
function U64 M_ArenaOffset(M_Arena *arena);
function void M_ArenaPop(M_Arena *arena, U64 offset);
function void M_ArenaPopSize(M_Arena *arena, U64 size);
// Temporary memory
typedef struct M_Temp M_Temp;
struct M_Temp {
M_Arena *arena;
U64 offset;
};
function M_Temp M_TempAcquire(U64 count, M_Arena **conflicts);
function void M_TempRelease(M_Temp temp);
#define M_TempScope(n, c) for (M_Temp temp = M_TempAcquire(n, c); temp.arena != 0; M_TempRelease(temp), temp.arena = 0)
#endif // LD_CORE_ARENA_H_

1
code/core/core.c Normal file
View File

@@ -0,0 +1 @@
#include "impl/arena.c"

9
code/core/core.h Normal file
View File

@@ -0,0 +1,9 @@
#if !defined(LD_CORE_CORE_H_)
#define LD_CORE_CORE_H_
#include "types.h"
#include "platform.h"
#include "macros.h"
#include "arena.h"
#endif // LD_CORE_CORE_H_

210
code/core/impl/arena.c Normal file
View File

@@ -0,0 +1,210 @@
void *M_ZeroSize(void *base, U64 size) {
U8 *bytes = (U8 *) base;
while (size--) {
*bytes++ = 0;
}
return base;
}
void *M_CopySize(void *dst, void *src, U64 size) {
U8 *se = (U8 *) src + (size - 1);
U8 *de = (U8 *) dst + (size - 1);
while (size--) {
*de-- = *se--;
}
return dst;
}
M_Arena *_M_ArenaAlloc(U64 limit, M_ArenaOpts *opts) {
M_Arena *result = 0;
local_persist M_ArenaOpts _opts;
if (!opts) {
opts = &_opts;
}
U64 page_size = VM_PageSize();
U64 granularity = VM_AllocationGranularity();
U64 reserve = Max(AlignUp(limit, granularity), granularity);
U64 initial = Clamp(page_size, AlignUp(opts->initial, page_size), reserve);
void *base = VM_Reserve(reserve);
if (base != 0) {
if (VM_Commit(base, initial)) {
result = cast(M_Arena *) base;
result->current = result;
result->prev = 0;
result->base = 0;
result->offset = sizeof(M_Arena);
result->limit = reserve;
result->committed = initial;
result->increment = Max(AlignUp(opts->increment, page_size), page_size);
result->flags = opts->flags;
}
else {
VM_Release(base, reserve);
}
}
Assert(result != 0);
return result;
}
void *_M_ArenaPush(M_Arena *arena, U64 esize, M_ArenaPushOpts *opts) {
void *result = 0;
local_persist M_ArenaPushOpts _opts = { .count = 1, .align = 8 };
if (!opts) {
opts = &_opts;
}
U64 alignment = Clamp(1, opts->align, 4096);
M_Arena *current = arena->current;
U64 total = esize * opts->count;
U64 offset = AlignUp(current->offset, alignment);
U64 end = offset + total;
if (end > current->limit) {
// Not enough space, chain a new arena if flags allow
if ((arena->flags & M_ARENA_FIXED_SIZE) == 0) {
U64 reserve = Min(total + sizeof(M_Arena), M_ARENA_CHAIN_RESERVE);
M_Arena *next = M_ArenaAlloc(reserve, .flags = arena->flags);
next->base = current->base + current->limit;
SLL_PushN(arena->current, next, prev);
current = next;
offset = AlignUp(current->offset, alignment);
end = offset + total;
}
}
if (end > current->committed) {
// Not enough committed, commit more memory
U64 commit_offset = AlignUp(end, current->increment);
U64 commit_limit = Min(commit_offset, current->limit);
U8 *commit_base = cast(U8 *) current + current->committed;
U64 commit_size = commit_limit - current->committed;
if (VM_Commit(commit_base, commit_size)) {
current->committed = commit_limit;
}
}
if (end <= current->committed) {
// Successfully got enough memory, push the allocation
result = cast(U8 *) current + offset;
current->offset = end;
if (((opts->flags | arena->flags) & M_ARENA_NO_ZERO) == 0){
M_ZeroSize(result, total);
}
}
Assert(result != 0);
Assert(((U64) result & (alignment - 1)) == 0);
return result;
}
void *_M_ArenaPushCopy(M_Arena *arena, void *from, U64 size, M_ArenaPushOpts *opts) {
void *result = M_CopySize(_M_ArenaPush(arena, size, opts), from, size);
return result;
}
void M_ArenaReset(M_Arena *arena) {
M_Arena *base = arena->current;
while (base->base != 0) {
M_Arena *prev = base->prev;
VM_Release(base, base->limit);
base = prev;
}
Assert(arena == base);
// @Todo: We could decommit some of the memory in the base arena, do we want to give a
// parameter to choose how much :decommit
base->offset = sizeof(M_Arena);
arena->current = base;
}
void M_ArenaRelease(M_Arena *arena) {
M_ArenaReset(arena);
VM_Release(arena, arena->limit);
}
U64 M_ArenaOffset(M_Arena *arena) {
U64 result = arena->current->base + arena->current->offset;
return result;
}
void M_ArenaPop(M_Arena *arena, U64 offset) {
M_Arena *base = arena->current;
while (base->base > offset) {
M_Arena *prev = base->prev;
VM_Release(base, base->limit);
base = prev;
}
// :decommit
arena->current = base;
base->offset = Max(offset - base->base, sizeof(M_Arena));
}
void M_ArenaPopSize(M_Arena *arena, U64 size) {
U64 offset = M_ArenaOffset(arena);
offset -= Min(offset, size);
M_ArenaPop(arena, offset);
}
#define M_TEMP_ARENA_LIMIT GB(4)
static thread_var M_Arena *__tls_temp[2];
M_Temp M_TempAcquire(U64 count, M_Arena **conflicts) {
M_Temp result = { 0 };
for (U32 it = 0; it < ArraySize(__tls_temp); ++it) {
if (!__tls_temp[it]) {
__tls_temp[it] = M_ArenaAlloc(M_TEMP_ARENA_LIMIT, .initial = MB(1), .increment = MB(1));
}
result.arena = __tls_temp[it];
for (U32 c = 0; c < count; ++c) {
if (__tls_temp[it] == conflicts[c]) {
result.arena = 0;
break;
}
}
if (result.arena) {
result.offset = M_ArenaOffset(result.arena);
break;
}
}
Assert(result.arena != 0);
return result;
}
void M_TempRelease(M_Temp temp) {
M_ArenaPop(temp.arena, temp.offset);
}

View File

@@ -1,9 +1,17 @@
#if !defined(LD_CORE_MACROS_H_)
#define LD_CORE_MACROS_H_
#include <assert.h>
#define Assert(exp) assert(exp)
#define ArraySize(x) (sizeof(x) / sizeof((x)[0]))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Clamp(min, x, max) (Min(Max(min, x), max))
#define Clamp01(x) Clamp(0, x, 1)
#define cast(x) (x)
#define _Glue(a, b) a##b
#define _Stringify(x) #x
@@ -11,5 +19,30 @@
#define Glue(a, b) _Glue(a, b)
#define Stringify(x) _Stringify(x)
#if LANG_CPP
#define Alignof(x) alignof(x)
#else
#define Alignof(x) _Alignof(x)
#endif
// Singly linked lists (named members)
//
#define SLL_EnqueueN(h, t, n, next) (((h) == 0) ? ((h) = (t) = (n), (n)->next = 0) : ((t)->next = (n), (t) = (n), (n)->next = 0))
#define SLL_EnqueueFrontN(h, t, n, next) (((h) == 0) ? ((h) = (t) = (n), (n)->next = 0) : ((n)->next = (h), (h) = (n)))
#define SLL_DequeueN(h, t, next) ((h) == (t) ? ((h) = 0, (t) = 0) : ((h) = (h)->next))
#define SLL_PushN(h, n, next) ((n)->next = (h), (h) = (n))
#define SLL_PopN(h, next) (((h) != 0) ? (h) = (h)->next : 0)
#define function static
#define internal static
#define global_var static
#define local_persist static
#if COMPILER_CL
#define thread_var __declspec(thread)
#else
#define thread_var __thread
#endif
#endif // LD_CORE_MACROS_H_

View File

@@ -68,6 +68,7 @@
#if OS_WINDOWS
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#pragma warning(disable : 4201)
#elif OS_LINUX
#include <dlfcn.h>
#endif