From 97adb0dffaaa408beb4fa7c32caa3aecca14dc0e Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Wed, 22 Nov 2023 11:23:52 +0100 Subject: [PATCH] various updates --- meson.build | 8 +- src/game/voyage.c | 5 +- src/renderer/vk/framebuffer.h | 22 +++ src/renderer/vk/gfx_pipelines.c | 30 ++-- src/renderer/vk/init.c | 72 ++++----- src/renderer/vk/swapchain.c | 74 +++++---- src/renderer/vk/swapchain.h | 6 +- src/runtime/app.c | 152 ++++++++++++++++-- src/runtime/app.h | 10 +- src/runtime/dynamic_libs.h | 8 +- src/runtime/fio.c | 16 +- src/runtime/fio.h | 7 +- src/runtime/gfx.h | 11 +- src/runtime/gfx_main.c | 25 +-- src/runtime/gfx_shader_loading.c | 44 ++--- src/runtime/handles.h | 8 + src/runtime/jobs.c | 65 ++++++++ src/runtime/jobs.h | 20 +++ src/runtime/renderer_api.h | 7 +- src/runtime/runtime.h | 9 +- src/runtime/threading.h | 21 +++ src/runtime/threading_cond.c | 68 ++++++++ .../{threading.c => threading_mutex.c} | 15 +- src/runtime/threading_thread.c | 74 +++++++++ 24 files changed, 609 insertions(+), 168 deletions(-) create mode 100644 src/renderer/vk/framebuffer.h create mode 100644 src/runtime/handles.h create mode 100644 src/runtime/jobs.c create mode 100644 src/runtime/jobs.h create mode 100644 src/runtime/threading_cond.c rename src/runtime/{threading.c => threading_mutex.c} (85%) create mode 100644 src/runtime/threading_thread.c diff --git a/meson.build b/meson.build index 79541a7..25056cd 100644 --- a/meson.build +++ b/meson.build @@ -49,16 +49,20 @@ runtime_lib = library('vyrt', 'src/runtime/threading.h', 'src/runtime/app.h', 'src/runtime/dynamic_libs.h', + 'src/runtime/jobs.h', '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/threading_mutex.c', + 'src/runtime/threading_thread.c', + 'src/runtime/threading_cond.c', 'src/runtime/fio.c', 'src/runtime/app.c', 'src/runtime/dynamic_libs.c', + 'src/runtime/jobs.c', # Contrib Sources 'contrib/xxhash/xxhash.c', @@ -87,7 +91,7 @@ if vk_dep.found() # Contrib Sources 'contrib/volk/volk.h', 'contrib/volk/volk.c', - dependencies : [m_dep, vk_inc_dep], + dependencies : [m_dep, vk_inc_dep, windowing_dep], include_directories : incdir, link_with : [runtime_lib], c_pch : 'pch/vk_pch.h', diff --git a/src/game/voyage.c b/src/game/voyage.c index 6e2e3f4..06311a8 100644 --- a/src/game/voyage.c +++ b/src/game/voyage.c @@ -8,13 +8,14 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, - int nCmdShow) { return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow); + int nCmdShow) { + return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow); } #elif defined(__linux__) int main(int argc, char **argv) { - return 0; + return vyXlibEntry(argc, argv); } #endif diff --git a/src/renderer/vk/framebuffer.h b/src/renderer/vk/framebuffer.h new file mode 100644 index 0000000..06a7a4c --- /dev/null +++ b/src/renderer/vk/framebuffer.h @@ -0,0 +1,22 @@ +#ifndef VY_VK_FRAMEBUFFER_H +#define VY_VK_FRAMEBUFFER_H + +#include + +typedef struct { + VkFramebuffer framebuffer; + uint32_t pass_idx; +} vy_framebuffer; + +typedef struct { + uint32_t index; +} vy_framebuffer_handle; + +/* Reserve a slot, but don't actually create the framebuffer yet. + * We can use this if we are unsure if the framebuffer will really be needed. + */ +vy_framebuffer_handle vy_reserve_framebuffer(void); + +vy_framebuffer *vy_get_framebuffer(vy_framebuffer_handle handle); + +#endif diff --git a/src/renderer/vk/gfx_pipelines.c b/src/renderer/vk/gfx_pipelines.c index 8700ebf..cdac58d 100644 --- a/src/renderer/vk/gfx_pipelines.c +++ b/src/renderer/vk/gfx_pipelines.c @@ -1,5 +1,5 @@ -#include "runtime/runtime.h" #include "runtime/gfx.h" +#include "runtime/runtime.h" #include "runtime/renderer_api.h" @@ -17,7 +17,7 @@ typedef struct { static vy_pipeline_storage _storage; -static vy_gfx_pipeline_id StorePipeline(vy_gl_pipeline pipeline) { +static vy_gfx_pipeline_handle StorePipeline(vy_gl_pipeline pipeline) { /* Search for free slot */ uint32_t slot = NUM_SLOTS; for (uint32_t i = 0; i < NUM_SLOTS; ++i) { @@ -28,7 +28,7 @@ static vy_gfx_pipeline_id StorePipeline(vy_gl_pipeline pipeline) { } if (slot == NUM_SLOTS) { vyReportError("GL_GFX", "Ran out of pipeline storage slots"); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } uint32_t generation = _storage.generation_in_use[slot] >> 1; @@ -37,12 +37,12 @@ static vy_gfx_pipeline_id StorePipeline(vy_gl_pipeline pipeline) { _storage.pipelines[slot] = pipeline; _storage.generation_in_use[slot] = (generation << 1) | 0x1; - vy_gfx_pipeline_id id; + vy_gfx_pipeline_handle id; id.index = (generation << 27) | slot; return id; } -static void ReleasePipelineSlot(vy_gfx_pipeline_id id) { +static void ReleasePipelineSlot(vy_gfx_pipeline_handle id) { uint32_t slot = id.index & 0x08ffffff; uint32_t gen = (id.index >> 27) & 0x1f; if (slot >= NUM_SLOTS) @@ -52,7 +52,7 @@ static void ReleasePipelineSlot(vy_gfx_pipeline_id id) { _storage.generation_in_use[slot] &= ~0x1; } -VY_DLLEXPORT vy_gfx_pipeline_id +VY_DLLEXPORT vy_gfx_pipeline_handle vyCompileComputePipeline(const vy_compute_pipeline_info *info) { #if 0 char info_log[512]; @@ -74,7 +74,7 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) { info_log); glDeleteProgram(prog); glDeleteShader(shader); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } glAttachShader(prog, shader); glLinkProgram(prog); @@ -84,20 +84,20 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) { vyReportError("GL_GFX", "Failed to link compute shader\n%s", info_log); glDeleteShader(shader); glDeleteProgram(prog); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } glDeleteShader(shader); - #endif +#endif vy_gl_pipeline pipeline; pipeline.prog = 0; return StorePipeline(pipeline); } -VY_DLLEXPORT vy_gfx_pipeline_id +VY_DLLEXPORT vy_gfx_pipeline_handle vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { - #if 0 +#if 0 char info_log[512]; GLuint prog = glCreateProgram(); GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); @@ -118,7 +118,7 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); glDeleteProgram(prog); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } glAttachShader(prog, vertex_shader); @@ -134,7 +134,7 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { glDeleteShader(fragment_shader); glDeleteShader(fragment_shader); glDeleteProgram(prog); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } glAttachShader(prog, fragment_shader); @@ -146,12 +146,12 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); glDeleteProgram(prog); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); - #endif +#endif vy_gl_pipeline pipeline; pipeline.prog = 0; diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index 83fdb1e..4690f79 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -92,11 +92,11 @@ VY_DLLEXPORT void vyRegisterCVars(void) { vyRegisterCVAR(&r_VkPreferMailboxMode); } -static int CreateInstance(void) { +static vy_result CreateInstance(void) { VkResult result = volkInitialize(); if (result != VK_SUCCESS) { vyReportError("vk", "Initialization failed: volkInitialize()"); - return 0; + return 1; } VkApplicationInfo app_info = { @@ -160,7 +160,7 @@ static int CreateInstance(void) { 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 1; } volkLoadInstance(g_gpu.instance); @@ -179,10 +179,10 @@ static int CreateInstance(void) { g_gpu.alloc_cb, &g_gpu.messenger); #endif - return 0; + return VY_SUCCESS; } -static int CreateSurface(const vy_renderer_init_info *info) { +static vy_result CreateSurface(const vy_renderer_init_info *info) { #ifdef _WIN32 g_gpu.native_window.hInstance = info->hInstance; g_gpu.native_window.hWnd = info->hWnd; @@ -195,24 +195,24 @@ static int CreateSurface(const vy_renderer_init_info *info) { &surface_info, g_gpu.alloc_cb, &g_gpu.surface) == VK_SUCCESS) - return 0; + return VY_SUCCESS; else - return -100; + return 100; #elif defined(VY_USE_XLIB_KHR) g_gpu.native_window.display = info->display; g_gpu.native_window.window = info->window; VkXlibSurfaceCreateInfoKHR surface_info = { - .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, - .dpy = info->display, - .window = info->window, + .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + .dpy = info->display, + .window = info->window, }; if (vkCreateXlibSurfaceKHR(g_gpu.instance, &surface_info, &g_gpu.alloc_cb, &g_gpu.surface) == VK_SUCCESS) - return 0; + return VY_SUCCESS; else - return -100; + return 100; #endif } @@ -222,7 +222,8 @@ typedef struct { uint32_t present; } vy_queue_indices; -static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, VkSurfaceKHR surface) { +static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, + VkSurfaceKHR surface) { vy_queue_indices indices = {.graphics = UINT32_MAX, .compute = UINT32_MAX, .present = UINT32_MAX}; @@ -242,7 +243,7 @@ static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, VkSurfac indices.graphics = i; if ((props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0) indices.compute = i; - + VkBool32 present_supported = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev, i, @@ -297,8 +298,7 @@ out: return supported; } -static int ChoosePhysicalDevice(void) { - +static vy_result ChoosePhysicalDevice(void) { g_gpu.phys_device = VK_NULL_HANDLE; uint32_t phys_device_count = 0; @@ -306,7 +306,7 @@ static int ChoosePhysicalDevice(void) { vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL); if (result != VK_SUCCESS) { vyReportError("vk", "Failed to enumerate the physical devices."); - return -2; + return 2; } VkPhysicalDevice *phys_devices = calloc(phys_device_count, sizeof(VkPhysicalDevice)); @@ -314,15 +314,14 @@ static int ChoosePhysicalDevice(void) { vyReportError( "vk", "Failed to enumerate the physical devices: Out of memory."); - return -2; + return 2; } vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, phys_devices); - - uint32_t highscore = 0; - uint32_t best_index = phys_device_count; + uint32_t highscore = 0; + uint32_t best_index = phys_device_count; for (uint32_t i = 0; i < phys_device_count; ++i) { VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = { .sType = @@ -362,7 +361,7 @@ static int ChoosePhysicalDevice(void) { : 0; if (score > highscore) { - highscore = score; + highscore = score; best_index = i; } @@ -392,12 +391,12 @@ static int ChoosePhysicalDevice(void) { if (g_gpu.phys_device == VK_NULL_HANDLE) { vyReportError("vk", "Failed to find a suitable physical device."); - return -3; + return 3; } - return 0; + return VY_SUCCESS; } -static int CreateDevice(void) { +static vy_result CreateDevice(void) { const char *extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; @@ -432,15 +431,14 @@ static int CreateDevice(void) { queue_indices.present != queue_indices.compute) { queue_info[distinct_queue_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_info[distinct_queue_count].pNext = NULL; - queue_info[distinct_queue_count].flags = 0; + queue_info[distinct_queue_count].pNext = NULL; + queue_info[distinct_queue_count].flags = 0; queue_info[distinct_queue_count].queueCount = 1; queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.present; queue_info[distinct_queue_count].pQueuePriorities = &priority; } - VkDeviceCreateInfo device_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .enabledExtensionCount = VY_ARRAY_COUNT(extensions), @@ -453,7 +451,7 @@ static int CreateDevice(void) { g_gpu.alloc_cb, &g_gpu.device) != VK_SUCCESS) { vyReportError("vk", "Device creation failed."); - return -10; + return 10; } vkGetDeviceQueue(g_gpu.device, @@ -469,10 +467,10 @@ static int CreateDevice(void) { 0, &g_gpu.present_queue); - return 0; + return VY_SUCCESS; } -VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) { +VY_DLLEXPORT vy_result vyInit(const vy_renderer_init_info *info) { vyLog("vk", "Init"); _tracking_alloc_cbs.pUserData = NULL; @@ -487,22 +485,22 @@ VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) { } int res = CreateInstance(); - if (res != 0) + if (res != VY_SUCCESS) return res; res = CreateSurface(info); - if (res != 0) + if (res != VY_SUCCESS) return res; res = ChoosePhysicalDevice(); - if (res != 0) + if (res != VY_SUCCESS) return res; res = CreateDevice(); - if (res != 0) + if (res != VY_SUCCESS) return res; res = vyCreateSwapchain(); - if (res != 0) + if (res != VY_SUCCESS) return res; - return 0; + return VY_SUCCESS; } VY_DLLEXPORT void vyShutdown(void) { diff --git a/src/renderer/vk/swapchain.c b/src/renderer/vk/swapchain.c index dab05bd..231f4b1 100644 --- a/src/renderer/vk/swapchain.c +++ b/src/renderer/vk/swapchain.c @@ -7,10 +7,10 @@ #include #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include + #define WIN32_LEAN_AND_MEAN + #include #elif defined(VY_USE_XLIB) -#include + #include #endif VY_CVAR_I(r_VkPreferredSwapchainImages, @@ -55,7 +55,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { params.surface_format = formats[0]; for (uint32_t i = 0; i < format_count; ++i) { if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && - formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { params.surface_format = formats[i]; break; } @@ -72,14 +72,14 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { #ifdef _WIN32 RECT client_area; GetClientRect(g_gpu.native_window.hWnd, &client_area); - params.extent.width = (uint32_t)client_area.right; + params.extent.width = (uint32_t)client_area.right; params.extent.height = (uint32_t)client_area.bottom; #else XWindowAttributes attribs; XGetWindowAttributes(g_gpu.native_window.display, g_gpu.native_window.window, &attribs); - params.extent.width = (uint32_t)attribs.width; + params.extent.width = (uint32_t)attribs.width; params.extent.height = (uint32_t)attribs.height; #endif } @@ -90,7 +90,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { vy_swapchain g_swapchain; -int vyCreateSwapchain(void) { +vy_result vyCreateSwapchain(void) { vy_device_swapchain_parameters device_params = DetermineSwapchainParameters(); @@ -117,22 +117,21 @@ int vyCreateSwapchain(void) { }; uint32_t queue_families[] = {g_gpu.graphics_family, g_gpu.present_family}; if (g_gpu.present_family != g_gpu.graphics_family) { - swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - swapchain_info.pQueueFamilyIndices = queue_families; + swapchain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapchain_info.pQueueFamilyIndices = queue_families; swapchain_info.queueFamilyIndexCount = 2; } else { - swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchain_info.pQueueFamilyIndices = NULL; swapchain_info.queueFamilyIndexCount = 0; } - if (vkCreateSwapchainKHR(g_gpu.device, &swapchain_info, g_gpu.alloc_cb, &g_swapchain.swapchain) != VK_SUCCESS) { vyReportError("vk", "Failed to create the swapchain"); - return -50; + return 50; } g_swapchain.format = device_params.surface_format.format; @@ -146,7 +145,7 @@ int vyCreateSwapchain(void) { vyReportError("vk", "Unsupported number of swapchain images: %u", g_swapchain.image_count); - return -51; + return 51; } vkGetSwapchainImagesKHR(g_gpu.device, g_swapchain.swapchain, @@ -156,37 +155,44 @@ int vyCreateSwapchain(void) { /* Create image views */ for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { VkImageViewCreateInfo view_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = g_swapchain.images[i], - .format = g_swapchain.format, + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = g_swapchain.images[i], + .format = g_swapchain.format, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .components = { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseArrayLayer = 0, - .layerCount = 1, - .baseMipLevel = 0, - .levelCount = 1, - }, + .components = + { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = 0, + .layerCount = 1, + .baseMipLevel = 0, + .levelCount = 1, + }, }; if (vkCreateImageView(g_gpu.device, - &view_info, - g_gpu.alloc_cb, + &view_info, + g_gpu.alloc_cb, &g_swapchain.image_views[i]) != VK_SUCCESS) { vyReportError("vk", "Failed to create an image view for the swapchain."); - return -52; + return 52; } } - return 0; + return VY_SUCCESS; } +vy_result vyRecreateSwapchain(void) { + /* TODO(Kevin): Old swapchain in swapchain create info */ + vyDestroySwapchain(); + return vyCreateSwapchain(); +} void vyDestroySwapchain(void) { for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { @@ -195,4 +201,4 @@ void vyDestroySwapchain(void) { g_gpu.alloc_cb); } vkDestroySwapchainKHR(g_gpu.device, g_swapchain.swapchain, g_gpu.alloc_cb); -} \ No newline at end of file +} diff --git a/src/renderer/vk/swapchain.h b/src/renderer/vk/swapchain.h index 3f47b7f..87276c7 100644 --- a/src/renderer/vk/swapchain.h +++ b/src/renderer/vk/swapchain.h @@ -3,6 +3,8 @@ #include +#include "runtime/runtime.h" + #define VY_VK_MAX_SWAPCHAIN_IMAGES 3 typedef struct { @@ -17,7 +19,9 @@ typedef struct { extern vy_swapchain g_swapchain; #endif -int vyCreateSwapchain(void); +vy_result vyCreateSwapchain(void); + +vy_result vyRecreateSwapchain(void); void vyDestroySwapchain(void); diff --git a/src/runtime/app.c b/src/runtime/app.c index 7f779cd..09e4e0d 100644 --- a/src/runtime/app.c +++ b/src/runtime/app.c @@ -1,7 +1,7 @@ #include "app.h" +#include "config.h" #include "fio.h" #include "gfx.h" -#include "config.h" #include "renderer_api.h" extern void __RegisterRuntimeCVars(void); @@ -11,15 +11,15 @@ 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 + #define WIN32_LEAN_AND_MEAN + #include static LRESULT CALLBACK win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { - case WM_CLOSE: + case WM_CLOSE: PostQuitMessage(0); return 0; default: @@ -28,9 +28,9 @@ static LRESULT CALLBACK win32WndProc(HWND hWnd, } VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR pCmdLine, - int nCmdShow) { + HINSTANCE hPrevInstance, + PWSTR pCmdLine, + int nCmdShow) { __RegisterRuntimeCVars(); vyRegisterRendererCVars(); @@ -43,7 +43,6 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, return 1; } - WNDCLASSEXW wndclass = { .cbSize = sizeof(wndclass), .hInstance = hInstance, @@ -56,12 +55,12 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, return 1; } - HWND wnd = NULL; + HWND wnd = NULL; if (rt_Fullscreen.i) { /* Fullscreen window */ - int w = GetSystemMetrics(SM_CXSCREEN); - int h = GetSystemMetrics(SM_CYSCREEN); - wnd = CreateWindowExW(WS_EX_APPWINDOW, + int w = GetSystemMetrics(SM_CXSCREEN); + int h = GetSystemMetrics(SM_CYSCREEN); + wnd = CreateWindowExW(WS_EX_APPWINDOW, L"vyWndClass", L"Voyage", WS_POPUP, @@ -121,10 +120,135 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, } vyShutdownGFX(); - + DestroyWindow(wnd); UnregisterClassW(L"vyWndClass", hInstance); - + + vyShutdownFIO(); + + return 0; +} + +#elif defined(VY_USE_XLIB) + + #include + #include + +static void +xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) { + Atom wm_state = XInternAtom(dpy, "_NET_W_STATE", False); + Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + if (wm_state == None || wm_fullscreen == None) { + vyLog("CORE", "Window manager does not support fullscreen mode."); + return; + } + + #define _NET_WM_STATE_REMOVE 0 + #define _NET_WM_STATE_ADD 1 + #define EVENT_SOURCE_APPLICATION 1 + XEvent ev; + ev.type = ClientMessage; + ev.xclient.display = dpy; + ev.xclient.window = window; + ev.xclient.message_type = wm_state; + ev.xclient.format = 32; + ev.xclient.data.l[0] = (enable) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + ev.xclient.data.l[1] = wm_fullscreen; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = EVENT_SOURCE_APPLICATION; + ev.xclient.data.l[4] = 0; + + Window root_window = XRootWindow(dpy, screen); + long ev_mask = SubstructureRedirectMask; + + if (!XSendEvent(dpy, root_window, False, ev_mask, &ev)) { + vyReportError("CORE", "Failed to send x11 fullscreen event."); + } + + #undef _NET_WM_STATE_ADD + #undef _NET_WM_STATE_REMOVE + #undef EVENT_SOURCE_APPLICATION +} + +VY_DLLEXPORT int vyXlibEntry(int argc, char **argv) { + __RegisterRuntimeCVars(); + vyRegisterRendererCVars(); + + vy_fio_config fio_config = {0}; + if (!vyInitFIO(&fio_config)) { + vyReportError("FIO", "Init failed."); + return 1; + } + + Display *dpy = XOpenDisplay(NULL); + if (!dpy) { + vyReportError("CORE", "Failed to open default display"); + return 1; + } + int screen = DefaultScreen(dpy); + Window window; + int w = rt_WindowWidth.i; + int h = rt_WindowHeight.i; + window = XCreateSimpleWindow(dpy, + RootWindow(dpy, screen), + 10, + 10, + w, + h, + 1, + BlackPixel(dpy, screen), + WhitePixel(dpy, screen)); + XSelectInput(dpy, window, KeyPressMask); + XMapWindow(dpy, window); + XStoreName(dpy, window, "Voyage"); + + Atom wm_close = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + if (!wm_close) { + vyReportError("CORE", "Failed to find WM_DELETE_WINDOW atom."); + XDestroyWindow(dpy, window); + XCloseDisplay(dpy); + return 1; + } + XSetWMProtocols(dpy, window, &wm_close, 1); + + if (rt_Fullscreen.i) + xlibSetFullscreen(dpy, screen, window, true); + + vy_renderer_init_info renderer_info = {.display = dpy, .window = window}; + if (!vyInitGFX(&renderer_info)) { + vyReportError("GFX", "Init failed."); + return 1; + } + + /* Main Loop */ + bool keep_running = true; + while (keep_running) { + while (XEventsQueued(dpy, QueuedAlready) > 0) { + XEvent event; + XNextEvent(dpy, &event); + switch (event.type) { + case KeyPress: + break; + case ButtonPressMask: + /* Mouse down */ + break; + case PointerMotionMask: + /* Mouse movement */ + break; + case ClientMessage: + if (event.xclient.data.l[0] == (long)wm_close) { + vyLog("CORE", "Received WM_DELETE_WINDOW"); + keep_running = false; + } + break; + } + } + } + + vyShutdownGFX(); + XDestroyWindow(dpy, window); + XCloseDisplay(dpy); + vyShutdownFIO(); return 0; diff --git a/src/runtime/app.h b/src/runtime/app.h index f836a8a..250acb7 100644 --- a/src/runtime/app.h +++ b/src/runtime/app.h @@ -11,9 +11,13 @@ struct HINSTANCE__; VY_DLLEXPORT int vyWin32Entry(struct HINSTANCE__ *hInstance, - struct HINSTANCE__ *hPrevInstance, - wchar_t *pCmdLine, - int nCmdShow); + struct HINSTANCE__ *hPrevInstance, + wchar_t *pCmdLine, + int nCmdShow); + +#elif defined(VY_USE_XLIB) + +VY_DLLEXPORT int vyXlibEntry(int argc, char **argv); #endif diff --git a/src/runtime/dynamic_libs.h b/src/runtime/dynamic_libs.h index 6ed9f5c..f2b47e1 100644 --- a/src/runtime/dynamic_libs.h +++ b/src/runtime/dynamic_libs.h @@ -4,9 +4,13 @@ #include "runtime.h" #ifdef _WIN32 - #define VY_DLLNAME(s) (s".dll") + #define VY_DLLNAME(s) \ + (".\\"s \ + ".dll") #elif defined(__linux__) - #define VY_DLLNAME(s) ("lib"s".so") + #define VY_DLLNAME(s) \ + ("./lib"s \ + ".so") #endif typedef void *vy_dynlib; diff --git a/src/runtime/fio.c b/src/runtime/fio.c index 1aacc29..3c042b9 100644 --- a/src/runtime/fio.c +++ b/src/runtime/fio.c @@ -59,7 +59,7 @@ static vy_file_tab _file_tab; #ifdef __linux__ static pthread_t _thread; #elif defined(_WIN32) -static HANDLE _fio_thread = NULL; +static HANDLE _fio_thread = NULL; static HANDLE _fio_term_event = NULL; #endif @@ -91,7 +91,7 @@ static void ShutdownFIOQueue(void) { #ifdef __linux__ pthread_cond_destroy(&_queue.pending_cond); pthread_mutex_destroy(&_queue.mutex); -#elif defined(_WIN32) +#elif defined(_WIN32) DeleteCriticalSection(&_queue.critical_section); #endif } @@ -173,7 +173,7 @@ VY_DLLEXPORT void vyShutdownFIO(void) { if (SetEvent(_fio_term_event)) { WakeAllConditionVariable(&_queue.pending_cond); WaitForSingleObject(_fio_thread, INFINITE); - CloseHandle(_fio_thread); + CloseHandle(_fio_thread); CloseHandle(_fio_term_event); } else { vyReportError("FIO", "Failed to signal the termination event."); @@ -406,8 +406,8 @@ static void ProcessRead(vy_file_op *op) { return; } - op->buffer.data = NULL; - op->buffer.size = 0; + op->buffer.data = NULL; + op->buffer.size = 0; op->buffer.flags = 0; FILE *file = fopen(path, "rb"); @@ -424,8 +424,8 @@ static void ProcessRead(vy_file_op *op) { op->buffer.size = (size_t)fsz; if (fread(op->buffer.data, fsz, 1, file) != 1) { free(op->buffer.data); - op->buffer.data = NULL; - op->buffer.size = 0; + op->buffer.data = NULL; + op->buffer.size = 0; op->buffer.flags = VY_FILE_BUFFER_FLAG_READ_FAILED; } @@ -475,4 +475,4 @@ static DWORD WINAPI win32FIOThreadProc(_In_ LPVOID lpParam) { vyLog("FIO", "Exit FIO thread"); return 0; } -#endif \ No newline at end of file +#endif diff --git a/src/runtime/fio.h b/src/runtime/fio.h index 946b207..7f49fbc 100644 --- a/src/runtime/fio.h +++ b/src/runtime/fio.h @@ -7,8 +7,7 @@ #include "runtime.h" -enum -{ +enum { VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1, VY_FILE_BUFFER_FLAG_READ_FAILED = 0x2, }; @@ -19,7 +18,7 @@ typedef struct { uint32_t flags; } vy_file_buffer; -inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) { +static inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) { return fb->flags == 0; } @@ -54,7 +53,7 @@ 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_file_buffer *buffer); VY_DLLEXPORT void vyFreeFileBuffer(vy_file_buffer buffer); diff --git a/src/runtime/gfx.h b/src/runtime/gfx.h index c08125a..5660c17 100644 --- a/src/runtime/gfx.h +++ b/src/runtime/gfx.h @@ -9,8 +9,8 @@ * - object renderer (for static models) */ -#include #include +#include #include "runtime.h" @@ -23,12 +23,11 @@ VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info); VY_DLLEXPORT void vyShutdownGFX(void); -/* Generational indices for backend objects */ +/* Handles backend objects */ + typedef struct { uint32_t index; -} vy_gfx_pipeline_id; - -#define VY_IS_GFX_ID_VALID(id) ((id).index != 0) +} vy_gfx_pipeline_handle; /* Attributes are used to bind buffers (or textures) to symbolic values. * For example, an attribute might be bound to "CELL_GRID", which would be @@ -52,7 +51,7 @@ typedef struct { vy_attribute_binding *storage_bindings; vy_attribute_binding *texture_bindings; - vy_gfx_pipeline_id pipeline; + vy_gfx_pipeline_handle pipeline; unsigned int uniform_binding_count; unsigned int storage_binding_count; diff --git a/src/runtime/gfx_main.c b/src/runtime/gfx_main.c index f942760..ad9f37c 100644 --- a/src/runtime/gfx_main.c +++ b/src/runtime/gfx_main.c @@ -2,10 +2,10 @@ #define VY_DONT_DEFINE_RENDERER_GLOBAL -#include "gfx.h" -#include "renderer_api.h" #include "config.h" #include "dynamic_libs.h" +#include "gfx.h" +#include "renderer_api.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 @@ -21,17 +21,18 @@ 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); +extern bool +vyLoadShaders(const char **paths, vy_shader *shaders, unsigned int count); -static bool LoadRenderer(void) -{ +static bool LoadRenderer(void) { #define RETRIEVE_SYMBOL(name, type) \ g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vy" #name); \ - if (!g_renderer.name) { \ + if (!g_renderer.name) { \ vyReportError( \ "GFX", \ "Unable to retrieve renderer function %s from backend %s", \ - #name, rt_Renderer.s); \ + #name, \ + rt_Renderer.s); \ } if (strcmp(rt_Renderer.s, "vk") == 0) { @@ -50,14 +51,16 @@ static bool LoadRenderer(void) vy_compile_graphics_pipeline_fn); return true; } else { - vyReportError("GFX", "Unsupported renderer backend: (%s) %s", rt_Renderer.name, rt_Renderer.s); + vyReportError("GFX", + "Unsupported renderer backend: (%s) %s", + rt_Renderer.name, + rt_Renderer.s); return false; } #undef RETRIEVE_SYMBOL } -VY_DLLEXPORT void vyRegisterRendererCVars(void) -{ +VY_DLLEXPORT void vyRegisterRendererCVars(void) { if (!_renderer_loaded) { if (!LoadRenderer()) return; @@ -73,7 +76,7 @@ VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info) { g_renderer.RegisterCVars(); } - if (g_renderer.Init(renderer_info) != 0) + if (g_renderer.Init(renderer_info) != VY_SUCCESS) return false; /* Init shader programs */ diff --git a/src/runtime/gfx_shader_loading.c b/src/runtime/gfx_shader_loading.c index fa80c34..baef7b2 100644 --- a/src/runtime/gfx_shader_loading.c +++ b/src/runtime/gfx_shader_loading.c @@ -6,10 +6,11 @@ #include #include -#include "runtime.h" #include "fio.h" #include "gfx.h" +#include "handles.h" #include "renderer_api.h" +#include "runtime.h" typedef enum { VY_STMT_FORM_VALUE, @@ -289,9 +290,9 @@ static vy_fio_handle DispatchShaderRead(const char *shader, return DispatchFileRead(path->value); } -static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, - const char *file_path, - unsigned int root_list) { +static vy_gfx_pipeline_handle 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); @@ -309,22 +310,24 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, vyAbortFIO(vertex_read); vyAbortFIO(fragment_read); vyAbortFIO(compute_read); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } while (!vyIsFIOFinished(compute_read)) { /* wait */ } vy_file_buffer compute_code; - if (!vyRetrieveReadBuffer(compute_read, &compute_code) || !vyWasFileBufferSuccessful(&compute_code)) { + if (!vyRetrieveReadBuffer(compute_read, &compute_code) || + !vyWasFileBufferSuccessful(&compute_code)) { vyReportError("GFX", "Failed to load compute shader required by: %s", file_path); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } vy_compute_pipeline_info info; - info.compute_source = compute_code.data; - info.compute_source_length = compute_code.size; - vy_gfx_pipeline_id pipeline = g_renderer.CompileComputePipeline(&info); + info.compute_source = compute_code.data; + info.compute_source_length = compute_code.size; + vy_gfx_pipeline_handle pipeline = + g_renderer.CompileComputePipeline(&info); vyFreeFileBuffer(compute_code); return pipeline; } else if (vertex_read || fragment_read) { @@ -336,7 +339,7 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, vyAbortFIO(vertex_read); vyAbortFIO(fragment_read); vyAbortFIO(compute_read); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } if (!vertex_read || !fragment_read) { vyReportError("GFX", @@ -346,7 +349,7 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, vyAbortFIO(vertex_read); vyAbortFIO(fragment_read); vyAbortFIO(compute_read); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } vy_graphics_pipeline_info info; @@ -356,13 +359,14 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, int remaining = 2; while (remaining > 0) { if (vyIsFIOFinished(vertex_read)) { - if (!vyRetrieveReadBuffer(vertex_read, &vertex_code) || !vyWasFileBufferSuccessful(&vertex_code)) { + if (!vyRetrieveReadBuffer(vertex_read, &vertex_code) || + !vyWasFileBufferSuccessful(&vertex_code)) { vyReportError( "GFX", "Failed to load vertex shader required by: %s", file_path); vyFreeFileBuffer(fragment_code); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } info.vertex_source = vertex_code.data; info.vertex_source_length = vertex_code.size; @@ -371,13 +375,14 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, } if (vyIsFIOFinished(fragment_read)) { - if (!vyRetrieveReadBuffer(fragment_read, &fragment_code) || !vyWasFileBufferSuccessful(&fragment_code)) { + if (!vyRetrieveReadBuffer(fragment_read, &fragment_code) || + !vyWasFileBufferSuccessful(&fragment_code)) { vyReportError( "GFX", "Failed to load fragment shader required by: %s", file_path); vyFreeFileBuffer(vertex_code); - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } info.fragment_source = fragment_code.data; info.fragment_source_length = fragment_code.size; @@ -386,7 +391,8 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, } } - vy_gfx_pipeline_id pipeline = g_renderer.CompileGraphicsPipeline(&info); + vy_gfx_pipeline_handle pipeline = + g_renderer.CompileGraphicsPipeline(&info); vyFreeFileBuffer(vertex_code); vyFreeFileBuffer(fragment_code); return pipeline; @@ -399,7 +405,7 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, vyAbortFIO(fragment_read); vyAbortFIO(compute_read); } - return (vy_gfx_pipeline_id){0}; + return (vy_gfx_pipeline_handle){0}; } static bool ParseBindingIndex(vy_text_span span, unsigned int *index) { @@ -516,7 +522,7 @@ ParseShaderFile(vy_file_id fid, vy_file_buffer fbuf, vy_shader *shader) { DbgPrintShaderFile(&state, root_list, 0); shader->pipeline = CreatePipeline(&state, file_path, root_list); - if (!VY_IS_GFX_ID_VALID(shader->pipeline)) { + if (!VY_IS_HANDLE_VALID(shader->pipeline)) { result = false; goto out; } diff --git a/src/runtime/handles.h b/src/runtime/handles.h new file mode 100644 index 0000000..04c1bd7 --- /dev/null +++ b/src/runtime/handles.h @@ -0,0 +1,8 @@ +#ifndef VY_HANDLES_H +#define VY_HANDLES_H + +/* All handle types should contain a uint32_t index */ + +#define VY_IS_HANDLE_VALID(handle) ((handle).index != 0) + +#endif diff --git a/src/runtime/jobs.c b/src/runtime/jobs.c new file mode 100644 index 0000000..a741a5e --- /dev/null +++ b/src/runtime/jobs.c @@ -0,0 +1,65 @@ +#include "jobs.h" +#include "threading.h" + +#define MAX_WORKERS 32 +#define JOB_QUEUE_SIZE 2048 + +/* A "chunk" of iterations for a particular job */ +typedef struct { + uint32_t base_iteration; + uint32_t iteration_count; + vy_job_fn *fn; + void *param; +} vy_job_chunk; + +typedef struct { + /* Queue */ + struct { + vy_job_chunk *chunks; + unsigned int head; + unsigned int tail; + vy_condition_var *lock; + } job_queue; + + /* Thread data */ + vy_thread *thread; +} vy_worker_data; + +static volatile bool _keep_running = true; +static vy_worker_data _worker_data[MAX_WORKERS]; + +static void ExecOneJobIfAvailable(vy_worker_data *wd) { + if (wd->job_queue.head == wd->job_queue.tail) { + /* No job available. + * TODO: Pick one job queue at random and check if we can steal? + */ + return; + } + + vy_job_chunk chunk = wd->job_queue.chunks[wd->job_queue.head]; + wd->job_queue.head = (wd->job_queue.head + 1) % JOB_QUEUE_SIZE; + for (uint32_t i = 0; i < chunk.iteration_count; ++i) { + chunk.fn(chunk.param, chunk.base_iteration + i); + } +} + +static void WorkerEntry(void *param) { + vy_worker_data *wd = param; + while (_keep_running) { + vyLockConditionVar(wd->job_queue.lock); + while (wd->job_queue.head == wd->job_queue.tail && _keep_running) { + vyWaitOnConditionVar(wd->job_queue.lock); + } + ExecOneJobIfAvailable(wd); + vyUnlockConditionVar(wd->job_queue.lock, false); + } +} + +void vyInitJobSystem(unsigned int worker_count) { + if (worker_count > MAX_WORKERS) + worker_count = MAX_WORKERS; + + for (unsigned int i = 0; i < worker_count; ++i) { + _worker_data[i].thread = vySpawnThread(WorkerEntry, &_worker_data[i]); + } +} diff --git a/src/runtime/jobs.h b/src/runtime/jobs.h new file mode 100644 index 0000000..e6d2a24 --- /dev/null +++ b/src/runtime/jobs.h @@ -0,0 +1,20 @@ +#ifndef VY_JOBS_H +#define VY_JOBS_H + +/* Work stealing job scheduler */ + +#include "runtime.h" + +#include + +typedef void vy_job_fn(void *param, uint32_t iteration); + +typedef struct { + uint32_t iterations; + vy_job_fn *fn; + void *param; +} vy_job_decl; + +VY_DLLEXPORT void vyDispatchJob(const vy_job_decl *decl); + +#endif diff --git a/src/runtime/renderer_api.h b/src/runtime/renderer_api.h index 8ff59f6..d7e98f5 100644 --- a/src/runtime/renderer_api.h +++ b/src/runtime/renderer_api.h @@ -6,6 +6,7 @@ #include #include "gfx.h" +#include "runtime.h" #ifdef _WIN32 struct HINSTANCE__; @@ -38,11 +39,11 @@ typedef struct { } vy_graphics_pipeline_info; typedef void vy_register_renderer_cvars_fn(void); -typedef int vy_init_renderer_fn(const vy_renderer_init_info *info); +typedef vy_result vy_init_renderer_fn(const vy_renderer_init_info *info); typedef void vy_shutdown_renderer_fn(void); -typedef vy_gfx_pipeline_id +typedef vy_gfx_pipeline_handle vy_compile_compute_pipeline_fn(const vy_compute_pipeline_info *info); -typedef vy_gfx_pipeline_id +typedef vy_gfx_pipeline_handle vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info); typedef struct { diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 65bc373..941c41f 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -6,14 +6,17 @@ #include #ifdef _WIN32 - #define VY_DLLEXPORT __declspec(dllexport) + #define VY_DLLEXPORT __declspec(dllexport) #else - #define VY_DLLEXPORT + #define VY_DLLEXPORT #endif -#define VY_UNUSED(x) ((void)sizeof((x))) +#define VY_UNUSED(x) ((void)sizeof((x))) #define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0])) +typedef unsigned int vy_result; +#define VY_SUCCESS 0 + typedef struct { const char *start; unsigned int length; diff --git a/src/runtime/threading.h b/src/runtime/threading.h index 83dd237..1ff215e 100644 --- a/src/runtime/threading.h +++ b/src/runtime/threading.h @@ -17,4 +17,25 @@ VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex); VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex); +typedef struct vy_condition_var_s vy_condition_var; + +VY_DLLEXPORT vy_condition_var *vyCreateConditionVar(void); + +VY_DLLEXPORT void vyDestroyConditionVar(vy_condition_var *var); + +VY_DLLEXPORT void vyLockConditionVar(vy_condition_var *var); + +VY_DLLEXPORT void vyUnlockConditionVar(vy_condition_var *var, bool signal); + +/* The condition variable must be locked by the thread! */ +VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var); + +typedef struct vy_thread_s vy_thread; + +typedef void vy_thread_entry_fn(void *param); + +VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param); + +VY_DLLEXPORT void vyJoinThread(vy_thread *thread); + #endif diff --git a/src/runtime/threading_cond.c b/src/runtime/threading_cond.c new file mode 100644 index 0000000..10213f8 --- /dev/null +++ b/src/runtime/threading_cond.c @@ -0,0 +1,68 @@ +#include "runtime.h" +#include "threading.h" + +#ifdef __linux__ + + #include +struct vy_condition_var_s { + pthread_mutex_t mutex; + pthread_cond_t cond; + ptrdiff_t next_reusable; +}; + + #define MAX_CONDS 1024 +vy_condition_var _conds[MAX_CONDS]; +static ptrdiff_t _first_reusable = MAX_CONDS; +static ptrdiff_t _next = 0; +static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER; + +VY_DLLEXPORT vy_condition_var *vyCreateConditionVar(void) { + pthread_mutex_lock(&_guard); + if (_first_reusable < MAX_CONDS) { + vy_condition_var *cond = &_conds[_first_reusable]; + _first_reusable = cond->next_reusable; + pthread_mutex_unlock(&_guard); + return cond; + } else if (_next < MAX_CONDS) { + vy_condition_var *cond = &_conds[_next]; + if (pthread_mutex_init(&cond->mutex, NULL) != 0) { + vyLog("core", "Condition variable creation failed"); + pthread_mutex_unlock(&_guard); + return NULL; + } + if (pthread_cond_init(&cond->cond, NULL) != 0) { + vyLog("core", "Condition variable creation failed"); + } + cond->next_reusable = MAX_CONDS; + ++_next; + pthread_mutex_unlock(&_guard); + return cond; + } + vyReportError("core", "Ran out of condition variable objects"); + pthread_mutex_unlock(&_guard); + return NULL; +} + +VY_DLLEXPORT void vyDestroyConditionVar(vy_condition_var *var) { + ptrdiff_t index = var - &_conds[0]; + pthread_mutex_lock(&_guard); + var->next_reusable = _first_reusable; + _first_reusable = index; + pthread_mutex_unlock(&_guard); +} + +VY_DLLEXPORT void vyLockConditionVar(vy_condition_var *var) { + pthread_mutex_lock(&var->mutex); +} + +VY_DLLEXPORT void vyUnlockConditionVar(vy_condition_var *var, bool signal) { + if (signal) + pthread_cond_signal(&var->cond); + pthread_mutex_unlock(&var->mutex); +} + +VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var) { + pthread_cond_wait(&var->cond, &var->mutex); +} + +#endif diff --git a/src/runtime/threading.c b/src/runtime/threading_mutex.c similarity index 85% rename from src/runtime/threading.c rename to src/runtime/threading_mutex.c index 4ca2c94..9049661 100644 --- a/src/runtime/threading.c +++ b/src/runtime/threading_mutex.c @@ -1,5 +1,5 @@ -#include "threading.h" #include "runtime.h" +#include "threading.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -44,8 +44,7 @@ VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) { VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) { return WaitForSingleObject(mutex->handle, INFINITE) == WAIT_OBJECT_0; } - -VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) { +v VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) { return ReleaseMutex(mutex->handle) != 0; } @@ -61,30 +60,38 @@ struct vy_mutex_s { static vy_mutex _mutex[MAX_MUTEX]; static ptrdiff_t _first_reusable = MAX_MUTEX; static ptrdiff_t _next = 0; +static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER; VY_DLLEXPORT vy_mutex *vyCreateMutex(void) { + pthread_mutex_lock(&_guard); if (_first_reusable < MAX_MUTEX) { vy_mutex *mtx = &_mutex[_first_reusable]; _first_reusable = mtx->next_reusable; + pthread_mutex_unlock(&_guard); return mtx; } else if (_next < MAX_MUTEX) { vy_mutex *mtx = &_mutex[_next]; if (pthread_mutex_init(&mtx->handle, NULL) != 0) { vyLog("core", "Mutex creation failed"); + pthread_mutex_unlock(&_guard); return NULL; } mtx->next_reusable = MAX_MUTEX; ++_next; + pthread_mutex_unlock(&_guard); return mtx; } vyReportError("core", "Ran out of mutex objects"); + pthread_mutex_unlock(&_guard); return NULL; } VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) { - ptrdiff_t index = mutex - &_mutex[0]; + ptrdiff_t index = mutex - &_mutex[0]; + pthread_mutex_lock(&_guard); mutex->next_reusable = _first_reusable; _first_reusable = index; + pthread_mutex_unlock(&_guard); } VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) { diff --git a/src/runtime/threading_thread.c b/src/runtime/threading_thread.c new file mode 100644 index 0000000..7a53fa2 --- /dev/null +++ b/src/runtime/threading_thread.c @@ -0,0 +1,74 @@ +#include "runtime.h" +#include "threading.h" + +#if defined(_WIN32) +#elif defined(__linux__) + + #include + +struct vy_thread_s { + pthread_t handle; + ptrdiff_t next_reusable; + + vy_thread_entry_fn *entry; + void *param; + + bool needs_join; +}; + + #define MAX_THREADS 256 +static vy_thread _threads[MAX_THREADS]; +static ptrdiff_t _first_reusable = MAX_THREADS; +static ptrdiff_t _next = 0; + +static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER; + +static void *linuxThreadWrapper(void *arg) { + vy_thread *user_thread = arg; + user_thread->needs_join = false; + user_thread->entry(user_thread->param); + user_thread->needs_join = true; + return NULL; +} + +VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) { + vy_thread *thrd = NULL; + pthread_mutex_lock(&_guard); + if (_first_reusable < MAX_THREADS) { + thrd = &_threads[_first_reusable]; + _first_reusable = thrd->next_reusable; + if (thrd->needs_join) { + pthread_join(thrd->handle, NULL); + thrd->needs_join = false; + } + } else if (_next < MAX_THREADS) { + thrd = &_threads[_next]; + thrd->next_reusable = MAX_THREADS; + ++_next; + } + if (thrd) { + thrd->entry = entry; + thrd->param = param; + if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) != + 0) { + vyLog("core", "Mutex creation failed"); + thrd = NULL; + } + } else { + vyReportError("core", "Ran out of thread objects"); + } + pthread_mutex_unlock(&_guard); + return thrd; +} + +VY_DLLEXPORT void vyJoinThread(vy_thread *thread) { + pthread_join(thread->handle, NULL); + thread->needs_join = false; + ptrdiff_t index = thread - &_threads[0]; + pthread_mutex_lock(&_guard); + thread->next_reusable = _first_reusable; + _first_reusable = index; + pthread_mutex_unlock(&_guard); +} + +#endif