various updates
This commit is contained in:
		
							parent
							
								
									6937e55888
								
							
						
					
					
						commit
						97adb0dffa
					
				@ -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',
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/renderer/vk/framebuffer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/renderer/vk/framebuffer.h
									
									
									
									
									
										Normal 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
 | 
			
		||||
@ -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,7 +84,7 @@ 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
 | 
			
		||||
@ -94,7 +94,7 @@ vyCompileComputePipeline(const vy_compute_pipeline_info *info) {
 | 
			
		||||
    return StorePipeline(pipeline);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VY_DLLEXPORT vy_gfx_pipeline_id
 | 
			
		||||
VY_DLLEXPORT vy_gfx_pipeline_handle
 | 
			
		||||
vyCompileGraphicsPipeline(const vy_graphics_pipeline_info *info) {
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
@ -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,7 +146,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};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    glDeleteShader(vertex_shader);
 | 
			
		||||
 | 
			
		||||
@ -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,9 +195,9 @@ 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;
 | 
			
		||||
@ -210,9 +210,9 @@ 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;
 | 
			
		||||
#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};
 | 
			
		||||
@ -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,13 +314,12 @@ 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;
 | 
			
		||||
    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) {
 | 
			
		||||
        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,
 | 
			
		||||
    };
 | 
			
		||||
@ -440,7 +439,6 @@ static int CreateDevice(void) {
 | 
			
		||||
        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) {
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -126,13 +126,12 @@ int vyCreateSwapchain(void) {
 | 
			
		||||
        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,
 | 
			
		||||
@ -160,13 +159,15 @@ int vyCreateSwapchain(void) {
 | 
			
		||||
            .image    = g_swapchain.images[i],
 | 
			
		||||
            .format   = g_swapchain.format,
 | 
			
		||||
            .viewType = VK_IMAGE_VIEW_TYPE_2D,
 | 
			
		||||
            .components = {
 | 
			
		||||
            .components =
 | 
			
		||||
                {
 | 
			
		||||
                             .r = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
			
		||||
                             .g = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
			
		||||
                             .b = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
			
		||||
                             .a = VK_COMPONENT_SWIZZLE_IDENTITY,
 | 
			
		||||
                             },
 | 
			
		||||
            .subresourceRange = {
 | 
			
		||||
            .subresourceRange =
 | 
			
		||||
                {
 | 
			
		||||
                             .aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT,
 | 
			
		||||
                             .baseArrayLayer = 0,
 | 
			
		||||
                             .layerCount     = 1,
 | 
			
		||||
@ -180,13 +181,18 @@ int vyCreateSwapchain(void) {
 | 
			
		||||
                              &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) {
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#include <volk/volk.h>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
@ -43,7 +43,6 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    WNDCLASSEXW wndclass = {
 | 
			
		||||
        .cbSize        = sizeof(wndclass),
 | 
			
		||||
        .hInstance     = hInstance,
 | 
			
		||||
@ -130,4 +129,129 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,10 @@ VY_DLLEXPORT int vyWin32Entry(struct HINSTANCE__ *hInstance,
 | 
			
		||||
                              wchar_t *pCmdLine,
 | 
			
		||||
                              int nCmdShow);
 | 
			
		||||
 | 
			
		||||
#elif defined(VY_USE_XLIB)
 | 
			
		||||
 | 
			
		||||
VY_DLLEXPORT int vyXlibEntry(int argc, char **argv);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,8 @@
 | 
			
		||||
 * - object renderer (for static models)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
@ -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) {                                                    \
 | 
			
		||||
        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 */
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,11 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#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,7 +290,7 @@ static vy_fio_handle DispatchShaderRead(const char *shader,
 | 
			
		||||
    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,
 | 
			
		||||
                                             unsigned int root_list) {
 | 
			
		||||
    /* Process the data */
 | 
			
		||||
@ -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);
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								src/runtime/handles.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/runtime/handles.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										65
									
								
								src/runtime/jobs.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										20
									
								
								src/runtime/jobs.h
									
									
									
									
									
										Normal 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
 | 
			
		||||
@ -6,6 +6,7 @@
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
#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 {
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,9 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										68
									
								
								src/runtime/threading_cond.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/runtime/threading_cond.c
									
									
									
									
									
										Normal 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
 | 
			
		||||
@ -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];
 | 
			
		||||
    pthread_mutex_lock(&_guard);
 | 
			
		||||
    mutex->next_reusable = _first_reusable;
 | 
			
		||||
    _first_reusable      = index;
 | 
			
		||||
    pthread_mutex_unlock(&_guard);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {
 | 
			
		||||
							
								
								
									
										74
									
								
								src/runtime/threading_thread.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/runtime/threading_thread.c
									
									
									
									
									
										Normal 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
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user