diff --git a/.clang-format b/.clang-format index f2c83ec..33b203f 100644 --- a/.clang-format +++ b/.clang-format @@ -14,5 +14,6 @@ AlwaysBreakAfterReturnType: None BinPackArguments: false BinPackParameters: false BreakBeforeBraces: Attach -IndentPPDirectives: BeforeHash +IndentPPDirectives: None PointerAlignment: Right +ColumnLimit: 100 \ No newline at end of file diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000..ef02da0 --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +contrib/**/* diff --git a/contrib/.gitignore b/contrib/.gitignore new file mode 100644 index 0000000..c9c962f --- /dev/null +++ b/contrib/.gitignore @@ -0,0 +1,2 @@ +# Placed there by scripts/download_shaderc.bat +shaderc/* diff --git a/meson.build b/meson.build index 3fee41c..060a0f9 100644 --- a/meson.build +++ b/meson.build @@ -22,7 +22,11 @@ elif compiler.get_argument_syntax() == 'msvc' endif if get_option('default_library') == 'static' - add_project_arguments(['-DVY_STATIC_LIB'], language : 'c') + add_project_arguments(['-DVY_STATIC_LIB'], language : 'c') +endif + +if get_option('error_report_debugbreak') + add_project_arguments(['-DVY_ERROR_REPORT_DEBUGBREAK'], language : 'c') endif # Debug specific flags @@ -45,32 +49,32 @@ incdir = include_directories(['contrib', 'src']) runtime_lib = library('vyrt', # Project Sources - 'src/runtime/runtime.h', - 'src/runtime/gfx.h', - 'src/runtime/renderer_api.h', - 'src/runtime/config.h', - 'src/runtime/threading.h', - 'src/runtime/app.h', - 'src/runtime/dynamic_libs.h', - 'src/runtime/jobs.h', 'src/runtime/aio.h', - 'src/runtime/file_tab.h', - 'src/runtime/buffer_manager.h', + 'src/runtime/app.h', 'src/runtime/assets.h', - - 'src/runtime/error_report.c', - 'src/runtime/gfx_main.c', + 'src/runtime/buffer_manager.h', + 'src/runtime/config.h', + 'src/runtime/dynamic_libs.h', + 'src/runtime/file_tab.h', + 'src/runtime/gfx.h', + 'src/runtime/jobs.h', + 'src/runtime/renderer_api.h', + 'src/runtime/runtime.h', + 'src/runtime/threading.h', + + 'src/runtime/aio.c', + 'src/runtime/app.c', + 'src/runtime/buffer_manager.c', 'src/runtime/config.c', + 'src/runtime/error_report.c', + 'src/runtime/file_tab.c', + 'src/runtime/gfx_main.c', + 'src/runtime/jobs.c', 'src/runtime/runtime_cvars.c', + 'src/runtime/text.c', + 'src/runtime/threading_cond.c', 'src/runtime/threading_mutex.c', 'src/runtime/threading_thread.c', - 'src/runtime/threading_cond.c', - 'src/runtime/app.c', - 'src/runtime/dynamic_libs.c', - 'src/runtime/jobs.c', - 'src/runtime/aio.c', - 'src/runtime/file_tab.c', - 'src/runtime/buffer_manager.c', # Contrib Sources 'contrib/xxhash/xxhash.c', @@ -112,21 +116,39 @@ if vk_dep.found() static_renderer_lib = vk_renderer_lib endif +shaderc_include = include_directories('contrib/shaderc/libshaderc/include') +shaderc_libdir = 'NONE' +if host_machine.system() == 'windows' + shaderc_libdir = meson.project_source_root() / 'contrib/shaderc/build-win/libshaderc/Release' +endif # Asset Compiler Tool executable('assetc', + 'src/tools/assetc/assetmeta.h', + 'src/tools/assetc/description_parser.h', + 'src/tools/assetc/options.h', + 'src/tools/assetc/packages.h', 'src/tools/assetc/processing.h', + 'src/tools/assetc/processing_flags.h', 'src/tools/assetc/utils.h', - + 'src/tools/assetc/assetc.c', - 'src/tools/assetc/processor.c', + 'src/tools/assetc/assetmeta.c', + 'src/tools/assetc/description_parser.c', + 'src/tools/assetc/discovery.c', + 'src/tools/assetc/packages.c', 'src/tools/assetc/pipeline_processor.c', + 'src/tools/assetc/processor.c', 'src/tools/assetc/shader_processor.c', + 'src/tools/assetc/uidtable.c', 'src/tools/assetc/utils.c', - 'src/tools/assetc/uidmap.c', - include_directories : incdir, + + # Contrib sources + 'contrib/xxhash/xxhash.c', + include_directories : [incdir, shaderc_include], dependencies : [], link_with : [runtime_lib], + link_args : ['-L'+shaderc_libdir, '-lshaderc_combined'], win_subsystem : 'console') # Game @@ -143,4 +165,10 @@ executable('voyage', link_with : game_link_libs, win_subsystem : 'windows') - +# Unit Tests +rttest_exe = executable('rttest', + 'src/tests/rttest.c', + link_with : [runtime_lib], + include_directories : incdir, + win_subsystem : 'console') +test('runtime test', rttest_exe) \ No newline at end of file diff --git a/meson_options.txt b/meson_options.txt index 259479f..5b10009 120000 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('use_xlib', type : 'boolean', value : false, description : 'Use Xlib for window creation under linux') +option('error_report_debugbreak', type : 'boolean', value : true, description : 'Debugbreak in vyReportError') diff --git a/pch/rt_pch.h b/pch/rt_pch.h index f98d17e..18a75d7 100644 --- a/pch/rt_pch.h +++ b/pch/rt_pch.h @@ -1,3 +1,3 @@ #ifdef _WIN32 - #include +#include #endif diff --git a/scripts/download_shaderc.bat b/scripts/download_shaderc.bat new file mode 100644 index 0000000..c694d04 --- /dev/null +++ b/scripts/download_shaderc.bat @@ -0,0 +1,35 @@ +@echo off + +REM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +REM +REM Downloads shaderc from github into contrib/ +REM +REM Last changed: 24/11/23 (DD/MM/YY) +REM +REM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SETLOCAL + +SET SHADERC_VER=v2023.7 +SET SHADERC_REPO=https://github.com/google/shaderc.git + +REM Check if we are beeing run from scripts/ or from root +IF EXIST meson.build GOTO :DOWNLOAD +CD .. + +:DOWNLOAD +git clone --quiet %SHADERC_REPO% contrib\shaderc +PUSHD contrib\shaderc +git checkout --quiet %SHADERC_VER% +python3 .\utils\git-sync-deps +RMDIR /S /Q .git + +MKDIR build-win +PUSHD build-win +cmake .. +cmake --build . --config Release +POPD + + +POPD +ENDLOCAL diff --git a/shader/cell.as b/shader/cell.as new file mode 100644 index 0000000..82568d6 --- /dev/null +++ b/shader/cell.as @@ -0,0 +1 @@ +package pipelines.pkg; diff --git a/shader/cell.pipeline b/shader/cell.pipeline index f88fb2c..1d9d122 100644 --- a/shader/cell.pipeline +++ b/shader/cell.pipeline @@ -1,5 +1,11 @@ -vertex shader/cell_vert.shader; -fragment shader/cell_frag.shader; +optimization speed; + +vertex { + vk shader/cell_vert.glsl; +} +fragment { + vk shader/cell_frag.glsl; +} texture_bindings { 0 MATERIAL_ALBEDO; diff --git a/shader/cell_frag.as b/shader/cell_frag.as new file mode 100644 index 0000000..8384067 --- /dev/null +++ b/shader/cell_frag.as @@ -0,0 +1 @@ +package shaders.pkg; diff --git a/shader/cell_frag.glsl b/shader/cell_frag.glsl new file mode 100644 index 0000000..994ddf3 --- /dev/null +++ b/shader/cell_frag.glsl @@ -0,0 +1,8 @@ +#version 450 +#pragma shader_stage(fragment) + +layout (location = 0) out vec3 color; + +void main() { + color = vec3(1, 1, 1); +} diff --git a/shader/cell_vert.as b/shader/cell_vert.as new file mode 100644 index 0000000..8384067 --- /dev/null +++ b/shader/cell_vert.as @@ -0,0 +1 @@ +package shaders.pkg; diff --git a/shader/cell_vert.glsl b/shader/cell_vert.glsl new file mode 100644 index 0000000..8511547 --- /dev/null +++ b/shader/cell_vert.glsl @@ -0,0 +1,6 @@ +#version 450 +#pragma shader_stage(vertex) + +void main() { + gl_Position = vec4(0, 0, 0, 1); +} diff --git a/shader/cell_vert.shader b/shader/cell_vert.shader deleted file mode 100644 index 0d8e064..0000000 --- a/shader/cell_vert.shader +++ /dev/null @@ -1,4 +0,0 @@ -#define VY_SHADER_TYPE_VERT -#ifdef VY_SHADER_VK - -#endif diff --git a/src/game/voyage.c b/src/game/voyage.c index 06311a8..579f170 100644 --- a/src/game/voyage.c +++ b/src/game/voyage.c @@ -2,13 +2,10 @@ #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include +#define WIN32_LEAN_AND_MEAN +#include -int WINAPI wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR pCmdLine, - int nCmdShow) { +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow); } diff --git a/src/renderer/vk/gfx_pipelines.c b/src/renderer/vk/gfx_pipelines.c index 630149f..f631534 100644 --- a/src/renderer/vk/gfx_pipelines.c +++ b/src/renderer/vk/gfx_pipelines.c @@ -52,7 +52,8 @@ static void ReleasePipelineSlot(vy_gfx_pipeline_handle id) { _storage.generation_in_use[slot] &= ~0x1; } -vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compute_pipeline_info *info) { +vy_gfx_pipeline_handle +VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compute_pipeline_info *info) { #if 0 char info_log[512]; @@ -93,7 +94,8 @@ vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compu return StorePipeline(pipeline); } -vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileGraphicsPipeline)(const vy_graphics_pipeline_info *info) { +vy_gfx_pipeline_handle +VY_RENDERER_API_FN(CompileGraphicsPipeline)(const vy_graphics_pipeline_info *info) { #if 0 char info_log[512]; diff --git a/src/renderer/vk/gpu.h b/src/renderer/vk/gpu.h index ec75cc2..ca09a03 100644 --- a/src/renderer/vk/gpu.h +++ b/src/renderer/vk/gpu.h @@ -3,7 +3,6 @@ #include - #ifdef _WIN32 struct HINSTANCE__; struct HWND__; @@ -21,7 +20,6 @@ typedef struct { #endif } vy_native_window; - typedef struct { VkInstance instance; VkDebugUtilsMessengerEXT messenger; diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index 65f2e34..893e2b7 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -10,14 +10,11 @@ #include "runtime/renderer_api.h" #include "runtime/runtime.h" -VY_CVAR_I( - r_VkEnableAPIAllocTracking, - "Enable tracking of allocations done by the vulkan api. [0/1] Default: 0", - 0); +VY_CVAR_I(r_VkEnableAPIAllocTracking, + "Enable tracking of allocations done by the vulkan api. [0/1] Default: 0", + 0); -VY_CVAR_S(r_VkPhysDeviceName, - "Name of the selected physical device. Default: \"\"", - ""); +VY_CVAR_S(r_VkPhysDeviceName, "Name of the selected physical device. Default: \"\"", ""); vy_vk_gpu g_gpu; @@ -40,10 +37,8 @@ static const char *AllocationScopeToString(VkSystemAllocationScope scope) { } } -static void *TrackAllocation(void *userData, - size_t size, - size_t alignment, - VkSystemAllocationScope scope) { +static void * +TrackAllocation(void *userData, size_t size, size_t alignment, VkSystemAllocationScope scope) { vyLog("vk", "Allocation. Size: %zu, Alignment: %zu, Scope: %s", size, @@ -127,13 +122,11 @@ static vy_result CreateInstance(void) { uint32_t available_layer_count = 0; result = vkEnumerateInstanceLayerProperties(&available_layer_count, NULL); if (result == VK_SUCCESS) { - VkLayerProperties *props = - calloc(available_layer_count, sizeof(VkLayerProperties)); + VkLayerProperties *props = calloc(available_layer_count, sizeof(VkLayerProperties)); if (props) { vkEnumerateInstanceLayerProperties(&available_layer_count, props); for (uint32_t i = 0; i < available_layer_count; ++i) { - if (strcmp(props[i].layerName, "VK_LAYER_KHRONOS_validation") == - 0) { + if (strcmp(props[i].layerName, "VK_LAYER_KHRONOS_validation") == 0) { layers[0] = "VK_LAYER_KHRONOS_validation"; layer_count = 1; break; @@ -141,8 +134,7 @@ static vy_result CreateInstance(void) { } free(props); } else { - vyLog("vk", - "Failed to allocate storage for instance layer properties."); + vyLog("vk", "Failed to allocate storage for instance layer properties."); } } else { vyLog("vk", "vkEnumerateInstanceLayerProperties failed."); @@ -167,7 +159,7 @@ static vy_result CreateInstance(void) { #ifdef VY_DEBUG /* Create the debug utils messenger */ VkDebugUtilsMessengerCreateInfoEXT messenger_info = { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | @@ -191,10 +183,8 @@ static vy_result CreateSurface(const vy_renderer_init_info *info) { .hinstance = info->hInstance, .hwnd = info->hWnd, }; - if (vkCreateWin32SurfaceKHR(g_gpu.instance, - &surface_info, - g_gpu.alloc_cb, - &g_gpu.surface) == VK_SUCCESS) + if (vkCreateWin32SurfaceKHR(g_gpu.instance, &surface_info, g_gpu.alloc_cb, &g_gpu.surface) == + VK_SUCCESS) return VY_SUCCESS; else return 100; @@ -206,10 +196,8 @@ static vy_result CreateSurface(const vy_renderer_init_info *info) { .dpy = info->display, .window = info->window, }; - if (vkCreateXlibSurfaceKHR(g_gpu.instance, - &surface_info, - &g_gpu.alloc_cb, - &g_gpu.surface) == VK_SUCCESS) + if (vkCreateXlibSurfaceKHR(g_gpu.instance, &surface_info, &g_gpu.alloc_cb, &g_gpu.surface) == + VK_SUCCESS) return VY_SUCCESS; else return 100; @@ -222,16 +210,14 @@ 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}; uint32_t count = 0; vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, NULL); - VkQueueFamilyProperties *props = - calloc(count, sizeof(VkQueueFamilyProperties)); + VkQueueFamilyProperties *props = calloc(count, sizeof(VkQueueFamilyProperties)); if (!props) { return indices; } @@ -245,10 +231,7 @@ static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, indices.compute = i; VkBool32 present_supported = VK_FALSE; - vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev, - i, - surface, - &present_supported); + vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev, i, surface, &present_supported); if (present_supported) indices.present = i; } @@ -262,19 +245,13 @@ static bool CheckDeviceExtensionSupported(VkPhysicalDevice phys_dev) { }; uint32_t extension_count; - vkEnumerateDeviceExtensionProperties(phys_dev, - NULL, - &extension_count, - NULL); + vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, NULL); VkExtensionProperties *supported_extensions = calloc(extension_count, sizeof(VkExtensionProperties)); if (!supported_extensions) return false; - vkEnumerateDeviceExtensionProperties(phys_dev, - NULL, - &extension_count, - supported_extensions); + vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, supported_extensions); bool supported = true; for (uint32_t i = 0; i < VY_ARRAY_COUNT(required_extensions); ++i) { @@ -302,30 +279,23 @@ static vy_result ChoosePhysicalDevice(void) { g_gpu.phys_device = VK_NULL_HANDLE; uint32_t phys_device_count = 0; - VkResult result = - vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL); + VkResult result = vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL); if (result != VK_SUCCESS) { vyReportError("vk", "Failed to enumerate the physical devices."); return 2; } - VkPhysicalDevice *phys_devices = - calloc(phys_device_count, sizeof(VkPhysicalDevice)); + VkPhysicalDevice *phys_devices = calloc(phys_device_count, sizeof(VkPhysicalDevice)); if (!phys_devices) { - vyReportError( - "vk", - "Failed to enumerate the physical devices: Out of memory."); + vyReportError("vk", "Failed to enumerate the physical devices: Out of memory."); return 2; } - vkEnumeratePhysicalDevices(g_gpu.instance, - &phys_device_count, - phys_devices); + 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) { VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = { - .sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, .pNext = NULL, }; VkPhysicalDeviceProperties2 props = { @@ -337,8 +307,7 @@ static vy_result ChoosePhysicalDevice(void) { if (!CheckDeviceExtensionSupported(phys_devices[i])) continue; - vy_queue_indices indices = - RetrieveQueueIndices(phys_devices[i], g_gpu.surface); + vy_queue_indices indices = RetrieveQueueIndices(phys_devices[i], g_gpu.surface); if (indices.compute == UINT32_MAX || indices.present == UINT32_MAX || indices.graphics == UINT32_MAX) continue; @@ -351,14 +320,10 @@ static vy_result ChoosePhysicalDevice(void) { score += (props.properties.limits.maxFramebufferWidth / 100) * (props.properties.limits.maxFramebufferHeight / 100); - score += (descriptor_indexing_props - .shaderStorageBufferArrayNonUniformIndexingNative) - ? 100 - : 0; - score += (descriptor_indexing_props - .shaderSampledImageArrayNonUniformIndexingNative) - ? 100 - : 0; + score += + (descriptor_indexing_props.shaderStorageBufferArrayNonUniformIndexingNative) ? 100 : 0; + score += + (descriptor_indexing_props.shaderSampledImageArrayNonUniformIndexingNative) ? 100 : 0; if (score > highscore) { highscore = score; @@ -373,10 +338,9 @@ static vy_result ChoosePhysicalDevice(void) { } } if (best_index < phys_device_count) { - g_gpu.phys_device = phys_devices[0]; + g_gpu.phys_device = phys_devices[0]; VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = { - .sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, .pNext = NULL, }; VkPhysicalDeviceProperties2 props = { @@ -401,8 +365,7 @@ static vy_result CreateDevice(void) { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; - vy_queue_indices queue_indices = - RetrieveQueueIndices(g_gpu.phys_device, g_gpu.surface); + vy_queue_indices queue_indices = RetrieveQueueIndices(g_gpu.phys_device, g_gpu.surface); g_gpu.compute_family = queue_indices.compute; g_gpu.graphics_family = queue_indices.graphics; @@ -419,23 +382,21 @@ static vy_result CreateDevice(void) { queue_info[0].queueFamilyIndex = queue_indices.graphics; queue_info[0].pQueuePriorities = &priority; if (queue_indices.compute != queue_indices.graphics) { - queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_info[1].pNext = NULL; - queue_info[1].flags = 0; - queue_info[1].queueCount = 1; + queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[1].pNext = NULL; + queue_info[1].flags = 0; + queue_info[1].queueCount = 1; queue_info[1].queueFamilyIndex = queue_indices.compute; queue_info[1].pQueuePriorities = &priority; ++distinct_queue_count; } if (queue_indices.present != queue_indices.graphics && queue_indices.present != queue_indices.compute) { - queue_info[distinct_queue_count].sType = - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[distinct_queue_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[distinct_queue_count].pNext = NULL; queue_info[distinct_queue_count].flags = 0; queue_info[distinct_queue_count].queueCount = 1; - queue_info[distinct_queue_count].queueFamilyIndex = - queue_indices.present; + queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.present; queue_info[distinct_queue_count].pQueuePriorities = &priority; } @@ -446,26 +407,15 @@ static vy_result CreateDevice(void) { .pQueueCreateInfos = queue_info, .queueCreateInfoCount = distinct_queue_count, }; - if (vkCreateDevice(g_gpu.phys_device, - &device_info, - g_gpu.alloc_cb, - &g_gpu.device) != VK_SUCCESS) { + if (vkCreateDevice(g_gpu.phys_device, &device_info, g_gpu.alloc_cb, &g_gpu.device) != + VK_SUCCESS) { vyReportError("vk", "Device creation failed."); return 10; } - vkGetDeviceQueue(g_gpu.device, - queue_indices.graphics, - 0, - &g_gpu.graphics_queue); - vkGetDeviceQueue(g_gpu.device, - queue_indices.compute, - 0, - &g_gpu.compute_queue); - vkGetDeviceQueue(g_gpu.device, - queue_indices.present, - 0, - &g_gpu.present_queue); + vkGetDeviceQueue(g_gpu.device, queue_indices.graphics, 0, &g_gpu.graphics_queue); + vkGetDeviceQueue(g_gpu.device, queue_indices.compute, 0, &g_gpu.compute_queue); + vkGetDeviceQueue(g_gpu.device, queue_indices.present, 0, &g_gpu.present_queue); return VY_SUCCESS; } diff --git a/src/renderer/vk/swapchain.c b/src/renderer/vk/swapchain.c index 231f4b1..63ad76a 100644 --- a/src/renderer/vk/swapchain.c +++ b/src/renderer/vk/swapchain.c @@ -7,18 +7,16 @@ #include #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include +#define WIN32_LEAN_AND_MEAN +#include #elif defined(VY_USE_XLIB) - #include +#include #endif VY_CVAR_I(r_VkPreferredSwapchainImages, "Preferred number of swapchain iamges. [2/3] Default: 2", 2); -VY_CVAR_I(r_VkPreferMailboxMode, - "Prefer mailbox present mode over fifo mode. [0/1] Default: 0", - 1); +VY_CVAR_I(r_VkPreferMailboxMode, "Prefer mailbox present mode over fifo mode. [0/1] Default: 0", 1); typedef struct { VkPresentModeKHR present_mode; @@ -35,10 +33,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { if (r_VkPreferMailboxMode.i) { VkPresentModeKHR modes[6]; uint32_t count = 6; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_gpu.phys_device, - g_gpu.surface, - &count, - modes); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_gpu.phys_device, g_gpu.surface, &count, modes); for (uint32_t i = 0; i < count; ++i) { if (modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) params.present_mode = VK_PRESENT_MODE_MAILBOX_KHR; @@ -48,10 +43,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { /* Determine surface format */ VkSurfaceFormatKHR formats[64]; uint32_t format_count = 64; - vkGetPhysicalDeviceSurfaceFormatsKHR(g_gpu.phys_device, - g_gpu.surface, - &format_count, - formats); + vkGetPhysicalDeviceSurfaceFormatsKHR(g_gpu.phys_device, g_gpu.surface, &format_count, formats); params.surface_format = formats[0]; for (uint32_t i = 0; i < format_count; ++i) { if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB && @@ -63,9 +55,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { /* get extent */ VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_gpu.phys_device, - g_gpu.surface, - &capabilities); + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_gpu.phys_device, g_gpu.surface, &capabilities); if (capabilities.currentExtent.width != UINT32_MAX) { params.extent = capabilities.currentExtent; } else { @@ -76,9 +66,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { params.extent.height = (uint32_t)client_area.bottom; #else XWindowAttributes attribs; - XGetWindowAttributes(g_gpu.native_window.display, - g_gpu.native_window.window, - &attribs); + XGetWindowAttributes(g_gpu.native_window.display, g_gpu.native_window.window, &attribs); params.extent.width = (uint32_t)attribs.width; params.extent.height = (uint32_t)attribs.height; #endif @@ -91,8 +79,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) { vy_swapchain g_swapchain; vy_result vyCreateSwapchain(void) { - vy_device_swapchain_parameters device_params = - DetermineSwapchainParameters(); + vy_device_swapchain_parameters device_params = DetermineSwapchainParameters(); uint32_t image_count = r_VkPreferredSwapchainImages.i; if (image_count < 2) @@ -137,14 +124,9 @@ vy_result vyCreateSwapchain(void) { /* Retrieve images */ g_swapchain.image_count = 0; - vkGetSwapchainImagesKHR(g_gpu.device, - g_swapchain.swapchain, - &g_swapchain.image_count, - NULL); + vkGetSwapchainImagesKHR(g_gpu.device, g_swapchain.swapchain, &g_swapchain.image_count, NULL); if (g_swapchain.image_count > VY_VK_MAX_SWAPCHAIN_IMAGES) { - vyReportError("vk", - "Unsupported number of swapchain images: %u", - g_swapchain.image_count); + vyReportError("vk", "Unsupported number of swapchain images: %u", g_swapchain.image_count); return 51; } vkGetSwapchainImagesKHR(g_gpu.device, @@ -179,8 +161,7 @@ vy_result vyCreateSwapchain(void) { &view_info, g_gpu.alloc_cb, &g_swapchain.image_views[i]) != VK_SUCCESS) { - vyReportError("vk", - "Failed to create an image view for the swapchain."); + vyReportError("vk", "Failed to create an image view for the swapchain."); return 52; } } @@ -196,9 +177,7 @@ vy_result vyRecreateSwapchain(void) { void vyDestroySwapchain(void) { for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { - vkDestroyImageView(g_gpu.device, - g_swapchain.image_views[i], - g_gpu.alloc_cb); + vkDestroyImageView(g_gpu.device, g_swapchain.image_views[i], g_gpu.alloc_cb); } vkDestroySwapchainKHR(g_gpu.device, g_swapchain.swapchain, g_gpu.alloc_cb); } diff --git a/src/runtime/aio.c b/src/runtime/aio.c index f7f76e6..e48a119 100644 --- a/src/runtime/aio.c +++ b/src/runtime/aio.c @@ -4,6 +4,10 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include + +#elif defined(__linux__) +#include + #endif #include @@ -11,7 +15,6 @@ /* Maintain a ringbuffer of pending operations */ - typedef struct { #ifdef _WIN32 HANDLE file_handle; @@ -49,11 +52,10 @@ static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) { if (_ringbuffer.head + count <= _ringbuffer.capacity) { result.a_count = count; result.a = &_ringbuffer.storage[_ringbuffer.head]; - _ringbuffer.head = - (_ringbuffer.head + count) % _ringbuffer.capacity; + _ringbuffer.head = (_ringbuffer.head + count) % _ringbuffer.capacity; } else { /* Check if enough space is free at the end */ - uint32_t a_count = _ringbuffer.capacity - _ringbuffer.head; + uint32_t a_count = _ringbuffer.capacity - _ringbuffer.head; uint32_t b_count = count - a_count; if (b_count <= _ringbuffer.tail) { @@ -70,10 +72,9 @@ static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) { /* Head is lower than tail */ uint32_t num_free = _ringbuffer.tail - _ringbuffer.head; if (count < num_free) { - result.a_count = count; - result.a = &_ringbuffer.storage[_ringbuffer.head]; - _ringbuffer.head = - (_ringbuffer.head + count) % _ringbuffer.capacity; + result.a_count = count; + result.a = &_ringbuffer.storage[_ringbuffer.head]; + _ringbuffer.head = (_ringbuffer.head + count) % _ringbuffer.capacity; } else { /* Not enough space, we would overwrite the tail */ vyLog("aio", "Ringbuffer is full."); @@ -85,10 +86,9 @@ static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) { } #ifdef _WIN32 -static void win32CompletionRoutine(DWORD error_code, - DWORD num_bytes_transfered, - LPOVERLAPPED overlapped) { - vy_aio *op = (vy_aio*)overlapped->hEvent; +static void +win32CompletionRoutine(DWORD error_code, DWORD num_bytes_transfered, LPOVERLAPPED overlapped) { + vy_aio *op = (vy_aio *)overlapped->hEvent; assert(op->state == VY_AIO_STATE_PENDING); if (error_code != ERROR_SUCCESS) { @@ -97,12 +97,11 @@ static void win32CompletionRoutine(DWORD error_code, } else { op->state = VY_AIO_STATE_FINISHED; } - + CloseHandle(op->file_handle); } #endif - VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations) { _ringbuffer.guard = vyCreateMutex(); if (!_ringbuffer.guard) { @@ -114,8 +113,8 @@ VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations) { _ringbuffer.storage = calloc(max_concurrent_operations, sizeof(vy_aio)); if (!_ringbuffer.storage) return VY_AIO_OUT_OF_MEMORY; - _ringbuffer.head = 0; - _ringbuffer.tail = 0; + _ringbuffer.head = 0; + _ringbuffer.tail = 0; _ringbuffer.capacity = max_concurrent_operations; return VY_SUCCESS; } @@ -126,8 +125,7 @@ VY_DLLEXPORT void vyShutdownAIO(void) { _ringbuffer.capacity = 0; } -VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, - vy_aio_handle *handles) { +VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, vy_aio_handle *handles) { if (batch->num_loads > VY_LOAD_BATCH_MAX_SIZE) { return VY_AIO_LOAD_TOO_LARGE; } @@ -139,13 +137,11 @@ VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, } for (unsigned int i = 0; i < batch->num_loads; ++i) { - vy_aio *op = (i < rbspace.a_count) ? &rbspace.a[i] - : &rbspace.b[i - rbspace.a_count]; - op->state = VY_AIO_STATE_PENDING; + vy_aio *op = (i < rbspace.a_count) ? &rbspace.a[i] : &rbspace.b[i - rbspace.a_count]; + op->state = VY_AIO_STATE_PENDING; const char *file_path = vyGetFilePath(batch->loads[i].file); if (!file_path) { - vyReportError("aio", - "Failed to resolve file path for a batched load"); + vyReportError("aio", "Failed to resolve file path for a batched load"); op->state = VY_AIO_STATE_INVALID; handles[i] = VY_AIO_INVALID_HANDLE; continue; @@ -168,9 +164,7 @@ VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, -1, wpath, VY_ARRAY_COUNT(wpath)) == 0) { - vyReportError("aio", - "MultiByteToWideChar failed with error code: %u", - GetLastError()); + vyReportError("aio", "MultiByteToWideChar failed with error code: %u", GetLastError()); op->state = VY_AIO_STATE_FINISHED; handles[i] = VY_AIO_INVALID_HANDLE; continue; @@ -193,15 +187,15 @@ VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, continue; } op->file_handle = file_handle; - BOOL result = ReadFileEx(file_handle, + BOOL result = ReadFileEx(file_handle, batch->loads[i].dest, (DWORD)batch->loads[i].num_bytes, &op->overlapped, win32CompletionRoutine); - DWORD err = GetLastError(); + DWORD err = GetLastError(); if (!result || err != ERROR_SUCCESS) { vyReportError("aio", "ReadFileEx failed with error code: %u", err); - op->state = VY_AIO_STATE_FINISHED; + op->state = VY_AIO_STATE_FINISHED; handles[i] = VY_AIO_INVALID_HANDLE; CloseHandle(file_handle); op->file_handle = NULL; @@ -229,14 +223,15 @@ VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle) { return state; } - VY_DLLEXPORT void vyReleaseAIO(vy_aio_handle handle) { +VY_DLLEXPORT void vyReleaseAIO(vy_aio_handle handle) { if (handle == VY_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity) { return; } vyLockMutex(_ringbuffer.guard); _ringbuffer.storage[handle - 1].state = VY_AIO_STATE_INVALID; if (handle - 1 == _ringbuffer.tail) { - /* Advance the tail such that it points to the last used slot. (Or to head, if the ringbuffer is now empty) */ + /* Advance the tail such that it points to the last used slot. (Or to head, if the + * ringbuffer is now empty) */ uint32_t i = _ringbuffer.tail; while ((_ringbuffer.storage[i].state == VY_AIO_STATE_INVALID) && i != _ringbuffer.head) { i = (i + 1) % _ringbuffer.capacity; @@ -245,3 +240,19 @@ VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle) { } vyUnlockMutex(_ringbuffer.guard); } + +VY_DLLEXPORT vy_aio_state vyWaitForAIOCompletion(vy_aio_handle handle) { + if (handle == VY_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity) + return VY_AIO_STATE_INVALID; + vy_aio_state state; + do { + state = vyGetAIOState(handle); + /* NOTE(Kevin): This is where we could temporarily run a job. */ +#ifdef _WIN32 + YieldProcessor(); +#elif defined(__linux__) + sched_yield(); +#endif + } while (state == VY_AIO_STATE_PENDING); + return state; +} \ No newline at end of file diff --git a/src/runtime/aio.h b/src/runtime/aio.h index a263e06..0beccb0 100644 --- a/src/runtime/aio.h +++ b/src/runtime/aio.h @@ -3,8 +3,8 @@ #include -#include "runtime.h" #include "file_tab.h" +#include "runtime.h" typedef struct { size_t offset; /**< Starting offset inside the file in bytes */ @@ -13,7 +13,7 @@ typedef struct { * Must be valid until the load is finished. */ void *dest; - vy_file_id file; /**< Unique identifier for the file */ + vy_file_id file; /**< Unique identifier for the file */ } vy_file_load; #define VY_LOAD_BATCH_MAX_SIZE 64 @@ -24,12 +24,11 @@ typedef struct { */ typedef struct { vy_file_load loads[VY_LOAD_BATCH_MAX_SIZE]; - + /** Must be smaller or equal to @c VY_LOAD_BATCH_MAX_SIZE */ unsigned int num_loads; } vy_load_batch; - #define VY_AIO_INVALID_HANDLE 0 /** Handle for an async io operation. Can be used to query the state and result. */ @@ -41,7 +40,6 @@ enum { VY_AIO_OUT_OF_MEMORY, }; - typedef enum { VY_AIO_STATE_INVALID, VY_AIO_STATE_PENDING, @@ -53,11 +51,12 @@ VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations); VY_DLLEXPORT void vyShutdownAIO(void); -VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, - vy_aio_handle *handles); +VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, vy_aio_handle *handles); VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle); +VY_DLLEXPORT vy_aio_state vyWaitForAIOCompletion(vy_aio_handle handle); + /* Releases the internal storage for the operation. * The system is allowed to re-use the same handle value for new operations after this was called. */ diff --git a/src/runtime/app.c b/src/runtime/app.c index 450df7d..f391299 100644 --- a/src/runtime/app.c +++ b/src/runtime/app.c @@ -1,9 +1,9 @@ #include "app.h" +#include "aio.h" +#include "buffer_manager.h" #include "config.h" #include "gfx.h" -#include "aio.h" #include "renderer_api.h" -#include "buffer_manager.h" extern void __RegisterRuntimeCVars(void); @@ -12,13 +12,10 @@ VY_CVAR_I(rt_WindowWidth, "Window width. Default: 1024", 1024); VY_CVAR_I(rt_WindowHeight, "Window height. Default: 768", 768); #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include +#define WIN32_LEAN_AND_MEAN +#include -static LRESULT CALLBACK win32WndProc(HWND hWnd, - UINT uMsg, - WPARAM wParam, - LPARAM lParam) { +static LRESULT CALLBACK win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); @@ -28,10 +25,8 @@ static LRESULT CALLBACK win32WndProc(HWND hWnd, } } -VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR pCmdLine, - int nCmdShow) { +VY_DLLEXPORT int +vyWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { __RegisterRuntimeCVars(); vyRegisterRendererCVars(); @@ -103,9 +98,7 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, } if (!wnd) { - vyReportError("CORE", - "Failed to create the game window: %u", - GetLastError()); + vyReportError("CORE", "Failed to create the game window: %u", GetLastError()); return 1; } @@ -144,11 +137,10 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance, #elif defined(VY_USE_XLIB) - #include - #include +#include +#include -static void -xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) { +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) { @@ -156,20 +148,20 @@ xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) { return; } - #define _NET_WM_STATE_REMOVE 0 - #define _NET_WM_STATE_ADD 1 - #define EVENT_SOURCE_APPLICATION 1 +#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; + 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; @@ -178,9 +170,9 @@ xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) { vyReportError("CORE", "Failed to send x11 fullscreen event."); } - #undef _NET_WM_STATE_ADD - #undef _NET_WM_STATE_REMOVE - #undef EVENT_SOURCE_APPLICATION +#undef _NET_WM_STATE_ADD +#undef _NET_WM_STATE_REMOVE +#undef EVENT_SOURCE_APPLICATION } VY_DLLEXPORT int vyXlibEntry(int argc, char **argv) { diff --git a/src/runtime/assets.h b/src/runtime/assets.h index 1b2d5fe..e313eb8 100644 --- a/src/runtime/assets.h +++ b/src/runtime/assets.h @@ -8,4 +8,13 @@ typedef uint32_t vy_uid; #define VY_INVALID_UID 0 +/* Used to identify renderer backend dependent assets. */ +enum { + VY_INVALID_RENDERER_BACKEND_CODE = 0, + VY_RENDERER_BACKEND_CODE_VK = 1, + + VY_RENDERER_BACKEND_CODE_ONE_PAST_LAST, +}; +typedef uint8_t vy_renderer_backend_code; + #endif \ No newline at end of file diff --git a/src/runtime/buffer_manager.c b/src/runtime/buffer_manager.c index eb359ee..ecc476a 100644 --- a/src/runtime/buffer_manager.c +++ b/src/runtime/buffer_manager.c @@ -1,12 +1,12 @@ +#include "buffer_manager.h" +#include "config.h" #include "runtime.h" #include "threading.h" -#include "config.h" -#include "buffer_manager.h" -#include -#include -#include #include +#include +#include +#include typedef struct vy_buffer_region_s { void *memory; @@ -22,7 +22,7 @@ typedef struct vy_buffer_region_s { #include -#define lzcnt32(x) __lzcnt((x)) +#define lzcnt32(x) __lzcnt((x)) #define popcnt32(x) __popcnt((x)) static __forceinline uint32_t tzcnt32(uint32_t x) { @@ -40,7 +40,7 @@ static inline bool IsLZCNTSupported(void) { } #elif defined(__GNUC__) -#define lzcnt32(x) __builtin_clz((x)) +#define lzcnt32(x) __builtin_clz((x)) #define tzcnt32(x) __builtin_ctz((x)) #define popcnt32(x) __builtin_popcount((x)) @@ -49,13 +49,13 @@ static inline bool IsLZCNTSupported(void) { #endif /* NOTE(Kevin): Keep these sorted! */ -static size_t _block_sizes[] = {VY_KB(512), VY_MB(1), VY_MB(4), VY_MB(8) }; +static size_t _block_sizes[] = {VY_KB(512), VY_MB(1), VY_MB(4), VY_MB(8)}; #define NUM_BLOCK_SIZES (sizeof(_block_sizes) / sizeof(_block_sizes[0])) static vy_buffer_region _regions[NUM_BLOCK_SIZES]; -VY_CVAR_SZ( - rt_BufferManagerMemory, - "Total number of bytes allocated for the buffer manager. Default: 1GB", VY_GB(1)); +VY_CVAR_SZ(rt_BufferManagerMemory, + "Total number of bytes allocated for the buffer manager. Default: 1GB", + VY_GB(1)); VY_DLLEXPORT vy_result vyInitBufferManager(void) { if ((rt_BufferManagerMemory.sz % NUM_BLOCK_SIZES) != 0) @@ -74,14 +74,14 @@ VY_DLLEXPORT vy_result vyInitBufferManager(void) { mem_per_size / (1024 * 1024), _block_sizes[i] / 1024); - size_t block_count = mem_per_size / _block_sizes[i]; + size_t block_count = mem_per_size / _block_sizes[i]; _regions[i].block_count = block_count; _regions[i].guard = vyCreateMutex(); if (!_regions[i].guard) { vyReportError("BUFFERMGR", "Failed to create guard mutex %u", i); return VY_BUFFER_MGR_MUTEX_CREATION_FAILED; } - _regions[i].memory = malloc(mem_per_size); + _regions[i].memory = malloc(mem_per_size); if (!_regions[i].memory) { vyDestroyMutex(_regions[i].guard); vyReportError("BUFFERMGR", "Failed to allocate memory.", i); @@ -117,7 +117,7 @@ VY_DLLEXPORT void vyShutdownBufferManager(void) { VY_DLLEXPORT void *vyAllocBuffer(size_t size) { assert(IsLZCNTSupported()); - + // Determine the best block size to use size_t required_blocks = (size + _block_sizes[0] - 1) / _block_sizes[0]; size_t best_fit = 0; @@ -148,18 +148,18 @@ VY_DLLEXPORT void *vyAllocBuffer(size_t size) { size_t first_free = 32 - free_high_blocks; region->bitmap[i] |= (in_use_mask << first_free); block_index = i * 32 + first_free; - result = (char *)region->memory + - block_index * _block_sizes[best_fit]; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; } else if (tzcnt32(region->bitmap[i]) >= required_blocks) { /* Low blocks are free */ region->bitmap[i] |= in_use_mask; block_index = i * 32; - result = (char *)region->memory + block_index * _block_sizes[best_fit]; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; } else { /* Check if we can find a large enough range of free blocks. * Start after the first set bit. */ - for (uint32_t j = tzcnt32(region->bitmap[i]) + 1; j < 32 - required_blocks; ++j) { + for (uint32_t j = tzcnt32(region->bitmap[i]) + 1; j < 32 - required_blocks; + ++j) { if ((region->bitmap[i] & in_use_mask << j) == 0) { region->bitmap[i] |= (in_use_mask << j); block_index = i * 32 + j; @@ -170,23 +170,25 @@ VY_DLLEXPORT void *vyAllocBuffer(size_t size) { } } else if (region->bitmap[i] == 0) { /* All free */ - region->bitmap[i] = in_use_mask; - block_index = i * 32; - result = (char *)region->memory + block_index * _block_sizes[best_fit]; + region->bitmap[i] = in_use_mask; + block_index = i * 32; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; } else if (i < dword_count - 1) { - /* Check if we can use high blocks from this dword and low blocks from the next one */ + /* Check if we can use high blocks from this dword and low blocks from the next one + */ size_t high_blocks = lzcnt32(region->bitmap[i]); - size_t low_blocks = (region->bitmap[i + 1] != 0) ? tzcnt32(region->bitmap[i + 1]) : 32; + size_t low_blocks = + (region->bitmap[i + 1] != 0) ? tzcnt32(region->bitmap[i + 1]) : 32; if (high_blocks + low_blocks >= required_blocks) { - size_t high_mask = (1u << high_blocks) - 1; + size_t high_mask = (1u << high_blocks) - 1; size_t first_free = 32 - high_blocks; - size_t low_mask = (1u << (required_blocks - high_blocks)) - 1; - + size_t low_mask = (1u << (required_blocks - high_blocks)) - 1; + region->bitmap[i] |= (high_mask << first_free); region->bitmap[i + 1] |= low_mask; block_index = i * 32 + first_free; - result = (char *)region->memory + block_index * _block_sizes[best_fit]; + result = (char *)region->memory + block_index * _block_sizes[best_fit]; } } @@ -202,13 +204,14 @@ VY_DLLEXPORT void *vyAllocBuffer(size_t size) { } VY_DLLEXPORT void vyReleaseBuffer(const void *begin, size_t size) { + if (!begin) + return; uintptr_t begin_addr = (uintptr_t)begin; for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) { uintptr_t region_addr = (uintptr_t)_regions[i].memory; size_t region_size = _block_sizes[i] * _regions[i].block_count; - if (begin_addr >= region_addr && - begin_addr + size <= region_addr + region_size) { - + if (begin_addr >= region_addr && begin_addr + size <= region_addr + region_size) { + size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i]; size_t first_block = (begin_addr - region_addr) / _block_sizes[i]; @@ -232,8 +235,7 @@ VY_DLLEXPORT void vyIncreaseBufferRefCount(const void *begin, size_t size) { for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) { uintptr_t region_addr = (uintptr_t)_regions[i].memory; size_t region_size = _block_sizes[i] * _regions[i].block_count; - if (begin_addr >= region_addr && - begin_addr + size <= region_addr + region_size) { + if (begin_addr >= region_addr && begin_addr + size <= region_addr + region_size) { size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i]; size_t first_block = (begin_addr - region_addr) / _block_sizes[i]; diff --git a/src/runtime/config.c b/src/runtime/config.c index 2e09b21..80020d7 100644 --- a/src/runtime/config.c +++ b/src/runtime/config.c @@ -1,6 +1,6 @@ #include "config.h" -#include "threading.h" #include "runtime.h" +#include "threading.h" #include #include diff --git a/src/runtime/config.h b/src/runtime/config.h index 813fd86..36c2da2 100644 --- a/src/runtime/config.h +++ b/src/runtime/config.h @@ -3,16 +3,14 @@ #include "runtime.h" -typedef enum -{ +typedef enum { VY_CVAR_TYPE_INT, VY_CVAR_TYPE_FLOAT, VY_CVAR_TYPE_STRING, VY_CVAR_TYPE_SIZE, } vy_cvar_type; -typedef struct -{ +typedef struct { const char *name; const char *description; union { @@ -24,26 +22,14 @@ typedef struct vy_cvar_type type; } vy_cvar; -#define VY_CVAR_I(n, d, v) \ - vy_cvar n = {.name = #n, \ - .description = d, \ - .i = (v), \ - .type = VY_CVAR_TYPE_INT} -#define VY_CVAR_F(n, d, v) \ - vy_cvar n = {.name = #n, \ - .description = d, \ - .f = (v), \ - .type = VY_CVAR_TYPE_FLOAT} -#define VY_CVAR_S(n, d, v) \ - vy_cvar n = {.name = #n, \ - .description = d, \ - .s = (v), \ - .type = VY_CVAR_TYPE_STRING} -#define VY_CVAR_SZ(n, d, v) \ - vy_cvar n = {.name = #n, \ - .description = d, \ - .sz = (v), \ - .type = VY_CVAR_TYPE_SIZE} +#define VY_CVAR_I(n, d, v) \ + vy_cvar n = {.name = #n, .description = d, .i = (v), .type = VY_CVAR_TYPE_INT} +#define VY_CVAR_F(n, d, v) \ + vy_cvar n = {.name = #n, .description = d, .f = (v), .type = VY_CVAR_TYPE_FLOAT} +#define VY_CVAR_S(n, d, v) \ + vy_cvar n = {.name = #n, .description = d, .s = (v), .type = VY_CVAR_TYPE_STRING} +#define VY_CVAR_SZ(n, d, v) \ + vy_cvar n = {.name = #n, .description = d, .sz = (v), .type = VY_CVAR_TYPE_SIZE} VY_DLLEXPORT void vyRegisterCVAR(vy_cvar *cvar); diff --git a/src/runtime/dynamic_libs.c b/src/runtime/dynamic_libs.c index 3055f69..b88e468 100644 --- a/src/runtime/dynamic_libs.c +++ b/src/runtime/dynamic_libs.c @@ -10,18 +10,13 @@ VY_DLLEXPORT vy_dynlib vyOpenCallerLib(void) { VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) { wchar_t libname_w[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, - MB_PRECOMPOSED, - libname, - -1, - libname_w, - MAX_PATH); + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, libname, -1, libname_w, MAX_PATH); HMODULE mod = LoadLibraryW(libname_w); return (vy_dynlib)mod; } VY_DLLEXPORT void *vyGetSymbol(vy_dynlib lib, const char *symbol) { - return (void*)GetProcAddress((HMODULE)lib, symbol); + return (void *)GetProcAddress((HMODULE)lib, symbol); } VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) { @@ -47,5 +42,4 @@ VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) { dlclose(lib); } - #endif diff --git a/src/runtime/dynamic_libs.h b/src/runtime/dynamic_libs.h index 4a4b8f4..1568bb5 100644 --- a/src/runtime/dynamic_libs.h +++ b/src/runtime/dynamic_libs.h @@ -4,13 +4,13 @@ #include "runtime.h" #ifdef _WIN32 - #define VY_DLLNAME(s) \ - (".\\"s \ - ".dll") +#define VY_DLLNAME(s) \ + (".\\"s \ + ".dll") #elif defined(__linux__) - #define VY_DLLNAME(s) \ - ("./lib"s \ - ".so") +#define VY_DLLNAME(s) \ + ("./lib"s \ + ".so") #endif typedef void *vy_dynlib; diff --git a/src/runtime/error_report.c b/src/runtime/error_report.c index 5cf987a..5cd701f 100644 --- a/src/runtime/error_report.c +++ b/src/runtime/error_report.c @@ -1,43 +1,47 @@ #include #include +#include +#include #include "runtime.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include + +#define DbgBreak DebugBreak() +#elif defined(__GNUC__) +#define DbgBreak __builtin_trap() +#else +/* Not available */ +#define DbgBreak #endif /* TODO(Kevin): Log to file, show error message box, ... */ -static void DisplayErrorBox(const char *text) -{ +static bool DisplayErrorBox(const char *text) { #ifdef _WIN32 WCHAR msg[256]; - MultiByteToWideChar(CP_UTF8, - MB_PRECOMPOSED, - text, - -1, - msg, - VY_ARRAY_COUNT(msg)); + vyUTF8ToWStr(text, msg, VY_ARRAY_COUNT(msg)); + +#ifdef VY_ERROR_REPORT_DEBUGBREAK + return MessageBoxW(NULL, msg, L"Error", MB_OKCANCEL | MB_ICONERROR) == IDCANCEL; +#else MessageBoxW(NULL, msg, L"Error", MB_ICONERROR); + return false; +#endif /* VY_ERROR_REPORT_DEBUGBREAK */ +#else + return false; #endif } -static void LogOut(const char *text) -{ +static void LogOut(const char *text) { #ifdef _WIN32 WCHAR msg[256]; - MultiByteToWideChar(CP_UTF8, - MB_PRECOMPOSED, - text, - -1, - msg, - VY_ARRAY_COUNT(msg)); + vyUTF8ToWStr(text, msg, VY_ARRAY_COUNT(msg)); OutputDebugStringW(msg); -#elif defined(__linux__) - fprintf(stderr, "%s", text); #endif + fprintf(stderr, "%s", text); } VY_DLLEXPORT void vyReportError(const char *subsystem, const char *fmt, ...) { @@ -51,7 +55,9 @@ VY_DLLEXPORT void vyReportError(const char *subsystem, const char *fmt, ...) { at += snprintf(&buf[at], VY_ARRAY_COUNT(buf) - at - 1, "\n"); LogOut(buf); - DisplayErrorBox(buf); + if (DisplayErrorBox(buf)) { + DbgBreak; + } } VY_DLLEXPORT void vyLog(const char *subsystem, const char *fmt, ...) { diff --git a/src/runtime/file_tab.c b/src/runtime/file_tab.c index 797d406..0a09256 100644 --- a/src/runtime/file_tab.c +++ b/src/runtime/file_tab.c @@ -29,7 +29,7 @@ vy_result vyInitFileTab(unsigned int max_files) { _file_tab.capacity = max_files; _file_tab.name_head = 0; - _file_tab.mutex = vyCreateMutex(); + _file_tab.mutex = vyCreateMutex(); return VY_SUCCESS; } @@ -40,7 +40,6 @@ void vyShutdownFileTab(void) { vyDestroyMutex(_file_tab.mutex); } - vy_file_id vyGetFileId(const char *path) { vy_text_span span; span.start = path; @@ -80,8 +79,8 @@ VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path) { } memcpy(_file_tab.names + _file_tab.name_head, path.start, slen); _file_tab.names[_file_tab.name_head + slen - 1] = '\0'; - _file_tab.name_offsets[at] = _file_tab.name_head; - _file_tab.ids[at] = fid; + _file_tab.name_offsets[at] = _file_tab.name_head; + _file_tab.ids[at] = fid; _file_tab.name_head += slen; break; } else if (_file_tab.ids[at] == fid) { diff --git a/src/runtime/file_tab.h b/src/runtime/file_tab.h index 015ff0c..6f69b15 100644 --- a/src/runtime/file_tab.h +++ b/src/runtime/file_tab.h @@ -23,5 +23,4 @@ VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path); VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid); - #endif diff --git a/src/runtime/gfx_main.c b/src/runtime/gfx_main.c index 1c9214a..9f0064b 100644 --- a/src/runtime/gfx_main.c +++ b/src/runtime/gfx_main.c @@ -17,47 +17,41 @@ vy_renderer_api g_renderer; static vy_dynlib _renderer_lib; static bool _renderer_loaded = false; -VY_CVAR_S(rt_Renderer, - "Select the render backend. Available options: [vk], Default: vk", - "vk"); +VY_CVAR_S(rt_Renderer, "Select the render backend. Available options: [vk], Default: vk", "vk"); #ifdef VY_STATIC_LIB extern void VY_RENDERER_API_FN(RegisterCVars)(void); extern vy_result VY_RENDERER_API_FN(Init)(const vy_renderer_init_info *); extern void VY_RENDERER_API_FN(Shutdown)(void); -extern vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)( - const vy_compute_pipeline_info *); -extern vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileGraphicsPipeline)( - const vy_graphics_pipeline_info *); +extern vy_gfx_pipeline_handle + VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compute_pipeline_info *); +extern vy_gfx_pipeline_handle + VY_RENDERER_API_FN(CompileGraphicsPipeline)(const vy_graphics_pipeline_info *); #endif static bool LoadRenderer(void) { #if !defined(VY_STATIC_LIB) - #define RETRIEVE_SYMBOL(name, type) \ - g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vyRen" #name); \ - if (!g_renderer.name) { \ - vyReportError( \ - "GFX", \ - "Unable to retrieve renderer function %s from backend %s", \ - #name, \ - rt_Renderer.s); \ - } +#define RETRIEVE_SYMBOL(name, type) \ + g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vyRen" #name); \ + if (!g_renderer.name) { \ + vyReportError("GFX", \ + "Unable to retrieve renderer function %s from backend %s", \ + #name, \ + rt_Renderer.s); \ + } if (strcmp(rt_Renderer.s, "vk") == 0) { _renderer_lib = vyOpenLib(VY_DLLNAME("vyvk")); if (!_renderer_lib) { - vyReportError("GFX", - "Unable to load renderer backend: %s", - VY_DLLNAME("vyvk")); + vyReportError("GFX", "Unable to load renderer backend: %s", VY_DLLNAME("vyvk")); return false; } RETRIEVE_SYMBOL(RegisterCVars, vy_register_renderer_cvars_fn); RETRIEVE_SYMBOL(Init, vy_init_renderer_fn); RETRIEVE_SYMBOL(Shutdown, vy_shutdown_renderer_fn); RETRIEVE_SYMBOL(CompileComputePipeline, vy_compile_compute_pipeline_fn); - RETRIEVE_SYMBOL(CompileGraphicsPipeline, - vy_compile_graphics_pipeline_fn); + RETRIEVE_SYMBOL(CompileGraphicsPipeline, vy_compile_graphics_pipeline_fn); } else { vyReportError("GFX", "Unsupported renderer backend: (%s) %s", @@ -65,12 +59,12 @@ static bool LoadRenderer(void) { rt_Renderer.s); return false; } - #undef RETRIEVE_SYMBOL +#undef RETRIEVE_SYMBOL #else - g_renderer.RegisterCVars = &vyRenRegisterCVars; - g_renderer.Init = &vyRenInit; - g_renderer.Shutdown = &vyRenShutdown; - g_renderer.CompileComputePipeline = &vyRenCompileComputePipeline; + g_renderer.RegisterCVars = &vyRenRegisterCVars; + g_renderer.Init = &vyRenInit; + g_renderer.Shutdown = &vyRenShutdown; + g_renderer.CompileComputePipeline = &vyRenCompileComputePipeline; g_renderer.CompileGraphicsPipeline = &vyRenCompileGraphicsPipeline; #endif return true; diff --git a/src/runtime/jobs.c b/src/runtime/jobs.c index a741a5e..ae3cddd 100644 --- a/src/runtime/jobs.c +++ b/src/runtime/jobs.c @@ -60,6 +60,6 @@ void vyInitJobSystem(unsigned int worker_count) { worker_count = MAX_WORKERS; for (unsigned int i = 0; i < worker_count; ++i) { - _worker_data[i].thread = vySpawnThread(WorkerEntry, &_worker_data[i]); + _worker_data[i].thread = vySpawnThread(WorkerEntry, &_worker_data[i], "JobWorker"); } } diff --git a/src/runtime/renderer_api.h b/src/runtime/renderer_api.h index 2366c2b..1dffc61 100644 --- a/src/runtime/renderer_api.h +++ b/src/runtime/renderer_api.h @@ -7,6 +7,7 @@ #include "gfx.h" #include "runtime.h" +#include "assets.h" #ifdef _WIN32 struct HINSTANCE__; @@ -38,11 +39,24 @@ typedef struct { size_t fragment_source_length; } vy_graphics_pipeline_info; +typedef struct { + vy_uid vertex_shader; + vy_uid fragment_shader; + vy_uid compute_shader; + + vy_relptr texture_bindings; + vy_relptr uniform_bindings; + vy_relptr storage_bindings; + + uint16_t texture_binding_count; + uint16_t uniform_binding_count; + uint16_t storage_binding_count; +} vy_pipeline_info; + typedef void vy_register_renderer_cvars_fn(void); typedef vy_result vy_init_renderer_fn(const vy_renderer_init_info *info); typedef void vy_shutdown_renderer_fn(void); -typedef vy_gfx_pipeline_handle -vy_compile_compute_pipeline_fn(const vy_compute_pipeline_info *info); +typedef vy_gfx_pipeline_handle vy_compile_compute_pipeline_fn(const vy_compute_pipeline_info *info); typedef vy_gfx_pipeline_handle vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 8126b91..2e8b03a 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -4,30 +4,82 @@ /* basic types and macros */ #include +#include -#if defined(_WIN32) && !defined(VY_STATIC_LIB) - #define VY_DLLEXPORT __declspec(dllexport) +#if defined(_MSC_VER) && !defined(VY_STATIC_LIB) +#define VY_DLLEXPORT __declspec(dllexport) #else - #define VY_DLLEXPORT +#define VY_DLLEXPORT #endif +#if defined(_MSC_VER) +#define VY_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +#define VY_INLINE inline __attribute__((always_inline)) +#endif + + #define VY_UNUSED(x) ((void)sizeof((x))) #define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0])) -#define VY_KB(n) ((n) * 1024U) -#define VY_MB(n) ((n) * 1024U * 1024U) -#define VY_GB(n) ((n) * 1024U * 1024U * 1024U) +#define VY_KB(n) ((n)*1024U) +#define VY_MB(n) ((n)*1024U * 1024U) +#define VY_GB(n) ((n)*1024U * 1024U * 1024U) typedef unsigned int vy_result; #define VY_SUCCESS 0 +#define VY_UNKNOWN_ERROR (vy_result)-1 typedef struct { const char *start; unsigned int length; } vy_text_span; +/* Returns results like strcmp(): + * - If the first differing character is smaller in span than in cmp: < 0 + * - If span and cmp are equal: 0 + * - If the first differing character is greater in span than in cmp: > 0 + * - If span.length is smaller than strlen(cmp): -1 + * - If span.length is greater than strlen(cmp): 1 + */ +VY_DLLEXPORT int vyCompareSpanToString(vy_text_span span, const char *cmp); + VY_DLLEXPORT void vyReportError(const char *subsystem, const char *fmt, ...); VY_DLLEXPORT void vyLog(const char *subsystem, const char *fmt, ...); +enum { + VY_INVALID_UNICODE = 1, + VY_INSUFFICIENT_BUFFER = 2, +}; + +/* Returns VY_SUCCESS if the string was successfully converted, + * VY_INVALID_UNICODE if invalid unicode characters were encountered or + * VY_INSUFFICIENT_BUFFER if the provided output buffer is too small */ +VY_DLLEXPORT vy_result vyUTF8ToWStr(const char *utf8, wchar_t *wstr, size_t len); + +/* Relative pointer */ +typedef struct { + int32_t off; +} vy_relptr; + +static VY_INLINE void *vyResolveRelptr(vy_relptr *ptr) { + if (ptr->off != 0) { + char *p = (char *)ptr; + return (void *)(p + (ptrdiff_t)ptr->off); + } else { + return NULL; + } +} + +static VY_INLINE void vySetRelptr(vy_relptr *ptr, void *target) { + if (target) { + char *p = (char *)ptr, *t = (char *)target; + ptrdiff_t d = t - p; + ptr->off = (int32_t)d; + } else { + ptr->off = 0; + } +} + #endif diff --git a/src/runtime/runtime_cvars.c b/src/runtime/runtime_cvars.c index 840aace..8dae30f 100644 --- a/src/runtime/runtime_cvars.c +++ b/src/runtime/runtime_cvars.c @@ -6,8 +6,7 @@ extern vy_cvar rt_WindowWidth; extern vy_cvar rt_WindowHeight; extern vy_cvar rt_BufferManagerMemory; -void __RegisterRuntimeCVars(void) -{ +void __RegisterRuntimeCVars(void) { vyRegisterCVAR(&rt_Renderer); vyRegisterCVAR(&rt_Fullscreen); vyRegisterCVAR(&rt_WindowWidth); diff --git a/src/runtime/text.c b/src/runtime/text.c new file mode 100644 index 0000000..4929a96 --- /dev/null +++ b/src/runtime/text.c @@ -0,0 +1,38 @@ +#include "runtime.h" + +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +VY_DLLEXPORT int vyCompareSpanToString(vy_text_span span, const char *cmp) { + size_t cmp_len = strlen(cmp); + if (cmp_len != (size_t)span.length) + return (span.length < cmp_len) ? -1 : 1; + for (size_t i = 0; i < cmp_len; ++i) { + if (span.start[i] != cmp[i]) + return span.start[i] - cmp[i]; + } + return 0; +} + +VY_DLLEXPORT vy_result vyUTF8ToWStr(const char *utf8, wchar_t *wstr, size_t len) { +#ifdef _WIN32 + int res = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, utf8, -1, wstr, (int)len); + if (res > 0) { + return VY_SUCCESS; + } else { + DWORD err = GetLastError(); + if (err == ERROR_INSUFFICIENT_BUFFER) + return VY_INSUFFICIENT_BUFFER; + else if (err == ERROR_NO_UNICODE_TRANSLATION) + return VY_INVALID_UNICODE; + else { + vyReportError("CORE", "Unexpected error in vyUTF8ToWStr(): %u", err); + return VY_UNKNOWN_ERROR; + } + } +#endif +} \ No newline at end of file diff --git a/src/runtime/threading.h b/src/runtime/threading.h index 1ff215e..59d2a3a 100644 --- a/src/runtime/threading.h +++ b/src/runtime/threading.h @@ -34,8 +34,10 @@ 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 vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param, const char *name); VY_DLLEXPORT void vyJoinThread(vy_thread *thread); +VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void); + #endif diff --git a/src/runtime/threading_cond.c b/src/runtime/threading_cond.c index 980392a..894b744 100644 --- a/src/runtime/threading_cond.c +++ b/src/runtime/threading_cond.c @@ -3,14 +3,14 @@ #ifdef __linux__ - #include +#include struct vy_condition_var_s { pthread_mutex_t mutex; pthread_cond_t cond; ptrdiff_t next_reusable; }; - #define MAX_CONDS 1024 +#define MAX_CONDS 1024 vy_condition_var _conds[MAX_CONDS]; static ptrdiff_t _first_reusable = MAX_CONDS; static ptrdiff_t _next = 0; @@ -68,23 +68,21 @@ VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var) { #elif defined(_WIN32) #define WIN32_LEAN_AND_MEAN - #include +#include struct vy_condition_var_s { CRITICAL_SECTION critical_section; CONDITION_VARIABLE cond; ptrdiff_t next_reusable; }; - #define MAX_CONDS 1024 +#define MAX_CONDS 1024 vy_condition_var _conds[MAX_CONDS]; static ptrdiff_t _first_reusable = MAX_CONDS; static ptrdiff_t _next = 0; static HANDLE _guard; static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT; -static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, - PVOID parameter, - PVOID *context) { +static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) { VY_UNUSED(initOnce); VY_UNUSED(parameter); VY_UNUSED(context); diff --git a/src/runtime/threading_mutex.c b/src/runtime/threading_mutex.c index 2619064..f88311d 100644 --- a/src/runtime/threading_mutex.c +++ b/src/runtime/threading_mutex.c @@ -2,24 +2,22 @@ #include "threading.h" #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include +#define WIN32_LEAN_AND_MEAN +#include struct vy_mutex_s { HANDLE handle; ptrdiff_t next_reusable; }; - #define MAX_MUTEX 1024 +#define MAX_MUTEX 1024 static vy_mutex _mutex[MAX_MUTEX]; static ptrdiff_t _first_reusable = MAX_MUTEX; static ptrdiff_t _next = 0; static HANDLE _guard; static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT; -static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, - PVOID parameter, - PVOID *context) { +static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) { VY_UNUSED(initOnce); VY_UNUSED(parameter); VY_UNUSED(context); @@ -28,7 +26,6 @@ static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, return _guard != NULL; } - VY_DLLEXPORT vy_mutex *vyCreateMutex(void) { if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) { vyReportError("core", "Failed to initialize the guard mutex."); @@ -74,13 +71,13 @@ VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) { #elif defined(__linux__) - #include +#include struct vy_mutex_s { pthread_mutex_t handle; ptrdiff_t next_reusable; }; - #define MAX_MUTEX 1024 +#define MAX_MUTEX 1024 static vy_mutex _mutex[MAX_MUTEX]; static ptrdiff_t _first_reusable = MAX_MUTEX; static ptrdiff_t _next = 0; diff --git a/src/runtime/threading_thread.c b/src/runtime/threading_thread.c index d615c8c..ed69f65 100644 --- a/src/runtime/threading_thread.c +++ b/src/runtime/threading_thread.c @@ -17,7 +17,7 @@ struct vy_thread_s { bool needs_join; }; - #define MAX_THREADS 256 +#define MAX_THREADS 256 static vy_thread _threads[MAX_THREADS]; static ptrdiff_t _first_reusable = MAX_THREADS; static ptrdiff_t _next = 0; @@ -25,9 +25,7 @@ static ptrdiff_t _next = 0; static HANDLE _guard; static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT; -static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, - PVOID parameter, - PVOID *context) { +static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) { VY_UNUSED(initOnce); VY_UNUSED(parameter); VY_UNUSED(context); @@ -44,7 +42,7 @@ static DWORD WINAPI win32ThreadWrapper(LPVOID arg) { return 0; } -VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) { +VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param, const char *name) { if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) { vyReportError("core", "Failed to initialize the guard mutex."); @@ -70,13 +68,18 @@ VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) { ++_next; } if (thrd) { - thrd->entry = entry; - thrd->param = param; + thrd->entry = entry; + thrd->param = param; thrd->handle = CreateThread(NULL, 0, win32ThreadWrapper, (LPVOID)thrd, 0, NULL); if (thrd->handle == NULL) { vyLog("core", "Thread creation failed"); thrd = NULL; } + + WCHAR wname[64]; + if (thrd && name && vyUTF8ToWStr(name, wname, sizeof(wname)) == VY_SUCCESS) + SetThreadDescription(thrd->handle, wname); + } else { vyReportError("core", "Ran out of thread objects"); } @@ -99,10 +102,17 @@ VY_DLLEXPORT void vyJoinThread(vy_thread *thread) { ReleaseMutex(_guard); } +VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void) { + SYSTEM_INFO info; + GetSystemInfo(&info); + return (unsigned int)info.dwNumberOfProcessors; +} #elif defined(__linux__) - #include +#define _GNU_SOURCE +#include +#include struct vy_thread_s { pthread_t handle; @@ -114,7 +124,7 @@ struct vy_thread_s { bool needs_join; }; - #define MAX_THREADS 256 +#define MAX_THREADS 256 static vy_thread _threads[MAX_THREADS]; static ptrdiff_t _first_reusable = MAX_THREADS; static ptrdiff_t _next = 0; @@ -129,7 +139,7 @@ static void *linuxThreadWrapper(void *arg) { return NULL; } -VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) { +VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param, const char *name) { vy_thread *thrd = NULL; pthread_mutex_lock(&_guard); if (_first_reusable < MAX_THREADS) { @@ -147,11 +157,12 @@ VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) { if (thrd) { thrd->entry = entry; thrd->param = param; - if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) != - 0) { + if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) != 0) { vyLog("core", "Thread creation failed"); thrd = NULL; } + if (thrd && name) + pthread_setname_np(thrd->handle, name); } else { vyReportError("core", "Ran out of thread objects"); } @@ -169,4 +180,9 @@ VY_DLLEXPORT void vyJoinThread(vy_thread *thread) { pthread_mutex_unlock(&_guard); } +VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void) { + int n = (int)sysconf(_SC_NPROCESSORS_ONLN); + return (n > 0) ? (unsigned int)n : (unsigned int)sysconf(_SC_NPROCESSORS_CONF); +} + #endif diff --git a/src/tests/rttest.c b/src/tests/rttest.c new file mode 100644 index 0000000..d22b88d --- /dev/null +++ b/src/tests/rttest.c @@ -0,0 +1,73 @@ +#include + +#include "runtime/runtime.h" + +/* Check basic relative pointer behaviour */ +static vy_result RelPtrTest(void) { + char buf[sizeof(vy_relptr) + sizeof(unsigned int)]; + vy_relptr *ptr = (vy_relptr *)buf; + unsigned int *target = (unsigned int *)&buf[sizeof(vy_relptr)]; + *target = 42; + vySetRelptr(ptr, target); + + void *resolved = vyResolveRelptr(ptr); + if ((uintptr_t)resolved != (uintptr_t)target) + return 1; + + if (*(unsigned int *)resolved != *target) + return 2; + + return VY_SUCCESS; +} + +static vy_result NegRelPtrTest(void) { + char buf[sizeof(unsigned int) + sizeof(vy_relptr)]; + unsigned int *target = (unsigned int *)buf; + vy_relptr *ptr = (vy_relptr *)&buf[sizeof(unsigned int)]; + *target = 42; + vySetRelptr(ptr, target); + + void *resolved = vyResolveRelptr(ptr); + if ((uintptr_t)resolved != (uintptr_t)target) + return 1; + + if (*(unsigned int *)resolved != *target) + return 2; + + return VY_SUCCESS; +} + +/* Scaffolding + * + * Run all the test cases, output if they passed or failed. + */ + +typedef vy_result vy_test_fnc(void); + +typedef struct { + const char *name; + vy_test_fnc *fnc; + +} vy_test_case; + +#define TEST_CASE(fn) { .name = #fn, .fnc = fn, } + +static vy_test_case _test_cases[] = {TEST_CASE(RelPtrTest), TEST_CASE(NegRelPtrTest)}; + +int main() { + int out = 0; + + for (size_t i = 0; i < VY_ARRAY_COUNT(_test_cases); ++i) { + printf("[%s] ... ", _test_cases[i].name); + vy_result res = _test_cases[i].fnc(); + if (res == VY_SUCCESS) { + printf("OK\n"); + } + else { + printf("FAILED (%u)\n", res); + ++out; + } + } + + return out; +} diff --git a/src/tools/assetc/assetc.c b/src/tools/assetc/assetc.c index 007334d..69408cd 100644 --- a/src/tools/assetc/assetc.c +++ b/src/tools/assetc/assetc.c @@ -1,20 +1,93 @@ +#include "assetmeta.h" #include "processing.h" +#include "utils.h" +#include "packages.h" + +#define VY_ASSETC_DONT_DEFINE_OPTIONS_GLOBAL +#include "options.h" #include "runtime/aio.h" #include "runtime/buffer_manager.h" +#include #include +#include extern vy_result vyProcessPipelineFile(vy_file_id file, void *buffer, size_t size, + uint32_t flags, vy_processor_output *output); extern vy_result vyProcessShaderFile(vy_file_id file, void *buffer, size_t size, + uint32_t flags, vy_processor_output *output); +vy_assetc_options g_assetc_options = { + .root_directory = ".", + .optimization = VY_ASSET_OPTIMIZATION_NONE, + .renderer_backend = VY_RENDERER_BACKEND_CODE_VK, +}; + +static bool ParseCommandLineArgs(int argc, char **argv) { + bool have_root_directory = false; + for (int i = 1; i < argc; ++i) { + if (i < argc - 1) { + const char *val = argv[i + 1]; + bool matched = false; + + if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--renderer") == 0) { + if (strcmp(val, "vk") == 0) { + g_assetc_options.renderer_backend = VY_RENDERER_BACKEND_CODE_VK; + } else { + vyReportError("ASSETC", + "Invalid render backend %s. Valid options are: vk", + val); + return false; + } + matched = true; + } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--optimization") == 0) { + if (strcmp(val, "none") == 0) { + g_assetc_options.optimization = VY_ASSET_OPTIMIZATION_NONE; + } else if (strcmp(val, "space") == 0) { + g_assetc_options.optimization = VY_ASSET_OPTIMIZATION_SPACE; + } else if (strcmp(val, "performance") == 0) { + g_assetc_options.optimization = VY_ASSET_OPTIMIZATION_PERFORMANCE; + } else { + vyReportError("ASSETC", + "Invalid optimization level %s. Valid options are: none, space, " + "performance", + val); + return false; + } + matched = true; + } else if (argv[i][0] == '-') { + vyReportError("ASSETC", "Invalid command line argument %s", argv[i]); + return false; + } + + if (matched) { + /* Skip the value */ + ++i; + continue; + } + } + + if (!have_root_directory) { + g_assetc_options.root_directory = argv[i]; + } else { + /* Maybe have secondary directories later? */ + vyReportError("ASSETC", + "More than one root directory passed as command line " + "argument."); + return false; + } + } + return true; +} + int main(int argc, char **argv) { /* Setup */ if (vyInitFileTab(4096) != VY_SUCCESS) { @@ -27,22 +100,37 @@ int main(int argc, char **argv) { return 1; } - vyInitUIDMap(); + if (!ParseCommandLineArgs(argc, argv)) { + return 1; + } + + vyInitPackages(); + + if (vyLoadAssetMeta() != VY_SUCCESS) { + return 1; + } + + vySetWorkingDirectory(g_assetc_options.root_directory); if (vyAddAssetProcessor(".pipeline", vyProcessPipelineFile) != VY_SUCCESS) return 1; - if (vyAddAssetProcessor(".shader", vyProcessShaderFile) != VY_SUCCESS) + if (vyAddAssetProcessor(".glsl", vyProcessShaderFile) != VY_SUCCESS) return 1; if (vyStartProcessing() != VY_SUCCESS) { return 1; } - vyAddFileToProcessingQueue(vyAddFile("shader\\cell.pipeline")); - while (1) - _sleep(10); + vyAddFileToProcessingQueue(vyAddFile("shader\\cell.pipeline"), 0); + + vyWaitUntilProcessingIsFinished(); vyStopProcessing(); + + vySaveAssetMeta(); + + vyShutdownBufferManager(); + vyShutdownAIO(); vyShutdownFileTab(); return 0; diff --git a/src/tools/assetc/assetmeta.c b/src/tools/assetc/assetmeta.c new file mode 100644 index 0000000..39eae69 --- /dev/null +++ b/src/tools/assetc/assetmeta.c @@ -0,0 +1,219 @@ +#include "assetmeta.h" +#include "utils.h" + +#include "runtime/threading.h" +#include "runtime/aio.h" +#include "runtime/buffer_manager.h" + +#include +#include + +#define MAP_SIZE 2048 +typedef struct { + vy_file_id fids[MAP_SIZE]; + vy_uid uids[MAP_SIZE]; + vy_assetmeta meta[MAP_SIZE]; + + unsigned int used_slots; +} vy_uid_map; + +#pragma pack(push, 1) +typedef struct { + XXH64_canonical_t checksum; + uint32_t num_entries; + uint32_t _reserved; +} vy_assetmeta_header; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + vy_uid uid; + vy_file_id source_file; + vy_assetmeta meta; +} vy_assetmeta_entry; +#pragma pack(pop) + +static vy_uid_map _map; +static vy_mutex *_guard; + +vy_result vyLoadAssetMeta(void) { + _guard = vyCreateMutex(); + + /* Load the meta file */ + size_t fsz = vyGetFileSize("meta.bin"); + if (fsz == 0) { + vyLog("ASSETC", "Metadata file 'meta.bin' not found. All assets will be processed."); + return VY_SUCCESS; + } + + void *buffer = vyAllocBuffer(fsz); + if (!buffer) { + vyReportError("ASSETC", "Failed to allocate buffer for holding asset metadata."); + return 1; + } + + vy_load_batch load; + load.loads[0].dest = buffer; + load.loads[0].file = vyAddFile("meta.bin"); + load.loads[0].num_bytes = fsz; + load.loads[0].offset = 0; + load.num_loads = 1; + + vy_aio_handle handle; + if (vySubmitLoadBatch(&load, &handle) != VY_SUCCESS) { + vyReportError("ASSETC", "Failed to submit load of 'meta.bin'."); + vyReleaseBuffer(buffer, fsz); + return 1; + } + if (vyWaitForAIOCompletion(handle) != VY_AIO_STATE_FINISHED) { + vyReportError("ASSETC", "Failed to load 'meta.bin'."); + vyReleaseBuffer(buffer, fsz); + vyReleaseAIO(handle); + return 1; + } + vyReleaseAIO(handle); + + const vy_assetmeta_header *header = buffer; + const vy_assetmeta_entry *entries = (vy_assetmeta_entry *)(header + 1); + + if ((sizeof(vy_assetmeta_entry) * header->num_entries + sizeof(*header)) > fsz) { + vyReportError("ASSETC", "Header of 'meta.bin' is corrupted: Mismatched num_entries and filesize."); + vyReleaseBuffer(buffer, fsz); + return 1; + } + XXH64_hash_t hash = XXH3_64bits(entries, sizeof(vy_assetmeta_entry) * header->num_entries); + XXH64_hash_t header_hash = XXH64_hashFromCanonical(&header->checksum); + if (hash != header_hash) { + vyReportError("ASSETC", + "Metadata file 'meta.bin' is corrupted: Wrong checksum."); + vyReleaseBuffer(buffer, fsz); + return 1; + } + + for (uint32_t i = 0; i < header->num_entries; ++i) { + /* Load here to avoid unaligned pointer */ + vy_assetmeta meta = entries[i].meta; + vyAddUIDMapping(entries[i].source_file, entries[i].uid, &meta); + } + + vyReleaseBuffer(buffer, fsz); + return 0; +} + +vy_result vySaveAssetMeta(void) { + vy_assetmeta_header header = {0}; + + /* Count number of entries */ + for (size_t i = 0; i < MAP_SIZE; ++i) { + if (_map.fids[i] != VY_INVALID_FILE_ID) + header.num_entries += 1; + } + + vy_assetmeta_entry *entries = vyAllocBuffer(sizeof(vy_assetmeta_entry) * header.num_entries); + if (!entries) + return VY_UNKNOWN_ERROR; + + /* Store entries */ + size_t j = 0; + for (size_t i = 0; i < MAP_SIZE; ++i) { + if (_map.fids[i] != VY_INVALID_FILE_ID) { + entries[j].source_file = _map.fids[i]; + entries[j].uid = _map.uids[i]; + entries[j].meta = _map.meta[i]; + ++j; + } + } + + XXH64_hash_t hash = XXH3_64bits(entries, sizeof(vy_assetmeta_entry) * header.num_entries); + XXH64_canonicalFromHash(&header.checksum, hash); + header._reserved = 0; + + FILE *f = fopen("meta.bin", "wb"); + if (!f) { + vyReportError("ASSETC", "Failed to open 'meta.bin'"); + vyReleaseBuffer(entries, sizeof(vy_assetmeta_entry) * header.num_entries); + return VY_UNKNOWN_ERROR; + } + if (fwrite(&header, sizeof(header), 1, f) != 1) { + vyReportError("ASSETC", "Failed to write 'meta.bin'"); + vyReleaseBuffer(entries, sizeof(vy_assetmeta_entry) * header.num_entries); + return VY_UNKNOWN_ERROR; + } + if (fwrite(entries, sizeof(vy_assetmeta_entry), header.num_entries, f) != header.num_entries) { + vyReportError("ASSETC", "Failed to write 'meta.bin'"); + vyReleaseBuffer(entries, sizeof(vy_assetmeta_entry) * header.num_entries); + return VY_UNKNOWN_ERROR; + } + + fclose(f); + + return VY_SUCCESS; +} + +vy_uid vyLookupUID(vy_file_id fid) { + vyLockMutex(_guard); + unsigned int i = 0; + vy_uid result = VY_INVALID_UID; + while (i < MAP_SIZE) { + unsigned int slot = (fid + i) % MAP_SIZE; + if (_map.fids[slot] == fid) { + result = _map.uids[slot]; + break; + } else if (_map.fids[slot] == VY_INVALID_FILE_ID) { + break; + } + ++i; + } + vyUnlockMutex(_guard); + return result; +} + +bool vyGetAssetMeta(vy_file_id source_file, vy_assetmeta *meta) { + vyLockMutex(_guard); + unsigned int i = 0; + bool result = false; + while (i < MAP_SIZE) { + unsigned int slot = (source_file + i) % MAP_SIZE; + if (_map.fids[slot] == source_file) { + *meta = _map.meta[slot]; + result = true; + break; + } else if (_map.fids[slot] == VY_INVALID_FILE_ID) { + break; + } + ++i; + } + vyUnlockMutex(_guard); + return result; +} + +void vyAddUIDMapping(vy_file_id fid, vy_uid uid, const vy_assetmeta *meta) { + vyLockMutex(_guard); + float fill_rate = (float)_map.used_slots / MAP_SIZE; + if (fill_rate >= .5f) { + vyLog("ASSETC", "UID map is above 50% filled."); + } + unsigned int i = 0; + while (i < MAP_SIZE) { + unsigned int slot = (fid + i) % MAP_SIZE; + if (_map.fids[slot] == VY_INVALID_FILE_ID) { + _map.fids[slot] = fid; + _map.uids[slot] = uid; + if (meta) { + _map.meta[slot] = *meta; + } else { + _map.meta[slot].last_changed = 0; + _map.meta[slot].compiled_ts = 0; + } + ++_map.used_slots; + break; + } else if (_map.fids[slot] == fid) { + break; + } + ++i; + } + if (i == MAP_SIZE) { + vyReportError("ASSETC", "Failed to insert entry into UID map."); + } + vyUnlockMutex(_guard); +} \ No newline at end of file diff --git a/src/tools/assetc/assetmeta.h b/src/tools/assetc/assetmeta.h new file mode 100644 index 0000000..60514e0 --- /dev/null +++ b/src/tools/assetc/assetmeta.h @@ -0,0 +1,27 @@ +#ifndef VY_ASSETC_ASSETMETA_H +#define VY_ASSETC_ASSETMETA_H + +#include "runtime/file_tab.h" +#include "runtime/assets.h" + +#include + +/* Metadata for assets used only by assetc */ +typedef struct { + uint64_t last_changed; + uint64_t compiled_ts; +} vy_assetmeta; + +vy_result vyLoadAssetMeta(void); + +vy_result vySaveAssetMeta(void); + +/* The UID map associates processed files with generated asset uids. */ +void vyAddUIDMapping(vy_file_id fid, vy_uid uid, const vy_assetmeta *meta); + +/* Returns true if the asset is found. false otherwise */ +bool vyGetAssetMeta(vy_file_id source_file, vy_assetmeta *meta); + +vy_uid vyLookupUID(vy_file_id fid); + +#endif diff --git a/src/tools/assetc/compiled.h b/src/tools/assetc/compiled.h new file mode 100644 index 0000000..f2e5e82 --- /dev/null +++ b/src/tools/assetc/compiled.h @@ -0,0 +1,13 @@ +#ifndef VY_ASSETC_COMPILED_H +#define VY_ASSETC_COMPILED_H + +#include "runtime/runtime.h" +#include "runtime/assets.h" + +vy_uid vyGetUID(const char *name); + +void vyStoreOutput(vy_uid uid, const void *data, size_t size); + +vy_result vyWriteCompiledFiles(void); + +#endif diff --git a/src/tools/assetc/description_parser.c b/src/tools/assetc/description_parser.c new file mode 100644 index 0000000..4219b62 --- /dev/null +++ b/src/tools/assetc/description_parser.c @@ -0,0 +1,209 @@ +#include "description_parser.h" + +#include "runtime/runtime.h" + +#include +#include +#include + +static bool IsAllowedChar(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '.') || (c == '_') || (c == '/'); +} + +static bool IsWhitespace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +static void SkipWhitespace(vy_parse_state *state) { + while (state->at < state->length && IsWhitespace(state->text[state->at])) { + if (state->text[state->at] == '\n') + ++state->line; + ++state->at; + } +} + +static bool ParseAttribute(vy_parse_state *state, vy_text_span *_name) { + vy_text_span name; + name.start = &state->text[state->at]; + name.length = 0; + while (state->at < state->length && !IsWhitespace(state->text[state->at])) { + if (IsAllowedChar(state->text[state->at])) { + ++state->at; + ++name.length; + } else { + vyReportError("GFX", + "%s:%d Unexpected character %c", + state->file, + state->line, + state->text[state->at]); + return false; + } + } + *_name = name; + return true; +} + +static bool ParseValue(vy_parse_state *state, vy_text_span *_value) { + vy_text_span value; + value.start = &state->text[state->at]; + value.length = 0; + while (state->at < state->length && !IsWhitespace(state->text[state->at]) && + state->text[state->at] != ';') { + if (IsAllowedChar(state->text[state->at])) { + ++state->at; + ++value.length; + } else { + vyReportError("GFX", + "%s:%d Unexpected character %c", + state->file, + state->line, + state->text[state->at]); + return false; + } + } + *_value = value; + return true; +} + +static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index); + +static bool ParseStmt(vy_parse_state *state, unsigned int *stmt_index) { + vy_parsed_stmt stmt; + stmt.next = UINT_MAX; /* end of list */ + + SkipWhitespace(state); + if (!ParseAttribute(state, &stmt.attribute)) + return false; + SkipWhitespace(state); + + if (state->at == state->length) { + vyReportError("GFX", "%s:%d Expected either a value or '{'", state->file, state->line); + return false; + } + + if (state->text[state->at] == '{') { + /* Consume '{' */ + ++state->at; + + stmt.form = VY_STMT_FORM_LIST; + if (!ParseStmtList(state, &stmt.list_index)) + return false; + + /* Consume '}' */ + if (state->at < state->length && state->text[state->at] == '}') + ++state->at; + } else { + stmt.form = VY_STMT_FORM_VALUE; + if (!ParseValue(state, &stmt.value)) + return false; + + /* Consume ';' */ + if (state->at < state->length && state->text[state->at] == ';') + ++state->at; + } + + SkipWhitespace(state); + + /* Add statement to array */ + if (state->statement_count == state->statement_capacity) { + unsigned int cap = (state->statement_capacity > 0) ? state->statement_capacity * 2 : 64; + vy_parsed_stmt *temp = realloc(state->statements, sizeof(vy_parsed_stmt) * cap); + if (!temp) { + vyReportError("GFX", "While parsing %s: Out of memory\n", state->file); + return false; + } + state->statements = temp; + state->statement_capacity = cap; + } + state->statements[state->statement_count] = stmt; + *stmt_index = state->statement_count++; + + return true; +} + +static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index) { + vy_parsed_stmt_list list; + list.first = UINT_MAX; + list.count = 0; + + unsigned int last = UINT_MAX; + + while (state->at < state->length && state->text[state->at] != '}') { + unsigned int stmt; + if (!ParseStmt(state, &stmt)) + return false; + + if (last != UINT_MAX) + state->statements[last].next = stmt; + else + list.first = stmt; + last = stmt; + + ++list.count; + } + + /* Add list to array */ + if (state->statement_list_count == state->statement_list_capacity) { + unsigned int cap = + (state->statement_list_capacity > 0) ? state->statement_list_capacity * 2 : 64; + vy_parsed_stmt_list *temp = + realloc(state->statement_lists, sizeof(vy_parsed_stmt_list) * cap); + if (!temp) { + vyReportError("GFX", "While parsing %s: Out of memory\n", state->file); + return false; + } + state->statement_lists = temp; + state->statement_list_capacity = cap; + } + state->statement_lists[state->statement_list_count] = list; + *list_index = state->statement_list_count++; + return true; +} + +const vy_parsed_stmt * vyFindStatement(const vy_parse_state *state, unsigned int list_index, const char *attribute) { + if (list_index >= state->statement_list_count) + return NULL; + const vy_parsed_stmt_list *list = &state->statement_lists[list_index]; + unsigned int stmt_index = list->first; + for (unsigned int i = 0; i < list->count; ++i) { + const vy_parsed_stmt *stmt = &state->statements[stmt_index]; + if (vyCompareSpanToString(stmt->attribute, attribute) == 0) + return stmt; + stmt_index = stmt->next; + } + return NULL; +} + + +vy_result vyParseDescription(const char *text, + size_t length, + const char *file_path, + unsigned int *_root_list, + vy_parse_state *_state) { + + vy_parse_state state = {.text = text, + .at = 0, + .length = length, + .line = 1, + .file = file_path, + .statements = NULL, + .statement_lists = NULL, + .statement_capacity = 0, + .statement_list_capacity = 0}; + + unsigned int root_list = 0; + if (!ParseStmtList(&state, &root_list)) { + return 1; + } + *_root_list = root_list; + *_state = state; + + return VY_SUCCESS; +} + + +void vyReleaseParseState(vy_parse_state *state) { + free(state->statements); + free(state->statement_lists); +} \ No newline at end of file diff --git a/src/tools/assetc/description_parser.h b/src/tools/assetc/description_parser.h new file mode 100644 index 0000000..b55821b --- /dev/null +++ b/src/tools/assetc/description_parser.h @@ -0,0 +1,53 @@ +#ifndef VY_ASSETC_DESCRIPTION_PARSER_H +#define VY_ASSETC_DESCRIPTION_PARSER_H + +#include "runtime/runtime.h" + +typedef enum { + VY_STMT_FORM_VALUE, + VY_STMT_FORM_LIST, +} vy_stmt_form; + +typedef struct { + unsigned int first; + unsigned int count; +} vy_parsed_stmt_list; + +typedef struct { + vy_stmt_form form; + vy_text_span attribute; + union { + vy_text_span value; + unsigned int list_index; + }; + /* For lists */ + unsigned int next; +} vy_parsed_stmt; + +typedef struct { + const char *file; + const char *text; + size_t at; + size_t length; + int line; + + vy_parsed_stmt *statements; + unsigned int statement_count; + unsigned int statement_capacity; + + vy_parsed_stmt_list *statement_lists; + unsigned int statement_list_count; + unsigned int statement_list_capacity; +} vy_parse_state; + +vy_result vyParseDescription(const char *text, + size_t length, + const char *file_path, + unsigned int *root_list, + vy_parse_state *state); + +const vy_parsed_stmt *vyFindStatement(const vy_parse_state *state, unsigned int list_index, const char *attribute); + +void vyReleaseParseState(vy_parse_state *state); + +#endif diff --git a/src/tools/assetc/discovery.c b/src/tools/assetc/discovery.c new file mode 100644 index 0000000..637a281 --- /dev/null +++ b/src/tools/assetc/discovery.c @@ -0,0 +1,5 @@ + +void DiscoverAssets(void) { + + +} \ No newline at end of file diff --git a/src/tools/assetc/options.h b/src/tools/assetc/options.h new file mode 100644 index 0000000..36f3ff2 --- /dev/null +++ b/src/tools/assetc/options.h @@ -0,0 +1,28 @@ +#ifndef VY_ASSETC_OPTIONS_H +#define VY_ASSETC_OPTIONS_H + +#include "runtime/assets.h" + +typedef enum { + /* No optimization */ + VY_ASSET_OPTIMIZATION_NONE, + + /* Create small assets */ + VY_ASSET_OPTIMIZATION_SPACE, + + /* Create assets for fast execution */ + VY_ASSET_OPTIMIZATION_PERFORMANCE, +} vy_asset_optimization_level; + +/* Options parsed from command line arguments */ +typedef struct { + const char *root_directory; + vy_renderer_backend_code renderer_backend; + vy_asset_optimization_level optimization; +} vy_assetc_options; + +#ifndef VY_ASSETC_DONT_DEFINE_OPTIONS_GLOBAL +extern vy_assetc_options g_assetc_options; +#endif + +#endif diff --git a/src/tools/assetc/packages.c b/src/tools/assetc/packages.c new file mode 100644 index 0000000..99a67bc --- /dev/null +++ b/src/tools/assetc/packages.c @@ -0,0 +1,90 @@ +#include "packages.h" + +#include "runtime/threading.h" +#include "runtime/assets.h" + +#include +#include +#include + +typedef struct { + vy_uid uid; + void *buffer; + size_t buffer_size; +} vy_package_entry; + +typedef struct { + char *name; + unsigned int num_entries; + unsigned int entry_capacity; + vy_package_entry *entries; +} vy_package; + +#define MAX_PACKAGES 1024 + +vy_package _packages[MAX_PACKAGES]; +unsigned int _package_count = 0; + +vy_mutex *_guard; + +unsigned int vyAddPackageFile(vy_text_span name) { + vyLockMutex(_guard); + + for (unsigned int i = 0; i < _package_count; ++i) { + if (vyCompareSpanToString(name, _packages[i].name) == 0) { + vyUnlockMutex(_guard); + return i; + } + } + + /* Create a new package */ + _packages[_package_count].name = malloc(name.length + 1); + if (!_packages[_package_count].name) { + vyReportError("ASSETC", + "Failed to allocate storage for new package %*.s", + name.length, + name.start); + vyUnlockMutex(_guard); + return UINT_MAX; + } + memcpy(_packages[_package_count].name, name.start, name.length); + _packages[_package_count].name[name.length] = '\0'; + + unsigned int index = _package_count++; + + vyUnlockMutex(_guard); + return index; +} + +void vyInitPackages(void) { + _guard = vyCreateMutex(); + /* Create the default package (0) */ + vyAddPackageFile((vy_text_span){.start = "default.pkg", .length = 12}); +} + +void vyAddAssetToPackage(unsigned int package, vy_uid uid, void *buffer, size_t size) { + vyLockMutex(_guard); + if (package >= _package_count) { + vyReportError("ASSETC", "Trying to add an asset to a non-existing package."); + vyUnlockMutex(_guard); + return; + } + + vy_package *pkg = &_packages[package]; + if (pkg->num_entries == pkg->entry_capacity) { + unsigned int new_cap = (pkg->entry_capacity > 0) ? 2 * pkg->entry_capacity : 256; + vy_package_entry *n = realloc(pkg->entries, new_cap * sizeof(vy_package_entry)); + if (!n) { + vyReportError("ASSETC", "Failed to grow storage for package %u.", package); + return; + } + pkg->entry_capacity = new_cap; + pkg->entries = n; + } + pkg->entries[pkg->num_entries].buffer = buffer; + pkg->entries[pkg->num_entries].buffer_size = size; + pkg->entries[pkg->num_entries].uid = uid; + ++pkg->num_entries; + + vyUnlockMutex(_guard); +} \ No newline at end of file diff --git a/src/tools/assetc/packages.h b/src/tools/assetc/packages.h new file mode 100644 index 0000000..fa35565 --- /dev/null +++ b/src/tools/assetc/packages.h @@ -0,0 +1,13 @@ +#ifndef VY_ASSETC_PACKAGES_H +#define VY_ASSETC_PACKAGES_H + +#include "runtime/runtime.h" +#include "runtime/assets.h" + +void vyInitPackages(void); + +unsigned int vyAddPackageFile(vy_text_span name); + +void vyAddAssetToPackage(unsigned int package, vy_uid uid, void *buffer, size_t size); + +#endif diff --git a/src/tools/assetc/pipeline_processor.c b/src/tools/assetc/pipeline_processor.c index 6664797..c2606c5 100644 --- a/src/tools/assetc/pipeline_processor.c +++ b/src/tools/assetc/pipeline_processor.c @@ -10,213 +10,36 @@ #include "runtime/gfx.h" #include "runtime/handles.h" #include "runtime/runtime.h" +#include "runtime/buffer_manager.h" +#include "runtime/renderer_api.h" +#include "assetmeta.h" +#include "description_parser.h" +#include "options.h" #include "processing.h" - -typedef enum { - VY_STMT_FORM_VALUE, - VY_STMT_FORM_LIST, -} vy_stmt_form; +#include "utils.h" +#include "processing_flags.h" typedef struct { - unsigned int first; - unsigned int count; -} vy_parsed_stmt_list; + vy_attribute_binding *uniform_bindings; + vy_attribute_binding *storage_bindings; + vy_attribute_binding *texture_bindings; -typedef struct { - vy_stmt_form form; - vy_text_span attribute; - union { - vy_text_span value; - unsigned int list_index; - }; - /* For lists */ - unsigned int next; -} vy_parsed_stmt; + vy_uid vertex_shader; + vy_uid fragment_shader; + vy_uid compute_shader; -typedef struct { - const char *file; - const char *text; - size_t at; - size_t length; - int line; + /* TODO Fixed function settings */ - vy_parsed_stmt *statements; - unsigned int statement_count; - unsigned int statement_capacity; + /* Sampler settings */ - vy_parsed_stmt_list *statement_lists; - unsigned int statement_list_count; - unsigned int statement_list_capacity; -} vy_parse_state; + uint16_t uniform_binding_count; + uint16_t storage_binding_count; + uint16_t texture_binding_count; +} vy_parsed_pipeline_data; -static bool IsAllowedChar(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || (c == '.') || (c == '_') || (c == '/'); -} - -static bool IsWhitespace(char c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r'; -} - -static void SkipWhitespace(vy_parse_state *state) { - while (state->at < state->length && IsWhitespace(state->text[state->at])) { - if (state->text[state->at] == '\n') - ++state->line; - ++state->at; - } -} - -static bool ParseAttribute(vy_parse_state *state, vy_text_span *_name) { - vy_text_span name; - name.start = &state->text[state->at]; - name.length = 0; - while (state->at < state->length && !IsWhitespace(state->text[state->at])) { - if (IsAllowedChar(state->text[state->at])) { - ++state->at; - ++name.length; - } else { - vyReportError("GFX", - "%s:%d Unexpected character %c", - state->file, - state->line, - state->text[state->at]); - return false; - } - } - *_name = name; - return true; -} - -static bool ParseValue(vy_parse_state *state, vy_text_span *_value) { - vy_text_span value; - value.start = &state->text[state->at]; - value.length = 0; - while (state->at < state->length && !IsWhitespace(state->text[state->at]) && - state->text[state->at] != ';') { - if (IsAllowedChar(state->text[state->at])) { - ++state->at; - ++value.length; - } else { - vyReportError("GFX", - "%s:%d Unexpected character %c", - state->file, - state->line, - state->text[state->at]); - return false; - } - } - *_value = value; - return true; -} - -static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index); - -static bool ParseStmt(vy_parse_state *state, unsigned int *stmt_index) { - vy_parsed_stmt stmt; - stmt.next = UINT_MAX; /* end of list */ - - SkipWhitespace(state); - if (!ParseAttribute(state, &stmt.attribute)) - return false; - SkipWhitespace(state); - - if (state->at == state->length) { - vyReportError("GFX", - "%s:%d Expected either a value or '{'", - state->file, - state->line); - return false; - } - - if (state->text[state->at] == '{') { - /* Consume '{' */ - ++state->at; - - stmt.form = VY_STMT_FORM_LIST; - if (!ParseStmtList(state, &stmt.list_index)) - return false; - - /* Consume '}' */ - if (state->at < state->length && state->text[state->at] == '}') - ++state->at; - } else { - stmt.form = VY_STMT_FORM_VALUE; - if (!ParseValue(state, &stmt.value)) - return false; - - /* Consume ';' */ - if (state->at < state->length && state->text[state->at] == ';') - ++state->at; - } - - SkipWhitespace(state); - - /* Add statement to array */ - if (state->statement_count == state->statement_capacity) { - unsigned int cap = (state->statement_capacity > 0) - ? state->statement_capacity * 2 - : 64; - vy_parsed_stmt *temp = - realloc(state->statements, sizeof(vy_parsed_stmt) * cap); - if (!temp) { - vyReportError("GFX", - "While parsing %s: Out of memory\n", - state->file); - return false; - } - state->statements = temp; - state->statement_capacity = cap; - } - state->statements[state->statement_count] = stmt; - *stmt_index = state->statement_count++; - - return true; -} - -static bool ParseStmtList(vy_parse_state *state, unsigned int *list_index) { - vy_parsed_stmt_list list; - list.first = state->statement_count; - list.count = 0; - - unsigned int last = UINT_MAX; - - while (state->at < state->length && state->text[state->at] != '}') { - unsigned int stmt; - if (!ParseStmt(state, &stmt)) - return false; - - if (last != UINT_MAX) - state->statements[last].next = stmt; - last = stmt; - - ++list.count; - } - - /* Add list to array */ - if (state->statement_list_count == state->statement_list_capacity) { - unsigned int cap = (state->statement_list_capacity > 0) - ? state->statement_list_capacity * 2 - : 64; - vy_parsed_stmt_list *temp = - realloc(state->statement_lists, sizeof(vy_parsed_stmt_list) * cap); - if (!temp) { - vyReportError("GFX", - "While parsing %s: Out of memory\n", - state->file); - return false; - } - state->statement_lists = temp; - state->statement_list_capacity = cap; - } - state->statement_lists[state->statement_list_count] = list; - *list_index = state->statement_list_count++; - return true; -} - -static void DbgPrintShaderFile(const vy_parse_state *state, - unsigned int list_index, - unsigned int indent) { +static void +DbgPrintShaderFile(const vy_parse_state *state, unsigned int list_index, unsigned int indent) { assert(list_index < state->statement_list_count); const vy_parsed_stmt_list *list = &state->statement_lists[list_index]; @@ -238,33 +61,6 @@ static void DbgPrintShaderFile(const vy_parse_state *state, assert(stmt_index == UINT_MAX || stmt_index == 0); } -static bool CompareSpanToString(vy_text_span span, const char *cmp) { - size_t cmp_len = strlen(cmp); - if (cmp_len != (size_t)span.length) - return false; - for (size_t i = 0; i < cmp_len; ++i) { - if (span.start[i] != cmp[i]) - return false; - } - return true; -} - -static const vy_parsed_stmt *FindStatement(const vy_parse_state *state, - unsigned int list_index, - const char *attribute) { - if (list_index >= state->statement_list_count) - return NULL; - const vy_parsed_stmt_list *list = &state->statement_lists[list_index]; - unsigned int stmt_index = list->first; - for (unsigned int i = 0; i < list->count; ++i) { - const vy_parsed_stmt *stmt = &state->statements[stmt_index]; - if (CompareSpanToString(stmt->attribute, attribute)) - return stmt; - stmt_index = stmt->next; - } - return NULL; -} - static bool ParseBindingIndex(vy_text_span span, unsigned int *index) { if (span.length == 0) return false; @@ -276,8 +72,7 @@ static bool ParseBindingIndex(vy_text_span span, unsigned int *index) { unsigned int digit = (unsigned int)(span.start[at] - '0'); n += digit * exp; } else { - vyReportError("GFX", - "Unexpected non-digit character in binding index"); + vyReportError("GFX", "Unexpected non-digit character in binding index"); return false; } --at; @@ -288,15 +83,12 @@ static bool ParseBindingIndex(vy_text_span span, unsigned int *index) { } static vy_attribute_value ParseBindingValue(vy_text_span span) { - if (CompareSpanToString(span, "MATERIAL_ALBEDO")) { + if (vyCompareSpanToString(span, "MATERIAL_ALBEDO") == 0) { return VY_ATTRIBUTE_VALUE_MATERIAL_ALBEDO; - } else if (CompareSpanToString(span, "MATERIAL_NORMAL")) { + } else if (vyCompareSpanToString(span, "MATERIAL_NORMAL") == 0) { return VY_ATTRIBUTE_VALUE_MATERIAL_NORMAL; } - vyReportError("GFX", - "Unsupported binding value %*.s", - span.length, - span.start); + vyReportError("GFX", "Unsupported binding value %*.s", span.length, span.start); return VY_ATTRIBUTE_VALUE_UNDEFINED; } @@ -305,8 +97,8 @@ static bool ParseBindings(vy_parse_state *state, const char *name, const char *file_path, vy_attribute_binding **p_bindings, - unsigned int *p_binding_count) { - const vy_parsed_stmt *bindings = FindStatement(state, root_list, name); + uint16_t *p_binding_count) { + const vy_parsed_stmt *bindings = vyFindStatement(state, root_list, name); if (bindings) { if (bindings->form != VY_STMT_FORM_LIST) { vyReportError("GFX", @@ -316,10 +108,9 @@ static bool ParseBindings(vy_parse_state *state, file_path); return false; } - const vy_parsed_stmt_list *binding_list = - &state->statement_lists[bindings->list_index]; + const vy_parsed_stmt_list *binding_list = &state->statement_lists[bindings->list_index]; vy_attribute_binding *shader_bindings = - malloc(sizeof(vy_attribute_binding) * binding_list->count); + vyAllocBuffer(sizeof(vy_attribute_binding) * binding_list->count); if (!bindings) { vyReportError("GFX", "Out of memory"); return false; @@ -329,22 +120,23 @@ static bool ParseBindings(vy_parse_state *state, unsigned int stmt_index = binding_list->first; for (unsigned int i = 0; i < binding_list->count; ++i) { const vy_parsed_stmt *stmt = &state->statements[stmt_index]; - if (!ParseBindingIndex(stmt->attribute, - &shader_bindings[i].index)) { - free(shader_bindings); + if (!ParseBindingIndex(stmt->attribute, &shader_bindings[i].index)) { + vyReleaseBuffer(shader_bindings, + sizeof(vy_attribute_binding) * binding_list->count); return false; } shader_bindings[i].value = ParseBindingValue(stmt->value); if (shader_bindings[i].value == VY_ATTRIBUTE_VALUE_UNDEFINED) { - free(shader_bindings); + vyReleaseBuffer(shader_bindings, + sizeof(vy_attribute_binding) * binding_list->count); return false; } stmt_index = stmt->next; } *p_bindings = shader_bindings; - *p_binding_count = binding_count; + *p_binding_count = (uint16_t)binding_count; return true; } else { *p_bindings = NULL; @@ -353,136 +145,163 @@ static bool ParseBindings(vy_parse_state *state, } } -enum -{ - VY_SHADER_STAGE_BIT_begin, - - VY_SHADER_STAGE_BIT_VERTEX = 1, - VY_SHADER_STAGE_BIT_FRAGMENT = 2, - VY_SHADER_STAGE_BIT_COMPUTE = 3, - - VY_SHADER_STAGE_BIT_count, -}; -#define VY_SSBIT(n) (1 << (n)) - -typedef struct { - vy_attribute_binding *uniform_bindings; - vy_attribute_binding *storage_bindings; - vy_attribute_binding *texture_bindings; - - /* Toggels shader stages on or off */ - uint32_t shader_stage_bitmask; - vy_uid vertex_shader; - vy_uid fragment_shader; - vy_uid compute_shader; - - /* TODO Fixed function settings */ - - /* Sampler settings */ - - unsigned int uniform_binding_count; - unsigned int storage_binding_count; - unsigned int texture_binding_count; -} vy_pipeline_data; - static vy_result ParseShader(vy_parse_state *state, unsigned int root_list, const char *name, const char *file_path, + uint32_t processing_flags, vy_uid *p_shader_uid) { - const vy_parsed_stmt *stmt = FindStatement(state, root_list, name); + const vy_parsed_stmt *stmt = vyFindStatement(state, root_list, name); if (stmt) { - if (stmt->form != VY_STMT_FORM_VALUE) { + if (stmt->form != VY_STMT_FORM_LIST) { vyReportError("GFX", - "Expected a file name as the value of " + "Expected a list as the value of " "\"%s\" in %s", name, file_path); return VY_PROCESSING_FAILED; } - vy_file_id shader_file = vyAddFileFromSpan(stmt->value); - vy_uid uid = vyLookupUID(shader_file); - if (uid == VY_INVALID_UID) { - /* Add the shader file to processing and wait until its done */ - if (vyAddFileToProcessingQueue(shader_file) != VY_SUCCESS) - return VY_PROCESSING_FAILED; - return VY_PROCESSING_TRY_AGAIN; - } + const vy_parsed_stmt_list *shader_list = &state->statement_lists[stmt->list_index]; - *p_shader_uid = uid; + unsigned int stmt_index = shader_list->first; + for (unsigned int i = 0; i < shader_list->count; ++i) { + const vy_parsed_stmt *shader = &state->statements[stmt_index]; + if (shader->form != VY_STMT_FORM_VALUE) { + vyReportError("GFX", + "Expected a list as the value of " + "\"%s.%*s\" in %s", + name, + (int)shader->attribute.length, + shader->attribute.start, + file_path); + return VY_PROCESSING_FAILED; + } + vy_renderer_backend_code backend = VY_INVALID_RENDERER_BACKEND_CODE; + if (vyCompareSpanToString(shader->attribute, "vk") == 0) { + backend = VY_RENDERER_BACKEND_CODE_VK; + } else { + vyReportError("GFX", + "Invalid renderer backend" + "\"%*s\" in %s of file %s", + (int)shader->attribute.length, + shader->attribute.start, + name, + file_path); + return VY_PROCESSING_FAILED; + } + + if (backend == g_assetc_options.renderer_backend) { + vy_file_id shader_file = vyAddFileFromSpan(shader->value); + vy_uid uid = vyLookupUID(shader_file); + if (uid == VY_INVALID_UID) { + /* Add the shader file to processing and wait until its done + */ + if (vyAddFileToProcessingQueue(shader_file, processing_flags) != VY_SUCCESS) + return VY_PROCESSING_FAILED; + return VY_PROCESSING_TRY_AGAIN; + } + *p_shader_uid = uid; + + /* Don't break, because that validates the file. (attributes are checked for invalid + * values). */ + } + stmt_index = shader->next; + } return VY_SUCCESS; } return VY_PROCESSING_FAILED; } -static vy_result ParsePipelineFile(vy_file_id fid, - const char *text, - size_t length, - vy_pipeline_data *pipeline) { +static uint32_t +ParseOptimizationLevel(vy_parse_state *state, unsigned int root_list, const char *file_path) { + uint32_t optimization_level; + switch (g_assetc_options.optimization) { + case VY_ASSET_OPTIMIZATION_PERFORMANCE: + optimization_level = VY_SHADER_FLAG_OPTIMIZE_SPEED; + break; + case VY_ASSET_OPTIMIZATION_SPACE: + optimization_level = VY_SHADER_FLAG_OPTIMIZE_SIZE; + break; + default: + optimization_level = 0; + } + + const vy_parsed_stmt *stmt = vyFindStatement(state, root_list, "optimization"); + + if (stmt) { + if (stmt->form != VY_STMT_FORM_VALUE) { + vyReportError("GFX", + "Expected a simple statement for" + "\"optimization\" in %s", + file_path); + return optimization_level; + } + + if (vyCompareSpanToString(stmt->value, "speed") == 0) { + optimization_level = VY_SHADER_FLAG_OPTIMIZE_SPEED; + } else if (vyCompareSpanToString(stmt->value, "size") == 0) { + optimization_level = VY_SHADER_FLAG_OPTIMIZE_SIZE; + } else if (vyCompareSpanToString(stmt->value, "none") == 0) { + optimization_level = 0; + } else { + vyReportError("GFX", + "Expected one of 'speed', 'size' and 'none' for \"optimization\" in %s", + file_path); + } + } + + return optimization_level; +} + +static vy_result +ParsePipelineFile(vy_file_id fid, const char *text, size_t length, vy_parsed_pipeline_data *pipeline) { /* This is the grammar for pipeline files: * ::= * * ::= ( ( ';' ) | ( '{' '}' ) ) * ::= [:alnum:]* * :: = [:alnum:]* */ const char *file_path = vyGetFilePath(fid); - vy_parse_state state = {.text = text, - .at = 0, - .length = length, - .line = 1, - .file = file_path, - .statements = NULL, - .statement_lists = NULL, - .statement_capacity = 0, - .statement_list_capacity = 0}; - - vy_result result = VY_SUCCESS; - unsigned int root_list = 0; - if (!ParseStmtList(&state, &root_list)) { - result = false; + vy_parse_state state; + unsigned int root_list; + vy_result result = vyParseDescription(text, length, file_path, &root_list, &state); + if (result != VY_SUCCESS) { goto out; } - DbgPrintShaderFile(&state, root_list, 0); + /* We allow the pipeline file to overwrite the optimization level */ + uint32_t optimization = ParseOptimizationLevel(&state, root_list, file_path); /* Process shader stages */ - pipeline->shader_stage_bitmask = 0; - vy_result shader_result; - if ((shader_result = ParseShader(&state, - root_list, - "vertex", - file_path, - &pipeline->vertex_shader)) == VY_SUCCESS) { - - pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_VERTEX; - } else if (shader_result == VY_PROCESSING_TRY_AGAIN) { + if (ParseShader(&state, + root_list, + "vertex", + file_path, + VY_SHADER_FLAG_VERTEX | optimization, + &pipeline->vertex_shader) == VY_PROCESSING_TRY_AGAIN) { result = VY_PROCESSING_TRY_AGAIN; goto out; } - if ((shader_result = ParseShader(&state, - root_list, - "fragment", - file_path, - &pipeline->vertex_shader)) == VY_SUCCESS) { - - pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_FRAGMENT; - } else if (shader_result == VY_PROCESSING_TRY_AGAIN) { + if (ParseShader(&state, + root_list, + "fragment", + file_path, + VY_SHADER_FLAG_FRAGMENT | optimization, + &pipeline->fragment_shader) == VY_PROCESSING_TRY_AGAIN) { result = VY_PROCESSING_TRY_AGAIN; goto out; } - if ((shader_result = ParseShader(&state, - root_list, - "compute", - file_path, - &pipeline->vertex_shader)) == VY_SUCCESS) { - - pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_COMPUTE; - } else if (shader_result == VY_PROCESSING_TRY_AGAIN) { + if (ParseShader(&state, + root_list, + "compute", + file_path, + VY_SHADER_FLAG_COMPUTE | optimization, + &pipeline->compute_shader) == VY_PROCESSING_TRY_AGAIN) { result = VY_PROCESSING_TRY_AGAIN; goto out; } /* Process bindings */ - pipeline->texture_bindings = NULL; + pipeline->texture_bindings = NULL; pipeline->texture_binding_count = 0; pipeline->uniform_bindings = NULL; pipeline->uniform_binding_count = 0; @@ -528,11 +347,68 @@ out: vy_result vyProcessPipelineFile(vy_file_id file, void *buffer, size_t size, + uint32_t flags, vy_processor_output *output) { - vy_pipeline_data tmp; + VY_UNUSED(flags); + vy_parsed_pipeline_data tmp; + memset(&tmp, 0, sizeof(tmp)); + vy_result result = ParsePipelineFile(file, buffer, size, &tmp); if (result == VY_SUCCESS) { - + /* parsed_pipeline_data contains arrays of bindings. + * We need to convert these to a flat buffer that can be written to a file */ + + size_t outbuffer_size = + sizeof(vy_pipeline_info) + + sizeof(vy_attribute_binding) * + (tmp.storage_binding_count + tmp.texture_binding_count + tmp.uniform_binding_count); + void *out_buffer = vyAllocBuffer(outbuffer_size); + if (!buffer) { + return VY_PROCESSING_FAILED; + } + vy_pipeline_info *info = out_buffer; + info->vertex_shader = tmp.vertex_shader; + info->fragment_shader = tmp.fragment_shader; + info->compute_shader = tmp.compute_shader; + info->texture_binding_count = tmp.texture_binding_count; + info->uniform_binding_count = tmp.uniform_binding_count; + info->storage_binding_count = tmp.storage_binding_count; + + vy_attribute_binding *texture_bindings = NULL; + if (tmp.texture_binding_count > 0) { + texture_bindings = (vy_attribute_binding *)(info + 1); + memcpy(texture_bindings, + tmp.texture_bindings, + sizeof(vy_attribute_binding) * tmp.texture_binding_count); + } + vy_attribute_binding *uniform_bindings = NULL; + if (tmp.uniform_binding_count > 0) { + uniform_bindings = texture_bindings + tmp.texture_binding_count; + memcpy(uniform_bindings, + tmp.uniform_bindings, + sizeof(vy_attribute_binding) * tmp.uniform_binding_count); + } + vy_attribute_binding *storage_bindings = NULL; + if (tmp.storage_binding_count > 0) { + storage_bindings = uniform_bindings + tmp.uniform_binding_count; + memcpy(storage_bindings, + tmp.storage_bindings, + sizeof(vy_attribute_binding) * tmp.storage_binding_count); + } + vySetRelptr(&info->texture_bindings, texture_bindings); + vySetRelptr(&info->uniform_bindings, uniform_bindings); + vySetRelptr(&info->storage_bindings, storage_bindings); + + vyReleaseBuffer(tmp.texture_bindings, + sizeof(vy_attribute_binding) * tmp.texture_binding_count); + vyReleaseBuffer(tmp.storage_bindings, + sizeof(vy_attribute_binding) * tmp.storage_binding_count); + vyReleaseBuffer(tmp.uniform_bindings, + sizeof(vy_attribute_binding) * tmp.uniform_binding_count); + + output->data = out_buffer; + output->size = outbuffer_size; + output->asset_uid = vyCalculateUID(vyGetFilePath(file)); } return result; } \ No newline at end of file diff --git a/src/tools/assetc/processing.h b/src/tools/assetc/processing.h index 793a261..1853545 100644 --- a/src/tools/assetc/processing.h +++ b/src/tools/assetc/processing.h @@ -1,8 +1,8 @@ #ifndef VY_ASSETC_PROCESSING_H #define VY_ASSETC_PROCESSING_H -#include "runtime/file_tab.h" #include "runtime/assets.h" +#include "runtime/file_tab.h" enum { VY_PROCESSING_FAILED = VY_SUCCESS + 1, @@ -10,22 +10,35 @@ enum { VY_PROCESSING_TRY_AGAIN, }; +typedef struct { + vy_file_id output_file; + +} vy_asset_options; + typedef struct { vy_uid asset_uid; + + /* Allocated via the vyAllocBuffer API */ + void *data; + size_t size; } vy_processor_output; -typedef vy_result vy_processor_fn(vy_file_id file, void *buffer, size_t size, vy_processor_output *output); +typedef vy_result vy_processor_fn(vy_file_id file, + void *buffer, + size_t size, + uint32_t flags, + vy_processor_output *output); vy_result vyAddAssetProcessor(const char *file_extension, vy_processor_fn fn); -vy_result vyAddFileToProcessingQueue(vy_file_id file); +/* Flags are file type specific */ +vy_result vyAddFileToProcessingQueue(vy_file_id file, uint32_t flags); vy_result vyStartProcessing(void); void vyStopProcessing(void); +vy_uid vyCalculateUID(const char *name); -void vyInitUIDMap(void); -void vyAddUIDMapping(vy_file_id fid, vy_uid uid); -vy_uid vyLookupUID(vy_file_id fid); +void vyWaitUntilProcessingIsFinished(void); #endif diff --git a/src/tools/assetc/processing_flags.h b/src/tools/assetc/processing_flags.h new file mode 100644 index 0000000..9c9f307 --- /dev/null +++ b/src/tools/assetc/processing_flags.h @@ -0,0 +1,20 @@ +#ifndef VY_ASSETC_PROCESSING_FLAGS_H +#define VY_ASSETC_PROCESSING_FLAGS_H + +/* Shader processing flags */ +enum { + /* Type is encoded in the lower bits */ + VY_SHADER_FLAG_VERTEX = 0x01, + VY_SHADER_FLAG_FRAGMENT = 0x02, + VY_SHADER_FLAG_COMPUTE = 0x03, + + VY_SHADER_FLAG_TYPE_MASK = + VY_SHADER_FLAG_VERTEX | VY_SHADER_FLAG_FRAGMENT | VY_SHADER_FLAG_COMPUTE, + + VY_SHADER_FLAG_OPTIMIZE_SPEED = 0x04, + VY_SHADER_FLAG_OPTIMIZE_SIZE = 0x08, + VY_SHADER_FLAG_OPTIMIZE_MASK = VY_SHADER_FLAG_OPTIMIZE_SPEED | VY_SHADER_FLAG_OPTIMIZE_SIZE, +}; + + +#endif diff --git a/src/tools/assetc/processor.c b/src/tools/assetc/processor.c index 9f7897d..b9a4ef7 100644 --- a/src/tools/assetc/processor.c +++ b/src/tools/assetc/processor.c @@ -1,17 +1,25 @@ #include "processing.h" #include "utils.h" +#include "description_parser.h" +#include "packages.h" +#include "assetmeta.h" -#include "runtime/file_tab.h" -#include "runtime/threading.h" #include "runtime/aio.h" #include "runtime/buffer_manager.h" +#include "runtime/file_tab.h" +#include "runtime/threading.h" -#include #include +#include +#include +#include + typedef struct { vy_file_id fid; + uint32_t flags; + /* How many times has this file been added? */ unsigned int turn; } vy_file_processing_queue_entry; @@ -23,40 +31,94 @@ typedef struct { unsigned int tail; } vy_file_processing_queue; -static vy_file_processing_queue _processing_queue; +static vy_file_processing_queue _queues[2]; +static vy_file_processing_queue *_processing_queue; +static vy_file_processing_queue *_retry_queue; + static vy_mutex *_guard; static bool _keep_running; +/* A single file could have a lot of dependencies. */ +#define MAX_TURNS 100 -static vy_result vyAddFileToProcessingQueueImpl(vy_file_id file, unsigned int turn) { - if (!_guard) - _guard = vyCreateMutex(); +#define MAX_PROCESSING_THREADS 16 +#define FORCE_SINGLE_THREAD 1 + +static unsigned int _num_processing_threads = 0; +static vy_thread *_processing_threads[MAX_PROCESSING_THREADS]; + +static unsigned int _processing_thread_count = 0; + +typedef struct { + unsigned int package; +} vy_asset_settings; + +static vy_result ParseAssetSettings(const char *text, + size_t length, + const char *file_path, + vy_asset_settings *settings) { + unsigned int root_list; + vy_parse_state state; + vy_result res = vyParseDescription(text, length, file_path, &root_list, &state); + if (res != VY_SUCCESS) { + vyReportError("ASSETC", "Failed to parse asset settings: %s", file_path); + return res; + } + + settings->package = 0; + + const vy_parsed_stmt *package_stmt = vyFindStatement(&state, root_list, "package"); + if (package_stmt) { + if (package_stmt->form != VY_STMT_FORM_VALUE) { + vyReportError("ASSETC", + "Expected a package name as the value of 'package' in %s.", + file_path); + res = VY_UNKNOWN_ERROR; + goto out; + } + settings->package = vyAddPackageFile(package_stmt->value); + } + +out: + vyReleaseParseState(&state); + return res; +} + + +static vy_result vyAddFileToProcessingQueueImpl(vy_file_processing_queue *queue, + vy_file_id file, + uint32_t flags, + unsigned int turn) { vy_result result = VY_SUCCESS; + vyLog("ASSETC", "Adding %s to processing queue.", vyGetFilePath(file)); + vy_file_processing_queue_entry entry = { - .fid = file, - .turn = turn, + .fid = file, + .flags = flags, + .turn = turn, }; - vyLockMutex(_guard); - if ((_processing_queue.tail + 1) % QUEUE_LENGTH != _processing_queue.head) { - unsigned int slot = _processing_queue.head; - _processing_queue.entries[slot] = entry; - _processing_queue.head = (_processing_queue.head + 1) % QUEUE_LENGTH; + if ((queue->head + 1) % QUEUE_LENGTH != queue->tail) { + unsigned int slot = queue->head; + queue->entries[slot] = entry; + queue->head = (queue->head + 1) % QUEUE_LENGTH; } else { vyReportError("ASSETC", "The processing queue is full!"); result = 1; } - vyUnlockMutex(_guard); return result; } +vy_result vyAddFileToProcessingQueue(vy_file_id file, uint32_t flags) { + assert(_guard != NULL); -vy_result vyAddFileToProcessingQueue(vy_file_id file) { - return vyAddFileToProcessingQueueImpl(file, 1); + vyLockMutex(_guard); + vy_result res = vyAddFileToProcessingQueueImpl(_processing_queue, file, flags, 1); + vyUnlockMutex(_guard); + return res; } - #define MAX_PROCESSORS 256 static vy_processor_fn *_processor_fns[MAX_PROCESSORS]; static const char *_processor_exts[MAX_PROCESSORS]; @@ -68,7 +130,7 @@ vy_result vyAddAssetProcessor(const char *file_extension, vy_processor_fn fn) { vyReportError("ASSETC", "Too many asset processor functions!"); return 1; } - _processor_fns[_processor_count] = fn; + _processor_fns[_processor_count] = fn; _processor_exts[_processor_count] = file_extension; ++_processor_count; return VY_SUCCESS; @@ -89,10 +151,10 @@ static void PopAndSwapSubmittedData(unsigned int at, *count = *count - 1; } -static void ProcessLoadedFile(vy_file_processing_queue_entry entry, void *buffer, size_t size) { +static vy_result ProcessLoadedFile(vy_file_processing_queue_entry entry, void *buffer, size_t size) { /* Search for a matching processor function */ const char *path = vyGetFilePath(entry.fid); - size_t path_len = strlen(path); + size_t path_len = strlen(path); for (unsigned int i = 0; i < _processor_count; ++i) { size_t ext_len = strlen(_processor_exts[i]); if (ext_len > path_len) @@ -100,30 +162,102 @@ static void ProcessLoadedFile(vy_file_processing_queue_entry entry, void *buffer const char *path_end = &path[path_len - ext_len]; if (memcmp(path_end, _processor_exts[i], ext_len) == 0) { + + /* Load the corresponding .as file. + * TODO: Using malloc here is probably relatively slow. + */ + vy_asset_settings settings; + { + char *as_path = malloc(path_len + 3); + if (!as_path) { + return VY_UNKNOWN_ERROR; + } + memcpy(as_path, path, path_len); + strcpy(&as_path[path_len - ext_len], ".as"); + size_t as_size = vyGetFileSize(as_path); + if (as_size == 0) { + vyReportError("ASSETC", "Failed to retrieve size of setting file %s", as_path); + free(as_path); + return VY_UNKNOWN_ERROR; + } + + + void *as_buffer = vyAllocBuffer(as_size); + + vy_load_batch as_load; + as_load.loads[0].file = vyAddFile(as_path); + as_load.loads[0].num_bytes = as_size; + as_load.loads[0].dest = as_buffer; + if (!as_load.loads[0].dest) { + vyReportError("ASSETC", + "Failed to allocate buffer for setting file %s", + as_path); + free(as_path); + return VY_UNKNOWN_ERROR; + } + as_load.loads[0].offset = 0; + as_load.num_loads = 1; + + vy_aio_handle as_handle; + if (vySubmitLoadBatch(&as_load, &as_handle) != VY_SUCCESS) { + vyReportError("ASSETC", + "Failed to submit load of setting file %s", + as_path); + free(as_path); + return VY_UNKNOWN_ERROR; + } + + if (vyWaitForAIOCompletion(as_handle) != VY_AIO_STATE_FINISHED) { + vyReportError("ASSETC", "Failed to load setting file %s", as_path); + free(as_path); + return VY_UNKNOWN_ERROR; + } + + vyReleaseAIO(as_handle); + + if (ParseAssetSettings(as_buffer, as_size, as_path, &settings) != VY_SUCCESS) { + free(as_path); + return VY_UNKNOWN_ERROR; + } + + free(as_path); + } + + /* Process the asset */ vy_processor_output out; - vy_result res = _processor_fns[i](entry.fid, buffer, size, &out); - if (res == VY_PROCESSING_FAILED) { - vyLog("ASSETC", "Failed to process file: %s", path); + vy_result res = _processor_fns[i](entry.fid, buffer, size, entry.flags, &out); + if (res == VY_SUCCESS) { + /* Add the output to the appropriate package file */ + vy_assetmeta meta; + meta.compiled_ts = vyGetCurrentTimestamp(); + meta.last_changed = vyGetFileModificationTimestamp(entry.fid); + vyAddUIDMapping(entry.fid, out.asset_uid, &meta); + + vyAddAssetToPackage(settings.package, out.asset_uid, out.data, out.size); } else if (res == VY_PROCESSING_TRY_AGAIN) { - if (entry.turn < 2) { - vyAddFileToProcessingQueueImpl(entry.fid, entry.turn + 1); + if (entry.turn < MAX_TURNS) { + vyLockMutex(_guard); + vyAddFileToProcessingQueueImpl(_retry_queue, entry.fid, entry.flags, entry.turn + 1); + vyUnlockMutex(_guard); } else { vyLog("ASSETC", "File '%s' took too many turns to process: %u", path, entry.turn); } + } else { + vyLog("ASSETC", "Failed to process file: %s (Result %u)", path, res); } - return; + return res; } } vyLog("ASSETC", "No asset processor for file: %s", path); + return VY_UNKNOWN_ERROR; } static void ProcessingThread(void *_param) { VY_UNUSED(_param); - vy_file_processing_queue_entry submitted_entries[VY_LOAD_BATCH_MAX_SIZE]; vy_aio_handle submitted_handles[VY_LOAD_BATCH_MAX_SIZE]; void *submitted_buffers[VY_LOAD_BATCH_MAX_SIZE]; @@ -140,16 +274,23 @@ static void ProcessingThread(void *_param) { bool got_entry = false; do { got_entry = false; - vy_file_processing_queue_entry entry; + vy_file_processing_queue_entry entry = {0}; vyLockMutex(_guard); - if (_processing_queue.head != _processing_queue.tail) { - unsigned int next = _processing_queue.tail; - entry = _processing_queue.entries[next]; - _processing_queue.tail = - (_processing_queue.tail + 1) % QUEUE_LENGTH; - got_entry = true; + if (_processing_queue->head != _processing_queue->tail) { + entry = _processing_queue->entries[_processing_queue->tail]; + _processing_queue->tail = (_processing_queue->tail + 1) % QUEUE_LENGTH; + got_entry = true; + } else if (load_batch.num_loads == 0) { + /* Switch the queues -> Retry all the entries that returned VY_PROCESSING_TRY_AGAIN */ + if (_retry_queue->head != _retry_queue->tail) { + vy_file_processing_queue *tmp = _retry_queue; + _retry_queue = _processing_queue; + _processing_queue = tmp; + } } vyUnlockMutex(_guard); + + /* Retry, if we did not get an entry */ if (!got_entry) continue; @@ -159,14 +300,15 @@ static void ProcessingThread(void *_param) { continue; } + vyLog("ASSETC", "Processing %s", path); + size_t fsz = vyGetFileSize(path); void *dest = vyAllocBuffer(fsz); if (!dest) { - vyLog("ASSETC", - "Ran out of memory for loading the file: %s", - path); + vyLog("ASSETC", "Ran out of memory for loading the file: %s", path); continue; } + memset(dest, 0, fsz); load_sizes[load_batch.num_loads] = fsz; load_buffers[load_batch.num_loads] = dest; @@ -176,22 +318,23 @@ static void ProcessingThread(void *_param) { load_batch.loads[load_batch.num_loads].offset = 0; load_entries[load_batch.num_loads] = entry; ++load_batch.num_loads; - } - while (got_entry && load_batch.num_loads < VY_LOAD_BATCH_MAX_SIZE); + } while (got_entry && load_batch.num_loads < VY_LOAD_BATCH_MAX_SIZE); vy_aio_handle load_handles[VY_LOAD_BATCH_MAX_SIZE]; if (load_batch.num_loads > 0) { - vy_result submit_result = - vySubmitLoadBatch(&load_batch, load_handles); + vy_result submit_result = vySubmitLoadBatch(&load_batch, load_handles); if (submit_result != VY_SUCCESS) { vyLog("ASSETC", "SubmitLoadBatch failed: %u", submit_result); continue; } } - /* Process the previously submitted loads */ while (submitted_outstanding > 0) { + vyLockMutex(_guard); + _processing_thread_count += 1; + vyUnlockMutex(_guard); + for (unsigned int i = 0; i < submitted_outstanding; ++i) { vy_aio_state state = vyGetAIOState(submitted_handles[i]); switch (state) { @@ -201,6 +344,7 @@ static void ProcessingThread(void *_param) { vyLog("ASSETC", "Loading file %s failed.", vyGetFilePath(submitted_entries[i].fid)); + vyReleaseAIO(submitted_handles[i]); vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]); PopAndSwapSubmittedData(i, &submitted_outstanding, @@ -227,6 +371,7 @@ static void ProcessingThread(void *_param) { ProcessLoadedFile(submitted_entries[i], submitted_buffers[i], submitted_sizes[i]); + vyReleaseAIO(submitted_handles[i]); vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]); PopAndSwapSubmittedData(i, &submitted_outstanding, @@ -237,6 +382,10 @@ static void ProcessingThread(void *_param) { --i; } } + + vyLockMutex(_guard); + _processing_thread_count -= 1; + vyUnlockMutex(_guard); } /* Start new round */ @@ -252,17 +401,24 @@ static void ProcessingThread(void *_param) { } } -#define NUM_PROCESSING_THREADS 8 - -vy_thread *_processing_threads[NUM_PROCESSING_THREADS]; - vy_result vyStartProcessing(void) { if (!_guard) _guard = vyCreateMutex(); - _keep_running = true; - for (unsigned int i = 0; i < NUM_PROCESSING_THREADS; ++i) { - _processing_threads[i] = vySpawnThread(ProcessingThread, NULL); +#if !FORCE_SINGLE_THREAD + _num_processing_threads = vyGetCPUCoreCount(); + if (_num_processing_threads > MAX_PROCESSING_THREADS) + _num_processing_threads = MAX_PROCESSING_THREADS; +#else + _num_processing_threads = 1; +#endif + + _processing_queue = &_queues[0]; + _retry_queue = &_queues[1]; + + _keep_running = true; + for (unsigned int i = 0; i < _num_processing_threads; ++i) { + _processing_threads[i] = vySpawnThread(ProcessingThread, NULL, "Processor"); if (!_processing_threads[i]) { vyReportError("ASSETC", "Failed to spawn processing thread %u!", i); _keep_running = false; @@ -274,6 +430,35 @@ vy_result vyStartProcessing(void) { void vyStopProcessing(void) { _keep_running = false; - for (unsigned int i = 0; i < NUM_PROCESSING_THREADS; ++i) + for (unsigned int i = 0; i < _num_processing_threads; ++i) vyJoinThread(_processing_threads[i]); +} + + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + + +void vyWaitUntilProcessingIsFinished(void) { + unsigned int done_counter = 0; + while (done_counter < 3) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + vyLockMutex(_guard); + volatile bool done = _processing_queue->head == _processing_queue->tail && + _retry_queue->head == _retry_queue->tail && + _processing_thread_count == 0; + vyUnlockMutex(_guard); + if (done) + ++done_counter; + else + done_counter = 0; + } } \ No newline at end of file diff --git a/src/tools/assetc/shader_processor.c b/src/tools/assetc/shader_processor.c index ff9d618..912d49b 100644 --- a/src/tools/assetc/shader_processor.c +++ b/src/tools/assetc/shader_processor.c @@ -1,16 +1,122 @@ +#include "assetmeta.h" #include "processing.h" +#include "processing_flags.h" +#include "utils.h" -extern vy_result vyProcessShaderFile(vy_file_id file, - void *buffer, - size_t size, - vy_processor_output *output) { - /* Scan the first line for the shader type */ - char *text = buffer; - for (size_t i = 0; i < size; ++i) { - if (text[i] == '\n' || text[i] == '\r') { - vyLog("ASSETC", "First Line"); - } +#include "runtime/buffer_manager.h" + +#include +#include + +static shaderc_include_result *ResolveInclude(void *user_data, + const char *requested_source, + int type, + const char *requesting_source, + size_t include_depth) { + shaderc_include_result *result = NULL; + + return result; +} + +static void ReleaseIncludeResult(void *user_data, shaderc_include_result *result) { +} + +vy_result vyProcessShaderFile(vy_file_id file, + void *buffer, + size_t size, + uint32_t flags, + vy_processor_output *output) { + /* If we determine that shader compilation takes too long, we can instead + * keep the compiler around */ + + shaderc_compiler_t compiler = shaderc_compiler_initialize(); + if (!compiler) { + vyLog("ASSETC", "Failed to initialize the shaderc compiler."); + return VY_PROCESSING_FAILED; } + const char *path = vyGetFilePath(file); + + shaderc_compile_options_t options = shaderc_compile_options_initialize(); + if (!options) { + vyLog("ASSETC", "Failed to initialize shader compile options."); + shaderc_compiler_release(compiler); + return VY_PROCESSING_FAILED; + } + + shaderc_compile_options_set_include_callbacks(options, + ResolveInclude, + ReleaseIncludeResult, + NULL); + + uint32_t optimize_from_flags = flags & VY_SHADER_FLAG_OPTIMIZE_MASK; + if (optimize_from_flags == VY_SHADER_FLAG_OPTIMIZE_SPEED) + shaderc_compile_options_set_optimization_level(options, + shaderc_optimization_level_performance); + else if (optimize_from_flags == VY_SHADER_FLAG_OPTIMIZE_SIZE) + shaderc_compile_options_set_optimization_level(options, shaderc_optimization_level_size); + else + shaderc_compile_options_set_optimization_level(options, shaderc_optimization_level_zero); + + uint32_t type_from_flags = flags & VY_SHADER_FLAG_TYPE_MASK; + shaderc_shader_kind shaderc_kind; + switch (type_from_flags) { + case VY_SHADER_FLAG_VERTEX: + shaderc_kind = shaderc_glsl_vertex_shader; + break; + case VY_SHADER_FLAG_FRAGMENT: + shaderc_kind = shaderc_glsl_fragment_shader; + break; + case VY_SHADER_FLAG_COMPUTE: + shaderc_kind = shaderc_glsl_compute_shader; + break; + default: + vyLog("ASSETC", "Invalid shader stage flag %u", type_from_flags); + shaderc_compile_options_release(options); + shaderc_compiler_release(compiler); + return VY_PROCESSING_FAILED; + } + + shaderc_compilation_result_t result = shaderc_compile_into_spv(compiler, + (const char *)buffer, + size, + shaderc_kind, + path, + "main", + options); + + + shaderc_compilation_status status = shaderc_result_get_compilation_status(result); + if (status != shaderc_compilation_status_success || shaderc_result_get_num_errors(result) > 0) { + + vyLog("ASSETC", + "Compilation of %s failed: %s", + path, + shaderc_result_get_error_message(result)); + + shaderc_result_release(result); + shaderc_compile_options_release(options); + shaderc_compiler_release(compiler); + return VY_PROCESSING_FAILED; + } + + size_t output_size = shaderc_result_get_length(result); + output->data = vyAllocBuffer(output_size); + if (!output->data) { + shaderc_result_release(result); + shaderc_compile_options_release(options); + shaderc_compiler_release(compiler); + vyLog("ASSETC", "Failed to allocate %zu bytes for shader compilation output.", output_size); + return VY_PROCESSING_FAILED; + } + const uint32_t *spv_bytes = (uint32_t *)shaderc_result_get_bytes(result); + memcpy(output->data, spv_bytes, output_size); + + shaderc_result_release(result); + shaderc_compile_options_release(options); + shaderc_compiler_release(compiler); + + output->asset_uid = vyCalculateUID(path); + return VY_SUCCESS; } \ No newline at end of file diff --git a/src/tools/assetc/uidmap.c b/src/tools/assetc/uidmap.c index c8234dd..e69de29 100644 --- a/src/tools/assetc/uidmap.c +++ b/src/tools/assetc/uidmap.c @@ -1,57 +0,0 @@ -#include "processing.h" - -#include "runtime/threading.h" - -#define MAP_SIZE 2048 -typedef struct { - vy_file_id fids[MAP_SIZE]; - vy_uid uids[MAP_SIZE]; - - unsigned int used_slots; -} vy_uid_map; - -static vy_uid_map _map; -static vy_mutex *_guard; - -void vyInitUIDMap(void) { - _guard = vyCreateMutex(); -} - -vy_uid vyLookupUID(vy_file_id fid) { - vyLockMutex(_guard); - unsigned int i = 0; - vy_uid result = VY_INVALID_UID; - while (i < MAP_SIZE) { - unsigned int slot = (fid + i) % MAP_SIZE; - if (_map.fids[slot] == fid) { - _map.fids[slot] = fid; - result = _map.uids[slot]; - break; - } else if (_map.fids[slot] == VY_INVALID_FILE_ID) { - break; - } - } - vyUnlockMutex(_guard); - return result; -} - -void vyAddUIDMapping(vy_file_id fid, vy_uid uid) { - vyLockMutex(_guard); - float fill_rate = (float)_map.used_slots / MAP_SIZE; - if (fill_rate >= .5f) { - vyLog("ASSETC", "UID map is above 50% filled."); - } - unsigned int i = 0; - while (i < MAP_SIZE) { - unsigned int slot = (fid + i) % MAP_SIZE; - if (_map.fids[slot] == 0) { - _map.fids[slot] = fid; - _map.uids[slot] = uid; - break; - } - } - if (i == MAP_SIZE) { - vyReportError("ASSETC", "Failed to insert entry into UID map."); - } - vyUnlockMutex(_guard); -} \ No newline at end of file diff --git a/src/tools/assetc/uidtable.c b/src/tools/assetc/uidtable.c new file mode 100644 index 0000000..ae85b6b --- /dev/null +++ b/src/tools/assetc/uidtable.c @@ -0,0 +1,32 @@ +#include "processing.h" +#include "utils.h" + +#include "runtime/threading.h" +#include "runtime/aio.h" +#include "runtime/buffer_manager.h" + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#pragma pack(push, 1) +typedef struct { + vy_file_id file; + uint64_t offset; + uint64_t size; + uint64_t last_changed; +} vy_uidtab_entry; +#pragma pack(pop) + + +vy_uid vyCalculateUID(const char *name) { + assert(sizeof(XXH32_hash_t) == sizeof(vy_uid)); + + size_t len = strlen(name); + return (vy_uid)XXH32(name, len, 0); +} \ No newline at end of file diff --git a/src/tools/assetc/utils.c b/src/tools/assetc/utils.c index 4b15796..97e1d06 100644 --- a/src/tools/assetc/utils.c +++ b/src/tools/assetc/utils.c @@ -5,19 +5,49 @@ #include #endif - size_t vyGetFileSize(const char *path) { #ifdef _WIN32 WCHAR wpath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, - MB_PRECOMPOSED, - path, - -1, - wpath, - MAX_PATH); + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH); WIN32_FILE_ATTRIBUTE_DATA attribs; if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs)) return 0; return (size_t)attribs.nFileSizeHigh << 32 | (size_t)attribs.nFileSizeLow; #endif +} + +void vySetWorkingDirectory(const char *path) { +#ifdef _WIN32 + WCHAR wpath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH); + SetCurrentDirectoryW(wpath); +#endif +} + +uint64_t vyGetCurrentTimestamp(void) { +#ifdef _WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t ts = ft.dwLowDateTime; + ts |= (uint64_t)ft.dwHighDateTime << 32; + return ts; +#endif +} + +uint64_t vyGetFileModificationTimestamp(vy_file_id fid) { +#ifdef _WIN32 + const char *path = vyGetFilePath(fid); + if (!path) { + vyLog("ASSETC", "Tried to get modification timestamp of unknown file."); + return 0; + } + WCHAR wpath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH); + WIN32_FILE_ATTRIBUTE_DATA attribs; + if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs)) + return 0; + uint64_t ts = attribs.ftLastWriteTime.dwLowDateTime; + ts |= (uint64_t)attribs.ftLastWriteTime.dwHighDateTime << 32; + return ts; +#endif } \ No newline at end of file diff --git a/src/tools/assetc/utils.h b/src/tools/assetc/utils.h index c664baf..02e5158 100644 --- a/src/tools/assetc/utils.h +++ b/src/tools/assetc/utils.h @@ -1,6 +1,16 @@ #ifndef VY_ASSETC_UTILS_H #define VY_ASSETC_UTILS_H +#include + +#include "runtime/file_tab.h" + size_t vyGetFileSize(const char *path); +void vySetWorkingDirectory(const char *path); + +uint64_t vyGetCurrentTimestamp(void); + +uint64_t vyGetFileModificationTimestamp(vy_file_id fid); + #endif