From 11ec5a0cd2db5a95f7348428d51ddff88faaeacd Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Thu, 23 Nov 2023 22:03:02 +0100 Subject: [PATCH] Various improvements - Support static linking - Buffer manager --- meson.build | 24 ++- src/renderer/vk/gfx_pipelines.c | 6 +- src/renderer/vk/init.c | 6 +- src/runtime/app.c | 13 ++ src/runtime/buffer_manager.c | 250 +++++++++++++++++++++++++++++++ src/runtime/buffer_manager.h | 21 +++ src/runtime/config.h | 7 + src/runtime/dynamic_libs.c | 8 + src/runtime/dynamic_libs.h | 2 + src/runtime/gfx_main.c | 41 +++-- src/runtime/gfx_shader_loading.c | 5 + src/runtime/renderer_api.h | 2 + src/runtime/runtime.h | 6 +- src/runtime/runtime_cvars.c | 2 + 14 files changed, 372 insertions(+), 21 deletions(-) create mode 100644 src/runtime/buffer_manager.c create mode 100644 src/runtime/buffer_manager.h diff --git a/meson.build b/meson.build index 6b83a0f..bbd3984 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('voyage', 'c', - default_options: ['buildtype=debugoptimized', 'b_sanitize=address', 'c_std=c17', 'warning_level=3']) + default_options: ['buildtype=debug', 'b_sanitize=address', 'c_std=c17', 'warning_level=3']) compiler = meson.get_compiler('c') buildtype = get_option('buildtype') @@ -21,6 +21,10 @@ elif compiler.get_argument_syntax() == 'msvc' endif endif +if get_option('default_library') == 'static' + add_project_arguments(['-DVY_STATIC_LIB'], language : 'c') +endif + # Debug specific flags if buildtype == 'debug' or buildtype == 'debugoptimized' add_project_arguments([ '-DVY_DEBUG'], language : 'c') @@ -51,6 +55,7 @@ runtime_lib = library('vyrt', 'src/runtime/jobs.h', 'src/runtime/aio.h', 'src/runtime/file_tab.h', + 'src/runtime/buffer_manager.h', 'src/runtime/error_report.c', 'src/runtime/gfx_main.c', @@ -65,6 +70,7 @@ runtime_lib = library('vyrt', 'src/runtime/jobs.c', 'src/runtime/aio.c', 'src/runtime/file_tab.c', + 'src/runtime/buffer_manager.c', # Contrib Sources 'contrib/xxhash/xxhash.c', @@ -72,6 +78,10 @@ runtime_lib = library('vyrt', include_directories : incdir, c_pch : 'pch/rt_pch.h') + +# Renderer libraries +static_renderer_lib = 'NONE' + if vk_dep.found() platform_defs = [] if get_option('use_xlib') @@ -98,12 +108,22 @@ if vk_dep.found() link_with : [runtime_lib], c_pch : 'pch/vk_pch.h', c_args : platform_defs) + + static_renderer_lib = vk_renderer_lib +endif + + +game_link_libs = [] +if get_option('default_library') == 'static' + game_link_libs = [runtime_lib, static_renderer_lib] +else + game_link_libs = [runtime_lib] endif executable('voyage', 'src/game/voyage.c', include_directories : incdir, - link_with : [runtime_lib], + link_with : game_link_libs, win_subsystem : 'windows') diff --git a/src/renderer/vk/gfx_pipelines.c b/src/renderer/vk/gfx_pipelines.c index cdac58d..630149f 100644 --- a/src/renderer/vk/gfx_pipelines.c +++ b/src/renderer/vk/gfx_pipelines.c @@ -52,8 +52,7 @@ static void ReleasePipelineSlot(vy_gfx_pipeline_handle id) { _storage.generation_in_use[slot] &= ~0x1; } -VY_DLLEXPORT vy_gfx_pipeline_handle -vyCompileComputePipeline(const vy_compute_pipeline_info *info) { +vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compute_pipeline_info *info) { #if 0 char info_log[512]; @@ -94,8 +93,7 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) { return StorePipeline(pipeline); } -VY_DLLEXPORT vy_gfx_pipeline_handle -vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { +vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileGraphicsPipeline)(const vy_graphics_pipeline_info *info) { #if 0 char info_log[512]; diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index 4690f79..65f2e34 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -85,7 +85,7 @@ DebugUtilsMessengerCb(VkDebugUtilsMessageSeverityFlagBitsEXT severity, extern vy_cvar r_VkPreferredSwapchainImages; extern vy_cvar r_VkPreferMailboxMode; -VY_DLLEXPORT void vyRegisterCVars(void) { +void VY_RENDERER_API_FN(RegisterCVars)(void) { vyRegisterCVAR(&r_VkEnableAPIAllocTracking); vyRegisterCVAR(&r_VkPhysDeviceName); vyRegisterCVAR(&r_VkPreferredSwapchainImages); @@ -470,7 +470,7 @@ static vy_result CreateDevice(void) { return VY_SUCCESS; } -VY_DLLEXPORT vy_result vyInit(const vy_renderer_init_info *info) { +vy_result VY_RENDERER_API_FN(Init)(const vy_renderer_init_info *info) { vyLog("vk", "Init"); _tracking_alloc_cbs.pUserData = NULL; @@ -503,7 +503,7 @@ VY_DLLEXPORT vy_result vyInit(const vy_renderer_init_info *info) { return VY_SUCCESS; } -VY_DLLEXPORT void vyShutdown(void) { +void VY_RENDERER_API_FN(Shutdown)(void) { vyLog("vk", "Shutdown"); vkDeviceWaitIdle(g_gpu.device); vyDestroySwapchain(); diff --git a/src/runtime/app.c b/src/runtime/app.c index 32a8eea..450df7d 100644 --- a/src/runtime/app.c +++ b/src/runtime/app.c @@ -3,6 +3,7 @@ #include "gfx.h" #include "aio.h" #include "renderer_api.h" +#include "buffer_manager.h" extern void __RegisterRuntimeCVars(void); @@ -37,6 +38,11 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, /* TODO: Parse the cvar config file */ + if (vyInitBufferManager() != VY_SUCCESS) { + vyReportError("BUFFERMGR", "Init failed."); + return 1; + } + if (vyInitFileTab(1024) != VY_SUCCESS) { vyReportError("FTAB", "Init failed."); return 1; @@ -131,6 +137,7 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, vyShutdownAIO(); vyShutdownAIO(); vyShutdownFileTab(); + vyShutdownBufferManager(); return 0; } @@ -180,6 +187,11 @@ VY_DLLEXPORT int vyXlibEntry(int argc, char **argv) { __RegisterRuntimeCVars(); vyRegisterRendererCVars(); + if (vyInitBufferManager() != VY_SUCCESS) { + vyReportError("BUFFERMGR", "Init failed."); + return 1; + } + if (vyInitFileTab(1024) != VY_SUCCESS) { vyReportError("FTAB", "Init failed."); return 1; @@ -260,6 +272,7 @@ VY_DLLEXPORT int vyXlibEntry(int argc, char **argv) { XCloseDisplay(dpy); vyShutdownAIO(); vyShutdownFileTab(); + vyShutdownBufferManager(); return 0; } diff --git a/src/runtime/buffer_manager.c b/src/runtime/buffer_manager.c new file mode 100644 index 0000000..eb359ee --- /dev/null +++ b/src/runtime/buffer_manager.c @@ -0,0 +1,250 @@ +#include "runtime.h" +#include "threading.h" +#include "config.h" +#include "buffer_manager.h" + +#include +#include +#include +#include + +typedef struct vy_buffer_region_s { + void *memory; + int16_t *refcounts; // One per block + uint32_t *bitmap; + size_t block_count; + vy_mutex *guard; +} vy_buffer_region; + +/* Count leading zeroes. + * Note that the return value of __builtin_clz(0) is undefined. */ +#ifdef _MSC_VER + +#include + +#define lzcnt32(x) __lzcnt((x)) +#define popcnt32(x) __popcnt((x)) + +static __forceinline uint32_t tzcnt32(uint32_t x) { + unsigned long i; + _BitScanForward(&i, x); + return (uint32_t)i; +} + +static inline bool IsLZCNTSupported(void) { +#define Type 0x80000001 + int info[4]; + __cpuid(info, Type); + return (info[2] & (1 << 5)) != 0; +#undef Type +} + +#elif defined(__GNUC__) +#define lzcnt32(x) __builtin_clz((x)) +#define tzcnt32(x) __builtin_ctz((x)) +#define popcnt32(x) __builtin_popcount((x)) + +#define IsLZCNTSupported() true + +#endif + +/* NOTE(Kevin): Keep these sorted! */ +static size_t _block_sizes[] = {VY_KB(512), VY_MB(1), VY_MB(4), VY_MB(8) }; +#define NUM_BLOCK_SIZES (sizeof(_block_sizes) / sizeof(_block_sizes[0])) +static vy_buffer_region _regions[NUM_BLOCK_SIZES]; + +VY_CVAR_SZ( + rt_BufferManagerMemory, + "Total number of bytes allocated for the buffer manager. Default: 1GB", VY_GB(1)); + +VY_DLLEXPORT vy_result vyInitBufferManager(void) { + if ((rt_BufferManagerMemory.sz % NUM_BLOCK_SIZES) != 0) + vyLog("BUFFERMGR", + "Configured memory amount is not dividable by number of block " + "sizes: %u MB/%u", + rt_BufferManagerMemory.sz / (1024 * 1024), + NUM_BLOCK_SIZES); + + size_t mem_per_size = rt_BufferManagerMemory.sz / NUM_BLOCK_SIZES; + for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) { + if ((mem_per_size % _block_sizes[i]) != 0) + vyLog("BUFFERMGR", + "Memory per block size is not dividable by block size: %u " + "MB/%u KB", + mem_per_size / (1024 * 1024), + _block_sizes[i] / 1024); + + size_t block_count = mem_per_size / _block_sizes[i]; + _regions[i].block_count = block_count; + _regions[i].guard = vyCreateMutex(); + if (!_regions[i].guard) { + vyReportError("BUFFERMGR", "Failed to create guard mutex %u", i); + return VY_BUFFER_MGR_MUTEX_CREATION_FAILED; + } + _regions[i].memory = malloc(mem_per_size); + if (!_regions[i].memory) { + vyDestroyMutex(_regions[i].guard); + vyReportError("BUFFERMGR", "Failed to allocate memory.", i); + return VY_BUFFER_MGR_OUT_OF_MEMORY; + } + _regions[i].bitmap = calloc((block_count + 31) / 32, sizeof(uint32_t)); + if (!_regions[i].bitmap) { + vyDestroyMutex(_regions[i].guard); + free(_regions[i].memory); + vyReportError("BUFFERMGR", "Failed to allocate memory.", i); + return VY_BUFFER_MGR_OUT_OF_MEMORY; + } + _regions[i].refcounts = calloc(block_count, sizeof(uint16_t)); + if (!_regions[i].refcounts) { + vyDestroyMutex(_regions[i].guard); + free(_regions[i].memory); + free(_regions[i].bitmap); + vyReportError("BUFFERMGR", "Failed to allocate memory.", i); + return VY_BUFFER_MGR_OUT_OF_MEMORY; + } + } + return VY_SUCCESS; +} + +VY_DLLEXPORT void vyShutdownBufferManager(void) { + for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) { + vyDestroyMutex(_regions[i].guard); + free(_regions[i].memory); + free(_regions[i].bitmap); + free(_regions[i].refcounts); + } +} + +VY_DLLEXPORT void *vyAllocBuffer(size_t size) { + assert(IsLZCNTSupported()); + + // Determine the best block size to use + size_t required_blocks = (size + _block_sizes[0] - 1) / _block_sizes[0]; + size_t best_fit = 0; + for (size_t i = 1; i < NUM_BLOCK_SIZES; ++i) { + size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i]; + if (block_count < required_blocks && size >= _block_sizes[i]) { + required_blocks = block_count; + best_fit = i; + } + } + + void *result = NULL; + + vy_buffer_region *region = &_regions[best_fit]; + vyLockMutex(region->guard); + size_t dword_count = (region->block_count + 31) / 32; + + if (required_blocks < 32) { + /* Fast path for allocations that potentially fit into one dword */ + uint32_t in_use_mask = (1ull << required_blocks) - 1; + size_t max_occupancy = 32 - required_blocks; + for (size_t i = 0; i < dword_count; ++i) { + size_t block_index = 0; + if (region->bitmap[i] != 0 && popcnt32(region->bitmap[i]) < max_occupancy) { + size_t free_high_blocks = lzcnt32(region->bitmap[i]); + if (free_high_blocks >= required_blocks) { + /* High blocks are free */ + size_t first_free = 32 - free_high_blocks; + region->bitmap[i] |= (in_use_mask << first_free); + block_index = i * 32 + first_free; + result = (char *)region->memory + + block_index * _block_sizes[best_fit]; + } else if (tzcnt32(region->bitmap[i]) >= required_blocks) { + /* Low blocks are free */ + region->bitmap[i] |= in_use_mask; + block_index = i * 32; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; + } else { + /* Check if we can find a large enough range of free blocks. + * Start after the first set bit. + */ + for (uint32_t j = tzcnt32(region->bitmap[i]) + 1; j < 32 - required_blocks; ++j) { + if ((region->bitmap[i] & in_use_mask << j) == 0) { + region->bitmap[i] |= (in_use_mask << j); + block_index = i * 32 + j; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; + break; + } + } + } + } else if (region->bitmap[i] == 0) { + /* All free */ + region->bitmap[i] = in_use_mask; + block_index = i * 32; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; + } else if (i < dword_count - 1) { + /* Check if we can use high blocks from this dword and low blocks from the next one */ + size_t high_blocks = lzcnt32(region->bitmap[i]); + size_t low_blocks = (region->bitmap[i + 1] != 0) ? tzcnt32(region->bitmap[i + 1]) : 32; + + if (high_blocks + low_blocks >= required_blocks) { + size_t high_mask = (1u << high_blocks) - 1; + size_t first_free = 32 - high_blocks; + size_t low_mask = (1u << (required_blocks - high_blocks)) - 1; + + region->bitmap[i] |= (high_mask << first_free); + region->bitmap[i + 1] |= low_mask; + block_index = i * 32 + first_free; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; + } + } + + if (result) { + for (size_t j = 0; j < required_blocks; ++j) + region->refcounts[block_index + j] = 1; + break; + } + } + } + vyUnlockMutex(region->guard); + return result; +} + +VY_DLLEXPORT void vyReleaseBuffer(const void *begin, size_t size) { + uintptr_t begin_addr = (uintptr_t)begin; + for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) { + uintptr_t region_addr = (uintptr_t)_regions[i].memory; + size_t region_size = _block_sizes[i] * _regions[i].block_count; + if (begin_addr >= region_addr && + begin_addr + size <= region_addr + region_size) { + + size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i]; + size_t first_block = (begin_addr - region_addr) / _block_sizes[i]; + + vyLockMutex(_regions[i].guard); + for (size_t j = 0; j < block_count; ++j) { + size_t dword = (first_block + j) / 32; + size_t bit = (first_block + j) % 32; + + if (--_regions[i].refcounts[first_block + j] == 0) + _regions[i].bitmap[dword] &= ~(1u << bit); + } + vyUnlockMutex(_regions[i].guard); + return; + } + } + vyLog("BUFFERMGR", "Tried to release an invalid buffer"); +} + +VY_DLLEXPORT void vyIncreaseBufferRefCount(const void *begin, size_t size) { + uintptr_t begin_addr = (uintptr_t)begin; + for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) { + uintptr_t region_addr = (uintptr_t)_regions[i].memory; + size_t region_size = _block_sizes[i] * _regions[i].block_count; + if (begin_addr >= region_addr && + begin_addr + size <= region_addr + region_size) { + + size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i]; + size_t first_block = (begin_addr - region_addr) / _block_sizes[i]; + + vyLockMutex(_regions[i].guard); + for (size_t j = 0; j < block_count; ++j) { + ++_regions[i].refcounts[first_block + j]; + } + vyUnlockMutex(_regions[i].guard); + return; + } + } + vyLog("BUFFERMGR", "Tried to increase the refcount of an invalid buffer"); +} \ No newline at end of file diff --git a/src/runtime/buffer_manager.h b/src/runtime/buffer_manager.h new file mode 100644 index 0000000..cf5e2d1 --- /dev/null +++ b/src/runtime/buffer_manager.h @@ -0,0 +1,21 @@ +#ifndef VY_BUFFER_MANAGER_H +#define VY_BUFFER_MANAGER_H + +#include "runtime.h" + +enum { + VY_BUFFER_MGR_OUT_OF_MEMORY = VY_SUCCESS + 1, + VY_BUFFER_MGR_MUTEX_CREATION_FAILED, +}; + +VY_DLLEXPORT vy_result vyInitBufferManager(void); + +VY_DLLEXPORT void vyShutdownBufferManager(void); + +VY_DLLEXPORT void *vyAllocBuffer(size_t size); + +VY_DLLEXPORT void vyReleaseBuffer(const void *begin, size_t size); + +VY_DLLEXPORT void vyIncreaseBufferRefCount(const void *begin, size_t size); + +#endif diff --git a/src/runtime/config.h b/src/runtime/config.h index a90f3d3..813fd86 100644 --- a/src/runtime/config.h +++ b/src/runtime/config.h @@ -8,6 +8,7 @@ typedef enum VY_CVAR_TYPE_INT, VY_CVAR_TYPE_FLOAT, VY_CVAR_TYPE_STRING, + VY_CVAR_TYPE_SIZE, } vy_cvar_type; typedef struct @@ -18,6 +19,7 @@ typedef struct int i; float f; const char *s; + size_t sz; }; vy_cvar_type type; } vy_cvar; @@ -37,6 +39,11 @@ typedef struct .description = d, \ .s = (v), \ .type = VY_CVAR_TYPE_STRING} +#define VY_CVAR_SZ(n, d, v) \ + vy_cvar n = {.name = #n, \ + .description = d, \ + .sz = (v), \ + .type = VY_CVAR_TYPE_SIZE} VY_DLLEXPORT void vyRegisterCVAR(vy_cvar *cvar); diff --git a/src/runtime/dynamic_libs.c b/src/runtime/dynamic_libs.c index d031f66..3055f69 100644 --- a/src/runtime/dynamic_libs.c +++ b/src/runtime/dynamic_libs.c @@ -4,6 +4,10 @@ #define WIN32_LEAN_AND_MEAN #include +VY_DLLEXPORT vy_dynlib vyOpenCallerLib(void) { + return (vy_dynlib)GetModuleHandleW(NULL); +} + VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) { wchar_t libname_w[MAX_PATH]; MultiByteToWideChar(CP_UTF8, @@ -27,6 +31,10 @@ VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) { #elif defined(__linux__) #include +VY_DLLEXPORT vy_dynlib vyOpenCallerLib(void) { + return dlopen(NULL, RTLD_NOW | RTLD_LOCAL); +} + VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) { return dlopen(libname, RTLD_NOW | RTLD_LOCAL); } diff --git a/src/runtime/dynamic_libs.h b/src/runtime/dynamic_libs.h index f2b47e1..4a4b8f4 100644 --- a/src/runtime/dynamic_libs.h +++ b/src/runtime/dynamic_libs.h @@ -17,6 +17,8 @@ typedef void *vy_dynlib; VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname); +VY_DLLEXPORT vy_dynlib vyOpenCallerLib(void); + VY_DLLEXPORT void *vyGetSymbol(vy_dynlib lib, const char *symbol); VY_DLLEXPORT void vyCloseLib(vy_dynlib lib); diff --git a/src/runtime/gfx_main.c b/src/runtime/gfx_main.c index ad9f37c..db4c4d1 100644 --- a/src/runtime/gfx_main.c +++ b/src/runtime/gfx_main.c @@ -24,16 +24,28 @@ VY_CVAR_S(rt_Renderer, extern bool vyLoadShaders(const char **paths, vy_shader *shaders, unsigned int count); +#ifdef VY_STATIC_LIB +extern void VY_RENDERER_API_FN(RegisterCVars)(void); +extern vy_result VY_RENDERER_API_FN(Init)(const vy_renderer_init_info *); +extern void VY_RENDERER_API_FN(Shutdown)(void); +extern vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)( + const vy_compute_pipeline_info *); +extern vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileGraphicsPipeline)( + const vy_graphics_pipeline_info *); +#endif + static bool LoadRenderer(void) { -#define RETRIEVE_SYMBOL(name, type) \ - g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vy" #name); \ - if (!g_renderer.name) { \ - vyReportError( \ - "GFX", \ - "Unable to retrieve renderer function %s from backend %s", \ - #name, \ - rt_Renderer.s); \ - } + +#if !defined(VY_STATIC_LIB) + #define RETRIEVE_SYMBOL(name, type) \ + g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vyRen" #name); \ + if (!g_renderer.name) { \ + vyReportError( \ + "GFX", \ + "Unable to retrieve renderer function %s from backend %s", \ + #name, \ + rt_Renderer.s); \ + } if (strcmp(rt_Renderer.s, "vk") == 0) { _renderer_lib = vyOpenLib(VY_DLLNAME("vyvk")); @@ -49,7 +61,6 @@ static bool LoadRenderer(void) { RETRIEVE_SYMBOL(CompileComputePipeline, vy_compile_compute_pipeline_fn); RETRIEVE_SYMBOL(CompileGraphicsPipeline, vy_compile_graphics_pipeline_fn); - return true; } else { vyReportError("GFX", "Unsupported renderer backend: (%s) %s", @@ -57,7 +68,15 @@ static bool LoadRenderer(void) { rt_Renderer.s); return false; } -#undef RETRIEVE_SYMBOL + #undef RETRIEVE_SYMBOL +#else + g_renderer.RegisterCVars = &vyRenRegisterCVars; + g_renderer.Init = &vyRenInit; + g_renderer.Shutdown = &vyRenShutdown; + g_renderer.CompileComputePipeline = &vyRenCompileComputePipeline; + g_renderer.CompileGraphicsPipeline = &vyRenCompileGraphicsPipeline; +#endif + return true; } VY_DLLEXPORT void vyRegisterRendererCVars(void) { diff --git a/src/runtime/gfx_shader_loading.c b/src/runtime/gfx_shader_loading.c index 75be23e..70b48c6 100644 --- a/src/runtime/gfx_shader_loading.c +++ b/src/runtime/gfx_shader_loading.c @@ -1,3 +1,8 @@ +/* TODO(Kevin): + * This should move into a standalone tool. + */ + + #include #include #include diff --git a/src/runtime/renderer_api.h b/src/runtime/renderer_api.h index d7e98f5..2366c2b 100644 --- a/src/runtime/renderer_api.h +++ b/src/runtime/renderer_api.h @@ -54,6 +54,8 @@ typedef struct { vy_compile_graphics_pipeline_fn *CompileGraphicsPipeline; } vy_renderer_api; +#define VY_RENDERER_API_FN(name) VY_DLLEXPORT vyRen##name + #ifndef VY_DONT_DEFINE_RENDERER_GLOBAL extern vy_renderer_api g_renderer; #endif diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 941c41f..39f08a9 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -5,7 +5,7 @@ #include -#ifdef _WIN32 +#if defined(_WIN32) #define VY_DLLEXPORT __declspec(dllexport) #else #define VY_DLLEXPORT @@ -14,6 +14,10 @@ #define VY_UNUSED(x) ((void)sizeof((x))) #define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0])) +#define VY_KB(n) ((n) * 1024U) +#define VY_MB(n) ((n) * 1024U * 1024U) +#define VY_GB(n) ((n) * 1024U * 1024U * 1024U) + typedef unsigned int vy_result; #define VY_SUCCESS 0 diff --git a/src/runtime/runtime_cvars.c b/src/runtime/runtime_cvars.c index 22a81f2..840aace 100644 --- a/src/runtime/runtime_cvars.c +++ b/src/runtime/runtime_cvars.c @@ -4,6 +4,7 @@ extern vy_cvar rt_Renderer; extern vy_cvar rt_Fullscreen; extern vy_cvar rt_WindowWidth; extern vy_cvar rt_WindowHeight; +extern vy_cvar rt_BufferManagerMemory; void __RegisterRuntimeCVars(void) { @@ -11,4 +12,5 @@ void __RegisterRuntimeCVars(void) vyRegisterCVAR(&rt_Fullscreen); vyRegisterCVAR(&rt_WindowWidth); vyRegisterCVAR(&rt_WindowHeight); + vyRegisterCVAR(&rt_BufferManagerMemory); } \ No newline at end of file