Progress on assetc

This commit is contained in:
Kevin Trogant 2023-12-10 23:09:19 +01:00
parent 4a0231a79b
commit 19f7e29215
61 changed files with 2024 additions and 871 deletions

View File

@ -14,5 +14,6 @@ AlwaysBreakAfterReturnType: None
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
IndentPPDirectives: BeforeHash
IndentPPDirectives: None
PointerAlignment: Right
ColumnLimit: 100

1
.clang-format-ignore Normal file
View File

@ -0,0 +1 @@
contrib/**/*

2
contrib/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Placed there by scripts/download_shaderc.bat
shaderc/*

View File

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

View File

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

View File

@ -1,3 +1,3 @@
#ifdef _WIN32
#include <Windows.h>
#include <Windows.h>
#endif

View File

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

1
shader/cell.as Normal file
View File

@ -0,0 +1 @@
package pipelines.pkg;

View File

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

1
shader/cell_frag.as Normal file
View File

@ -0,0 +1 @@
package shaders.pkg;

8
shader/cell_frag.glsl Normal file
View File

@ -0,0 +1,8 @@
#version 450
#pragma shader_stage(fragment)
layout (location = 0) out vec3 color;
void main() {
color = vec3(1, 1, 1);
}

1
shader/cell_vert.as Normal file
View File

@ -0,0 +1 @@
package shaders.pkg;

6
shader/cell_vert.glsl Normal file
View File

@ -0,0 +1,6 @@
#version 450
#pragma shader_stage(vertex)
void main() {
gl_Position = vec4(0, 0, 0, 1);
}

View File

@ -1,4 +0,0 @@
#define VY_SHADER_TYPE_VERT
#ifdef VY_SHADER_VK
#endif

View File

@ -2,13 +2,10 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
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);
}

View File

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

View File

@ -3,7 +3,6 @@
#include <volk/volk.h>
#ifdef _WIN32
struct HINSTANCE__;
struct HWND__;
@ -21,7 +20,6 @@ typedef struct {
#endif
} vy_native_window;
typedef struct {
VkInstance instance;
VkDebugUtilsMessengerEXT messenger;

View File

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

View File

@ -7,18 +7,16 @@
#include <stdlib.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#elif defined(VY_USE_XLIB)
#include <X11/Xlib.h>
#include <X11/Xlib.h>
#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);
}

View File

@ -4,6 +4,10 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#elif defined(__linux__)
#include <sched.h>
#endif
#include <assert.h>
@ -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;
}

View File

@ -3,8 +3,8 @@
#include <stdint.h>
#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.
*/

View File

@ -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 <Windows.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
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 <X11/Xlib.h>
#include <assert.h>
#include <X11/Xlib.h>
#include <assert.h>
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) {

View File

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

View File

@ -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 <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct vy_buffer_region_s {
void *memory;
@ -22,7 +22,7 @@ typedef struct vy_buffer_region_s {
#include <intrin.h>
#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];

View File

@ -1,6 +1,6 @@
#include "config.h"
#include "threading.h"
#include "runtime.h"
#include "threading.h"
#include <stddef.h>
#include <string.h>

View File

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

View File

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

View File

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

View File

@ -1,43 +1,47 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdio.h>
#include "runtime.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#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, ...) {

View File

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

View File

@ -23,5 +23,4 @@ VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path);
VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid);
#endif

View File

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

View File

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

View File

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

View File

@ -4,30 +4,82 @@
/* basic types and macros */
#include <stddef.h>
#include <stdint.h>
#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

View File

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

38
src/runtime/text.c Normal file
View File

@ -0,0 +1,38 @@
#include "runtime.h"
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#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
}

View File

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

View File

@ -3,14 +3,14 @@
#ifdef __linux__
#include <pthread.h>
#include <pthread.h>
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 <Windows.h>
#include <Windows.h>
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);

View File

@ -2,24 +2,22 @@
#include "threading.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
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 <pthread.h>
#include <pthread.h>
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;

View File

@ -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 <pthread.h>
#define _GNU_SOURCE
#include <pthread.h>
#include <unistd.h>
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

73
src/tests/rttest.c Normal file
View File

@ -0,0 +1,73 @@
#include <stdio.h>
#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;
}

View File

@ -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 <stdbool.h>
#include <stdlib.h>
#include <string.h>
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;

View File

@ -0,0 +1,219 @@
#include "assetmeta.h"
#include "utils.h"
#include "runtime/threading.h"
#include "runtime/aio.h"
#include "runtime/buffer_manager.h"
#include <xxhash/xxhash.h>
#include <stdio.h>
#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);
}

View File

@ -0,0 +1,27 @@
#ifndef VY_ASSETC_ASSETMETA_H
#define VY_ASSETC_ASSETMETA_H
#include "runtime/file_tab.h"
#include "runtime/assets.h"
#include <stdbool.h>
/* 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

View File

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

View File

@ -0,0 +1,209 @@
#include "description_parser.h"
#include "runtime/runtime.h"
#include <stdbool.h>
#include <limits.h>
#include <stdlib.h>
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);
}

View File

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

View File

@ -0,0 +1,5 @@
void DiscoverAssets(void) {
}

View File

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

View File

@ -0,0 +1,90 @@
#include "packages.h"
#include "runtime/threading.h"
#include "runtime/assets.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
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);
}

View File

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

View File

@ -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:
* <stmt-list> ::= <stmt>*
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
* <attribute> ::= [:alnum:]*
* <value>:: = [: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;
}

View File

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

View File

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

View File

@ -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 <string.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
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 <Windows.h>
#else
#include <unistd.h>
#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;
}
}

View File

@ -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 <shaderc/shaderc.h>
#include <string.h>
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;
}

View File

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

View File

@ -0,0 +1,32 @@
#include "processing.h"
#include "utils.h"
#include "runtime/threading.h"
#include "runtime/aio.h"
#include "runtime/buffer_manager.h"
#include <xxhash/xxhash.h>
#include <string.h>
#include <assert.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#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);
}

View File

@ -5,19 +5,49 @@
#include <Windows.h>
#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
}

View File

@ -1,6 +1,16 @@
#ifndef VY_ASSETC_UTILS_H
#define VY_ASSETC_UTILS_H
#include <stdint.h>
#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