From 81798df3e9e6fcb2f5c321a177bd0ab8aed3ce5c Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Tue, 17 Oct 2023 00:54:51 +0200 Subject: [PATCH] ref, feat: Restructure - The renderer backend is now its own library, loaded at runtime. - Removed GLFW dependency, instead use win32 directly. - Broke linux window creation, because it's not implemented yet. - Maybe use GLFW for linux? --- include/fio.h | 60 ---------- include/gfx_backend.h | 29 ----- include/threading.h | 18 --- meson.build | 74 ++++++++---- pch/rt_pch.h | 1 + pch/vk_pch.h | 1 + src/game/voyage.c | 15 +++ src/gfx_main.c | 23 ---- src/{ => renderer/vk}/gfx_pipelines.c | 24 ++-- src/renderer/vk/gpu.h | 15 +++ src/renderer/vk/init.c | 137 ++++++++++++++++++++++ src/runtime/app.c | 130 ++++++++++++++++++++ src/runtime/app.h | 20 ++++ src/{ => runtime}/config.c | 2 +- {include => src/runtime}/config.h | 6 +- src/runtime/dynamic_libs.c | 43 +++++++ src/runtime/dynamic_libs.h | 20 ++++ src/{ => runtime}/error_report.c | 6 +- src/{ => runtime}/fio.c | 21 ++-- src/runtime/fio.h | 61 ++++++++++ {include => src/runtime}/gfx.h | 12 +- src/runtime/gfx_main.c | 75 ++++++++++++ src/{ => runtime}/gfx_shader_loading.c | 16 +-- src/runtime/renderer_api.h | 53 +++++++++ include/voyage.h => src/runtime/runtime.h | 10 +- src/runtime/runtime_cvars.c | 14 +++ src/{ => runtime}/threading.c | 20 ++-- src/runtime/threading.h | 20 ++++ src/voyage.c | 115 ------------------ 29 files changed, 720 insertions(+), 321 deletions(-) delete mode 100644 include/fio.h delete mode 100644 include/gfx_backend.h delete mode 100644 include/threading.h create mode 100644 pch/rt_pch.h create mode 100644 pch/vk_pch.h create mode 100644 src/game/voyage.c delete mode 100644 src/gfx_main.c rename src/{ => renderer/vk}/gfx_pipelines.c (94%) create mode 100644 src/renderer/vk/gpu.h create mode 100644 src/renderer/vk/init.c create mode 100644 src/runtime/app.c create mode 100644 src/runtime/app.h rename src/{ => runtime}/config.c (97%) rename {include => src/runtime}/config.h (92%) create mode 100644 src/runtime/dynamic_libs.c create mode 100644 src/runtime/dynamic_libs.h rename src/{ => runtime}/error_report.c (89%) rename src/{ => runtime}/fio.c (95%) create mode 100644 src/runtime/fio.h rename {include => src/runtime}/gfx.h (83%) create mode 100644 src/runtime/gfx_main.c rename src/{ => runtime}/gfx_shader_loading.c (97%) create mode 100644 src/runtime/renderer_api.h rename include/voyage.h => src/runtime/runtime.h (52%) create mode 100644 src/runtime/runtime_cvars.c rename src/{ => runtime}/threading.c (82%) create mode 100644 src/runtime/threading.h delete mode 100644 src/voyage.c diff --git a/include/fio.h b/include/fio.h deleted file mode 100644 index 71fd3e7..0000000 --- a/include/fio.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VY_FIO_H -#define VY_FIO_H - -#include -#include -#include - -#include "voyage.h" - -enum -{ - VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1, - VY_FILE_BUFFER_FLAG_READ_FAILED = 0x2, -}; - -typedef struct { - void *data; - size_t size; - uint32_t flags; -} vy_file_buffer; - -inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) { - return fb->flags == 0; -} - -/* used to identify a file (XXH3 hash of the path) */ -typedef uint64_t vy_file_id; - -typedef unsigned int vy_fio_handle; - -typedef struct { - unsigned int queue_size; - unsigned int max_file_count; -} vy_fio_config; - -bool vyInitFIO(const vy_fio_config *config); - -void vyShutdownFIO(void); - -vy_file_id vyGetFileId(const char *path); - -vy_file_id vyGetFileIdFromSpan(vy_text_span path); - -vy_file_id vyAddFile(const char *path); - -vy_file_id vyAddFileFromSpan(vy_text_span path); - -const char *vyGetFilePath(vy_file_id fid); - -vy_fio_handle vyEnqueueRead(vy_file_id fid); - -void vyAbortFIO(vy_fio_handle fio); - -bool vyIsFIOFinished(vy_fio_handle fio); - -bool vyRetrieveReadBuffer(vy_fio_handle fio, vy_file_buffer *buffer); - -void vyFreeFileBuffer(vy_file_buffer buffer); - -#endif diff --git a/include/gfx_backend.h b/include/gfx_backend.h deleted file mode 100644 index 73130fa..0000000 --- a/include/gfx_backend.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef VY_GFX_BACKEND_H -#define VY_GFX_BACKEND_H - -/* Backend functions and types. */ - -#include - -#include "gfx.h" - -typedef struct { - const char *compute_source; - size_t compute_source_length; -} vy_compute_pipeline_info; - -typedef struct { - const char *vertex_source; - size_t vertex_source_length; - - const char *fragment_source; - size_t fragment_source_length; -} vy_graphics_pipeline_info; - -vy_gfx_pipeline_id -vyCompileComputePipeline(const vy_compute_pipeline_info *info); - -vy_gfx_pipeline_id -vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info); - -#endif diff --git a/include/threading.h b/include/threading.h deleted file mode 100644 index 83a1199..0000000 --- a/include/threading.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef VY_THREADING_H -#define VY_THREADING_H - -/* platform independent threading */ - -#include - -typedef struct vy_mutex_s vy_mutex; - -vy_mutex *vyCreateMutex(void); - -void vyDestroyMutex(vy_mutex *mutex); - -bool vyLockMutex(vy_mutex *mutex); - -bool vyUnlockMutex(vy_mutex *mutex); - -#endif diff --git a/meson.build b/meson.build index 6414bad..ec4d97f 100644 --- a/meson.build +++ b/meson.build @@ -22,42 +22,64 @@ elif compiler.get_argument_syntax() == 'msvc' endif # Debug specific flags -if buildtype == 'debug' +if buildtype == 'debug' or buildtype == 'debugoptimized' add_project_arguments([ '-DDEBUG'], language : 'c') endif -# "Select" renderer backend -# Currently only OpenGL is supported -add_project_arguments([ '-DRENDERER_GL'], language : 'c') - # Gather dependencies thread_dep = dependency('threads') m_dep = compiler.find_library('m', required : false) -glfw_proj = subproject('glfw', default_options : 'default_library=static') -glfw_dep = glfw_proj.get_variable('glfw_dep') +vk_dep = dependency('vulkan', required : false) -incdir = include_directories(['contrib', 'include']) +# glfw_proj = subproject('glfw', default_options : 'default_library=static') +# glfw_dep = glfw_proj.get_variable('glfw_dep') -executable('voyage', +incdir = include_directories(['contrib', 'src']) + +runtime_lib = library('vyrt', # Project Sources - 'include/voyage.h', - 'include/gfx.h', - 'include/gfx_backend.h', - 'include/fio.h', - 'include/config.h', - 'include/threading.h', + 'src/runtime/runtime.h', + 'src/runtime/gfx.h', + 'src/runtime/renderer_api.h', + 'src/runtime/fio.h', + 'src/runtime/config.h', + 'src/runtime/threading.h', + 'src/runtime/app.h', + 'src/runtime/dynamic_libs.h', - 'src/voyage.c', - 'src/fio.c', - 'src/error_report.c', - 'src/gfx_main.c', - 'src/gfx_shader_loading.c', - 'src/gfx_pipelines.c', - 'src/config.c', - 'src/threading.c', + 'src/runtime/error_report.c', + 'src/runtime/gfx_main.c', + 'src/runtime/gfx_shader_loading.c', + 'src/runtime/config.c', + 'src/runtime/runtime_cvars.c', + 'src/runtime/threading.c', + 'src/runtime/fio.c', + 'src/runtime/app.c', + 'src/runtime/dynamic_libs.c', # Contrib Sources - 'contrib/glad/glad.c', 'contrib/xxhash/xxhash.c', - dependencies : [thread_dep, m_dep, glfw_dep], - include_directories: incdir) + dependencies : [thread_dep, m_dep], + include_directories : incdir, + c_pch : 'pch/rt_pch.h') + +if vk_dep.found() + vk_renderer_lib = library('vyvk', + # Project Sources + 'src/renderer/vk/gpu.h', + + 'src/renderer/vk/init.c', + 'src/renderer/vk/gfx_pipelines.c', + dependencies : [m_dep, vk_dep], + include_directories : incdir, + link_with : [runtime_lib], + c_pch : 'pch/vk_pch.h') +endif + +executable('voyage', + 'src/game/voyage.c', + include_directories : incdir, + link_with : [runtime_lib], + win_subsystem : 'windows') + + diff --git a/pch/rt_pch.h b/pch/rt_pch.h new file mode 100644 index 0000000..198556f --- /dev/null +++ b/pch/rt_pch.h @@ -0,0 +1 @@ +#include diff --git a/pch/vk_pch.h b/pch/vk_pch.h new file mode 100644 index 0000000..48ce5f5 --- /dev/null +++ b/pch/vk_pch.h @@ -0,0 +1 @@ +#include diff --git a/src/game/voyage.c b/src/game/voyage.c new file mode 100644 index 0000000..de7e52e --- /dev/null +++ b/src/game/voyage.c @@ -0,0 +1,15 @@ +#include "runtime/app.h" + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +int WINAPI wWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PWSTR pCmdLine, + int nCmdShow) { + return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow); +} + +#endif \ No newline at end of file diff --git a/src/gfx_main.c b/src/gfx_main.c deleted file mode 100644 index 22107bd..0000000 --- a/src/gfx_main.c +++ /dev/null @@ -1,23 +0,0 @@ -#include - -#include "gfx.h" - -/* Attributes are used to bind buffers (or textures) to symbolic values. - * For example, an attribute might be bound to "CELL_GRID", which would be - * replaced with the (at the time of the invoke) grid buffer of the current - * world cell. - */ - -bool vyLoadShaders(const char **paths, vy_shader *shaders, unsigned int count); - -bool vyInitRenderer(void) { - /* Init shader programs */ - const char *shader_files[] = {"shader/cell.shader"}; - vy_shader shaders[1]; - if (!vyLoadShaders(shader_files, shaders, 1)) - return false; - return true; -} - -void vyShutdownRenderer(void) { -} diff --git a/src/gfx_pipelines.c b/src/renderer/vk/gfx_pipelines.c similarity index 94% rename from src/gfx_pipelines.c rename to src/renderer/vk/gfx_pipelines.c index acda984..8700ebf 100644 --- a/src/gfx_pipelines.c +++ b/src/renderer/vk/gfx_pipelines.c @@ -1,11 +1,11 @@ -#include +#include "runtime/runtime.h" +#include "runtime/gfx.h" -#include "gfx.h" -#include "gfx_backend.h" -#include "voyage.h" +#include "runtime/renderer_api.h" typedef struct { - GLuint prog; + + unsigned int prog; } vy_gl_pipeline; #define NUM_SLOTS 256 @@ -52,8 +52,9 @@ static void ReleasePipelineSlot(vy_gfx_pipeline_id id) { _storage.generation_in_use[slot] &= ~0x1; } -vy_gfx_pipeline_id +VY_DLLEXPORT vy_gfx_pipeline_id vyCompileComputePipeline(const vy_compute_pipeline_info *info) { +#if 0 char info_log[512]; GLuint prog = glCreateProgram(); @@ -86,16 +87,18 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) { return (vy_gfx_pipeline_id){0}; } glDeleteShader(shader); + #endif vy_gl_pipeline pipeline; - pipeline.prog = prog; + pipeline.prog = 0; return StorePipeline(pipeline); } -vy_gfx_pipeline_id +VY_DLLEXPORT vy_gfx_pipeline_id vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { - char info_log[512]; + #if 0 + char info_log[512]; GLuint prog = glCreateProgram(); GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); @@ -148,8 +151,9 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); + #endif vy_gl_pipeline pipeline; - pipeline.prog = prog; + pipeline.prog = 0; return StorePipeline(pipeline); } diff --git a/src/renderer/vk/gpu.h b/src/renderer/vk/gpu.h new file mode 100644 index 0000000..a171f79 --- /dev/null +++ b/src/renderer/vk/gpu.h @@ -0,0 +1,15 @@ +#ifndef VY_VK_GPU_H +#define VY_VK_GPU_H + +#include + +typedef struct { + VkInstance instance; + VkAllocationCallbacks *alloc_cb; +} vy_vk_gpu; + +#ifndef VY_VK_DONT_DEFINE_GPU_GLOBAL +extern vy_vk_gpu g_gpu; +#endif + +#endif diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c new file mode 100644 index 0000000..c2e8578 --- /dev/null +++ b/src/renderer/vk/init.c @@ -0,0 +1,137 @@ +#include +#include + +#define VY_VK_DONT_DEFINE_GPU_GLOBAL +#include "gpu.h" + +#include "runtime/runtime.h" +#include "runtime/renderer_api.h" +#include "runtime/config.h" + +VY_CVAR_I(r_EnableAPIAllocTracking, + "Enable tracking of allocations done by the vulkan api. [0/1] Default: 0", + 0); + + +typedef struct { + size_t allocated; + size_t reallocated; + size_t freed; + size_t max_alignment; +} vy_vk_alloc_stats; + +vy_vk_gpu g_gpu; + +static VkAllocationCallbacks _tracking_alloc_cbs; +static vy_vk_alloc_stats _alloc_stats; + +static const char *AllocationScopeToString(VkSystemAllocationScope scope) +{ + switch (scope) { + case VK_SYSTEM_ALLOCATION_SCOPE_COMMAND: + return "COMMAND"; + case VK_SYSTEM_ALLOCATION_SCOPE_OBJECT: + return "OBJECT"; + case VK_SYSTEM_ALLOCATION_SCOPE_CACHE: + return "CACHE"; + case VK_SYSTEM_ALLOCATION_SCOPE_DEVICE: + return "DEVICE"; + case VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE: + return "INSTANCE"; + default: + return "UNKNOWN"; + } +} + +static void LogAllocationStats(const vy_vk_alloc_stats *stats) { + vyLog("vk", + "Allocated: %zu bytes. Reallocated: %zu bytes. Max. requested " + "alignment: %zu bytes", + stats->allocated, + stats->reallocated, + stats->max_alignment); +} + +static void *TrackAllocation(void *userData, size_t size, size_t alignment, VkSystemAllocationScope scope) { + vy_vk_alloc_stats *stats = userData; + if (alignment > stats->max_alignment) + stats->max_alignment = alignment; + stats->allocated += size; + vyLog("vk", + "Allocation. Size: %zu, Alignment: %zu, Scope: %s", + size, + alignment, + AllocationScopeToString(scope)); + LogAllocationStats(stats); + return _aligned_malloc(size, alignment); +} + +static void *TrackReallocation(void *userData, + void *original, + size_t size, + size_t alignment, + VkSystemAllocationScope scope) { + vy_vk_alloc_stats *stats = userData; + if (alignment > stats->max_alignment) + stats->max_alignment = alignment; + stats->reallocated += size; + vyLog("vk", + "Reallocation. Size: %zu, Alignment: %zu, Scope: %s", + size, + alignment, + AllocationScopeToString(scope)); + LogAllocationStats(stats); + return _aligned_realloc(original, size, alignment); +} + +static void TrackFree(void *userData, void *memory) { + vy_vk_alloc_stats *stats = userData; + vyLog("vk", "Free."); + LogAllocationStats(stats); + free(memory); +} + +VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) { + vyLog("vk", "Init"); + + vyRegisterCVAR(&r_EnableAPIAllocTracking); + + _tracking_alloc_cbs.pUserData = &_alloc_stats; + _tracking_alloc_cbs.pfnAllocation = TrackAllocation; + _tracking_alloc_cbs.pfnReallocation = TrackReallocation; + _tracking_alloc_cbs.pfnFree = TrackFree; + + if (r_EnableAPIAllocTracking.i) { + g_gpu.alloc_cb = &_tracking_alloc_cbs; + } else { + g_gpu.alloc_cb = NULL; + } + + VkResult result; + + VkApplicationInfo app_info = { + .apiVersion = VK_API_VERSION_1_0, + .applicationVersion = 0x00001000, + .engineVersion = 0x00001000, + .pEngineName = "voyage", + .pApplicationName = "Voyage", + }; + + VkInstanceCreateInfo instance_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &app_info, + }; + result = vkCreateInstance(&instance_info, g_gpu.alloc_cb, &g_gpu.instance); + if (result != VK_SUCCESS) { + vyReportError("vk", "Failed to create the vulkan instance!"); + return 1; + } + + return 0; +} + +VY_DLLEXPORT void vyShutdown(void) { + vyLog("vk", "Shutdown"); + + vkDestroyInstance(g_gpu.instance, g_gpu.alloc_cb); +} \ No newline at end of file diff --git a/src/runtime/app.c b/src/runtime/app.c new file mode 100644 index 0000000..1f125f0 --- /dev/null +++ b/src/runtime/app.c @@ -0,0 +1,130 @@ +#include "app.h" +#include "fio.h" +#include "gfx.h" +#include "config.h" +#include "renderer_api.h" + +extern void __RegisterRuntimeCVars(void); + +VY_CVAR_I(rt_Fullscreen, "Show window in fullscreen mode. [0/1] Default: 0", 0); +VY_CVAR_I(rt_WindowWidth, "Window width. Default: 1024", 1024); +VY_CVAR_I(rt_WindowHeight, "Window height. Default: 768", 768); + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +static LRESULT CALLBACK win32WndProc(HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + default: + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } +} + +VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PWSTR pCmdLine, + int nCmdShow) { + + __RegisterRuntimeCVars(); + + vy_fio_config fio_config = {0}; + if (!vyInitFIO(&fio_config)) { + vyReportError("FIO", "Init failed."); + return 1; + } + + + WNDCLASSEXW wndclass = { + .cbSize = sizeof(wndclass), + .hInstance = hInstance, + .lpszClassName = L"vyWndClass", + .style = CS_OWNDC, + .lpfnWndProc = win32WndProc, + }; + if (!RegisterClassExW(&wndclass)) { + vyReportError("CORE", "RegisterClassEx failed: %u", GetLastError()); + return 1; + } + + HWND wnd = NULL; + if (rt_Fullscreen.i) { + /* Fullscreen window */ + int w = GetSystemMetrics(SM_CXSCREEN); + int h = GetSystemMetrics(SM_CYSCREEN); + wnd = CreateWindowExW(WS_EX_APPWINDOW, + L"vyWndClass", + L"Voyage", + WS_POPUP, + 0, + 0, + w, + h, + NULL, + NULL, + hInstance, + NULL); + ShowWindow(wnd, SW_SHOW); + } else { + /* Windowed mode */ + int w = rt_WindowWidth.i; + int h = rt_WindowHeight.i; + wnd = CreateWindowExW(WS_EX_APPWINDOW, + L"vyWndClass", + L"Voyage", + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, + CW_USEDEFAULT, + CW_USEDEFAULT, + w, + h, + NULL, + NULL, + hInstance, + NULL); + ShowWindow(wnd, SW_SHOW); + } + + if (!wnd) { + vyReportError("CORE", + "Failed to create the game window: %u", + GetLastError()); + return 1; + } + + vy_renderer_init_info renderer_info = {.hWnd = wnd, .hInstance = hInstance}; + if (!vyInitGFX(&renderer_info)) { + vyReportError("GFX", "Init failed."); + return 1; + } + + /* Main Loop */ + bool keep_running = true; + while (keep_running) { + MSG msg; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + keep_running = false; + } else { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + } + + vyShutdownGFX(); + + DestroyWindow(wnd); + UnregisterClassW(L"vyWndClass", hInstance); + + vyShutdownFIO(); + + return 0; +} + +#endif diff --git a/src/runtime/app.h b/src/runtime/app.h new file mode 100644 index 0000000..f836a8a --- /dev/null +++ b/src/runtime/app.h @@ -0,0 +1,20 @@ +#ifndef VY_APP_H +#define VY_APP_H + +/* Platform specific application entry point */ + +#include "runtime.h" + +#ifdef _WIN32 + +/* Forward declared here, to avoid including windows.h */ +struct HINSTANCE__; + +VY_DLLEXPORT int vyWin32Entry(struct HINSTANCE__ *hInstance, + struct HINSTANCE__ *hPrevInstance, + wchar_t *pCmdLine, + int nCmdShow); + +#endif + +#endif diff --git a/src/config.c b/src/runtime/config.c similarity index 97% rename from src/config.c rename to src/runtime/config.c index 90aa82e..2e09b21 100644 --- a/src/config.c +++ b/src/runtime/config.c @@ -1,6 +1,6 @@ #include "config.h" #include "threading.h" -#include "voyage.h" +#include "runtime.h" #include #include diff --git a/include/config.h b/src/runtime/config.h similarity index 92% rename from include/config.h rename to src/runtime/config.h index 8675a66..a90f3d3 100644 --- a/include/config.h +++ b/src/runtime/config.h @@ -1,6 +1,8 @@ #ifndef VY_CONFIG_H #define VY_CONFIG_H +#include "runtime.h" + typedef enum { VY_CVAR_TYPE_INT, @@ -36,8 +38,8 @@ typedef struct .s = (v), \ .type = VY_CVAR_TYPE_STRING} -void vyRegisterCVAR(vy_cvar *cvar); +VY_DLLEXPORT void vyRegisterCVAR(vy_cvar *cvar); -vy_cvar *vyGetCVAR(const char *name); +VY_DLLEXPORT vy_cvar *vyGetCVAR(const char *name); #endif \ No newline at end of file diff --git a/src/runtime/dynamic_libs.c b/src/runtime/dynamic_libs.c new file mode 100644 index 0000000..d031f66 --- /dev/null +++ b/src/runtime/dynamic_libs.c @@ -0,0 +1,43 @@ +#include "dynamic_libs.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) { + wchar_t libname_w[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, + MB_PRECOMPOSED, + libname, + -1, + libname_w, + MAX_PATH); + HMODULE mod = LoadLibraryW(libname_w); + return (vy_dynlib)mod; +} + +VY_DLLEXPORT void *vyGetSymbol(vy_dynlib lib, const char *symbol) { + return (void*)GetProcAddress((HMODULE)lib, symbol); +} + +VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) { + FreeLibrary((HMODULE)lib); +} + +#elif defined(__linux__) +#include + +VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) { + return dlopen(libname, RTLD_NOW | RTLD_LOCAL); +} + +VY_DLLEXPORT void *vyGetSymbol(vy_dynlib lib, const char *symbol) { + return dlsym(lib, symbol); +} + +VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) { + dlclose(lib); +} + + +#endif diff --git a/src/runtime/dynamic_libs.h b/src/runtime/dynamic_libs.h new file mode 100644 index 0000000..6ed9f5c --- /dev/null +++ b/src/runtime/dynamic_libs.h @@ -0,0 +1,20 @@ +#ifndef VY_DYNAMIC_LIBS_H +#define VY_DYNAMIC_LIBS_H + +#include "runtime.h" + +#ifdef _WIN32 + #define VY_DLLNAME(s) (s".dll") +#elif defined(__linux__) + #define VY_DLLNAME(s) ("lib"s".so") +#endif + +typedef void *vy_dynlib; + +VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname); + +VY_DLLEXPORT void *vyGetSymbol(vy_dynlib lib, const char *symbol); + +VY_DLLEXPORT void vyCloseLib(vy_dynlib lib); + +#endif diff --git a/src/error_report.c b/src/runtime/error_report.c similarity index 89% rename from src/error_report.c rename to src/runtime/error_report.c index b8210e0..5cf987a 100644 --- a/src/error_report.c +++ b/src/runtime/error_report.c @@ -1,7 +1,7 @@ #include #include -#include "voyage.h" +#include "runtime.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -40,7 +40,7 @@ static void LogOut(const char *text) #endif } -void vyReportError(const char *subsystem, const char *fmt, ...) { +VY_DLLEXPORT void vyReportError(const char *subsystem, const char *fmt, ...) { char buf[256]; int at = snprintf(buf, VY_ARRAY_COUNT(buf) - 1, "[%s] ", subsystem); @@ -54,7 +54,7 @@ void vyReportError(const char *subsystem, const char *fmt, ...) { DisplayErrorBox(buf); } -void vyLog(const char *subsystem, const char *fmt, ...) { +VY_DLLEXPORT void vyLog(const char *subsystem, const char *fmt, ...) { char buf[256]; int at = snprintf(buf, VY_ARRAY_COUNT(buf) - 1, "[%s] ", subsystem); diff --git a/src/fio.c b/src/runtime/fio.c similarity index 95% rename from src/fio.c rename to src/runtime/fio.c index 3641c42..1aacc29 100644 --- a/src/fio.c +++ b/src/runtime/fio.c @@ -139,7 +139,7 @@ static void *linuxFIOThreadProc(void *); static DWORD WINAPI win32FIOThreadProc(_In_ LPVOID); #endif -bool vyInitFIO(const vy_fio_config *config) { +VY_DLLEXPORT bool vyInitFIO(const vy_fio_config *config) { unsigned int queue_size = (config->queue_size) ? config->queue_size : 512; unsigned int max_file_count = (config->max_file_count) ? config->max_file_count : 512; @@ -165,7 +165,7 @@ bool vyInitFIO(const vy_fio_config *config) { return true; } -void vyShutdownFIO(void) { +VY_DLLEXPORT void vyShutdownFIO(void) { #ifdef __linux__ pthread_cancel(_thread); pthread_join(_thread, NULL); @@ -199,7 +199,7 @@ vy_file_id vyGetFileIdFromSpan(vy_text_span path) { return fid; } -vy_file_id vyAddFileFromSpan(vy_text_span path) { +VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path) { vy_file_id fid = vyGetFileIdFromSpan(path); #ifdef __linux__ @@ -246,14 +246,14 @@ vy_file_id vyAddFileFromSpan(vy_text_span path) { return fid; } -vy_file_id vyAddFile(const char *path) { +VY_DLLEXPORT vy_file_id vyAddFile(const char *path) { vy_text_span span; span.start = path; span.length = (unsigned int)strlen(path); return vyAddFileFromSpan(span); } -const char *vyGetFilePath(vy_file_id fid) { +VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid) { /* Hash Lookup */ if (fid == 0) return NULL; @@ -286,7 +286,7 @@ const char *vyGetFilePath(vy_file_id fid) { return result; } -vy_fio_handle vyEnqueueRead(vy_file_id fid) { +VY_DLLEXPORT vy_fio_handle vyEnqueueRead(vy_file_id fid) { vy_fio_handle handle = 0; do { @@ -326,7 +326,7 @@ vy_fio_handle vyEnqueueRead(vy_file_id fid) { return handle; } -void vyAbortFIO(vy_fio_handle fio) { +VY_DLLEXPORT void vyAbortFIO(vy_fio_handle fio) { if (fio == 0) return; #ifdef __linux__ @@ -346,7 +346,7 @@ void vyAbortFIO(vy_fio_handle fio) { #endif } -bool vyIsFIOFinished(vy_fio_handle fio) { +VY_DLLEXPORT bool vyIsFIOFinished(vy_fio_handle fio) { if (fio == 0) return false; #ifdef __linux__ @@ -363,7 +363,8 @@ bool vyIsFIOFinished(vy_fio_handle fio) { return result; } -bool vyRetrieveReadBuffer(vy_fio_handle fio, vy_file_buffer *buffer) { +VY_DLLEXPORT bool vyRetrieveReadBuffer(vy_fio_handle fio, + vy_file_buffer *buffer) { if (fio == 0) return false; @@ -394,7 +395,7 @@ bool vyRetrieveReadBuffer(vy_fio_handle fio, vy_file_buffer *buffer) { return is_finished; } -void vyFreeFileBuffer(vy_file_buffer buffer) { +VY_DLLEXPORT void vyFreeFileBuffer(vy_file_buffer buffer) { free(buffer.data); } diff --git a/src/runtime/fio.h b/src/runtime/fio.h new file mode 100644 index 0000000..946b207 --- /dev/null +++ b/src/runtime/fio.h @@ -0,0 +1,61 @@ +#ifndef VY_FIO_H +#define VY_FIO_H + +#include +#include +#include + +#include "runtime.h" + +enum +{ + VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1, + VY_FILE_BUFFER_FLAG_READ_FAILED = 0x2, +}; + +typedef struct { + void *data; + size_t size; + uint32_t flags; +} vy_file_buffer; + +inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) { + return fb->flags == 0; +} + +/* used to identify a file (XXH3 hash of the path) */ +typedef uint64_t vy_file_id; + +typedef unsigned int vy_fio_handle; + +typedef struct { + unsigned int queue_size; + unsigned int max_file_count; +} vy_fio_config; + +VY_DLLEXPORT bool vyInitFIO(const vy_fio_config *config); + +VY_DLLEXPORT void vyShutdownFIO(void); + +VY_DLLEXPORT vy_file_id vyGetFileId(const char *path); + +VY_DLLEXPORT vy_file_id vyGetFileIdFromSpan(vy_text_span path); + +VY_DLLEXPORT vy_file_id vyAddFile(const char *path); + +VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path); + +VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid); + +VY_DLLEXPORT vy_fio_handle vyEnqueueRead(vy_file_id fid); + +VY_DLLEXPORT void vyAbortFIO(vy_fio_handle fio); + +VY_DLLEXPORT bool vyIsFIOFinished(vy_fio_handle fio); + +VY_DLLEXPORT bool vyRetrieveReadBuffer(vy_fio_handle fio, + vy_file_buffer *buffer); + +VY_DLLEXPORT void vyFreeFileBuffer(vy_file_buffer buffer); + +#endif diff --git a/include/gfx.h b/src/runtime/gfx.h similarity index 83% rename from include/gfx.h rename to src/runtime/gfx.h index d18fe00..d141246 100644 --- a/include/gfx.h +++ b/src/runtime/gfx.h @@ -1,8 +1,6 @@ #ifndef VY_GFX_H #define VY_GFX_H -#include - /* graphics system. this is the interface of the rendering code. * * we need (at least) three different renderers: @@ -11,11 +9,17 @@ * - object renderer (for static models) */ +#include #include -bool vyInitRenderer(void); +#include "runtime.h" -void vyShutdownRenderer(void); +/* In renderer_api.h -> Not necessary for almost all gfx usage */ +typedef struct vy_renderer_init_info_s vy_renderer_init_info; + +VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info); + +VY_DLLEXPORT void vyShutdownGFX(void); /* Generational indices for backend objects */ typedef struct { diff --git a/src/runtime/gfx_main.c b/src/runtime/gfx_main.c new file mode 100644 index 0000000..3ca67f1 --- /dev/null +++ b/src/runtime/gfx_main.c @@ -0,0 +1,75 @@ +#include + +#define VY_DONT_DEFINE_RENDERER_GLOBAL + +#include "gfx.h" +#include "renderer_api.h" +#include "config.h" +#include "dynamic_libs.h" + +/* Attributes are used to bind buffers (or textures) to symbolic values. + * For example, an attribute might be bound to "CELL_GRID", which would be + * replaced with the (at the time of the invoke) grid buffer of the current + * world cell. + */ + +vy_renderer_api g_renderer; +static vy_dynlib _renderer_lib; + +VY_CVAR_S(rt_Renderer, + "Select the render backend. Available options: [vk], Default: vk", + "vk"); + +extern bool vyLoadShaders(const char **paths, vy_shader *shaders, unsigned int count); + +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 (strcmp(rt_Renderer.s, "vk") == 0) { + _renderer_lib = vyOpenLib(VY_DLLNAME("vyvk")); + if (!_renderer_lib) { + vyReportError("GFX", + "Unable to load renderer backend: %s", + VY_DLLNAME("vyvk")); + return false; + } + RETRIEVE_SYMBOL(Init, vy_init_renderer_fn); + RETRIEVE_SYMBOL(Shutdown, vy_shutdown_renderer_fn); + 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", rt_Renderer.name, rt_Renderer.s); + return false; + } +#undef RETRIEVE_SYMBOL +} + +VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info) { + + if (!LoadRenderer()) + return false; + + if (g_renderer.Init(renderer_info) != 0) + return false; + + /* Init shader programs */ + const char *shader_files[] = {"shader/cell.shader"}; + vy_shader shaders[1]; + if (!vyLoadShaders(shader_files, shaders, 1)) + return false; + return true; +} + +VY_DLLEXPORT void vyShutdownGFX(void) { + g_renderer.Shutdown(); +} diff --git a/src/gfx_shader_loading.c b/src/runtime/gfx_shader_loading.c similarity index 97% rename from src/gfx_shader_loading.c rename to src/runtime/gfx_shader_loading.c index e9acf15..fa80c34 100644 --- a/src/gfx_shader_loading.c +++ b/src/runtime/gfx_shader_loading.c @@ -6,10 +6,10 @@ #include #include +#include "runtime.h" #include "fio.h" #include "gfx.h" -#include "gfx_backend.h" -#include "voyage.h" +#include "renderer_api.h" typedef enum { VY_STMT_FORM_VALUE, @@ -289,9 +289,9 @@ static vy_fio_handle DispatchShaderRead(const char *shader, return DispatchFileRead(path->value); } -static vy_gfx_pipeline_id LinkProgram(vy_parse_state *state, - const char *file_path, - unsigned int root_list) { +static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, + const char *file_path, + unsigned int root_list) { /* Process the data */ vy_fio_handle vertex_read = DispatchShaderRead("vertex", state, root_list, file_path); @@ -324,7 +324,7 @@ static vy_gfx_pipeline_id LinkProgram(vy_parse_state *state, vy_compute_pipeline_info info; info.compute_source = compute_code.data; info.compute_source_length = compute_code.size; - vy_gfx_pipeline_id pipeline = vyCompileComputePipeline(&info); + vy_gfx_pipeline_id pipeline = g_renderer.CompileComputePipeline(&info); vyFreeFileBuffer(compute_code); return pipeline; } else if (vertex_read || fragment_read) { @@ -386,7 +386,7 @@ static vy_gfx_pipeline_id LinkProgram(vy_parse_state *state, } } - vy_gfx_pipeline_id pipeline = vyCompileGraphicsPipeline(&info); + vy_gfx_pipeline_id pipeline = g_renderer.CompileGraphicsPipeline(&info); vyFreeFileBuffer(vertex_code); vyFreeFileBuffer(fragment_code); return pipeline; @@ -515,7 +515,7 @@ ParseShaderFile(vy_file_id fid, vy_file_buffer fbuf, vy_shader *shader) { DbgPrintShaderFile(&state, root_list, 0); - shader->pipeline = LinkProgram(&state, file_path, root_list); + shader->pipeline = CreatePipeline(&state, file_path, root_list); if (!VY_IS_GFX_ID_VALID(shader->pipeline)) { result = false; goto out; diff --git a/src/runtime/renderer_api.h b/src/runtime/renderer_api.h new file mode 100644 index 0000000..468aef0 --- /dev/null +++ b/src/runtime/renderer_api.h @@ -0,0 +1,53 @@ +#ifndef VY_GFX_BACKEND_H +#define VY_GFX_BACKEND_H + +/* Backend functions and types. */ + +#include + +#include "gfx.h" + +#ifdef _WIN32 +struct HINSTANCE__; +struct HWND__; +#endif + +struct vy_renderer_init_info_s { +#ifdef _WIN32 + struct HINSTANCE__ *hInstance; + struct HWND__ *hWnd; +#endif +}; + +typedef struct { + const char *compute_source; + size_t compute_source_length; +} vy_compute_pipeline_info; + +typedef struct { + const char *vertex_source; + size_t vertex_source_length; + + const char *fragment_source; + size_t fragment_source_length; +} vy_graphics_pipeline_info; + +typedef int vy_init_renderer_fn(const vy_renderer_init_info *info); +typedef void vy_shutdown_renderer_fn(void); +typedef vy_gfx_pipeline_id +vy_compile_compute_pipeline_fn(const vy_compute_pipeline_info *info); +typedef vy_gfx_pipeline_id +vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info); + +typedef struct { + vy_init_renderer_fn *Init; + vy_shutdown_renderer_fn *Shutdown; + vy_compile_compute_pipeline_fn *CompileComputePipeline; + vy_compile_graphics_pipeline_fn *CompileGraphicsPipeline; +} vy_renderer_api; + +#ifndef VY_DONT_DEFINE_RENDERER_GLOBAL +extern vy_renderer_api g_renderer; +#endif + +#endif diff --git a/include/voyage.h b/src/runtime/runtime.h similarity index 52% rename from include/voyage.h rename to src/runtime/runtime.h index 7541080..65bc373 100644 --- a/include/voyage.h +++ b/src/runtime/runtime.h @@ -5,6 +5,12 @@ #include +#ifdef _WIN32 + #define VY_DLLEXPORT __declspec(dllexport) +#else + #define VY_DLLEXPORT +#endif + #define VY_UNUSED(x) ((void)sizeof((x))) #define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0])) @@ -13,8 +19,8 @@ typedef struct { unsigned int length; } vy_text_span; -void vyReportError(const char *subsystem, const char *fmt, ...); +VY_DLLEXPORT void vyReportError(const char *subsystem, const char *fmt, ...); -void vyLog(const char *subsystem, const char *fmt, ...); +VY_DLLEXPORT void vyLog(const char *subsystem, const char *fmt, ...); #endif diff --git a/src/runtime/runtime_cvars.c b/src/runtime/runtime_cvars.c new file mode 100644 index 0000000..22a81f2 --- /dev/null +++ b/src/runtime/runtime_cvars.c @@ -0,0 +1,14 @@ +#include "config.h" + +extern vy_cvar rt_Renderer; +extern vy_cvar rt_Fullscreen; +extern vy_cvar rt_WindowWidth; +extern vy_cvar rt_WindowHeight; + +void __RegisterRuntimeCVars(void) +{ + vyRegisterCVAR(&rt_Renderer); + vyRegisterCVAR(&rt_Fullscreen); + vyRegisterCVAR(&rt_WindowWidth); + vyRegisterCVAR(&rt_WindowHeight); +} \ No newline at end of file diff --git a/src/threading.c b/src/runtime/threading.c similarity index 82% rename from src/threading.c rename to src/runtime/threading.c index 4c6ba18..4558ef3 100644 --- a/src/threading.c +++ b/src/runtime/threading.c @@ -1,5 +1,5 @@ #include "threading.h" -#include "voyage.h" +#include "runtime.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -15,7 +15,7 @@ static vy_mutex _mutex[MAX_MUTEX]; static ptrdiff_t _first_reusable = MAX_MUTEX; static ptrdiff_t _next = 0; -vy_mutex *vyCreateMutex(void) { +VY_DLLEXPORT vy_mutex *vyCreateMutex(void) { if (_first_reusable < MAX_MUTEX) { vy_mutex *mtx = &_mutex[_first_reusable]; _first_reusable = mtx->next_reusable; @@ -35,18 +35,18 @@ vy_mutex *vyCreateMutex(void) { return NULL; } -void vyDestroyMutex(vy_mutex *mutex) { +VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) { ptrdiff_t index = mutex - &_mutex[0]; mutex->next_reusable = _first_reusable; _first_reusable = index; } -bool vyLockMutex(vy_mutex *mutex) { +VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) { return WaitForSingleObject(mutex->handle, INFINITE) == WAIT_OBJECT_0; } -bool vyUnlockMutex(vy_mutex *mutex) { - return ReleaseMutex(mutex) != 0; +VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) { + return ReleaseMutex(mutex->handle) != 0; } #elif defined(__linux__) @@ -62,7 +62,7 @@ static vy_mutex _mutex[MAX_MUTEX]; static ptrdiff_t _first_reusable = MAX_MUTEX; static ptrdiff_t _next = 0; -vy_mutex *vyCreateMutex(void) { +VY_DLLEXPORT vy_mutex *vyCreateMutex(void) { if (_first_reusable < MAX_MUTEX) { vy_mutex *mtx = &_mutex[_first_reusable]; _first_reusable = mtx->next_reusable; @@ -81,17 +81,17 @@ vy_mutex *vyCreateMutex(void) { return NULL; } -void vyDestroyMutex(vy_mutex *mutex) { +VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) { ptrdiff_t index = mutex - &_mutex[0]; mutex->next_reusable = _first_reusable; _first_reusable = index; } -bool vyLockMutex(vy_mutex *mutex) { +VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) { return pthread_mutex_lock(&mutex->handle) == 0; } -bool vyUnlockMutex(vy_mutex *mutex) { +VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) { return pthread_mutex_unlock(&mutex->handle) == 0; } diff --git a/src/runtime/threading.h b/src/runtime/threading.h new file mode 100644 index 0000000..83dd237 --- /dev/null +++ b/src/runtime/threading.h @@ -0,0 +1,20 @@ +#ifndef VY_THREADING_H +#define VY_THREADING_H + +/* platform independent threading */ + +#include + +#include "runtime.h" + +typedef struct vy_mutex_s vy_mutex; + +VY_DLLEXPORT vy_mutex *vyCreateMutex(void); + +VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex); + +VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex); + +VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex); + +#endif diff --git a/src/voyage.c b/src/voyage.c deleted file mode 100644 index 10536fe..0000000 --- a/src/voyage.c +++ /dev/null @@ -1,115 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "fio.h" -#include "gfx.h" -#include "threading.h" - -static void glfw_error_cb(int err, const char *description) { - fprintf(stderr, "[GLFW] %u: %s\n", err, description); -} - -static void make_window_windowed_fullscreen(GLFWwindow *window, - GLFWmonitor *monitor) { - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - glfwSetWindowMonitor(window, - monitor, - 0, - 0, - mode->width, - mode->height, - mode->refreshRate); -} - -static void make_window_windowed(GLFWwindow *window, int w, int h) { - glfwSetWindowMonitor(window, NULL, 0, 0, w, h, GLFW_DONT_CARE); -} - -typedef struct { - int f1; - int esc; -} key_states; - -int main(int argc, char **argv) { - glfwSetErrorCallback(glfw_error_cb); - if (!glfwInit()) { - return 1; - } - printf("[GFLW] Version: %s\n", glfwGetVersionString()); - - /* Create a windowed full screen window */ - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); - GLFWwindow *window = glfwCreateWindow(640, 480, "Voyage", NULL, NULL); - if (!window) { - fprintf(stderr, "[GFX] Window creation failed.\n"); - return 1; - } - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - if (!gladLoadGL()) { - fprintf(stderr, "[GFX] OpenGL load failed.\n"); - return 1; - } - - vy_fio_config fio_config = {0}; - if (!vyInitFIO(&fio_config)) { - fprintf(stderr, "[FIO] Init failed.\n"); - return 1; - } - - if (!vyInitRenderer()) { - fprintf(stderr, "[GFX] Init failed.\n"); - return 1; - } - printf("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %u\n", - GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS); - printf("GL_MAX_UNIFORM_BUFFER_BINDINGS: %u\n", - GL_MAX_UNIFORM_BUFFER_BINDINGS); - - key_states prev_keys = {0}; - int window_state = 0; - - while (!glfwWindowShouldClose(window)) { - /* Gather input */ - key_states keys; - keys.f1 = glfwGetKey(window, GLFW_KEY_F1); - keys.esc = glfwGetKey(window, GLFW_KEY_ESCAPE); - - if (keys.f1 && !prev_keys.f1) { - if (window_state) - make_window_windowed(window, 640, 480); - else - make_window_windowed_fullscreen(window, - glfwGetPrimaryMonitor()); - window_state = !window_state; - } - - if (keys.esc) - glfwSetWindowShouldClose(window, GLFW_TRUE); - - int fbw, fbh; - glfwGetFramebufferSize(window, &fbw, &fbh); - glViewport(0, 0, fbw, fbh); - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - - glfwSwapBuffers(window); - glfwPollEvents(); - prev_keys = keys; - } - - vyShutdownRenderer(); - vyShutdownFIO(); - - glfwDestroyWindow(window); - glfwTerminate(); - return 0; -}