From 271c2cc22af1b56fa29152fb493c70dfde6c4ec6 Mon Sep 17 00:00:00 2001 From: Valerio Santinelli Date: Wed, 27 Sep 2023 16:39:45 +0200 Subject: [PATCH] More work with the memory arena implementation --- .idea/fileTemplates/includes/C File Header.h | 2 +- CMakeLists.txt | 4 + CMakeOptions.txt | 1 + docs/conf.py | 2 +- example/main.c | 10 + src/binocle/core/binocle_math.h | 10 + src/binocle/core/binocle_memory.c | 297 +++++++++++++++++++ src/binocle/core/binocle_memory.h | 151 ++++++++++ 8 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 src/binocle/core/binocle_memory.c create mode 100644 src/binocle/core/binocle_memory.h diff --git a/.idea/fileTemplates/includes/C File Header.h b/.idea/fileTemplates/includes/C File Header.h index b9836c4c..4cb5a568 100644 --- a/.idea/fileTemplates/includes/C File Header.h +++ b/.idea/fileTemplates/includes/C File Header.h @@ -1,7 +1,7 @@ #if ($HEADER_COMMENTS) // // Binocle -// Copyright (c) 2015-2019 Valerio Santinelli +// Copyright (c) 2015-2023 Valerio Santinelli // All rights reserved. // #end diff --git a/CMakeLists.txt b/CMakeLists.txt index da5c8be3..9522e62a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,10 @@ if(BINOCLE_SHOW_CONSOLE) add_definitions(-DBINOCLE_SHOW_CONSOLE) endif() +if(BINOCLE_LOG_MEMORY_ALLOCATIONS) + add_definitions(-DBINOCLE_LOG_MEMORY_ALLOCATIONS) +endif() + include(BinocleUtils) SET(VERSION_MAJOR "0") diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 45153483..1225181d 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -1,3 +1,4 @@ option(BINOCLE_LUAJIT "Enable LuaJIT on supported platforms (Windows, macOS)" OFF) option(BINOCLE_SHOW_CONSOLE "Enable console output on Windows" OFF) option(BINOCLE_HTTP "Enable HTTP support on supported platforms (Windows, macOS, web)" ON) +option(BINOCLE_LOG_MEMORY_ALLOCATIONS "Enable logging of memory allocations through the memory arena" OFF) diff --git a/docs/conf.py b/docs/conf.py index 98e3ee46..909bb272 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ # -- Project information ----------------------------------------------------- project = 'Binocle' -copyright = '2019-2021, Valerio Santinelli' +copyright = '2019-2023, Valerio Santinelli' author = 'Valerio Santinelli' diff --git a/example/main.c b/example/main.c index 1af32696..a75075f3 100644 --- a/example/main.c +++ b/example/main.c @@ -29,6 +29,7 @@ #include "binocle_audio.h" #include "sokol_gfx.h" #include "binocle_http.h" +#include "binocle_memory.h" //#define GAMELOOP 1 //#define DEMOLOOP @@ -72,6 +73,10 @@ typedef struct screen_shader_vs_params_t { float transform[16]; } screen_shader_vs_params_t; +typedef struct game_state_t { + binocle_memory_arena *main_arena; +} game_state_t; + binocle_window *window; binocle_input input; binocle_viewport_adapter *adapter; @@ -97,6 +102,7 @@ sg_image render_target; sg_shader default_shader; sg_shader screen_shader; sg_image wabbit_image; +game_state_t *game_state; #if (defined(__APPLE__) && !defined(__IPHONEOS__)) || defined(__EMSCRIPTEN__) #define WITH_PHYSICS @@ -444,6 +450,10 @@ void main_loop() { int main(int argc, char *argv[]) { +// binocle_memory_init(); +// game_state = binocle_memory_bootstrap_push_struct(game_state_t, main_arena, binocle_memory_default_bootstrap_params(), binocle_memory_default_arena_params()); +// binocle_string s1 = binocle_memory_push_cstring(game_state->main_arena, "Something"); +// binocle_string s2 = binocle_memory_push_cstring(game_state->main_arena, "Different"); binocle_app_desc_t app_desc = {0}; app = binocle_app_new(); binocle_app_init(&app, &app_desc); diff --git a/src/binocle/core/binocle_math.h b/src/binocle/core/binocle_math.h index b9832460..971ba070 100644 --- a/src/binocle/core/binocle_math.h +++ b/src/binocle/core/binocle_math.h @@ -7,6 +7,8 @@ #ifndef BINOCLE_MATH_H #define BINOCLE_MATH_H +#include +#include #include #ifndef MIN @@ -65,4 +67,12 @@ static kmMat4 binocle_math_create_orthographic_matrix_off_center( } //#endif //BINOCLE_MATH_IMPL +static bool binocle_math_is_pow_2(uint32_t value) +{ + bool result = ((value & ~(value - 1)) == value); + return result; +} + +#define binocle_math_align_pow_2(value, alignment) (((value) + ((alignment) - 1)) & ~(((value) - (value)) + (alignment) - 1)) + #endif //BINOCLE_MATH_H diff --git a/src/binocle/core/binocle_memory.c b/src/binocle/core/binocle_memory.c new file mode 100644 index 00000000..b5aecc32 --- /dev/null +++ b/src/binocle/core/binocle_memory.c @@ -0,0 +1,297 @@ +// +// Binocle +// Copyright (c) 2015-2019 Valerio Santinelli +// All rights reserved. +// + +#include "binocle_memory.h" +#include "SDL_stdinc.h" +#include "binocle_math.h" +#include +#include + +static binocle_memory_state g_memory_state; + +void binocle_memory_init() { + g_memory_state.memory_sentinel.prev = &g_memory_state.memory_sentinel; + g_memory_state.memory_sentinel.next = &g_memory_state.memory_sentinel; +} + +binocle_memory_arena_push_params binocle_memory_default_arena_params(void) { + binocle_memory_arena_push_params params; + params.flags = BINOCLE_MEMORY_ARENA_PUSH_FLAG_CLEAR_TO_ZERO; + params.alignment = 4; + return params; +} + +binocle_memory_arena_bootstrap_params +binocle_memory_default_bootstrap_params(void) { + binocle_memory_arena_bootstrap_params params = {}; + return params; +} + +binocle_memory_index +binocle_memory_get_alignment_offset(binocle_memory_arena *Arena, + binocle_memory_index alignment) { + binocle_memory_index alignment_offset = 0; + + binocle_memory_index result_pointer = + (binocle_memory_index)Arena->current_block->base + + Arena->current_block->used; + binocle_memory_index alignment_mask = alignment - 1; + if (result_pointer & alignment_mask) { + alignment_offset = alignment - (result_pointer & alignment_mask); + } + + return (alignment_offset); +} + +binocle_memory_index +binocle_memory_get_effective_size_for(binocle_memory_arena *arena, + binocle_memory_index size_init, + binocle_memory_arena_push_params params) { + binocle_memory_index size = size_init; + + binocle_memory_index alignment_offset = + binocle_memory_get_alignment_offset(arena, params.alignment); + size += alignment_offset; + + return (size); +} + +void binocle_memory_zero_size(binocle_memory_index size, void *ptr) { + uint8_t *byte = (uint8_t *)ptr; + while (size--) { + *byte++ = 0; + } +} + +binocle_memory_block *binocle_memory_allocate(binocle_memory_index size, + uint64_t flags) { + // We require memory block headers not to change the cache + // line alignment of an allocation + assert(sizeof(binocle_memory_platform_block) == 56); + + uintptr_t PageSize = 4096; + uintptr_t TotalSize = size + sizeof(binocle_memory_platform_block); + uintptr_t BaseOffset = sizeof(binocle_memory_platform_block); + uintptr_t ProtectOffset = 0; + if(flags & BINOCLE_MEMORY_FLAG_CHECK_UNDERFLOW) + { + TotalSize = size + 2*PageSize; + BaseOffset = 2*PageSize; + ProtectOffset = PageSize; + } + else if(flags & BINOCLE_MEMORY_FLAG_CHECK_OVERFLOW) + { + uintptr_t SizeRoundedUp = binocle_math_align_pow_2(size, PageSize); + TotalSize = SizeRoundedUp + 2*PageSize; + BaseOffset = PageSize + SizeRoundedUp - size; + ProtectOffset = PageSize + SizeRoundedUp; + } + + binocle_memory_platform_block *Block = (binocle_memory_platform_block *) + mmap(0, TotalSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(Block != MAP_FAILED); + Block->block.base = (uint8_t *)Block + BaseOffset; + assert(Block->block.used == 0); + assert(Block->block.prev_arena == 0); + + if(flags & (BINOCLE_MEMORY_FLAG_CHECK_UNDERFLOW|BINOCLE_MEMORY_FLAG_CHECK_OVERFLOW)) + { + int Protected = mprotect((uint8_t *)Block + ProtectOffset, PageSize, PROT_NONE); + assert(Protected != -1); + } + + binocle_memory_platform_block *Sentinel = &g_memory_state.memory_sentinel; + Block->next = Sentinel; + Block->block.size = size; + Block->block.flags = flags; + Block->prev = Sentinel->prev; + Block->prev->next = Block; + Block->next->prev = Block; + + binocle_memory_block *PlatBlock = &Block->block; + return PlatBlock; +} + + + + +// binocle_memory_block *block = SDL_calloc(1, size); +// block->flags = flags; +// block->size = size; +// block->base = (uint8_t *)block; +// return block; +//} + +void * +binocle_memory_push_size_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + binocle_memory_index size_init, + binocle_memory_arena_push_params params) { + void *result; + + assert(params.alignment <= 128); + assert(binocle_math_is_pow_2(params.alignment)); + + binocle_memory_index size = 0; + if (arena->current_block) { + size = binocle_memory_get_effective_size_for(arena, size_init, params); + } + + if (!arena->current_block || + ((arena->current_block->used + size) > arena->current_block->size)) { + size = size_init; + if (arena->allocation_flags & (BINOCLE_MEMORY_FLAG_CHECK_OVERFLOW | + BINOCLE_MEMORY_FLAG_CHECK_UNDERFLOW)) { + arena->minimum_block_size = 0; + size = binocle_math_align_pow_2(size, params.alignment); + } else if (!arena->minimum_block_size) { + arena->minimum_block_size = 1024 * 1024; + } + + binocle_memory_index block_size = MAX(size, arena->minimum_block_size); + + binocle_memory_block *new_block = + binocle_memory_allocate(block_size, arena->allocation_flags); + new_block->prev_arena = arena->current_block; + arena->current_block = new_block; + } + + assert((arena->current_block->used + size) <= arena->current_block->size); + + binocle_memory_index alignment_offset = + binocle_memory_get_alignment_offset(arena, params.alignment); + uintptr_t OffsetInBlock = arena->current_block->used + alignment_offset; + result = arena->current_block->base + OffsetInBlock; + arena->current_block->used += size; + + assert(size >= size_init); + assert(arena->current_block->used <= arena->current_block->size); + + if (params.flags & BINOCLE_MEMORY_ARENA_PUSH_FLAG_CLEAR_TO_ZERO) { + binocle_memory_zero_size(size_init, result); + } + + return result; +} + +void *binocle_memory_bootstrap_push_size( + BINOCLE_MEMORY_PARAM uintptr_t struct_size, uintptr_t offset_to_arena, + binocle_memory_arena_bootstrap_params bootstrap_params, + binocle_memory_arena_push_params push_params) { + binocle_memory_arena bootstrap = {}; + bootstrap.allocation_flags = bootstrap_params.allocation_flags; + bootstrap.minimum_block_size = bootstrap_params.minimum_block_size; + void *res_struct = binocle_memory_push_size_(BINOCLE_MEMORY_PASS & bootstrap, + struct_size, push_params); + *(binocle_memory_arena *)((uint8_t *)res_struct + offset_to_arena) = + bootstrap; + + return (res_struct); +} + +uint32_t binocle_string_length(char *string) { + uint32_t count = 0; + if (string) { + while (*string++) { + ++count; + } + } + + return (count); +} + +void *binocle_memory_copy(binocle_memory_index size, void *source_init, + void *dest_init) { + uint8_t *Source = (uint8_t *)source_init; + uint8_t *Dest = (uint8_t *)dest_init; + while (size--) { + *Dest++ = *Source++; + } + + return dest_init; +} + +binocle_memory_arena_push_params binocle_memory_no_clear(void) { + binocle_memory_arena_push_params params = + binocle_memory_default_arena_params(); + params.flags &= ~BINOCLE_MEMORY_ARENA_PUSH_FLAG_CLEAR_TO_ZERO; + return params; +} + +void * +binocle_memory_push_copy_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + uintptr_t size, void *source, + binocle_memory_arena_push_params params) { + void *result = + binocle_memory_push_size_(BINOCLE_MEMORY_PASS arena, size, params); + binocle_memory_copy(size, source, result); + return (result); +} + +char * +binocle_memory_push_string_z_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + char *source) { + uint32_t size = 1; + for (char *At = source; *At; ++At) { + ++size; + } + + char *dest = (char *)binocle_memory_push_size_( + BINOCLE_MEMORY_PASS arena, size, binocle_memory_no_clear()); + for (uint32_t char_index = 0; char_index < size; ++char_index) { + dest[char_index] = source[char_index]; + } + + return (dest); +} + +binocle_buffer +binocle_memory_push_buffer_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + uintptr_t size, + binocle_memory_arena_push_params params) { + binocle_buffer result; + result.count = size; + result.data = (uint8_t *)binocle_memory_push_size_(BINOCLE_MEMORY_PASS arena, + result.count, params); + + return (result); +} + +binocle_string +binocle_memory_push_cstring_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + char *source) { + binocle_string result; + result.count = binocle_string_length(source); + result.data = (uint8_t *)binocle_memory_push_copy_( + BINOCLE_MEMORY_PASS arena, result.count, source, + binocle_memory_default_arena_params()); + + return (result); +} + +binocle_string +binocle_memory_push_string_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + binocle_string source) { + binocle_string result; + result.count = source.count; + result.data = (uint8_t *)binocle_memory_push_copy_( + BINOCLE_MEMORY_PASS arena, result.count, source.data, + binocle_memory_default_arena_params()); + + return (result); +} + +char *binocle_memory_push_and_null_terminate_( + BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, uint32_t length, + const char *source) { + char *dest = (char *)binocle_memory_push_size_( + BINOCLE_MEMORY_PASS arena, length + 1, binocle_memory_no_clear()); + for (uint32_t char_index = 0; char_index < length; ++char_index) { + dest[char_index] = source[char_index]; + } + dest[length] = 0; + + return (dest); +} \ No newline at end of file diff --git a/src/binocle/core/binocle_memory.h b/src/binocle/core/binocle_memory.h new file mode 100644 index 00000000..4c6c981f --- /dev/null +++ b/src/binocle/core/binocle_memory.h @@ -0,0 +1,151 @@ +// +// Binocle +// Copyright (c) 2015-2019 Valerio Santinelli +// All rights reserved. +// + +#ifndef BINOCLE_BINOCLE_MEMORY_H +#define BINOCLE_BINOCLE_MEMORY_H + +#include +#include + +#if BINOCLE_LOG_MEMORY_ALLOCATIONS +#define BINOCLE_DEBUG_NAME__(A, B, C) A "|" #B "|" #C +#define BINOCLE_DEBUG_NAME_(A, B, C) BINOCLE_DEBUG_NAME__(A, B, C) +#define BINOCLE_DEBUG_NAME(Name) BINOCLE_DEBUG_NAME_(__FILE__, __LINE__, __COUNTER__) +#define BINOCLE_DEBUG_MEMORY_NAME(Name) BINOCLE_DEBUG_NAME_(__FILE__, __LINE__, __COUNTER__), +#define BINOCLE_MEMORY_PARAM char *GUID, +#define BINOCLE_MEMORY_PASS GUID, +#else +#define BINOCLE_DEBUG_MEMORY_NAME(Name) +#define BINOCLE_MEMORY_PARAM +#define BINOCLE_MEMORY_PASS +#endif + +#define BINOCLE_MEMORY_OFFSET_OF(type, Member) ((uintptr_t)&(((type *)0)->Member)) + +typedef enum binocle_memory_block_flags { + BINOCLE_MEMORY_FLAG_NOT_RESTORED = 1, + BINOCLE_MEMORY_FLAG_CHECK_OVERFLOW = 1 << 1, + BINOCLE_MEMORY_FLAG_CHECK_UNDERFLOW = 1 << 2, +} binocle_memory_flags; + +typedef struct binocle_memory_block { + uint64_t flags; + uint64_t size; + uint8_t *base; + uintptr_t used; + struct binocle_memory_block *prev_arena; +} binocle_memory_block; + +typedef struct binocle_memory_platform_block { + binocle_memory_block block; + struct binocle_memory_platform_block *prev; + struct binocle_memory_platform_block *next; +} binocle_memory_platform_block; + +typedef struct binocle_memory_arena { + binocle_memory_block *current_block; + uintptr_t minimum_block_size; + uint64_t allocation_flags; + int32_t temp_count; +} binocle_memory_arena; + +typedef struct binocle_temporary_memory { + binocle_memory_arena *arena; + binocle_memory_block *block; + uintptr_t used; +} binocle_temporary_memory; + +typedef enum binocle_memory_arena_push_flag +{ + BINOCLE_MEMORY_ARENA_PUSH_FLAG_CLEAR_TO_ZERO = 1, +} binocle_memory_arena_push_flag; + +typedef struct binocle_memory_arena_push_params +{ + uint32_t flags; + uint32_t alignment; +} binocle_memory_arena_push_params; + +typedef struct binocle_memory_arena_bootstrap_params +{ + uint64_t allocation_flags; + uintptr_t minimum_block_size; +} binocle_memory_arena_bootstrap_params; + +typedef size_t binocle_memory_index; + +typedef struct binocle_buffer +{ + uintptr_t count; + uint8_t *data; +} binocle_buffer; + +typedef binocle_buffer binocle_string; + +typedef struct binocle_memory_state { + binocle_memory_platform_block memory_sentinel; +} binocle_memory_state; + +void binocle_memory_init(); + +binocle_memory_arena_push_params binocle_memory_default_arena_params(void); + +binocle_memory_arena_bootstrap_params +binocle_memory_default_bootstrap_params(void); + +void *binocle_memory_bootstrap_push_size( + BINOCLE_MEMORY_PARAM uintptr_t struct_size, uintptr_t offset_to_arena, + binocle_memory_arena_bootstrap_params bootstrap_params, + binocle_memory_arena_push_params push_params); + +void * +binocle_memory_push_size_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + binocle_memory_index size_init, + binocle_memory_arena_push_params params); + +void * +binocle_memory_push_copy_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + uintptr_t size, void *source, + binocle_memory_arena_push_params params); + +binocle_buffer +binocle_memory_push_buffer_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + uintptr_t size, + binocle_memory_arena_push_params params); + +char * +binocle_memory_push_string_z_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + char *source); + +binocle_string +binocle_memory_push_cstring_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + char *source); + +binocle_string +binocle_memory_push_string_(BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, + binocle_string source); + +char *binocle_memory_push_and_null_terminate_( + BINOCLE_MEMORY_PARAM binocle_memory_arena *arena, uint32_t length, + const char *source); + +#define binocle_memory_copy_array(count, source, dest) binocle_memory_copy((count)*sizeof(*(source)), (source), (dest)) + +#define binocle_memory_zero_struct(instance) binocle_memory_zero_size(sizeof(instance), &(instance)) +#define binocle_memory_zero_array(count, pointer) binocle_memory_zero_size((count)*sizeof((pointer)[0]), pointer) + +#define binocle_memory_push_struct(arena, type, ...) (type *)binocle_memory_push_size_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_struct") arena, sizeof(type), ## __VA_ARGS__) +#define binocle_memory_push_array(arena, count, type, ...) (type *)binocle_memory_push_size_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_array") arena, (count)*sizeof(type), ## __VA_ARGS__) +#define binocle_memory_push_size(arena, size, ...) binocle_memory_push_size_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_size") arena, size, ## __VA_ARGS__) +#define binocle_memory_push_copy(...) binocle_memory_push_copy_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_copy") __VA_ARGS__) +#define binocle_memory_push_string_z(...) binocle_memory_push_string_z_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_string_z") __VA_ARGS__) +#define binocle_memory_push_string(...) binocle_memory_push_string_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_string") __VA_ARGS__) +#define binocle_memory_push_cstring(...) binocle_memory_push_cstring_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_cstring") __VA_ARGS__) +#define binocle_memory_push_buffer(...) binocle_memory_push_buffer_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_buffer") __VA_ARGS__) +#define binocle_memory_push_and_null_terminate(...) binocle_memory_push_and_null_terminate_(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_push_and_null_terminate_") __VA_ARGS__) +#define binocle_memory_bootstrap_push_struct(type, Member, ...) (type *)binocle_memory_bootstrap_push_size(BINOCLE_DEBUG_MEMORY_NAME("binocle_memory_bootstrap_push_size") sizeof(type), BINOCLE_MEMORY_OFFSET_OF(type, Member), ## __VA_ARGS__) + +#endif // BINOCLE_BINOCLE_MEMORY_H