various updates

This commit is contained in:
Kevin Trogant 2023-11-22 11:23:52 +01:00
parent 6937e55888
commit 97adb0dffa
24 changed files with 609 additions and 168 deletions

View File

@ -49,16 +49,20 @@ runtime_lib = library('vyrt',
'src/runtime/threading.h', 'src/runtime/threading.h',
'src/runtime/app.h', 'src/runtime/app.h',
'src/runtime/dynamic_libs.h', 'src/runtime/dynamic_libs.h',
'src/runtime/jobs.h',
'src/runtime/error_report.c', 'src/runtime/error_report.c',
'src/runtime/gfx_main.c', 'src/runtime/gfx_main.c',
'src/runtime/gfx_shader_loading.c', 'src/runtime/gfx_shader_loading.c',
'src/runtime/config.c', 'src/runtime/config.c',
'src/runtime/runtime_cvars.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/fio.c',
'src/runtime/app.c', 'src/runtime/app.c',
'src/runtime/dynamic_libs.c', 'src/runtime/dynamic_libs.c',
'src/runtime/jobs.c',
# Contrib Sources # Contrib Sources
'contrib/xxhash/xxhash.c', 'contrib/xxhash/xxhash.c',
@ -87,7 +91,7 @@ if vk_dep.found()
# Contrib Sources # Contrib Sources
'contrib/volk/volk.h', 'contrib/volk/volk.h',
'contrib/volk/volk.c', 'contrib/volk/volk.c',
dependencies : [m_dep, vk_inc_dep], dependencies : [m_dep, vk_inc_dep, windowing_dep],
include_directories : incdir, include_directories : incdir,
link_with : [runtime_lib], link_with : [runtime_lib],
c_pch : 'pch/vk_pch.h', c_pch : 'pch/vk_pch.h',

View File

@ -8,13 +8,14 @@
int WINAPI wWinMain(HINSTANCE hInstance, int WINAPI wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, PWSTR pCmdLine,
int nCmdShow) { return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow); int nCmdShow) {
return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow);
} }
#elif defined(__linux__) #elif defined(__linux__)
int main(int argc, char **argv) { int main(int argc, char **argv) {
return 0; return vyXlibEntry(argc, argv);
} }
#endif #endif

View File

@ -0,0 +1,22 @@
#ifndef VY_VK_FRAMEBUFFER_H
#define VY_VK_FRAMEBUFFER_H
#include <volk/volk.h>
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

View File

@ -1,5 +1,5 @@
#include "runtime/runtime.h"
#include "runtime/gfx.h" #include "runtime/gfx.h"
#include "runtime/runtime.h"
#include "runtime/renderer_api.h" #include "runtime/renderer_api.h"
@ -17,7 +17,7 @@ typedef struct {
static vy_pipeline_storage _storage; 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 */ /* Search for free slot */
uint32_t slot = NUM_SLOTS; uint32_t slot = NUM_SLOTS;
for (uint32_t i = 0; i < NUM_SLOTS; ++i) { 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) { if (slot == NUM_SLOTS) {
vyReportError("GL_GFX", "Ran out of pipeline storage 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; 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.pipelines[slot] = pipeline;
_storage.generation_in_use[slot] = (generation << 1) | 0x1; _storage.generation_in_use[slot] = (generation << 1) | 0x1;
vy_gfx_pipeline_id id; vy_gfx_pipeline_handle id;
id.index = (generation << 27) | slot; id.index = (generation << 27) | slot;
return id; 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 slot = id.index & 0x08ffffff;
uint32_t gen = (id.index >> 27) & 0x1f; uint32_t gen = (id.index >> 27) & 0x1f;
if (slot >= NUM_SLOTS) if (slot >= NUM_SLOTS)
@ -52,7 +52,7 @@ static void ReleasePipelineSlot(vy_gfx_pipeline_id id) {
_storage.generation_in_use[slot] &= ~0x1; _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) { vyCompileComputePipeline(const vy_compute_pipeline_info *info) {
#if 0 #if 0
char info_log[512]; char info_log[512];
@ -74,7 +74,7 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) {
info_log); info_log);
glDeleteProgram(prog); glDeleteProgram(prog);
glDeleteShader(shader); glDeleteShader(shader);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
glAttachShader(prog, shader); glAttachShader(prog, shader);
glLinkProgram(prog); glLinkProgram(prog);
@ -84,7 +84,7 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) {
vyReportError("GL_GFX", "Failed to link compute shader\n%s", info_log); vyReportError("GL_GFX", "Failed to link compute shader\n%s", info_log);
glDeleteShader(shader); glDeleteShader(shader);
glDeleteProgram(prog); glDeleteProgram(prog);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
glDeleteShader(shader); glDeleteShader(shader);
#endif #endif
@ -94,7 +94,7 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) {
return StorePipeline(pipeline); return StorePipeline(pipeline);
} }
VY_DLLEXPORT vy_gfx_pipeline_id VY_DLLEXPORT vy_gfx_pipeline_handle
vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) { vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) {
#if 0 #if 0
@ -118,7 +118,7 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) {
glDeleteShader(vertex_shader); glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader); glDeleteShader(fragment_shader);
glDeleteProgram(prog); glDeleteProgram(prog);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
glAttachShader(prog, vertex_shader); glAttachShader(prog, vertex_shader);
@ -134,7 +134,7 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) {
glDeleteShader(fragment_shader); glDeleteShader(fragment_shader);
glDeleteShader(fragment_shader); glDeleteShader(fragment_shader);
glDeleteProgram(prog); glDeleteProgram(prog);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
glAttachShader(prog, fragment_shader); glAttachShader(prog, fragment_shader);
@ -146,7 +146,7 @@ vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) {
glDeleteShader(vertex_shader); glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader); glDeleteShader(fragment_shader);
glDeleteProgram(prog); glDeleteProgram(prog);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
glDeleteShader(vertex_shader); glDeleteShader(vertex_shader);

View File

@ -92,11 +92,11 @@ VY_DLLEXPORT void vyRegisterCVars(void) {
vyRegisterCVAR(&r_VkPreferMailboxMode); vyRegisterCVAR(&r_VkPreferMailboxMode);
} }
static int CreateInstance(void) { static vy_result CreateInstance(void) {
VkResult result = volkInitialize(); VkResult result = volkInitialize();
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
vyReportError("vk", "Initialization failed: volkInitialize()"); vyReportError("vk", "Initialization failed: volkInitialize()");
return 0; return 1;
} }
VkApplicationInfo app_info = { VkApplicationInfo app_info = {
@ -160,7 +160,7 @@ static int CreateInstance(void) {
result = vkCreateInstance(&instance_info, g_gpu.alloc_cb, &g_gpu.instance); result = vkCreateInstance(&instance_info, g_gpu.alloc_cb, &g_gpu.instance);
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
vyReportError("vk", "Failed to create the vulkan instance."); vyReportError("vk", "Failed to create the vulkan instance.");
return -1; return 1;
} }
volkLoadInstance(g_gpu.instance); volkLoadInstance(g_gpu.instance);
@ -179,10 +179,10 @@ static int CreateInstance(void) {
g_gpu.alloc_cb, g_gpu.alloc_cb,
&g_gpu.messenger); &g_gpu.messenger);
#endif #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 #ifdef _WIN32
g_gpu.native_window.hInstance = info->hInstance; g_gpu.native_window.hInstance = info->hInstance;
g_gpu.native_window.hWnd = info->hWnd; g_gpu.native_window.hWnd = info->hWnd;
@ -195,9 +195,9 @@ static int CreateSurface(const vy_renderer_init_info *info) {
&surface_info, &surface_info,
g_gpu.alloc_cb, g_gpu.alloc_cb,
&g_gpu.surface) == VK_SUCCESS) &g_gpu.surface) == VK_SUCCESS)
return 0; return VY_SUCCESS;
else else
return -100; return 100;
#elif defined(VY_USE_XLIB_KHR) #elif defined(VY_USE_XLIB_KHR)
g_gpu.native_window.display = info->display; g_gpu.native_window.display = info->display;
g_gpu.native_window.window = info->window; g_gpu.native_window.window = info->window;
@ -210,9 +210,9 @@ static int CreateSurface(const vy_renderer_init_info *info) {
&surface_info, &surface_info,
&g_gpu.alloc_cb, &g_gpu.alloc_cb,
&g_gpu.surface) == VK_SUCCESS) &g_gpu.surface) == VK_SUCCESS)
return 0; return VY_SUCCESS;
else else
return -100; return 100;
#endif #endif
} }
@ -222,7 +222,8 @@ typedef struct {
uint32_t present; uint32_t present;
} vy_queue_indices; } 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, vy_queue_indices indices = {.graphics = UINT32_MAX,
.compute = UINT32_MAX, .compute = UINT32_MAX,
.present = UINT32_MAX}; .present = UINT32_MAX};
@ -297,8 +298,7 @@ out:
return supported; return supported;
} }
static int ChoosePhysicalDevice(void) { static vy_result ChoosePhysicalDevice(void) {
g_gpu.phys_device = VK_NULL_HANDLE; g_gpu.phys_device = VK_NULL_HANDLE;
uint32_t phys_device_count = 0; uint32_t phys_device_count = 0;
@ -306,7 +306,7 @@ static int ChoosePhysicalDevice(void) {
vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL); vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL);
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
vyReportError("vk", "Failed to enumerate the physical devices."); vyReportError("vk", "Failed to enumerate the physical devices.");
return -2; return 2;
} }
VkPhysicalDevice *phys_devices = VkPhysicalDevice *phys_devices =
calloc(phys_device_count, sizeof(VkPhysicalDevice)); calloc(phys_device_count, sizeof(VkPhysicalDevice));
@ -314,13 +314,12 @@ static int ChoosePhysicalDevice(void) {
vyReportError( vyReportError(
"vk", "vk",
"Failed to enumerate the physical devices: Out of memory."); "Failed to enumerate the physical devices: Out of memory.");
return -2; return 2;
} }
vkEnumeratePhysicalDevices(g_gpu.instance, vkEnumeratePhysicalDevices(g_gpu.instance,
&phys_device_count, &phys_device_count,
phys_devices); phys_devices);
uint32_t highscore = 0; uint32_t highscore = 0;
uint32_t best_index = phys_device_count; uint32_t best_index = phys_device_count;
for (uint32_t i = 0; i < phys_device_count; ++i) { for (uint32_t i = 0; i < phys_device_count; ++i) {
@ -392,12 +391,12 @@ static int ChoosePhysicalDevice(void) {
if (g_gpu.phys_device == VK_NULL_HANDLE) { if (g_gpu.phys_device == VK_NULL_HANDLE) {
vyReportError("vk", "Failed to find a suitable physical device."); 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[] = { const char *extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
}; };
@ -440,7 +439,6 @@ static int CreateDevice(void) {
queue_info[distinct_queue_count].pQueuePriorities = &priority; queue_info[distinct_queue_count].pQueuePriorities = &priority;
} }
VkDeviceCreateInfo device_info = { VkDeviceCreateInfo device_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.enabledExtensionCount = VY_ARRAY_COUNT(extensions), .enabledExtensionCount = VY_ARRAY_COUNT(extensions),
@ -453,7 +451,7 @@ static int CreateDevice(void) {
g_gpu.alloc_cb, g_gpu.alloc_cb,
&g_gpu.device) != VK_SUCCESS) { &g_gpu.device) != VK_SUCCESS) {
vyReportError("vk", "Device creation failed."); vyReportError("vk", "Device creation failed.");
return -10; return 10;
} }
vkGetDeviceQueue(g_gpu.device, vkGetDeviceQueue(g_gpu.device,
@ -469,10 +467,10 @@ static int CreateDevice(void) {
0, 0,
&g_gpu.present_queue); &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"); vyLog("vk", "Init");
_tracking_alloc_cbs.pUserData = NULL; _tracking_alloc_cbs.pUserData = NULL;
@ -487,22 +485,22 @@ VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) {
} }
int res = CreateInstance(); int res = CreateInstance();
if (res != 0) if (res != VY_SUCCESS)
return res; return res;
res = CreateSurface(info); res = CreateSurface(info);
if (res != 0) if (res != VY_SUCCESS)
return res; return res;
res = ChoosePhysicalDevice(); res = ChoosePhysicalDevice();
if (res != 0) if (res != VY_SUCCESS)
return res; return res;
res = CreateDevice(); res = CreateDevice();
if (res != 0) if (res != VY_SUCCESS)
return res; return res;
res = vyCreateSwapchain(); res = vyCreateSwapchain();
if (res != 0) if (res != VY_SUCCESS)
return res; return res;
return 0; return VY_SUCCESS;
} }
VY_DLLEXPORT void vyShutdown(void) { VY_DLLEXPORT void vyShutdown(void) {

View File

@ -90,7 +90,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) {
vy_swapchain g_swapchain; vy_swapchain g_swapchain;
int vyCreateSwapchain(void) { vy_result vyCreateSwapchain(void) {
vy_device_swapchain_parameters device_params = vy_device_swapchain_parameters device_params =
DetermineSwapchainParameters(); DetermineSwapchainParameters();
@ -126,13 +126,12 @@ int vyCreateSwapchain(void) {
swapchain_info.queueFamilyIndexCount = 0; swapchain_info.queueFamilyIndexCount = 0;
} }
if (vkCreateSwapchainKHR(g_gpu.device, if (vkCreateSwapchainKHR(g_gpu.device,
&swapchain_info, &swapchain_info,
g_gpu.alloc_cb, g_gpu.alloc_cb,
&g_swapchain.swapchain) != VK_SUCCESS) { &g_swapchain.swapchain) != VK_SUCCESS) {
vyReportError("vk", "Failed to create the swapchain"); vyReportError("vk", "Failed to create the swapchain");
return -50; return 50;
} }
g_swapchain.format = device_params.surface_format.format; g_swapchain.format = device_params.surface_format.format;
@ -146,7 +145,7 @@ int vyCreateSwapchain(void) {
vyReportError("vk", vyReportError("vk",
"Unsupported number of swapchain images: %u", "Unsupported number of swapchain images: %u",
g_swapchain.image_count); g_swapchain.image_count);
return -51; return 51;
} }
vkGetSwapchainImagesKHR(g_gpu.device, vkGetSwapchainImagesKHR(g_gpu.device,
g_swapchain.swapchain, g_swapchain.swapchain,
@ -160,13 +159,15 @@ int vyCreateSwapchain(void) {
.image = g_swapchain.images[i], .image = g_swapchain.images[i],
.format = g_swapchain.format, .format = g_swapchain.format,
.viewType = VK_IMAGE_VIEW_TYPE_2D, .viewType = VK_IMAGE_VIEW_TYPE_2D,
.components = { .components =
{
.r = VK_COMPONENT_SWIZZLE_IDENTITY, .r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY,
}, },
.subresourceRange = { .subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 1, .layerCount = 1,
@ -180,13 +181,18 @@ int vyCreateSwapchain(void) {
&g_swapchain.image_views[i]) != VK_SUCCESS) { &g_swapchain.image_views[i]) != VK_SUCCESS) {
vyReportError("vk", vyReportError("vk",
"Failed to create an image view for the swapchain."); "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) { void vyDestroySwapchain(void) {
for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { for (uint32_t i = 0; i < g_swapchain.image_count; ++i) {

View File

@ -3,6 +3,8 @@
#include <volk/volk.h> #include <volk/volk.h>
#include "runtime/runtime.h"
#define VY_VK_MAX_SWAPCHAIN_IMAGES 3 #define VY_VK_MAX_SWAPCHAIN_IMAGES 3
typedef struct { typedef struct {
@ -17,7 +19,9 @@ typedef struct {
extern vy_swapchain g_swapchain; extern vy_swapchain g_swapchain;
#endif #endif
int vyCreateSwapchain(void); vy_result vyCreateSwapchain(void);
vy_result vyRecreateSwapchain(void);
void vyDestroySwapchain(void); void vyDestroySwapchain(void);

View File

@ -1,7 +1,7 @@
#include "app.h" #include "app.h"
#include "config.h"
#include "fio.h" #include "fio.h"
#include "gfx.h" #include "gfx.h"
#include "config.h"
#include "renderer_api.h" #include "renderer_api.h"
extern void __RegisterRuntimeCVars(void); extern void __RegisterRuntimeCVars(void);
@ -43,7 +43,6 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
return 1; return 1;
} }
WNDCLASSEXW wndclass = { WNDCLASSEXW wndclass = {
.cbSize = sizeof(wndclass), .cbSize = sizeof(wndclass),
.hInstance = hInstance, .hInstance = hInstance,
@ -130,4 +129,129 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
return 0; return 0;
} }
#elif defined(VY_USE_XLIB)
#include <X11/Xlib.h>
#include <assert.h>
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;
}
#endif #endif

View File

@ -15,6 +15,10 @@ VY_DLLEXPORT int vyWin32Entry(struct HINSTANCE__ *hInstance,
wchar_t *pCmdLine, wchar_t *pCmdLine,
int nCmdShow); int nCmdShow);
#elif defined(VY_USE_XLIB)
VY_DLLEXPORT int vyXlibEntry(int argc, char **argv);
#endif #endif
#endif #endif

View File

@ -4,9 +4,13 @@
#include "runtime.h" #include "runtime.h"
#ifdef _WIN32 #ifdef _WIN32
#define VY_DLLNAME(s) (s".dll") #define VY_DLLNAME(s) \
(".\\"s \
".dll")
#elif defined(__linux__) #elif defined(__linux__)
#define VY_DLLNAME(s) ("lib"s".so") #define VY_DLLNAME(s) \
("./lib"s \
".so")
#endif #endif
typedef void *vy_dynlib; typedef void *vy_dynlib;

View File

@ -7,8 +7,7 @@
#include "runtime.h" #include "runtime.h"
enum enum {
{
VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1, VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1,
VY_FILE_BUFFER_FLAG_READ_FAILED = 0x2, VY_FILE_BUFFER_FLAG_READ_FAILED = 0x2,
}; };
@ -19,7 +18,7 @@ typedef struct {
uint32_t flags; uint32_t flags;
} vy_file_buffer; } vy_file_buffer;
inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) { static inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) {
return fb->flags == 0; return fb->flags == 0;
} }

View File

@ -9,8 +9,8 @@
* - object renderer (for static models) * - object renderer (for static models)
*/ */
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "runtime.h" #include "runtime.h"
@ -23,12 +23,11 @@ VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info);
VY_DLLEXPORT void vyShutdownGFX(void); VY_DLLEXPORT void vyShutdownGFX(void);
/* Generational indices for backend objects */ /* Handles backend objects */
typedef struct { typedef struct {
uint32_t index; uint32_t index;
} vy_gfx_pipeline_id; } vy_gfx_pipeline_handle;
#define VY_IS_GFX_ID_VALID(id) ((id).index != 0)
/* Attributes are used to bind buffers (or textures) to symbolic values. /* Attributes are used to bind buffers (or textures) to symbolic values.
* For example, an attribute might be bound to "CELL_GRID", which would be * 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 *storage_bindings;
vy_attribute_binding *texture_bindings; vy_attribute_binding *texture_bindings;
vy_gfx_pipeline_id pipeline; vy_gfx_pipeline_handle pipeline;
unsigned int uniform_binding_count; unsigned int uniform_binding_count;
unsigned int storage_binding_count; unsigned int storage_binding_count;

View File

@ -2,10 +2,10 @@
#define VY_DONT_DEFINE_RENDERER_GLOBAL #define VY_DONT_DEFINE_RENDERER_GLOBAL
#include "gfx.h"
#include "renderer_api.h"
#include "config.h" #include "config.h"
#include "dynamic_libs.h" #include "dynamic_libs.h"
#include "gfx.h"
#include "renderer_api.h"
/* Attributes are used to bind buffers (or textures) to symbolic values. /* Attributes are used to bind buffers (or textures) to symbolic values.
* For example, an attribute might be bound to "CELL_GRID", which would be * 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", "Select the render backend. Available options: [vk], Default: vk",
"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) \ #define RETRIEVE_SYMBOL(name, type) \
g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vy" #name); \ g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vy" #name); \
if (!g_renderer.name) { \ if (!g_renderer.name) { \
vyReportError( \ vyReportError( \
"GFX", \ "GFX", \
"Unable to retrieve renderer function %s from backend %s", \ "Unable to retrieve renderer function %s from backend %s", \
#name, rt_Renderer.s); \ #name, \
rt_Renderer.s); \
} }
if (strcmp(rt_Renderer.s, "vk") == 0) { if (strcmp(rt_Renderer.s, "vk") == 0) {
@ -50,14 +51,16 @@ static bool LoadRenderer(void)
vy_compile_graphics_pipeline_fn); vy_compile_graphics_pipeline_fn);
return true; return true;
} else { } 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; return false;
} }
#undef RETRIEVE_SYMBOL #undef RETRIEVE_SYMBOL
} }
VY_DLLEXPORT void vyRegisterRendererCVars(void) VY_DLLEXPORT void vyRegisterRendererCVars(void) {
{
if (!_renderer_loaded) { if (!_renderer_loaded) {
if (!LoadRenderer()) if (!LoadRenderer())
return; return;
@ -73,7 +76,7 @@ VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info) {
g_renderer.RegisterCVars(); g_renderer.RegisterCVars();
} }
if (g_renderer.Init(renderer_info) != 0) if (g_renderer.Init(renderer_info) != VY_SUCCESS)
return false; return false;
/* Init shader programs */ /* Init shader programs */

View File

@ -6,10 +6,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "runtime.h"
#include "fio.h" #include "fio.h"
#include "gfx.h" #include "gfx.h"
#include "handles.h"
#include "renderer_api.h" #include "renderer_api.h"
#include "runtime.h"
typedef enum { typedef enum {
VY_STMT_FORM_VALUE, VY_STMT_FORM_VALUE,
@ -289,7 +290,7 @@ static vy_fio_handle DispatchShaderRead(const char *shader,
return DispatchFileRead(path->value); return DispatchFileRead(path->value);
} }
static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state, static vy_gfx_pipeline_handle CreatePipeline(vy_parse_state *state,
const char *file_path, const char *file_path,
unsigned int root_list) { unsigned int root_list) {
/* Process the data */ /* Process the data */
@ -309,22 +310,24 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state,
vyAbortFIO(vertex_read); vyAbortFIO(vertex_read);
vyAbortFIO(fragment_read); vyAbortFIO(fragment_read);
vyAbortFIO(compute_read); vyAbortFIO(compute_read);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
while (!vyIsFIOFinished(compute_read)) { while (!vyIsFIOFinished(compute_read)) {
/* wait */ /* wait */
} }
vy_file_buffer compute_code; 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", vyReportError("GFX",
"Failed to load compute shader required by: %s", "Failed to load compute shader required by: %s",
file_path); file_path);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
vy_compute_pipeline_info info; vy_compute_pipeline_info info;
info.compute_source = compute_code.data; info.compute_source = compute_code.data;
info.compute_source_length = compute_code.size; info.compute_source_length = compute_code.size;
vy_gfx_pipeline_id pipeline = g_renderer.CompileComputePipeline(&info); vy_gfx_pipeline_handle pipeline =
g_renderer.CompileComputePipeline(&info);
vyFreeFileBuffer(compute_code); vyFreeFileBuffer(compute_code);
return pipeline; return pipeline;
} else if (vertex_read || fragment_read) { } else if (vertex_read || fragment_read) {
@ -336,7 +339,7 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state,
vyAbortFIO(vertex_read); vyAbortFIO(vertex_read);
vyAbortFIO(fragment_read); vyAbortFIO(fragment_read);
vyAbortFIO(compute_read); vyAbortFIO(compute_read);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
if (!vertex_read || !fragment_read) { if (!vertex_read || !fragment_read) {
vyReportError("GFX", vyReportError("GFX",
@ -346,7 +349,7 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state,
vyAbortFIO(vertex_read); vyAbortFIO(vertex_read);
vyAbortFIO(fragment_read); vyAbortFIO(fragment_read);
vyAbortFIO(compute_read); vyAbortFIO(compute_read);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
vy_graphics_pipeline_info info; vy_graphics_pipeline_info info;
@ -356,13 +359,14 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state,
int remaining = 2; int remaining = 2;
while (remaining > 0) { while (remaining > 0) {
if (vyIsFIOFinished(vertex_read)) { if (vyIsFIOFinished(vertex_read)) {
if (!vyRetrieveReadBuffer(vertex_read, &vertex_code) || !vyWasFileBufferSuccessful(&vertex_code)) { if (!vyRetrieveReadBuffer(vertex_read, &vertex_code) ||
!vyWasFileBufferSuccessful(&vertex_code)) {
vyReportError( vyReportError(
"GFX", "GFX",
"Failed to load vertex shader required by: %s", "Failed to load vertex shader required by: %s",
file_path); file_path);
vyFreeFileBuffer(fragment_code); vyFreeFileBuffer(fragment_code);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
info.vertex_source = vertex_code.data; info.vertex_source = vertex_code.data;
info.vertex_source_length = vertex_code.size; 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 (vyIsFIOFinished(fragment_read)) {
if (!vyRetrieveReadBuffer(fragment_read, &fragment_code) || !vyWasFileBufferSuccessful(&fragment_code)) { if (!vyRetrieveReadBuffer(fragment_read, &fragment_code) ||
!vyWasFileBufferSuccessful(&fragment_code)) {
vyReportError( vyReportError(
"GFX", "GFX",
"Failed to load fragment shader required by: %s", "Failed to load fragment shader required by: %s",
file_path); file_path);
vyFreeFileBuffer(vertex_code); vyFreeFileBuffer(vertex_code);
return (vy_gfx_pipeline_id){0}; return (vy_gfx_pipeline_handle){0};
} }
info.fragment_source = fragment_code.data; info.fragment_source = fragment_code.data;
info.fragment_source_length = fragment_code.size; 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(vertex_code);
vyFreeFileBuffer(fragment_code); vyFreeFileBuffer(fragment_code);
return pipeline; return pipeline;
@ -399,7 +405,7 @@ static vy_gfx_pipeline_id CreatePipeline(vy_parse_state *state,
vyAbortFIO(fragment_read); vyAbortFIO(fragment_read);
vyAbortFIO(compute_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) { 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); DbgPrintShaderFile(&state, root_list, 0);
shader->pipeline = CreatePipeline(&state, file_path, root_list); 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; result = false;
goto out; goto out;
} }

8
src/runtime/handles.h Normal file
View File

@ -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

65
src/runtime/jobs.c Normal file
View File

@ -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]);
}
}

20
src/runtime/jobs.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef VY_JOBS_H
#define VY_JOBS_H
/* Work stealing job scheduler */
#include "runtime.h"
#include <stdint.h>
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

View File

@ -6,6 +6,7 @@
#include <stddef.h> #include <stddef.h>
#include "gfx.h" #include "gfx.h"
#include "runtime.h"
#ifdef _WIN32 #ifdef _WIN32
struct HINSTANCE__; struct HINSTANCE__;
@ -38,11 +39,11 @@ typedef struct {
} vy_graphics_pipeline_info; } vy_graphics_pipeline_info;
typedef void vy_register_renderer_cvars_fn(void); 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 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); 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); vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info);
typedef struct { typedef struct {

View File

@ -14,6 +14,9 @@
#define VY_UNUSED(x) ((void)sizeof((x))) #define VY_UNUSED(x) ((void)sizeof((x)))
#define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0])) #define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0]))
typedef unsigned int vy_result;
#define VY_SUCCESS 0
typedef struct { typedef struct {
const char *start; const char *start;
unsigned int length; unsigned int length;

View File

@ -17,4 +17,25 @@ VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex);
VY_DLLEXPORT bool vyUnlockMutex(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 #endif

View File

@ -0,0 +1,68 @@
#include "runtime.h"
#include "threading.h"
#ifdef __linux__
#include <pthread.h>
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

View File

@ -1,5 +1,5 @@
#include "threading.h"
#include "runtime.h" #include "runtime.h"
#include "threading.h"
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
@ -44,8 +44,7 @@ VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) {
VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) { VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {
return WaitForSingleObject(mutex->handle, INFINITE) == WAIT_OBJECT_0; return WaitForSingleObject(mutex->handle, INFINITE) == WAIT_OBJECT_0;
} }
v VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
return ReleaseMutex(mutex->handle) != 0; return ReleaseMutex(mutex->handle) != 0;
} }
@ -61,30 +60,38 @@ struct vy_mutex_s {
static vy_mutex _mutex[MAX_MUTEX]; static vy_mutex _mutex[MAX_MUTEX];
static ptrdiff_t _first_reusable = MAX_MUTEX; static ptrdiff_t _first_reusable = MAX_MUTEX;
static ptrdiff_t _next = 0; static ptrdiff_t _next = 0;
static pthread_mutex_t _guard = PTHREAD_MUTEX_INITIALIZER;
VY_DLLEXPORT vy_mutex *vyCreateMutex(void) { VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
pthread_mutex_lock(&_guard);
if (_first_reusable < MAX_MUTEX) { if (_first_reusable < MAX_MUTEX) {
vy_mutex *mtx = &_mutex[_first_reusable]; vy_mutex *mtx = &_mutex[_first_reusable];
_first_reusable = mtx->next_reusable; _first_reusable = mtx->next_reusable;
pthread_mutex_unlock(&_guard);
return mtx; return mtx;
} else if (_next < MAX_MUTEX) { } else if (_next < MAX_MUTEX) {
vy_mutex *mtx = &_mutex[_next]; vy_mutex *mtx = &_mutex[_next];
if (pthread_mutex_init(&mtx->handle, NULL) != 0) { if (pthread_mutex_init(&mtx->handle, NULL) != 0) {
vyLog("core", "Mutex creation failed"); vyLog("core", "Mutex creation failed");
pthread_mutex_unlock(&_guard);
return NULL; return NULL;
} }
mtx->next_reusable = MAX_MUTEX; mtx->next_reusable = MAX_MUTEX;
++_next; ++_next;
pthread_mutex_unlock(&_guard);
return mtx; return mtx;
} }
vyReportError("core", "Ran out of mutex objects"); vyReportError("core", "Ran out of mutex objects");
pthread_mutex_unlock(&_guard);
return NULL; return NULL;
} }
VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) { 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; mutex->next_reusable = _first_reusable;
_first_reusable = index; _first_reusable = index;
pthread_mutex_unlock(&_guard);
} }
VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) { VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {

View File

@ -0,0 +1,74 @@
#include "runtime.h"
#include "threading.h"
#if defined(_WIN32)
#elif defined(__linux__)
#include <pthread.h>
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