base memory allocator and chunked arenas for orca

This commit is contained in:
Martin Fouilleul 2023-04-17 18:12:21 +02:00
parent f36e144bc0
commit e9e9ab68c2
16 changed files with 312 additions and 142 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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<n; i++)
{
((char*)b)[i] = (u8)c;
}
return(b);
}
void* memcpy(void *restrict dst, const void *restrict src, size_t n)
{
for(size_t i = 0; i<n; i++)
{
((char*)dst)[i] = ((char*)src)[i];
}
return(dst);
}
void* memmove(void *dst, const void *src, size_t n)
{
if(src < dst)
{
for(size_t i = n-1; i>=0; i--)
{
((char*)dst)[i] = ((char*)src)[i];
}
}
else if(src > dst)
{
for(size_t i = 0; i<n; i++)
{
((char*)dst)[i] = ((char*)src)[i];
}
}
return(dst);
}
int memcmp(const void *s1, const void *s2, size_t n)
{
const unsigned char* c1 = (const unsigned char*)s1;
const unsigned char* c2 = (const unsigned char*)s2;
for(size_t i = 0; i<n; i++)
{
if(c1[i] != c2[i])
{
return(c1 - c2);
}
}
return(0);
}

View File

@ -33,17 +33,17 @@
// OS identification
//-----------------------------------------------------------------
#if defined(_WIN64)
#define OS_WIN64 1
#define PLATFORM_WIN64 1
#elif defined(_WIN32)
#error "Unsupported OS (32bit only version of Windows)"
#elif defined(__APPLE__) && defined(__MACH__)
#define OS_MACOS 1
#define PLATFORM_MACOS 1
#elif defined(__gnu_linux__)
#define OS_LINUX 1
#define PLATFORM_LINUX 1
#elif defined(__ORCA__)
#define OS_ORCA 1
#define PLATFORM_ORCA 1
#else
#error "Can't identify OS"
#error "Can't identify platform"
#endif
//-----------------------------------------------------------------

View File

@ -1,28 +0,0 @@
/************************************************************//**
*
* @file: platform_base_allocator.h
* @author: Martin Fouilleul
* @date: 10/09/2021
* @revision:
*
*****************************************************************/
#ifndef __PLATFORM_BASE_ALLOCATOR_H_
#define __PLATFORM_BASE_ALLOCATOR_H_
#include"typedefs.h"
#include"memory.h"
#ifdef __cplusplus
extern "C" {
#endif
void* mem_base_reserve_mmap(void* context, u64 size);
void mem_base_release_mmap(void* context, void* ptr, u64 size);
mem_base_allocator* mem_base_allocator_default();
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__PLATFORM_BASE_ALLOCATOR_H_

View File

@ -0,0 +1,74 @@
/************************************************************//**
*
* @file: platform_memory.h
* @author: Martin Fouilleul
* @date: 10/09/2021
* @revision:
*
*****************************************************************/
#ifndef __PLATFORM_MEMORY_H_
#define __PLATFORM_MEMORY_H_
#include"typedefs.h"
#include"platform.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------------------
//NOTE(martin): base allocator
//--------------------------------------------------------------------------------
typedef struct mem_base_allocator mem_base_allocator;
typedef void*(*mem_reserve_function)(mem_base_allocator* context, u64 size);
typedef void(*mem_modify_function)(mem_base_allocator* 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;
} mem_base_allocator;
MP_API mem_base_allocator* mem_base_allocator_default();
#define mem_base_reserve(base, size) base->reserve(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<stdlib.h>
#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<string.h>
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__PLATFORM_MEMORY_H_

View File

@ -1,25 +1,25 @@
/************************************************************//**
*
* @file: unix_base_allocator.c
* @file: unix_memory.c
* @author: Martin Fouilleul
* @date: 10/09/2021
* @revision:
*
*****************************************************************/
#include<sys/mman.h>
#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);
}

View File

@ -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<windows.h>
#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);
}

View File

@ -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

View File

@ -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<assert.h>
#define _ASSERT_(x, msg) assert(x && msg)

View File

@ -6,19 +6,43 @@
* @revision:
*
*****************************************************************/
#include<string.h> // 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);
}

View File

@ -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

View File

@ -10,7 +10,8 @@
#ifndef __TYPEDEFS_H_
#define __TYPEDEFS_H_
#include<inttypes.h>
#include<stddef.h>
#include<stdint.h>
#include<float.h> //FLT_MAX/MIN etc...
#ifndef __cplusplus