diff --git a/gs.h b/gs.h index 2c04f1a..759d446 100644 --- a/gs.h +++ b/gs.h @@ -736,6 +736,8 @@ typedef bool32_t bool32; ===================================*/ // Operating system function pointer +// NOTE(Samdal): the name "OS" is misleading, +// these are just wrappers around libc, not the Operating System typedef struct gs_os_api_s { void* (* malloc)(size_t sz); @@ -795,7 +797,8 @@ gs_os_api_new_default(); #ifndef gs_strdup #define gs_strdup(__STR) (gs_ctx()->os.strdup(__STR)) -#endif +#endif + // Modified from: https://stackoverflow.com/questions/11815894/how-to-read-write-arbitrary-bits-in-c-c #define gs_bit_mask(INDEX, SIZE)\ @@ -2729,6 +2732,99 @@ void gs_command_buffer_free(gs_command_buffer_t* cb) * @{ */ +/*================================================================================ +// Low Level OS Memory +================================================================================*/ + +#if defined(GS_PLATFORM_WEB) || defined(GS_MEMORY_NO_LOW_LEVEL_OS_API) +# ifndef GS_MEMORY_NO_LOW_LEVEL_OS_API +# define GS_MEMORY_NO_LOW_LEVEL_OS_API +# endif +# define gs_memory_reserve(_1) _Static_assert(0, "low level memory api is not available for this platform") +# define gs_memory_commit(_1, _2) _Static_assert(0, "low level memory api is not available for this platform") +# define gs_memory_decommit(_1, _2) _Static_assert(0, "low level memory api is not available for this platform") +# define gs_memory_release(_1, _2) _Static_assert(0, "low level memory api is not available for this platform") +#else +GS_API_DECL void* gs_memory_reserve(uint32_t size); +GS_API_DECL bool32 gs_memory_commit(void* ptr, uint32_t size); +GS_API_DECL void gs_memory_decommit(void* ptr, uint32_t size); +GS_API_DECL void gs_memory_release(void* ptr, uint32_t size); +#endif + +/*================================================================================ +// Arena Allocator / Linear Allocator / Stack Allocator / Bump Allocator +================================================================================*/ + +//=== arena impl ===// + +#ifndef GS_DEFAULT_ARENA + +typedef struct gs_arena_default gs_arena_default; +struct gs_arena_default +{ + gs_arena_default *prev; + gs_arena_default *current; + uint64_t base_pos; + uint64_t pos; + uint64_t cmt; + uint64_t cap; +}; +#define GS_IMPL_ARENA gs_arena_default + +#endif // GS_DEFAULT_ARENA + +#if !defined(GS_IMPL_ARENA) +# error Missing implementation for GS_IMPL_ARENA +#endif + +typedef GS_IMPL_ARENA gs_arena; + +typedef struct gs_arena_temp gs_arena_temp; +struct gs_arena_temp +{ + gs_arena *arena; + uint64_t pos; +}; + +// arena functions + +GS_API_DECL gs_arena* gs_arena_alloc(void); +GS_API_DECL void gs_arena_release(gs_arena* arena); + +GS_API_DECL void* _gs_arena_push(gs_arena* arena, uint64_t size_bytes, uint64_t align_bytes); +GS_API_DECL void* _gs_arena_push_nz(gs_arena* arena, uint64_t size_bytes, uint64_t align_bytes); +GS_API_DECL void gs_arena_reset(gs_arena* arena); + +#if 1 // _Alignof is C11... + +#define gs_arena_push(a,T) (T*)(_gs_arena_push((a), sizeof(T), _Alignof(T))) +#define gs_arena_push_array(a,T,c) (T*)(_gs_arena_push((a), sizeof(T)*(c), _Alignof(T))) +#define gs_arena_push_no_zero(a,T) (T*)(_gs_arena_push_nz((a), sizeof(T), _Alignof(T))) +#define gs_arena_push_array_no_zero(a,T,c) (T*)(_gs_arena_push_nz((a), sizeof(T)*(c), _Alignof(T))) + +#else + +#define gs_arena_push(a,T) (T*)(_gs_arena_push((a), sizeof(T), 16)) +#define gs_arena_push_array(a,T,c) (T*)(_gs_arena_push((a), sizeof(T)*(c), 16)) +#define gs_arena_push_no_zero(a,T) (T*)(_gs_arena_push_nz((a), sizeof(T), 16)) +#define gs_arena_push_array_no_zero(a,T,c) (T*)(_gs_arena_push_nz((a), sizeof(T)*(c), 16)) + +#endif + + +GS_API_DECL gs_arena_temp gs_arena_begin_temp(gs_arena* arena); +GS_API_DECL void gs_arena_end_temp(gs_arena_temp temp); + +// arena scratch pool + +GS_API_DECL gs_arena_temp gs_arena_get_scratch(gs_arena **conflicts, uint64_t count); +#define gs_arena_release_scratch(scratch) gs_arena_end_temp(scratch) + + +/*================================================================================ +// Memory Block +================================================================================*/ + #define gs_ptr_add(P, BYTES) \ (((uint8_t*)P + (BYTES))) @@ -2746,6 +2842,9 @@ GS_API_DECL size_t gs_memory_calc_padding_w_header(size_t base_address, size_t a // Linear Allocator ================================================================================*/ +// NOTE(Samdal): +// You may want to use gs_arena instead + typedef struct gs_linear_allocator_t { uint8_t* memory; size_t total_size; @@ -2761,6 +2860,9 @@ GS_API_DECL void gs_linear_allocator_clear(gs_linear_allocator_t* la); // Stack Allocator ================================================================================*/ +// NOTE(Samdal): +// You may want to use gs_arena instead + typedef struct gs_stack_allocator_header_t { uint32_t size; } gs_stack_allocator_header_t; @@ -7427,6 +7529,317 @@ __gs_pqueue_pop_internal(void** pqueue, void* tmp, void** data, int32_t* priorit // GS_MEMORY ========================*/ +/*================================================================================ +// Low Level OS Memory +================================================================================*/ + +#ifndef GS_MEMORY_NO_LOW_LEVEL_OS_API + +#ifndef GS_PLATFORM_WINDOWS +#include +#endif // !GS_PLATFORM_WINDOWS + +GS_API_DECL void* gs_memory_reserve(uint32_t size) +{ +#ifdef GS_PLATFORM_WINDOWS + return VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); +#else + return mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, (off_t)0); +#endif +} +GS_API_DECL bool32 gs_memory_commit(void* ptr, uint32_t size) +{ +#ifdef GS_PLATFORM_WINDOWS + return (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); +#else + return (mprotect(ptr, size, PROT_READ|PROT_WRITE) == 0); +#endif +} +GS_API_DECL void gs_memory_decommit(void* ptr, uint32_t size) +{ +#ifdef GS_PLATFORM_WINDOWS + VirtualFree(ptr, size, MEM_DECOMMIT); +#else + mprotect(ptr, size, PROT_NONE); + madvise(ptr, size, MADV_DONTNEED); +#endif +} +GS_API_DECL void gs_memory_release(void* ptr, uint32_t size) +{ +#ifdef GS_PLATFORM_WINDOWS + VirtualFree(ptr, 0, MEM_RELEASE); +#else + munmap(ptr, size); +#endif +} + +#endif // !GS_MEMORY_NO_LOW_LEVEL_OS_API + +/*================================================================================ +// Arena Allocator / Linear Allocator / Stack Allocator / Bump Allocator +================================================================================*/ + + +////////////////////////////////////////////////// +// TODO(Samdal): allow more customization +// +// 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; +// }; +// +/////////////////////////////////// + + +#define gs_arena_self_size (gs_max(sizeof(gs_arena), 64)) +#define GS_DEFAULT_ARENA_CMT_SIZE (64 << 10) +#define GS_DEFAULT_ARENA_RES_SIZE (64 << 20) +#define GS_DEFAULT_ARENA_VERY_BIG (GS_DEFAULT_ARENA_RES_SIZE - gs_arena_self_size)/2 + +#define _gs_align_pow2(x,b) (((x)+((b)-1))&(~((b)-1))) + + +static gs_arena* +gs_arena_alloc_size(uint64_t cmt, uint64_t res) +{ + gs_assert(gs_arena_self_size < cmt && cmt <= res); + uint64_t cmt_clamped = gs_min(cmt, res); + gs_arena* result = 0; + +#ifdef GS_MEMORY_NO_LOW_LEVEL_OS_API + void *mem = malloc(res); + + if (mem) { + result = (gs_arena*)mem; + result->prev = 0; + result->current = result; + result->base_pos = 0; + result->pos = gs_arena_self_size; + result->cmt = res; + result->cap = res; + } +#else + void *mem = gs_memory_reserve(res); + + if (gs_memory_commit(mem, cmt_clamped)) { + result = (gs_arena*)mem; + result->prev = 0; + result->current = result; + result->base_pos = 0; + result->pos = gs_arena_self_size; + result->cmt = cmt_clamped; + result->cap = res; + } +#endif + + return result; +} + +GS_API_DECL gs_arena* +gs_arena_alloc(void) +{ + return gs_arena_alloc_size(GS_DEFAULT_ARENA_CMT_SIZE, GS_DEFAULT_ARENA_RES_SIZE); +} + +GS_API_DECL void +gs_arena_release(gs_arena* arena) +{ + for (gs_arena *node = arena->current, *prev = 0; node; node = prev) { + prev = node->prev; +#ifdef GS_MEMORY_NO_LOW_LEVEL_OS_API + free(node); +#else + gs_memory_release(node, node->cap); +#endif + } +} + +static uint64_t +gs_arena_get_pos(gs_arena* arena) +{ + gs_arena *current = arena->current; + uint64_t result = current->base_pos + current->pos; + return result; +} + +GS_API_DECL void* +_gs_arena_push_nz(gs_arena* arena, uint64_t size_bytes, uint64_t align_bytes) +{ + // try to be fast! + gs_arena *current = arena->current; + uint64_t pos = current->pos; + uint64_t pos_aligned = _gs_align_pow2(pos, align_bytes); + uint64_t new_pos = pos_aligned + size_bytes; + void *result = (uint8_t*)current + pos_aligned; + current->pos = new_pos; + + // if it's not going to work do the slow path + if (new_pos > current->cmt) { + result = 0; + current->pos = pos; + + // new chunk if necessary + if (new_pos > current->cap) { + gs_arena *new_arena = 0; + if (size_bytes > GS_DEFAULT_ARENA_VERY_BIG) { + uint64_t big_size_unrounded = size_bytes + gs_arena_self_size; + uint64_t big_size = _gs_align_pow2(big_size_unrounded, (4 << 10)); + new_arena = gs_arena_alloc_size(big_size, big_size); + } else { + new_arena = gs_arena_alloc(); + } + + // link in new chunk & recompute new_pos + if (new_arena != 0) { + new_arena->base_pos = current->base_pos + current->cap; + new_arena->prev = current; + current = new_arena; + arena->current = current; + pos_aligned = current->pos; + new_pos = pos_aligned + size_bytes; + } + } + + // move ahead if the current chunk has enough reserve + if (new_pos <= current->cap) { + // extend commit if necessary +#ifndef GS_MEMORY_NO_LOW_LEVEL_OS_API + if (new_pos > current->cmt) { + uint64_t new_cmt_unclamped = _gs_align_pow2(new_pos, GS_DEFAULT_ARENA_CMT_SIZE); + uint64_t new_cmt = gs_min(new_cmt_unclamped, current->cap); + uint64_t cmt_size = new_cmt - current->cmt; + if (gs_memory_commit((uint8_t*)current + current->cmt, cmt_size)) { + current->cmt = new_cmt; + } + } +#endif + + // move ahead if the current chunk has enough commit + if (new_pos <= current->cmt) { + result = (uint8_t*)current + current->pos; + current->pos = new_pos; + } + } + } + + return result; +} + +GS_API_DECL void* +_gs_arena_push(gs_arena* arena, uint64_t size_bytes, uint64_t align_bytes) +{ + return memset(_gs_arena_push_nz(arena, size_bytes, align_bytes), 0, size_bytes); +} + +static void +gs_arena_pop_to(gs_arena *arena, uint64_t pos) +{ + + // pop chunks in the chain + uint64_t pos_clamped = gs_max(gs_arena_self_size, pos); + { + gs_arena *node = arena->current; + for (gs_arena *prev = 0; node && node->base_pos >= pos; node = prev) { + prev = node->prev; +#ifdef GS_MEMORY_NO_LOW_LEVEL_OS_API + free(node); +#else + gs_memory_release(node, node->cap); +#endif + } + arena->current = node; + } + + // reset the pos of the current + { + gs_arena *current = arena->current; + uint64_t local_pos_unclamped = pos - current->base_pos; + uint64_t local_pos = gs_max(local_pos_unclamped, gs_arena_self_size); + current->pos = local_pos; + } +} + + +GS_API_DECL void +gs_arena_reset(gs_arena* arena) +{ + gs_arena_pop_to(arena, gs_arena_self_size); +} + +GS_API_DECL gs_arena_temp +gs_arena_begin_temp(gs_arena* arena) +{ + return (gs_arena_temp){ + .arena = arena, + .pos = gs_arena_get_pos(arena) + }; +} + +GS_API_DECL void +gs_arena_end_temp(gs_arena_temp temp) +{ + gs_arena_pop_to(temp.arena, temp.pos); +} + + +// arena scratch pool + + +#ifndef _gs_scratch_count +#define _gs_scratch_count 2 +#endif + +#ifdef _WIN32 +__declspec(thread) gs_arena* _gs_thread_scratch_pool[_gs_scratch_count] = {0, 0}; +#else +__thread gs_arena* _gs_thread_scratch_pool[_gs_scratch_count] = {0, 0}; +#endif + +GS_API_DECL gs_arena_temp +gs_arena_get_scratch(gs_arena **conflicts, uint64_t count) +{ + gs_arena** scratch_pool = _gs_thread_scratch_pool; + if (scratch_pool[0] == 0) { + gs_arena **arena_ptr = scratch_pool; + for (uint64_t i = 0; i < _gs_scratch_count; i += 1, arena_ptr += 1) { + *arena_ptr = gs_arena_alloc(); + } + } + gs_arena *result = 0; + gs_arena **arena_ptr = scratch_pool; + for (uint64_t i = 0; i < _gs_scratch_count; i += 1, arena_ptr += 1) { + gs_arena *arena = *arena_ptr; + gs_arena **conflict_ptr = conflicts; + for (uint32_t j = 0; j < count; j += 1, conflict_ptr += 1) { + if (arena == *conflict_ptr) { + arena = 0; + break; + } + } + if (arena != 0) { + result = arena; + break; + } + } + + return gs_arena_begin_temp(result); +} + +/*================================================================================ +// Memory Block +================================================================================*/ + // typedef struct gs_memory_block_t { // uint8_t* data; // size_t size; diff --git a/impl/gs_graphics_impl.h b/impl/gs_graphics_impl.h index d55d064..39c6d09 100644 --- a/impl/gs_graphics_impl.h +++ b/impl/gs_graphics_impl.h @@ -207,7 +207,7 @@ void gsgl_pipeline_state() void GLAPIENTRY gsgl_message_cb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, - const GLchar* message, void* user_param) + const GLchar* message, const void* user_param) { return; if (type == GL_DEBUG_TYPE_ERROR) { @@ -1499,7 +1499,7 @@ gs_grapics_storage_buffer_unlock_impl(gs_handle(gs_graphics_storage_buffer_t) hn gsgl_data_t* ogl = (gsgl_data_t*)gs_subsystem(graphics)->user_data; if (!gs_slot_array_handle_valid(ogl->storage_buffers, hndl.id)) { gs_log_warning("Storage buffer handle invalid: %zu", hndl.id); - return NULL; + return; } gsgl_storage_buffer_t* sbo = gs_slot_array_getp(ogl->storage_buffers, hndl.id); @@ -1567,7 +1567,7 @@ gs_storage_buffer_get_data_impl(gs_handle(gs_graphics_storage_buffer_t) hndl, si gsgl_data_t* ogl = (gsgl_data_t*)gs_subsystem(graphics)->user_data; if (!gs_slot_array_handle_valid(ogl->storage_buffers, hndl.id)) { gs_log_warning("Storage buffer handle invalid: %zu", hndl.id); - return NULL; + return; } gsgl_storage_buffer_t* sbo = gs_slot_array_getp(ogl->storage_buffers, hndl.id); glBindBuffer(GL_SHADER_STORAGE_BUFFER, sbo->buffer);