From e9e9ab68c250c597fd6b7552d97977b99b103d33 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Mon, 17 Apr 2023 18:12:21 +0200 Subject: [PATCH] base memory allocator and chunked arenas for orca --- src/egl_surface.c | 2 +- src/graphics.c | 48 +++++----- src/graphics.h | 6 +- src/milepost.c | 16 ++-- src/mp_app_internal.h | 4 +- src/platform/orca_memory.c | 87 +++++++++++++++++ src/platform/platform.h | 10 +- src/platform/platform_base_allocator.h | 28 ------ src/platform/platform_memory.h | 74 ++++++++++++++ .../{unix_base_allocator.c => unix_memory.c} | 10 +- ...{win32_base_allocator.c => win32_memory.c} | 14 ++- src/ui.c | 4 +- src/util/macro_helpers.h | 11 +-- src/util/memory.c | 96 ++++++++++++++----- src/util/memory.h | 41 +++----- src/util/typedefs.h | 3 +- 16 files changed, 312 insertions(+), 142 deletions(-) create mode 100644 src/platform/orca_memory.c delete mode 100644 src/platform/platform_base_allocator.h create mode 100644 src/platform/platform_memory.h rename src/platform/{unix_base_allocator.c => unix_memory.c} (73%) rename src/platform/{win32_base_allocator.c => win32_memory.c} (67%) diff --git a/src/egl_surface.c b/src/egl_surface.c index 5283389..5af49a6 100644 --- a/src/egl_surface.c +++ b/src/egl_surface.c @@ -14,7 +14,7 @@ #include"graphics_internal.h" #include"gl_loader.h" -#if OS_MACOS +#if PLATFORM_MACOS //NOTE: EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE on osx defaults to CGL backend, which doesn't handle SwapInterval correctly #define MG_EGL_PLATFORM_ANGLE_TYPE EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE diff --git a/src/graphics.c b/src/graphics.c index 6c6d0a9..b50c71d 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -85,11 +85,14 @@ typedef struct mg_handle_slot } mg_handle_slot; +static const u32 MG_HANDLES_MAX_COUNT = 512; + typedef struct mg_data { bool init; - mem_arena handleArena; + mg_handle_slot handleArray[MG_HANDLES_MAX_COUNT]; + int handleNextIndex; list_info handleFreeList; mem_arena resourceArena; @@ -146,7 +149,7 @@ void mg_init() { if(!__mgData.init) { - mem_arena_init(&__mgData.handleArena); + __mgData.handleNextIndex = 0; mem_arena_init(&__mgData.resourceArena); __mgData.init = true; } @@ -162,21 +165,24 @@ u64 mg_handle_alloc(mg_handle_kind kind, void* data) { mg_init(); } - mem_arena* arena = &__mgData.handleArena; mg_handle_slot* slot = list_pop_entry(&__mgData.handleFreeList, mg_handle_slot, freeListElt); - if(!slot) + if(!slot && __mgData.handleNextIndex < MG_HANDLES_MAX_COUNT) { - slot = mem_arena_alloc_type(arena, mg_handle_slot); - DEBUG_ASSERT(slot); + slot = &__mgData.handleArray[__mgData.handleNextIndex]; + __mgData.handleNextIndex++; + slot->generation = 1; } - slot->kind = kind; - slot->data = data; - - u64 h = ((u64)(slot - (mg_handle_slot*)arena->ptr))<<32 - |((u64)(slot->generation)); + u64 h = 0; + if(slot) + { + slot->kind = kind; + slot->data = data; + h = ((u64)(slot - __mgData.handleArray))<<32 + |((u64)(slot->generation)); + } return(h); } @@ -186,11 +192,10 @@ void mg_handle_recycle(u64 h) u32 index = h>>32; u32 generation = h & 0xffffffff; - mem_arena* arena = &__mgData.handleArena; - if(index*sizeof(mg_handle_slot) < arena->offset) + if(index*sizeof(mg_handle_slot) < __mgData.handleNextIndex) { - mg_handle_slot* slot = (mg_handle_slot*)arena->ptr + index; + mg_handle_slot* slot = &__mgData.handleArray[index]; if(slot->generation == generation) { DEBUG_ASSERT(slot->generation != UINT32_MAX, "surface slot generation wrap around\n"); @@ -208,11 +213,10 @@ void* mg_data_from_handle(mg_handle_kind kind, u64 h) u32 index = h>>32; u32 generation = h & 0xffffffff; - mem_arena* arena = &__mgData.handleArena; - if(index*sizeof(mg_handle_slot) < arena->offset) + if(index < __mgData.handleNextIndex) { - mg_handle_slot* slot = (mg_handle_slot*)arena->ptr + index; + mg_handle_slot* slot = &__mgData.handleArray[index]; if( slot->generation == generation && slot->kind == kind) { @@ -278,10 +282,10 @@ mg_image_data* mg_image_data_from_handle(mg_image handle) //--------------------------------------------------------------- #if MG_COMPILE_BACKEND_GL - #if defined(OS_WIN64) + #if defined(PLATFORM_WIN64) #include"wgl_surface.h" #define gl_surface_create_for_window mg_wgl_surface_create_for_window - #elif defined(OS_MACOS) + #elif defined(PLATFORM_MACOS) /* #include"nsgl_surface.h" #define gl_surface_create_for_window nsgl_surface_create_for_window @@ -328,7 +332,7 @@ bool mg_is_canvas_backend_available(mg_backend_id backend) #if MG_COMPILE_BACKEND_METAL case MG_BACKEND_METAL: #endif - #if MG_COMPILE_BACKEND_GL && defined(OS_WIN64) + #if MG_COMPILE_BACKEND_GL && defined(PLATFORM_WIN64) case MG_BACKEND_GL: #endif result = true; @@ -417,9 +421,9 @@ mg_surface mg_surface_create_host(mp_window window) } mg_surface handle = mg_surface_nil(); mg_surface_data* surface = 0; - #if OS_MACOS + #if PLATFORM_MACOS surface = mg_osx_surface_create_host(window); - #elif OS_WIN64 + #elif PLATFORM_WIN64 surface = mg_win32_surface_create_host(window); #endif diff --git a/src/graphics.h b/src/graphics.h index 3af4585..c27b009 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -26,7 +26,7 @@ typedef enum { //NOTE: these macros are used to select which backend to include when building milepost // they can be overridden by passing them to the compiler command line -#if defined(OS_MACOS) +#if defined(PLATFORM_MACOS) #ifndef MG_COMPILE_BACKEND_METAL #define MG_COMPILE_BACKEND_METAL 1 #endif @@ -45,7 +45,7 @@ typedef enum { #define MG_BACKEND_DEFAULT MG_BACKEND_NONE #endif -#elif defined(OS_WIN64) +#elif defined(PLATFORM_WIN64) #ifndef MG_COMPILE_BACKEND_GL #define MG_COMPILE_BACKEND_GL 1 #endif @@ -60,7 +60,7 @@ typedef enum { #define MG_BACKEND_DEFAULT MG_BACKEND_NONE #endif -#elif defined(OS_LINUX) +#elif defined(PLATFORM_LINUX) #ifndef MG_COMPILE_BACKEND_GL #define MG_COMPILE_BACKEND_GL 1 #endif diff --git a/src/milepost.c b/src/milepost.c index 93e0811..f5b3b9f 100644 --- a/src/milepost.c +++ b/src/milepost.c @@ -22,12 +22,12 @@ //--------------------------------------------------------------- #include"platform.h" -#if defined(OS_WIN64) - #include"platform/win32_base_allocator.c" +#if defined(PLATFORM_WIN64) + #include"platform/win32_memory.c" #include"platform/win32_clock.c" //TODO -#elif defined(OS_MACOS) - #include"platform/unix_base_allocator.c" +#elif defined(PLATFORM_MACOS) + #include"platform/unix_memory.c" #include"platform/osx_clock.c" /* #include"platform/unix_rng.c" @@ -35,8 +35,8 @@ #include"platform/posix_socket.c" */ -#elif defined(OS_LINUX) - #include"platform/unix_base_allocator.c" +#elif defined(PLATFORM_LINUX) + #include"platform/unix_base_memory.c" #include"platform/linux_clock.c" /* #include"platform/unix_rng.c" @@ -51,7 +51,7 @@ // app/graphics layer //--------------------------------------------------------------- -#if defined(OS_WIN64) +#if defined(PLATFORM_WIN64) #include"win32_app.c" #include"graphics.c" @@ -68,7 +68,7 @@ #include"egl_surface.c" #endif -#elif defined(OS_MACOS) +#elif defined(PLATFORM_MACOS) //NOTE: macos application layer and graphics backends are defined in milepost.m #else #error "Unsupported platform" diff --git a/src/mp_app_internal.h b/src/mp_app_internal.h index 7f6facf..e0d61a2 100644 --- a/src/mp_app_internal.h +++ b/src/mp_app_internal.h @@ -14,9 +14,9 @@ #include"platform.h" #include"ringbuffer.h" -#if defined(OS_WIN64) || defined(OS_WIN32) +#if defined(PLATFORM_WIN64) || defined(PLATFORM_WIN32) #include"win32_app.h" -#elif defined(OS_MACOS) +#elif defined(PLATFORM_MACOS) #include"osx_app.h" #else #error "platform not supported yet" diff --git a/src/platform/orca_memory.c b/src/platform/orca_memory.c new file mode 100644 index 0000000..c5248c1 --- /dev/null +++ b/src/platform/orca_memory.c @@ -0,0 +1,87 @@ +/************************************************************//** +* +* @file: orca_memory.c +* @author: Martin Fouilleul +* @date: 17/04/2023 +* +*****************************************************************/ + +#include"platform_memory.h" + +extern void* orca_mem_grow(u64 size); + +void* orca_mem_base_reserve(mem_base_allocator* context, u64 size) +{ + return(orca_mem_grow(size)); +} + +void orca_mem_base_nop(mem_base_allocator* context, void* ptr, u64 size) {} + +mem_base_allocator* mem_base_allocator_default() +{ + static mem_base_allocator base = {0}; + if(base.reserve == 0) + { + base.reserve = orca_mem_base_reserve; + base.commit = orca_mem_base_nop; + base.decommit = orca_mem_base_nop; + base.release = orca_mem_base_nop; + } + return(&base); +} + + +//TODO: implement malloc/realloc/free/memset/etc here... + + +void* memset(void* b, int c, size_t n) +{ + for(size_t i = 0; i=0; i--) + { + ((char*)dst)[i] = ((char*)src)[i]; + } + } + else if(src > dst) + { + for(size_t i = 0; ireserve(base, size) +#define mem_base_commit(base, ptr, size) base->commit(base, ptr, size) +#define mem_base_decommit(base, ptr, size) base->decommit(base, ptr, size) +#define mem_base_release(base, ptr, size) base->release(base, ptr, size) + +//-------------------------------------------------------------------------------- +//NOTE(martin): malloc/free +//-------------------------------------------------------------------------------- +#if PLATFORM_ORCA + void* malloc(size_t size); + void* realloc(void* ptr, size_t size); + void free(void* ptr); +#else + #include +#endif + +#define malloc_type(type) ((type*)malloc(sizeof(type))) +#define malloc_array(type, count) ((type*)malloc(sizeof(type)*count)) + +//-------------------------------------------------------------------------------- +//NOTE(martin): memset / memcpy +//-------------------------------------------------------------------------------- + +#if PLATFORM_ORCA + void* memset(void *b, int c, size_t len); + void* memcpy(void *restrict dst, const void *restrict src, size_t n); + void* memmove(void *dst, const void *src, size_t len); + int memcmp(const void *s1, const void *s2, size_t n); +#else + #include +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //__PLATFORM_MEMORY_H_ diff --git a/src/platform/unix_base_allocator.c b/src/platform/unix_memory.c similarity index 73% rename from src/platform/unix_base_allocator.c rename to src/platform/unix_memory.c index 839dae7..2b8e5aa 100644 --- a/src/platform/unix_base_allocator.c +++ b/src/platform/unix_memory.c @@ -1,25 +1,25 @@ /************************************************************//** * -* @file: unix_base_allocator.c +* @file: unix_memory.c * @author: Martin Fouilleul * @date: 10/09/2021 * @revision: * *****************************************************************/ #include -#include"platform_base_allocator.h" +#include"platform_memory.h" /*NOTE(martin): Linux and MacOS don't make a distinction between reserved and committed memory, contrary to Windows */ -void mem_base_nop(void* context, void* ptr, u64 size) {} +void mem_base_nop(mem_base_allocator* context, void* ptr, u64 size) {} -void* mem_base_reserve_mmap(void* context, u64 size) +void* mem_base_reserve_mmap(mem_base_allocator* context, u64 size) { return(mmap(0, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0)); } -void mem_base_release_mmap(void* context, void* ptr, u64 size) +void mem_base_release_mmap(mem_base_allocator* context, void* ptr, u64 size) { munmap(ptr, size); } diff --git a/src/platform/win32_base_allocator.c b/src/platform/win32_memory.c similarity index 67% rename from src/platform/win32_base_allocator.c rename to src/platform/win32_memory.c index 92e58c7..2b6e391 100644 --- a/src/platform/win32_base_allocator.c +++ b/src/platform/win32_memory.c @@ -1,6 +1,6 @@ /************************************************************//** * -* @file: win32_base_allocator.c +* @file: win32_memory.c * @author: Martin Fouilleul * @date: 17/12/2022 * @revision: @@ -8,27 +8,25 @@ *****************************************************************/ #define WIN32_LEAN_AND_MEAN #include -#include"platform_base_allocator.h" +#include"platform_memory.h" -void mem_base_nop(void* context, void* ptr, u64 size) {} - -void* mem_base_reserve_win32(void* context, u64 size) +void* mem_base_reserve_win32(mem_base_allocator* context, u64 size) { void* result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); return(result); } -void mem_base_commit_win32(void* context, void* ptr, u64 size) +void mem_base_commit_win32(mem_base_allocator* context, void* ptr, u64 size) { VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE); } -void mem_base_release_win32(void* context, void* ptr, u64 size) +void mem_base_release_win32(mem_base_allocator* context, void* ptr, u64 size) { VirtualFree(ptr, size, MEM_RELEASE); } -void mem_base_decommit_win32(void* context, void* ptr, u64 size) +void mem_base_decommit_win32(mem_base_allocator* context, void* ptr, u64 size) { VirtualFree(ptr, size, MEM_DECOMMIT); } diff --git a/src/ui.c b/src/ui.c index 126c417..5acf27c 100644 --- a/src/ui.c +++ b/src/ui.c @@ -2340,9 +2340,9 @@ typedef struct ui_edit_command } ui_edit_command; -#if OS_WIN64 +#if PLATFORM_WIN64 #define OS_COPY_PASTE_MOD MP_KEYMOD_CTRL -#elif OS_MACOS +#elif PLATFORM_MACOS #define OS_COPY_PASTE_MOD MP_KEYMOD_CMD #endif diff --git a/src/util/macro_helpers.h b/src/util/macro_helpers.h index e7f5cfd..dc3b93c 100644 --- a/src/util/macro_helpers.h +++ b/src/util/macro_helpers.h @@ -21,11 +21,6 @@ //TODO(martin): this is a compiler-specific attribute, recognized by clang and gcc. See if there's a more portable approach //#define INLINE_GEN __attribute__((used)) static inline - -//NOTE(martin): typed and array mallocs -#define malloc_type(type) ((type*)malloc(sizeof(type))) -#define malloc_array(type, count) ((type*)malloc(sizeof(type)*count)) - //NOTE(martin): 'hygienic' templates, to replace macros and avoid multiple evaluation problems. #ifdef __cplusplus //NOTE(martin): in C++ we use templates and decltype/declval @@ -128,9 +123,11 @@ static inline u64 next_pow2_u64(u64 x) //NOTE: assert macros #ifndef NO_ASSERT - #ifdef OS_ORCA + #ifdef PLATFORM_ORCA //TODO add a runtime-provided assert - #define _ASSERT_(x, msg) + extern void orca_assert(bool x); + + #define _ASSERT_(x, msg) orca_assert(x) #else #include #define _ASSERT_(x, msg) assert(x && msg) diff --git a/src/util/memory.c b/src/util/memory.c index 93e6d92..1cc19c4 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -6,19 +6,43 @@ * @revision: * *****************************************************************/ -#include // memset - #include"platform.h" #include"memory.h" -#include"platform_base_allocator.h" +#include"platform_memory.h" #include"macro_helpers.h" -static const u32 MEM_ARENA_DEFAULT_RESERVE_SIZE = 1<<30; -static const u32 MEM_ARENA_COMMIT_ALIGNMENT = 1<<20; +#if PLATFORM_ORCA + static const u32 MEM_ARENA_DEFAULT_RESERVE_SIZE = 1<<20; +#else + static const u32 MEM_ARENA_DEFAULT_RESERVE_SIZE = 1<<30; +#endif + +static const u32 MEM_ARENA_COMMIT_ALIGNMENT = 4<<10; //-------------------------------------------------------------------------------- //NOTE(martin): memory arena //-------------------------------------------------------------------------------- + +mem_arena_chunk* mem_arena_chunk_alloc(mem_arena* arena, u64 reserveSize) +{ + reserveSize = AlignUpOnPow2(reserveSize, MEM_ARENA_COMMIT_ALIGNMENT); + u64 commitSize = AlignUpOnPow2(sizeof(mem_arena_chunk), MEM_ARENA_COMMIT_ALIGNMENT); + + char* mem = mem_base_reserve(arena->base, reserveSize); + mem_base_commit(arena->base, mem, commitSize); + + mem_arena_chunk* chunk = (mem_arena_chunk*)mem; + + chunk->ptr = mem; + chunk->cap = reserveSize; + chunk->offset = sizeof(mem_arena_chunk); + chunk->committed = commitSize; + + list_push_back(&arena->chunks, &chunk->listElt); + + return(chunk); +} + void mem_arena_init(mem_arena* arena) { mem_arena_init_with_options(arena, &(mem_arena_options){0}); @@ -26,42 +50,69 @@ void mem_arena_init(mem_arena* arena) void mem_arena_init_with_options(mem_arena* arena, mem_arena_options* options) { - arena->base = options->base ? options->base : mem_base_allocator_default(); - arena->cap = options->reserve ? options->reserve : MEM_ARENA_DEFAULT_RESERVE_SIZE; + memset(arena, 0, sizeof(mem_arena)); - arena->ptr = mem_base_reserve(arena->base, arena->cap); - arena->committed = 0; - arena->offset = 0; + arena->base = options->base ? options->base : mem_base_allocator_default(); + + u64 reserveSize = options->reserve ? (options->reserve + sizeof(mem_arena_chunk)) : MEM_ARENA_DEFAULT_RESERVE_SIZE; + + arena->currentChunk = mem_arena_chunk_alloc(arena, reserveSize); } void mem_arena_release(mem_arena* arena) { - mem_base_release(arena->base, arena->ptr, arena->cap); + for_list_safe(&arena->chunks, chunk, mem_arena_chunk, listElt) + { + mem_base_release(arena->base, chunk, chunk->cap); + } memset(arena, 0, sizeof(mem_arena)); } void* mem_arena_alloc(mem_arena* arena, u64 size) { - u64 nextOffset = arena->offset + size; - ASSERT(nextOffset <= arena->cap); + mem_arena_chunk* chunk = arena->currentChunk; + ASSERT(chunk); - if(nextOffset > arena->committed) + u64 nextOffset = chunk->offset + size; + u64 lastCap = chunk->cap; + while(chunk && nextOffset > chunk->cap) + { + chunk = list_next_entry(&arena->chunks, chunk, mem_arena_chunk, listElt); + nextOffset = chunk->offset + size; + lastCap = chunk->cap; + } + if(!chunk) + { + u64 reserveSize = maximum(lastCap * 1.5, size); + + chunk = mem_arena_chunk_alloc(arena, reserveSize); + nextOffset = chunk->offset + size; + } + ASSERT(nextOffset <= chunk->cap); + + arena->currentChunk = chunk; + + if(nextOffset > chunk->committed) { u64 nextCommitted = AlignUpOnPow2(nextOffset, MEM_ARENA_COMMIT_ALIGNMENT); - nextCommitted = ClampHighBound(nextCommitted, arena->cap); - u64 commitSize = nextCommitted - arena->committed; - mem_base_commit(arena->base, arena->ptr + arena->committed, commitSize); - arena->committed = nextCommitted; + nextCommitted = ClampHighBound(nextCommitted, chunk->cap); + u64 commitSize = nextCommitted - chunk->committed; + mem_base_commit(arena->base, chunk->ptr + chunk->committed, commitSize); + chunk->committed = nextCommitted; } - char* p = arena->ptr + arena->offset; - arena->offset += size; + char* p = chunk->ptr + chunk->offset; + chunk->offset += size; return(p); } void mem_arena_clear(mem_arena* arena) { - arena->offset = 0; + for_list(&arena->chunks, chunk, mem_arena_chunk, listElt) + { + chunk->offset = sizeof(mem_arena_chunk); + } + arena->currentChunk = list_first_entry(&arena->chunks, mem_arena_chunk, listElt); } //-------------------------------------------------------------------------------- @@ -98,7 +149,6 @@ void* mem_pool_alloc(mem_pool* pool) void mem_pool_recycle(mem_pool* pool, void* ptr) { - ASSERT((((char*)ptr) >= pool->arena.ptr) && (((char*)ptr) < (pool->arena.ptr + pool->arena.offset))); list_push(&pool->freeList, (list_elt*)ptr); } @@ -117,7 +167,7 @@ mp_thread_local mem_arena __scratchArena = {0}; mem_arena* mem_scratch() { - if(__scratchArena.ptr == 0) + if(__scratchArena.base == 0) { mem_arena_init(&__scratchArena); } diff --git a/src/util/memory.h b/src/util/memory.h index 64db02c..1659942 100644 --- a/src/util/memory.h +++ b/src/util/memory.h @@ -9,46 +9,33 @@ #ifndef __MEMORY_H_ #define __MEMORY_H_ -#include"typedefs.h" -#include"lists.h" +#include"util/typedefs.h" +#include"util/lists.h" +#include"platform/platform_memory.h" #ifdef __cplusplus extern "C" { #endif -//-------------------------------------------------------------------------------- -//NOTE(martin): base allocator -//-------------------------------------------------------------------------------- -typedef void*(*mem_reserve_function)(void* context, u64 size); -typedef void(*mem_modify_function)(void* context, void* ptr, u64 size); - -typedef struct mem_base_allocator -{ - mem_reserve_function reserve; - mem_modify_function commit; - mem_modify_function decommit; - mem_modify_function release; - void* context; - -} mem_base_allocator; - -MP_API mem_base_allocator* mem_base_allocator_default(); - -#define mem_base_reserve(base, size) base->reserve(base->context, size) -#define mem_base_commit(base, ptr, size) base->commit(base->context, ptr, size) -#define mem_base_decommit(base, ptr, size) base->decommit(base->context, ptr, size) -#define mem_base_release(base, ptr, size) base->release(base->context, ptr, size) - //-------------------------------------------------------------------------------- //NOTE(martin): memory arena //-------------------------------------------------------------------------------- -typedef struct mem_arena + +typedef struct mem_arena_chunk { - mem_base_allocator* base; + list_elt listElt; char* ptr; u64 offset; u64 committed; u64 cap; +} mem_arena_chunk; + +typedef struct mem_arena +{ + mem_base_allocator* base; + list_info chunks; + mem_arena_chunk* currentChunk; + } mem_arena; typedef struct mem_arena_options diff --git a/src/util/typedefs.h b/src/util/typedefs.h index 434a462..05a4e6f 100644 --- a/src/util/typedefs.h +++ b/src/util/typedefs.h @@ -10,7 +10,8 @@ #ifndef __TYPEDEFS_H_ #define __TYPEDEFS_H_ -#include +#include +#include #include //FLT_MAX/MIN etc... #ifndef __cplusplus