Progress on assetc
This commit is contained in:
parent
4a0231a79b
commit
19f7e29215
@ -14,5 +14,6 @@ AlwaysBreakAfterReturnType: None
|
|||||||
BinPackArguments: false
|
BinPackArguments: false
|
||||||
BinPackParameters: false
|
BinPackParameters: false
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Attach
|
||||||
IndentPPDirectives: BeforeHash
|
IndentPPDirectives: None
|
||||||
PointerAlignment: Right
|
PointerAlignment: Right
|
||||||
|
ColumnLimit: 100
|
1
.clang-format-ignore
Normal file
1
.clang-format-ignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
contrib/**/*
|
2
contrib/.gitignore
vendored
Normal file
2
contrib/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Placed there by scripts/download_shaderc.bat
|
||||||
|
shaderc/*
|
80
meson.build
80
meson.build
@ -22,7 +22,11 @@ elif compiler.get_argument_syntax() == 'msvc'
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if get_option('default_library') == 'static'
|
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
|
endif
|
||||||
|
|
||||||
# Debug specific flags
|
# Debug specific flags
|
||||||
@ -45,32 +49,32 @@ incdir = include_directories(['contrib', 'src'])
|
|||||||
|
|
||||||
runtime_lib = library('vyrt',
|
runtime_lib = library('vyrt',
|
||||||
# Project Sources
|
# 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/aio.h',
|
||||||
'src/runtime/file_tab.h',
|
'src/runtime/app.h',
|
||||||
'src/runtime/buffer_manager.h',
|
|
||||||
'src/runtime/assets.h',
|
'src/runtime/assets.h',
|
||||||
|
'src/runtime/buffer_manager.h',
|
||||||
'src/runtime/error_report.c',
|
'src/runtime/config.h',
|
||||||
'src/runtime/gfx_main.c',
|
'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/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/runtime_cvars.c',
|
||||||
|
'src/runtime/text.c',
|
||||||
|
'src/runtime/threading_cond.c',
|
||||||
'src/runtime/threading_mutex.c',
|
'src/runtime/threading_mutex.c',
|
||||||
'src/runtime/threading_thread.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 Sources
|
||||||
'contrib/xxhash/xxhash.c',
|
'contrib/xxhash/xxhash.c',
|
||||||
@ -112,21 +116,39 @@ if vk_dep.found()
|
|||||||
static_renderer_lib = vk_renderer_lib
|
static_renderer_lib = vk_renderer_lib
|
||||||
endif
|
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
|
# Asset Compiler Tool
|
||||||
executable('assetc',
|
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.h',
|
||||||
|
'src/tools/assetc/processing_flags.h',
|
||||||
'src/tools/assetc/utils.h',
|
'src/tools/assetc/utils.h',
|
||||||
|
|
||||||
'src/tools/assetc/assetc.c',
|
'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/pipeline_processor.c',
|
||||||
|
'src/tools/assetc/processor.c',
|
||||||
'src/tools/assetc/shader_processor.c',
|
'src/tools/assetc/shader_processor.c',
|
||||||
|
'src/tools/assetc/uidtable.c',
|
||||||
'src/tools/assetc/utils.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 : [],
|
dependencies : [],
|
||||||
link_with : [runtime_lib],
|
link_with : [runtime_lib],
|
||||||
|
link_args : ['-L'+shaderc_libdir, '-lshaderc_combined'],
|
||||||
win_subsystem : 'console')
|
win_subsystem : 'console')
|
||||||
|
|
||||||
# Game
|
# Game
|
||||||
@ -143,4 +165,10 @@ executable('voyage',
|
|||||||
link_with : game_link_libs,
|
link_with : game_link_libs,
|
||||||
win_subsystem : 'windows')
|
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)
|
@ -1 +1,2 @@
|
|||||||
option('use_xlib', type : 'boolean', value : false, description : 'Use Xlib for window creation under linux')
|
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')
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
35
scripts/download_shaderc.bat
Normal file
35
scripts/download_shaderc.bat
Normal 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
1
shader/cell.as
Normal file
@ -0,0 +1 @@
|
|||||||
|
package pipelines.pkg;
|
@ -1,5 +1,11 @@
|
|||||||
vertex shader/cell_vert.shader;
|
optimization speed;
|
||||||
fragment shader/cell_frag.shader;
|
|
||||||
|
vertex {
|
||||||
|
vk shader/cell_vert.glsl;
|
||||||
|
}
|
||||||
|
fragment {
|
||||||
|
vk shader/cell_frag.glsl;
|
||||||
|
}
|
||||||
|
|
||||||
texture_bindings {
|
texture_bindings {
|
||||||
0 MATERIAL_ALBEDO;
|
0 MATERIAL_ALBEDO;
|
||||||
|
1
shader/cell_frag.as
Normal file
1
shader/cell_frag.as
Normal file
@ -0,0 +1 @@
|
|||||||
|
package shaders.pkg;
|
8
shader/cell_frag.glsl
Normal file
8
shader/cell_frag.glsl
Normal 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
1
shader/cell_vert.as
Normal file
@ -0,0 +1 @@
|
|||||||
|
package shaders.pkg;
|
6
shader/cell_vert.glsl
Normal file
6
shader/cell_vert.glsl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#version 450
|
||||||
|
#pragma shader_stage(vertex)
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(0, 0, 0, 1);
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
#define VY_SHADER_TYPE_VERT
|
|
||||||
#ifdef VY_SHADER_VK
|
|
||||||
|
|
||||||
#endif
|
|
@ -2,13 +2,10 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
int WINAPI wWinMain(HINSTANCE hInstance,
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
|
||||||
HINSTANCE hPrevInstance,
|
|
||||||
PWSTR pCmdLine,
|
|
||||||
int nCmdShow) {
|
|
||||||
return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow);
|
return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ static void ReleasePipelineSlot(vy_gfx_pipeline_handle id) {
|
|||||||
_storage.generation_in_use[slot] &= ~0x1;
|
_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
|
#if 0
|
||||||
char info_log[512];
|
char info_log[512];
|
||||||
|
|
||||||
@ -93,7 +94,8 @@ vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compu
|
|||||||
return StorePipeline(pipeline);
|
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
|
#if 0
|
||||||
char info_log[512];
|
char info_log[512];
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <volk/volk.h>
|
#include <volk/volk.h>
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct HINSTANCE__;
|
struct HINSTANCE__;
|
||||||
struct HWND__;
|
struct HWND__;
|
||||||
@ -21,7 +20,6 @@ typedef struct {
|
|||||||
#endif
|
#endif
|
||||||
} vy_native_window;
|
} vy_native_window;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
VkInstance instance;
|
VkInstance instance;
|
||||||
VkDebugUtilsMessengerEXT messenger;
|
VkDebugUtilsMessengerEXT messenger;
|
||||||
|
@ -10,14 +10,11 @@
|
|||||||
#include "runtime/renderer_api.h"
|
#include "runtime/renderer_api.h"
|
||||||
#include "runtime/runtime.h"
|
#include "runtime/runtime.h"
|
||||||
|
|
||||||
VY_CVAR_I(
|
VY_CVAR_I(r_VkEnableAPIAllocTracking,
|
||||||
r_VkEnableAPIAllocTracking,
|
"Enable tracking of allocations done by the vulkan api. [0/1] Default: 0",
|
||||||
"Enable tracking of allocations done by the vulkan api. [0/1] Default: 0",
|
0);
|
||||||
0);
|
|
||||||
|
|
||||||
VY_CVAR_S(r_VkPhysDeviceName,
|
VY_CVAR_S(r_VkPhysDeviceName, "Name of the selected physical device. Default: \"\"", "");
|
||||||
"Name of the selected physical device. Default: \"\"",
|
|
||||||
"");
|
|
||||||
|
|
||||||
vy_vk_gpu g_gpu;
|
vy_vk_gpu g_gpu;
|
||||||
|
|
||||||
@ -40,10 +37,8 @@ static const char *AllocationScopeToString(VkSystemAllocationScope scope) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *TrackAllocation(void *userData,
|
static void *
|
||||||
size_t size,
|
TrackAllocation(void *userData, size_t size, size_t alignment, VkSystemAllocationScope scope) {
|
||||||
size_t alignment,
|
|
||||||
VkSystemAllocationScope scope) {
|
|
||||||
vyLog("vk",
|
vyLog("vk",
|
||||||
"Allocation. Size: %zu, Alignment: %zu, Scope: %s",
|
"Allocation. Size: %zu, Alignment: %zu, Scope: %s",
|
||||||
size,
|
size,
|
||||||
@ -127,13 +122,11 @@ static vy_result CreateInstance(void) {
|
|||||||
uint32_t available_layer_count = 0;
|
uint32_t available_layer_count = 0;
|
||||||
result = vkEnumerateInstanceLayerProperties(&available_layer_count, NULL);
|
result = vkEnumerateInstanceLayerProperties(&available_layer_count, NULL);
|
||||||
if (result == VK_SUCCESS) {
|
if (result == VK_SUCCESS) {
|
||||||
VkLayerProperties *props =
|
VkLayerProperties *props = calloc(available_layer_count, sizeof(VkLayerProperties));
|
||||||
calloc(available_layer_count, sizeof(VkLayerProperties));
|
|
||||||
if (props) {
|
if (props) {
|
||||||
vkEnumerateInstanceLayerProperties(&available_layer_count, props);
|
vkEnumerateInstanceLayerProperties(&available_layer_count, props);
|
||||||
for (uint32_t i = 0; i < available_layer_count; ++i) {
|
for (uint32_t i = 0; i < available_layer_count; ++i) {
|
||||||
if (strcmp(props[i].layerName, "VK_LAYER_KHRONOS_validation") ==
|
if (strcmp(props[i].layerName, "VK_LAYER_KHRONOS_validation") == 0) {
|
||||||
0) {
|
|
||||||
layers[0] = "VK_LAYER_KHRONOS_validation";
|
layers[0] = "VK_LAYER_KHRONOS_validation";
|
||||||
layer_count = 1;
|
layer_count = 1;
|
||||||
break;
|
break;
|
||||||
@ -141,8 +134,7 @@ static vy_result CreateInstance(void) {
|
|||||||
}
|
}
|
||||||
free(props);
|
free(props);
|
||||||
} else {
|
} else {
|
||||||
vyLog("vk",
|
vyLog("vk", "Failed to allocate storage for instance layer properties.");
|
||||||
"Failed to allocate storage for instance layer properties.");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vyLog("vk", "vkEnumerateInstanceLayerProperties failed.");
|
vyLog("vk", "vkEnumerateInstanceLayerProperties failed.");
|
||||||
@ -167,7 +159,7 @@ static vy_result CreateInstance(void) {
|
|||||||
#ifdef VY_DEBUG
|
#ifdef VY_DEBUG
|
||||||
/* Create the debug utils messenger */
|
/* Create the debug utils messenger */
|
||||||
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
|
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 |
|
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_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,
|
.hinstance = info->hInstance,
|
||||||
.hwnd = info->hWnd,
|
.hwnd = info->hWnd,
|
||||||
};
|
};
|
||||||
if (vkCreateWin32SurfaceKHR(g_gpu.instance,
|
if (vkCreateWin32SurfaceKHR(g_gpu.instance, &surface_info, g_gpu.alloc_cb, &g_gpu.surface) ==
|
||||||
&surface_info,
|
VK_SUCCESS)
|
||||||
g_gpu.alloc_cb,
|
|
||||||
&g_gpu.surface) == VK_SUCCESS)
|
|
||||||
return VY_SUCCESS;
|
return VY_SUCCESS;
|
||||||
else
|
else
|
||||||
return 100;
|
return 100;
|
||||||
@ -206,10 +196,8 @@ static vy_result CreateSurface(const vy_renderer_init_info *info) {
|
|||||||
.dpy = info->display,
|
.dpy = info->display,
|
||||||
.window = info->window,
|
.window = info->window,
|
||||||
};
|
};
|
||||||
if (vkCreateXlibSurfaceKHR(g_gpu.instance,
|
if (vkCreateXlibSurfaceKHR(g_gpu.instance, &surface_info, &g_gpu.alloc_cb, &g_gpu.surface) ==
|
||||||
&surface_info,
|
VK_SUCCESS)
|
||||||
&g_gpu.alloc_cb,
|
|
||||||
&g_gpu.surface) == VK_SUCCESS)
|
|
||||||
return VY_SUCCESS;
|
return VY_SUCCESS;
|
||||||
else
|
else
|
||||||
return 100;
|
return 100;
|
||||||
@ -222,16 +210,14 @@ typedef struct {
|
|||||||
uint32_t present;
|
uint32_t present;
|
||||||
} vy_queue_indices;
|
} vy_queue_indices;
|
||||||
|
|
||||||
static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev,
|
static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, VkSurfaceKHR surface) {
|
||||||
VkSurfaceKHR surface) {
|
|
||||||
vy_queue_indices indices = {.graphics = UINT32_MAX,
|
vy_queue_indices indices = {.graphics = UINT32_MAX,
|
||||||
.compute = UINT32_MAX,
|
.compute = UINT32_MAX,
|
||||||
.present = UINT32_MAX};
|
.present = UINT32_MAX};
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, NULL);
|
vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, NULL);
|
||||||
VkQueueFamilyProperties *props =
|
VkQueueFamilyProperties *props = calloc(count, sizeof(VkQueueFamilyProperties));
|
||||||
calloc(count, sizeof(VkQueueFamilyProperties));
|
|
||||||
if (!props) {
|
if (!props) {
|
||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
@ -245,10 +231,7 @@ static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev,
|
|||||||
indices.compute = i;
|
indices.compute = i;
|
||||||
|
|
||||||
VkBool32 present_supported = VK_FALSE;
|
VkBool32 present_supported = VK_FALSE;
|
||||||
vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev,
|
vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev, i, surface, &present_supported);
|
||||||
i,
|
|
||||||
surface,
|
|
||||||
&present_supported);
|
|
||||||
if (present_supported)
|
if (present_supported)
|
||||||
indices.present = i;
|
indices.present = i;
|
||||||
}
|
}
|
||||||
@ -262,19 +245,13 @@ static bool CheckDeviceExtensionSupported(VkPhysicalDevice phys_dev) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uint32_t extension_count;
|
uint32_t extension_count;
|
||||||
vkEnumerateDeviceExtensionProperties(phys_dev,
|
vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, NULL);
|
||||||
NULL,
|
|
||||||
&extension_count,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
VkExtensionProperties *supported_extensions =
|
VkExtensionProperties *supported_extensions =
|
||||||
calloc(extension_count, sizeof(VkExtensionProperties));
|
calloc(extension_count, sizeof(VkExtensionProperties));
|
||||||
if (!supported_extensions)
|
if (!supported_extensions)
|
||||||
return false;
|
return false;
|
||||||
vkEnumerateDeviceExtensionProperties(phys_dev,
|
vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, supported_extensions);
|
||||||
NULL,
|
|
||||||
&extension_count,
|
|
||||||
supported_extensions);
|
|
||||||
|
|
||||||
bool supported = true;
|
bool supported = true;
|
||||||
for (uint32_t i = 0; i < VY_ARRAY_COUNT(required_extensions); ++i) {
|
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;
|
g_gpu.phys_device = VK_NULL_HANDLE;
|
||||||
uint32_t phys_device_count = 0;
|
uint32_t phys_device_count = 0;
|
||||||
VkResult result =
|
VkResult result = vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL);
|
||||||
vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL);
|
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
vyReportError("vk", "Failed to enumerate the physical devices.");
|
vyReportError("vk", "Failed to enumerate the physical devices.");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
VkPhysicalDevice *phys_devices =
|
VkPhysicalDevice *phys_devices = calloc(phys_device_count, sizeof(VkPhysicalDevice));
|
||||||
calloc(phys_device_count, sizeof(VkPhysicalDevice));
|
|
||||||
if (!phys_devices) {
|
if (!phys_devices) {
|
||||||
vyReportError(
|
vyReportError("vk", "Failed to enumerate the physical devices: Out of memory.");
|
||||||
"vk",
|
|
||||||
"Failed to enumerate the physical devices: Out of memory.");
|
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
vkEnumeratePhysicalDevices(g_gpu.instance,
|
vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, phys_devices);
|
||||||
&phys_device_count,
|
|
||||||
phys_devices);
|
|
||||||
|
|
||||||
uint32_t highscore = 0;
|
uint32_t highscore = 0;
|
||||||
uint32_t best_index = phys_device_count;
|
uint32_t best_index = phys_device_count;
|
||||||
for (uint32_t i = 0; i < phys_device_count; ++i) {
|
for (uint32_t i = 0; i < phys_device_count; ++i) {
|
||||||
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
|
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
|
||||||
.sType =
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
|
|
||||||
.pNext = NULL,
|
.pNext = NULL,
|
||||||
};
|
};
|
||||||
VkPhysicalDeviceProperties2 props = {
|
VkPhysicalDeviceProperties2 props = {
|
||||||
@ -337,8 +307,7 @@ static vy_result ChoosePhysicalDevice(void) {
|
|||||||
if (!CheckDeviceExtensionSupported(phys_devices[i]))
|
if (!CheckDeviceExtensionSupported(phys_devices[i]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
vy_queue_indices indices =
|
vy_queue_indices indices = RetrieveQueueIndices(phys_devices[i], g_gpu.surface);
|
||||||
RetrieveQueueIndices(phys_devices[i], g_gpu.surface);
|
|
||||||
if (indices.compute == UINT32_MAX || indices.present == UINT32_MAX ||
|
if (indices.compute == UINT32_MAX || indices.present == UINT32_MAX ||
|
||||||
indices.graphics == UINT32_MAX)
|
indices.graphics == UINT32_MAX)
|
||||||
continue;
|
continue;
|
||||||
@ -351,14 +320,10 @@ static vy_result ChoosePhysicalDevice(void) {
|
|||||||
score += (props.properties.limits.maxFramebufferWidth / 100) *
|
score += (props.properties.limits.maxFramebufferWidth / 100) *
|
||||||
(props.properties.limits.maxFramebufferHeight / 100);
|
(props.properties.limits.maxFramebufferHeight / 100);
|
||||||
|
|
||||||
score += (descriptor_indexing_props
|
score +=
|
||||||
.shaderStorageBufferArrayNonUniformIndexingNative)
|
(descriptor_indexing_props.shaderStorageBufferArrayNonUniformIndexingNative) ? 100 : 0;
|
||||||
? 100
|
score +=
|
||||||
: 0;
|
(descriptor_indexing_props.shaderSampledImageArrayNonUniformIndexingNative) ? 100 : 0;
|
||||||
score += (descriptor_indexing_props
|
|
||||||
.shaderSampledImageArrayNonUniformIndexingNative)
|
|
||||||
? 100
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
if (score > highscore) {
|
if (score > highscore) {
|
||||||
highscore = score;
|
highscore = score;
|
||||||
@ -373,10 +338,9 @@ static vy_result ChoosePhysicalDevice(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (best_index < phys_device_count) {
|
if (best_index < phys_device_count) {
|
||||||
g_gpu.phys_device = phys_devices[0];
|
g_gpu.phys_device = phys_devices[0];
|
||||||
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
|
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
|
||||||
.sType =
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
|
|
||||||
.pNext = NULL,
|
.pNext = NULL,
|
||||||
};
|
};
|
||||||
VkPhysicalDeviceProperties2 props = {
|
VkPhysicalDeviceProperties2 props = {
|
||||||
@ -401,8 +365,7 @@ static vy_result CreateDevice(void) {
|
|||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
vy_queue_indices queue_indices =
|
vy_queue_indices queue_indices = RetrieveQueueIndices(g_gpu.phys_device, g_gpu.surface);
|
||||||
RetrieveQueueIndices(g_gpu.phys_device, g_gpu.surface);
|
|
||||||
|
|
||||||
g_gpu.compute_family = queue_indices.compute;
|
g_gpu.compute_family = queue_indices.compute;
|
||||||
g_gpu.graphics_family = queue_indices.graphics;
|
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].queueFamilyIndex = queue_indices.graphics;
|
||||||
queue_info[0].pQueuePriorities = &priority;
|
queue_info[0].pQueuePriorities = &priority;
|
||||||
if (queue_indices.compute != queue_indices.graphics) {
|
if (queue_indices.compute != queue_indices.graphics) {
|
||||||
queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
queue_info[1].pNext = NULL;
|
queue_info[1].pNext = NULL;
|
||||||
queue_info[1].flags = 0;
|
queue_info[1].flags = 0;
|
||||||
queue_info[1].queueCount = 1;
|
queue_info[1].queueCount = 1;
|
||||||
queue_info[1].queueFamilyIndex = queue_indices.compute;
|
queue_info[1].queueFamilyIndex = queue_indices.compute;
|
||||||
queue_info[1].pQueuePriorities = &priority;
|
queue_info[1].pQueuePriorities = &priority;
|
||||||
++distinct_queue_count;
|
++distinct_queue_count;
|
||||||
}
|
}
|
||||||
if (queue_indices.present != queue_indices.graphics &&
|
if (queue_indices.present != queue_indices.graphics &&
|
||||||
queue_indices.present != queue_indices.compute) {
|
queue_indices.present != queue_indices.compute) {
|
||||||
queue_info[distinct_queue_count].sType =
|
queue_info[distinct_queue_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
||||||
queue_info[distinct_queue_count].pNext = NULL;
|
queue_info[distinct_queue_count].pNext = NULL;
|
||||||
queue_info[distinct_queue_count].flags = 0;
|
queue_info[distinct_queue_count].flags = 0;
|
||||||
queue_info[distinct_queue_count].queueCount = 1;
|
queue_info[distinct_queue_count].queueCount = 1;
|
||||||
queue_info[distinct_queue_count].queueFamilyIndex =
|
queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.present;
|
||||||
queue_indices.present;
|
|
||||||
queue_info[distinct_queue_count].pQueuePriorities = &priority;
|
queue_info[distinct_queue_count].pQueuePriorities = &priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,26 +407,15 @@ static vy_result CreateDevice(void) {
|
|||||||
.pQueueCreateInfos = queue_info,
|
.pQueueCreateInfos = queue_info,
|
||||||
.queueCreateInfoCount = distinct_queue_count,
|
.queueCreateInfoCount = distinct_queue_count,
|
||||||
};
|
};
|
||||||
if (vkCreateDevice(g_gpu.phys_device,
|
if (vkCreateDevice(g_gpu.phys_device, &device_info, g_gpu.alloc_cb, &g_gpu.device) !=
|
||||||
&device_info,
|
VK_SUCCESS) {
|
||||||
g_gpu.alloc_cb,
|
|
||||||
&g_gpu.device) != VK_SUCCESS) {
|
|
||||||
vyReportError("vk", "Device creation failed.");
|
vyReportError("vk", "Device creation failed.");
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
vkGetDeviceQueue(g_gpu.device,
|
vkGetDeviceQueue(g_gpu.device, queue_indices.graphics, 0, &g_gpu.graphics_queue);
|
||||||
queue_indices.graphics,
|
vkGetDeviceQueue(g_gpu.device, queue_indices.compute, 0, &g_gpu.compute_queue);
|
||||||
0,
|
vkGetDeviceQueue(g_gpu.device, queue_indices.present, 0, &g_gpu.present_queue);
|
||||||
&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;
|
return VY_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,16 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#elif defined(VY_USE_XLIB)
|
#elif defined(VY_USE_XLIB)
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VY_CVAR_I(r_VkPreferredSwapchainImages,
|
VY_CVAR_I(r_VkPreferredSwapchainImages,
|
||||||
"Preferred number of swapchain iamges. [2/3] Default: 2",
|
"Preferred number of swapchain iamges. [2/3] Default: 2",
|
||||||
2);
|
2);
|
||||||
VY_CVAR_I(r_VkPreferMailboxMode,
|
VY_CVAR_I(r_VkPreferMailboxMode, "Prefer mailbox present mode over fifo mode. [0/1] Default: 0", 1);
|
||||||
"Prefer mailbox present mode over fifo mode. [0/1] Default: 0",
|
|
||||||
1);
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
VkPresentModeKHR present_mode;
|
VkPresentModeKHR present_mode;
|
||||||
@ -35,10 +33,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) {
|
|||||||
if (r_VkPreferMailboxMode.i) {
|
if (r_VkPreferMailboxMode.i) {
|
||||||
VkPresentModeKHR modes[6];
|
VkPresentModeKHR modes[6];
|
||||||
uint32_t count = 6;
|
uint32_t count = 6;
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(g_gpu.phys_device,
|
vkGetPhysicalDeviceSurfacePresentModesKHR(g_gpu.phys_device, g_gpu.surface, &count, modes);
|
||||||
g_gpu.surface,
|
|
||||||
&count,
|
|
||||||
modes);
|
|
||||||
for (uint32_t i = 0; i < count; ++i) {
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
if (modes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
if (modes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
||||||
params.present_mode = 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 */
|
/* Determine surface format */
|
||||||
VkSurfaceFormatKHR formats[64];
|
VkSurfaceFormatKHR formats[64];
|
||||||
uint32_t format_count = 64;
|
uint32_t format_count = 64;
|
||||||
vkGetPhysicalDeviceSurfaceFormatsKHR(g_gpu.phys_device,
|
vkGetPhysicalDeviceSurfaceFormatsKHR(g_gpu.phys_device, g_gpu.surface, &format_count, formats);
|
||||||
g_gpu.surface,
|
|
||||||
&format_count,
|
|
||||||
formats);
|
|
||||||
params.surface_format = formats[0];
|
params.surface_format = formats[0];
|
||||||
for (uint32_t i = 0; i < format_count; ++i) {
|
for (uint32_t i = 0; i < format_count; ++i) {
|
||||||
if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB &&
|
if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB &&
|
||||||
@ -63,9 +55,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) {
|
|||||||
|
|
||||||
/* get extent */
|
/* get extent */
|
||||||
VkSurfaceCapabilitiesKHR capabilities;
|
VkSurfaceCapabilitiesKHR capabilities;
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_gpu.phys_device,
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_gpu.phys_device, g_gpu.surface, &capabilities);
|
||||||
g_gpu.surface,
|
|
||||||
&capabilities);
|
|
||||||
if (capabilities.currentExtent.width != UINT32_MAX) {
|
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||||||
params.extent = capabilities.currentExtent;
|
params.extent = capabilities.currentExtent;
|
||||||
} else {
|
} else {
|
||||||
@ -76,9 +66,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) {
|
|||||||
params.extent.height = (uint32_t)client_area.bottom;
|
params.extent.height = (uint32_t)client_area.bottom;
|
||||||
#else
|
#else
|
||||||
XWindowAttributes attribs;
|
XWindowAttributes attribs;
|
||||||
XGetWindowAttributes(g_gpu.native_window.display,
|
XGetWindowAttributes(g_gpu.native_window.display, g_gpu.native_window.window, &attribs);
|
||||||
g_gpu.native_window.window,
|
|
||||||
&attribs);
|
|
||||||
params.extent.width = (uint32_t)attribs.width;
|
params.extent.width = (uint32_t)attribs.width;
|
||||||
params.extent.height = (uint32_t)attribs.height;
|
params.extent.height = (uint32_t)attribs.height;
|
||||||
#endif
|
#endif
|
||||||
@ -91,8 +79,7 @@ static vy_device_swapchain_parameters DetermineSwapchainParameters(void) {
|
|||||||
vy_swapchain g_swapchain;
|
vy_swapchain g_swapchain;
|
||||||
|
|
||||||
vy_result vyCreateSwapchain(void) {
|
vy_result vyCreateSwapchain(void) {
|
||||||
vy_device_swapchain_parameters device_params =
|
vy_device_swapchain_parameters device_params = DetermineSwapchainParameters();
|
||||||
DetermineSwapchainParameters();
|
|
||||||
|
|
||||||
uint32_t image_count = r_VkPreferredSwapchainImages.i;
|
uint32_t image_count = r_VkPreferredSwapchainImages.i;
|
||||||
if (image_count < 2)
|
if (image_count < 2)
|
||||||
@ -137,14 +124,9 @@ vy_result vyCreateSwapchain(void) {
|
|||||||
|
|
||||||
/* Retrieve images */
|
/* Retrieve images */
|
||||||
g_swapchain.image_count = 0;
|
g_swapchain.image_count = 0;
|
||||||
vkGetSwapchainImagesKHR(g_gpu.device,
|
vkGetSwapchainImagesKHR(g_gpu.device, g_swapchain.swapchain, &g_swapchain.image_count, NULL);
|
||||||
g_swapchain.swapchain,
|
|
||||||
&g_swapchain.image_count,
|
|
||||||
NULL);
|
|
||||||
if (g_swapchain.image_count > VY_VK_MAX_SWAPCHAIN_IMAGES) {
|
if (g_swapchain.image_count > VY_VK_MAX_SWAPCHAIN_IMAGES) {
|
||||||
vyReportError("vk",
|
vyReportError("vk", "Unsupported number of swapchain images: %u", g_swapchain.image_count);
|
||||||
"Unsupported number of swapchain images: %u",
|
|
||||||
g_swapchain.image_count);
|
|
||||||
return 51;
|
return 51;
|
||||||
}
|
}
|
||||||
vkGetSwapchainImagesKHR(g_gpu.device,
|
vkGetSwapchainImagesKHR(g_gpu.device,
|
||||||
@ -179,8 +161,7 @@ vy_result vyCreateSwapchain(void) {
|
|||||||
&view_info,
|
&view_info,
|
||||||
g_gpu.alloc_cb,
|
g_gpu.alloc_cb,
|
||||||
&g_swapchain.image_views[i]) != VK_SUCCESS) {
|
&g_swapchain.image_views[i]) != VK_SUCCESS) {
|
||||||
vyReportError("vk",
|
vyReportError("vk", "Failed to create an image view for the swapchain.");
|
||||||
"Failed to create an image view for the swapchain.");
|
|
||||||
return 52;
|
return 52;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,9 +177,7 @@ vy_result vyRecreateSwapchain(void) {
|
|||||||
|
|
||||||
void vyDestroySwapchain(void) {
|
void vyDestroySwapchain(void) {
|
||||||
for (uint32_t i = 0; i < g_swapchain.image_count; ++i) {
|
for (uint32_t i = 0; i < g_swapchain.image_count; ++i) {
|
||||||
vkDestroyImageView(g_gpu.device,
|
vkDestroyImageView(g_gpu.device, g_swapchain.image_views[i], g_gpu.alloc_cb);
|
||||||
g_swapchain.image_views[i],
|
|
||||||
g_gpu.alloc_cb);
|
|
||||||
}
|
}
|
||||||
vkDestroySwapchainKHR(g_gpu.device, g_swapchain.swapchain, g_gpu.alloc_cb);
|
vkDestroySwapchainKHR(g_gpu.device, g_swapchain.swapchain, g_gpu.alloc_cb);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <sched.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -11,7 +15,6 @@
|
|||||||
|
|
||||||
/* Maintain a ringbuffer of pending operations */
|
/* Maintain a ringbuffer of pending operations */
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
HANDLE file_handle;
|
HANDLE file_handle;
|
||||||
@ -49,11 +52,10 @@ static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) {
|
|||||||
if (_ringbuffer.head + count <= _ringbuffer.capacity) {
|
if (_ringbuffer.head + count <= _ringbuffer.capacity) {
|
||||||
result.a_count = count;
|
result.a_count = count;
|
||||||
result.a = &_ringbuffer.storage[_ringbuffer.head];
|
result.a = &_ringbuffer.storage[_ringbuffer.head];
|
||||||
_ringbuffer.head =
|
_ringbuffer.head = (_ringbuffer.head + count) % _ringbuffer.capacity;
|
||||||
(_ringbuffer.head + count) % _ringbuffer.capacity;
|
|
||||||
} else {
|
} else {
|
||||||
/* Check if enough space is free at the end */
|
/* 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;
|
uint32_t b_count = count - a_count;
|
||||||
|
|
||||||
if (b_count <= _ringbuffer.tail) {
|
if (b_count <= _ringbuffer.tail) {
|
||||||
@ -70,10 +72,9 @@ static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) {
|
|||||||
/* Head is lower than tail */
|
/* Head is lower than tail */
|
||||||
uint32_t num_free = _ringbuffer.tail - _ringbuffer.head;
|
uint32_t num_free = _ringbuffer.tail - _ringbuffer.head;
|
||||||
if (count < num_free) {
|
if (count < num_free) {
|
||||||
result.a_count = count;
|
result.a_count = count;
|
||||||
result.a = &_ringbuffer.storage[_ringbuffer.head];
|
result.a = &_ringbuffer.storage[_ringbuffer.head];
|
||||||
_ringbuffer.head =
|
_ringbuffer.head = (_ringbuffer.head + count) % _ringbuffer.capacity;
|
||||||
(_ringbuffer.head + count) % _ringbuffer.capacity;
|
|
||||||
} else {
|
} else {
|
||||||
/* Not enough space, we would overwrite the tail */
|
/* Not enough space, we would overwrite the tail */
|
||||||
vyLog("aio", "Ringbuffer is full.");
|
vyLog("aio", "Ringbuffer is full.");
|
||||||
@ -85,10 +86,9 @@ static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static void win32CompletionRoutine(DWORD error_code,
|
static void
|
||||||
DWORD num_bytes_transfered,
|
win32CompletionRoutine(DWORD error_code, DWORD num_bytes_transfered, LPOVERLAPPED overlapped) {
|
||||||
LPOVERLAPPED overlapped) {
|
vy_aio *op = (vy_aio *)overlapped->hEvent;
|
||||||
vy_aio *op = (vy_aio*)overlapped->hEvent;
|
|
||||||
assert(op->state == VY_AIO_STATE_PENDING);
|
assert(op->state == VY_AIO_STATE_PENDING);
|
||||||
|
|
||||||
if (error_code != ERROR_SUCCESS) {
|
if (error_code != ERROR_SUCCESS) {
|
||||||
@ -97,12 +97,11 @@ static void win32CompletionRoutine(DWORD error_code,
|
|||||||
} else {
|
} else {
|
||||||
op->state = VY_AIO_STATE_FINISHED;
|
op->state = VY_AIO_STATE_FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(op->file_handle);
|
CloseHandle(op->file_handle);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations) {
|
VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations) {
|
||||||
_ringbuffer.guard = vyCreateMutex();
|
_ringbuffer.guard = vyCreateMutex();
|
||||||
if (!_ringbuffer.guard) {
|
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));
|
_ringbuffer.storage = calloc(max_concurrent_operations, sizeof(vy_aio));
|
||||||
if (!_ringbuffer.storage)
|
if (!_ringbuffer.storage)
|
||||||
return VY_AIO_OUT_OF_MEMORY;
|
return VY_AIO_OUT_OF_MEMORY;
|
||||||
_ringbuffer.head = 0;
|
_ringbuffer.head = 0;
|
||||||
_ringbuffer.tail = 0;
|
_ringbuffer.tail = 0;
|
||||||
_ringbuffer.capacity = max_concurrent_operations;
|
_ringbuffer.capacity = max_concurrent_operations;
|
||||||
return VY_SUCCESS;
|
return VY_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -126,8 +125,7 @@ VY_DLLEXPORT void vyShutdownAIO(void) {
|
|||||||
_ringbuffer.capacity = 0;
|
_ringbuffer.capacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch,
|
VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, vy_aio_handle *handles) {
|
||||||
vy_aio_handle *handles) {
|
|
||||||
if (batch->num_loads > VY_LOAD_BATCH_MAX_SIZE) {
|
if (batch->num_loads > VY_LOAD_BATCH_MAX_SIZE) {
|
||||||
return VY_AIO_LOAD_TOO_LARGE;
|
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) {
|
for (unsigned int i = 0; i < batch->num_loads; ++i) {
|
||||||
vy_aio *op = (i < rbspace.a_count) ? &rbspace.a[i]
|
vy_aio *op = (i < rbspace.a_count) ? &rbspace.a[i] : &rbspace.b[i - rbspace.a_count];
|
||||||
: &rbspace.b[i - rbspace.a_count];
|
op->state = VY_AIO_STATE_PENDING;
|
||||||
op->state = VY_AIO_STATE_PENDING;
|
|
||||||
const char *file_path = vyGetFilePath(batch->loads[i].file);
|
const char *file_path = vyGetFilePath(batch->loads[i].file);
|
||||||
if (!file_path) {
|
if (!file_path) {
|
||||||
vyReportError("aio",
|
vyReportError("aio", "Failed to resolve file path for a batched load");
|
||||||
"Failed to resolve file path for a batched load");
|
|
||||||
op->state = VY_AIO_STATE_INVALID;
|
op->state = VY_AIO_STATE_INVALID;
|
||||||
handles[i] = VY_AIO_INVALID_HANDLE;
|
handles[i] = VY_AIO_INVALID_HANDLE;
|
||||||
continue;
|
continue;
|
||||||
@ -168,9 +164,7 @@ VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch,
|
|||||||
-1,
|
-1,
|
||||||
wpath,
|
wpath,
|
||||||
VY_ARRAY_COUNT(wpath)) == 0) {
|
VY_ARRAY_COUNT(wpath)) == 0) {
|
||||||
vyReportError("aio",
|
vyReportError("aio", "MultiByteToWideChar failed with error code: %u", GetLastError());
|
||||||
"MultiByteToWideChar failed with error code: %u",
|
|
||||||
GetLastError());
|
|
||||||
op->state = VY_AIO_STATE_FINISHED;
|
op->state = VY_AIO_STATE_FINISHED;
|
||||||
handles[i] = VY_AIO_INVALID_HANDLE;
|
handles[i] = VY_AIO_INVALID_HANDLE;
|
||||||
continue;
|
continue;
|
||||||
@ -193,15 +187,15 @@ VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
op->file_handle = file_handle;
|
op->file_handle = file_handle;
|
||||||
BOOL result = ReadFileEx(file_handle,
|
BOOL result = ReadFileEx(file_handle,
|
||||||
batch->loads[i].dest,
|
batch->loads[i].dest,
|
||||||
(DWORD)batch->loads[i].num_bytes,
|
(DWORD)batch->loads[i].num_bytes,
|
||||||
&op->overlapped,
|
&op->overlapped,
|
||||||
win32CompletionRoutine);
|
win32CompletionRoutine);
|
||||||
DWORD err = GetLastError();
|
DWORD err = GetLastError();
|
||||||
if (!result || err != ERROR_SUCCESS) {
|
if (!result || err != ERROR_SUCCESS) {
|
||||||
vyReportError("aio", "ReadFileEx failed with error code: %u", err);
|
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;
|
handles[i] = VY_AIO_INVALID_HANDLE;
|
||||||
CloseHandle(file_handle);
|
CloseHandle(file_handle);
|
||||||
op->file_handle = NULL;
|
op->file_handle = NULL;
|
||||||
@ -229,14 +223,15 @@ VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle) {
|
|||||||
return state;
|
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) {
|
if (handle == VY_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vyLockMutex(_ringbuffer.guard);
|
vyLockMutex(_ringbuffer.guard);
|
||||||
_ringbuffer.storage[handle - 1].state = VY_AIO_STATE_INVALID;
|
_ringbuffer.storage[handle - 1].state = VY_AIO_STATE_INVALID;
|
||||||
if (handle - 1 == _ringbuffer.tail) {
|
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;
|
uint32_t i = _ringbuffer.tail;
|
||||||
while ((_ringbuffer.storage[i].state == VY_AIO_STATE_INVALID) && i != _ringbuffer.head) {
|
while ((_ringbuffer.storage[i].state == VY_AIO_STATE_INVALID) && i != _ringbuffer.head) {
|
||||||
i = (i + 1) % _ringbuffer.capacity;
|
i = (i + 1) % _ringbuffer.capacity;
|
||||||
@ -245,3 +240,19 @@ VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle) {
|
|||||||
}
|
}
|
||||||
vyUnlockMutex(_ringbuffer.guard);
|
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;
|
||||||
|
}
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "runtime.h"
|
|
||||||
#include "file_tab.h"
|
#include "file_tab.h"
|
||||||
|
#include "runtime.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t offset; /**< Starting offset inside the file in bytes */
|
size_t offset; /**< Starting offset inside the file in bytes */
|
||||||
@ -13,7 +13,7 @@ typedef struct {
|
|||||||
* Must be valid until the load is finished.
|
* Must be valid until the load is finished.
|
||||||
*/
|
*/
|
||||||
void *dest;
|
void *dest;
|
||||||
vy_file_id file; /**< Unique identifier for the file */
|
vy_file_id file; /**< Unique identifier for the file */
|
||||||
} vy_file_load;
|
} vy_file_load;
|
||||||
|
|
||||||
#define VY_LOAD_BATCH_MAX_SIZE 64
|
#define VY_LOAD_BATCH_MAX_SIZE 64
|
||||||
@ -24,12 +24,11 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vy_file_load loads[VY_LOAD_BATCH_MAX_SIZE];
|
vy_file_load loads[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
|
|
||||||
/** Must be smaller or equal to @c VY_LOAD_BATCH_MAX_SIZE */
|
/** Must be smaller or equal to @c VY_LOAD_BATCH_MAX_SIZE */
|
||||||
unsigned int num_loads;
|
unsigned int num_loads;
|
||||||
} vy_load_batch;
|
} vy_load_batch;
|
||||||
|
|
||||||
|
|
||||||
#define VY_AIO_INVALID_HANDLE 0
|
#define VY_AIO_INVALID_HANDLE 0
|
||||||
|
|
||||||
/** Handle for an async io operation. Can be used to query the state and result. */
|
/** 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,
|
VY_AIO_OUT_OF_MEMORY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VY_AIO_STATE_INVALID,
|
VY_AIO_STATE_INVALID,
|
||||||
VY_AIO_STATE_PENDING,
|
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 void vyShutdownAIO(void);
|
||||||
|
|
||||||
VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch,
|
VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch, vy_aio_handle *handles);
|
||||||
vy_aio_handle *handles);
|
|
||||||
|
|
||||||
VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle);
|
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.
|
/* 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.
|
* The system is allowed to re-use the same handle value for new operations after this was called.
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
#include "aio.h"
|
||||||
|
#include "buffer_manager.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "gfx.h"
|
#include "gfx.h"
|
||||||
#include "aio.h"
|
|
||||||
#include "renderer_api.h"
|
#include "renderer_api.h"
|
||||||
#include "buffer_manager.h"
|
|
||||||
|
|
||||||
extern void __RegisterRuntimeCVars(void);
|
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);
|
VY_CVAR_I(rt_WindowHeight, "Window height. Default: 768", 768);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
static LRESULT CALLBACK win32WndProc(HWND hWnd,
|
static LRESULT CALLBACK win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||||
UINT uMsg,
|
|
||||||
WPARAM wParam,
|
|
||||||
LPARAM lParam) {
|
|
||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
@ -28,10 +25,8 @@ static LRESULT CALLBACK win32WndProc(HWND hWnd,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
|
VY_DLLEXPORT int
|
||||||
HINSTANCE hPrevInstance,
|
vyWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
|
||||||
PWSTR pCmdLine,
|
|
||||||
int nCmdShow) {
|
|
||||||
|
|
||||||
__RegisterRuntimeCVars();
|
__RegisterRuntimeCVars();
|
||||||
vyRegisterRendererCVars();
|
vyRegisterRendererCVars();
|
||||||
@ -103,9 +98,7 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!wnd) {
|
if (!wnd) {
|
||||||
vyReportError("CORE",
|
vyReportError("CORE", "Failed to create the game window: %u", GetLastError());
|
||||||
"Failed to create the game window: %u",
|
|
||||||
GetLastError());
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +137,10 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
|
|||||||
|
|
||||||
#elif defined(VY_USE_XLIB)
|
#elif defined(VY_USE_XLIB)
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static void
|
static void xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) {
|
||||||
xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) {
|
|
||||||
Atom wm_state = XInternAtom(dpy, "_NET_W_STATE", False);
|
Atom wm_state = XInternAtom(dpy, "_NET_W_STATE", False);
|
||||||
Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
|
Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
|
||||||
if (wm_state == None || wm_fullscreen == None) {
|
if (wm_state == None || wm_fullscreen == None) {
|
||||||
@ -156,20 +148,20 @@ xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define _NET_WM_STATE_REMOVE 0
|
#define _NET_WM_STATE_REMOVE 0
|
||||||
#define _NET_WM_STATE_ADD 1
|
#define _NET_WM_STATE_ADD 1
|
||||||
#define EVENT_SOURCE_APPLICATION 1
|
#define EVENT_SOURCE_APPLICATION 1
|
||||||
XEvent ev;
|
XEvent ev;
|
||||||
ev.type = ClientMessage;
|
ev.type = ClientMessage;
|
||||||
ev.xclient.display = dpy;
|
ev.xclient.display = dpy;
|
||||||
ev.xclient.window = window;
|
ev.xclient.window = window;
|
||||||
ev.xclient.message_type = wm_state;
|
ev.xclient.message_type = wm_state;
|
||||||
ev.xclient.format = 32;
|
ev.xclient.format = 32;
|
||||||
ev.xclient.data.l[0] = (enable) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
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[1] = wm_fullscreen;
|
||||||
ev.xclient.data.l[2] = 0;
|
ev.xclient.data.l[2] = 0;
|
||||||
ev.xclient.data.l[3] = EVENT_SOURCE_APPLICATION;
|
ev.xclient.data.l[3] = EVENT_SOURCE_APPLICATION;
|
||||||
ev.xclient.data.l[4] = 0;
|
ev.xclient.data.l[4] = 0;
|
||||||
|
|
||||||
Window root_window = XRootWindow(dpy, screen);
|
Window root_window = XRootWindow(dpy, screen);
|
||||||
long ev_mask = SubstructureRedirectMask;
|
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.");
|
vyReportError("CORE", "Failed to send x11 fullscreen event.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef _NET_WM_STATE_ADD
|
#undef _NET_WM_STATE_ADD
|
||||||
#undef _NET_WM_STATE_REMOVE
|
#undef _NET_WM_STATE_REMOVE
|
||||||
#undef EVENT_SOURCE_APPLICATION
|
#undef EVENT_SOURCE_APPLICATION
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT int vyXlibEntry(int argc, char **argv) {
|
VY_DLLEXPORT int vyXlibEntry(int argc, char **argv) {
|
||||||
|
@ -8,4 +8,13 @@ typedef uint32_t vy_uid;
|
|||||||
|
|
||||||
#define VY_INVALID_UID 0
|
#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
|
#endif
|
@ -1,12 +1,12 @@
|
|||||||
|
#include "buffer_manager.h"
|
||||||
|
#include "config.h"
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "threading.h"
|
#include "threading.h"
|
||||||
#include "config.h"
|
|
||||||
#include "buffer_manager.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
typedef struct vy_buffer_region_s {
|
typedef struct vy_buffer_region_s {
|
||||||
void *memory;
|
void *memory;
|
||||||
@ -22,7 +22,7 @@ typedef struct vy_buffer_region_s {
|
|||||||
|
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
|
||||||
#define lzcnt32(x) __lzcnt((x))
|
#define lzcnt32(x) __lzcnt((x))
|
||||||
#define popcnt32(x) __popcnt((x))
|
#define popcnt32(x) __popcnt((x))
|
||||||
|
|
||||||
static __forceinline uint32_t tzcnt32(uint32_t x) {
|
static __forceinline uint32_t tzcnt32(uint32_t x) {
|
||||||
@ -40,7 +40,7 @@ static inline bool IsLZCNTSupported(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
#define lzcnt32(x) __builtin_clz((x))
|
#define lzcnt32(x) __builtin_clz((x))
|
||||||
#define tzcnt32(x) __builtin_ctz((x))
|
#define tzcnt32(x) __builtin_ctz((x))
|
||||||
#define popcnt32(x) __builtin_popcount((x))
|
#define popcnt32(x) __builtin_popcount((x))
|
||||||
|
|
||||||
@ -49,13 +49,13 @@ static inline bool IsLZCNTSupported(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* NOTE(Kevin): Keep these sorted! */
|
/* 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]))
|
#define NUM_BLOCK_SIZES (sizeof(_block_sizes) / sizeof(_block_sizes[0]))
|
||||||
static vy_buffer_region _regions[NUM_BLOCK_SIZES];
|
static vy_buffer_region _regions[NUM_BLOCK_SIZES];
|
||||||
|
|
||||||
VY_CVAR_SZ(
|
VY_CVAR_SZ(rt_BufferManagerMemory,
|
||||||
rt_BufferManagerMemory,
|
"Total number of bytes allocated for the buffer manager. Default: 1GB",
|
||||||
"Total number of bytes allocated for the buffer manager. Default: 1GB", VY_GB(1));
|
VY_GB(1));
|
||||||
|
|
||||||
VY_DLLEXPORT vy_result vyInitBufferManager(void) {
|
VY_DLLEXPORT vy_result vyInitBufferManager(void) {
|
||||||
if ((rt_BufferManagerMemory.sz % NUM_BLOCK_SIZES) != 0)
|
if ((rt_BufferManagerMemory.sz % NUM_BLOCK_SIZES) != 0)
|
||||||
@ -74,14 +74,14 @@ VY_DLLEXPORT vy_result vyInitBufferManager(void) {
|
|||||||
mem_per_size / (1024 * 1024),
|
mem_per_size / (1024 * 1024),
|
||||||
_block_sizes[i] / 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].block_count = block_count;
|
||||||
_regions[i].guard = vyCreateMutex();
|
_regions[i].guard = vyCreateMutex();
|
||||||
if (!_regions[i].guard) {
|
if (!_regions[i].guard) {
|
||||||
vyReportError("BUFFERMGR", "Failed to create guard mutex %u", i);
|
vyReportError("BUFFERMGR", "Failed to create guard mutex %u", i);
|
||||||
return VY_BUFFER_MGR_MUTEX_CREATION_FAILED;
|
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) {
|
if (!_regions[i].memory) {
|
||||||
vyDestroyMutex(_regions[i].guard);
|
vyDestroyMutex(_regions[i].guard);
|
||||||
vyReportError("BUFFERMGR", "Failed to allocate memory.", i);
|
vyReportError("BUFFERMGR", "Failed to allocate memory.", i);
|
||||||
@ -117,7 +117,7 @@ VY_DLLEXPORT void vyShutdownBufferManager(void) {
|
|||||||
|
|
||||||
VY_DLLEXPORT void *vyAllocBuffer(size_t size) {
|
VY_DLLEXPORT void *vyAllocBuffer(size_t size) {
|
||||||
assert(IsLZCNTSupported());
|
assert(IsLZCNTSupported());
|
||||||
|
|
||||||
// Determine the best block size to use
|
// Determine the best block size to use
|
||||||
size_t required_blocks = (size + _block_sizes[0] - 1) / _block_sizes[0];
|
size_t required_blocks = (size + _block_sizes[0] - 1) / _block_sizes[0];
|
||||||
size_t best_fit = 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;
|
size_t first_free = 32 - free_high_blocks;
|
||||||
region->bitmap[i] |= (in_use_mask << first_free);
|
region->bitmap[i] |= (in_use_mask << first_free);
|
||||||
block_index = i * 32 + first_free;
|
block_index = i * 32 + first_free;
|
||||||
result = (char *)region->memory +
|
result = (char *)region->memory + block_index * _block_sizes[best_fit];
|
||||||
block_index * _block_sizes[best_fit];
|
|
||||||
} else if (tzcnt32(region->bitmap[i]) >= required_blocks) {
|
} else if (tzcnt32(region->bitmap[i]) >= required_blocks) {
|
||||||
/* Low blocks are free */
|
/* Low blocks are free */
|
||||||
region->bitmap[i] |= in_use_mask;
|
region->bitmap[i] |= in_use_mask;
|
||||||
block_index = i * 32;
|
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 {
|
} else {
|
||||||
/* Check if we can find a large enough range of free blocks.
|
/* Check if we can find a large enough range of free blocks.
|
||||||
* Start after the first set bit.
|
* 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) {
|
if ((region->bitmap[i] & in_use_mask << j) == 0) {
|
||||||
region->bitmap[i] |= (in_use_mask << j);
|
region->bitmap[i] |= (in_use_mask << j);
|
||||||
block_index = i * 32 + j;
|
block_index = i * 32 + j;
|
||||||
@ -170,23 +170,25 @@ VY_DLLEXPORT void *vyAllocBuffer(size_t size) {
|
|||||||
}
|
}
|
||||||
} else if (region->bitmap[i] == 0) {
|
} else if (region->bitmap[i] == 0) {
|
||||||
/* All free */
|
/* All free */
|
||||||
region->bitmap[i] = in_use_mask;
|
region->bitmap[i] = in_use_mask;
|
||||||
block_index = i * 32;
|
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 if (i < dword_count - 1) {
|
} 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 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) {
|
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 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] |= (high_mask << first_free);
|
||||||
region->bitmap[i + 1] |= low_mask;
|
region->bitmap[i + 1] |= low_mask;
|
||||||
block_index = i * 32 + 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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,13 +204,14 @@ VY_DLLEXPORT void *vyAllocBuffer(size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT void vyReleaseBuffer(const void *begin, size_t size) {
|
VY_DLLEXPORT void vyReleaseBuffer(const void *begin, size_t size) {
|
||||||
|
if (!begin)
|
||||||
|
return;
|
||||||
uintptr_t begin_addr = (uintptr_t)begin;
|
uintptr_t begin_addr = (uintptr_t)begin;
|
||||||
for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
|
for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
|
||||||
uintptr_t region_addr = (uintptr_t)_regions[i].memory;
|
uintptr_t region_addr = (uintptr_t)_regions[i].memory;
|
||||||
size_t region_size = _block_sizes[i] * _regions[i].block_count;
|
size_t region_size = _block_sizes[i] * _regions[i].block_count;
|
||||||
if (begin_addr >= region_addr &&
|
if (begin_addr >= region_addr && begin_addr + size <= region_addr + region_size) {
|
||||||
begin_addr + size <= region_addr + region_size) {
|
|
||||||
|
|
||||||
size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
|
size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
|
||||||
size_t first_block = (begin_addr - region_addr) / _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) {
|
for (unsigned int i = 0; i < NUM_BLOCK_SIZES; ++i) {
|
||||||
uintptr_t region_addr = (uintptr_t)_regions[i].memory;
|
uintptr_t region_addr = (uintptr_t)_regions[i].memory;
|
||||||
size_t region_size = _block_sizes[i] * _regions[i].block_count;
|
size_t region_size = _block_sizes[i] * _regions[i].block_count;
|
||||||
if (begin_addr >= region_addr &&
|
if (begin_addr >= region_addr && begin_addr + size <= region_addr + region_size) {
|
||||||
begin_addr + size <= region_addr + region_size) {
|
|
||||||
|
|
||||||
size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
|
size_t block_count = (size + _block_sizes[i] - 1) / _block_sizes[i];
|
||||||
size_t first_block = (begin_addr - region_addr) / _block_sizes[i];
|
size_t first_block = (begin_addr - region_addr) / _block_sizes[i];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "threading.h"
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
#include "threading.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -3,16 +3,14 @@
|
|||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
|
||||||
VY_CVAR_TYPE_INT,
|
VY_CVAR_TYPE_INT,
|
||||||
VY_CVAR_TYPE_FLOAT,
|
VY_CVAR_TYPE_FLOAT,
|
||||||
VY_CVAR_TYPE_STRING,
|
VY_CVAR_TYPE_STRING,
|
||||||
VY_CVAR_TYPE_SIZE,
|
VY_CVAR_TYPE_SIZE,
|
||||||
} vy_cvar_type;
|
} vy_cvar_type;
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *description;
|
const char *description;
|
||||||
union {
|
union {
|
||||||
@ -24,26 +22,14 @@ typedef struct
|
|||||||
vy_cvar_type type;
|
vy_cvar_type type;
|
||||||
} vy_cvar;
|
} vy_cvar;
|
||||||
|
|
||||||
#define VY_CVAR_I(n, d, v) \
|
#define VY_CVAR_I(n, d, v) \
|
||||||
vy_cvar n = {.name = #n, \
|
vy_cvar n = {.name = #n, .description = d, .i = (v), .type = VY_CVAR_TYPE_INT}
|
||||||
.description = d, \
|
#define VY_CVAR_F(n, d, v) \
|
||||||
.i = (v), \
|
vy_cvar n = {.name = #n, .description = d, .f = (v), .type = VY_CVAR_TYPE_FLOAT}
|
||||||
.type = VY_CVAR_TYPE_INT}
|
#define VY_CVAR_S(n, d, v) \
|
||||||
#define VY_CVAR_F(n, d, v) \
|
vy_cvar n = {.name = #n, .description = d, .s = (v), .type = VY_CVAR_TYPE_STRING}
|
||||||
vy_cvar n = {.name = #n, \
|
#define VY_CVAR_SZ(n, d, v) \
|
||||||
.description = d, \
|
vy_cvar n = {.name = #n, .description = d, .sz = (v), .type = VY_CVAR_TYPE_SIZE}
|
||||||
.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);
|
VY_DLLEXPORT void vyRegisterCVAR(vy_cvar *cvar);
|
||||||
|
|
||||||
|
@ -10,18 +10,13 @@ VY_DLLEXPORT vy_dynlib vyOpenCallerLib(void) {
|
|||||||
|
|
||||||
VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) {
|
VY_DLLEXPORT vy_dynlib vyOpenLib(const char *libname) {
|
||||||
wchar_t libname_w[MAX_PATH];
|
wchar_t libname_w[MAX_PATH];
|
||||||
MultiByteToWideChar(CP_UTF8,
|
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, libname, -1, libname_w, MAX_PATH);
|
||||||
MB_PRECOMPOSED,
|
|
||||||
libname,
|
|
||||||
-1,
|
|
||||||
libname_w,
|
|
||||||
MAX_PATH);
|
|
||||||
HMODULE mod = LoadLibraryW(libname_w);
|
HMODULE mod = LoadLibraryW(libname_w);
|
||||||
return (vy_dynlib)mod;
|
return (vy_dynlib)mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT void *vyGetSymbol(vy_dynlib lib, const char *symbol) {
|
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) {
|
VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) {
|
||||||
@ -47,5 +42,4 @@ VY_DLLEXPORT void vyCloseLib(vy_dynlib lib) {
|
|||||||
dlclose(lib);
|
dlclose(lib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define VY_DLLNAME(s) \
|
#define VY_DLLNAME(s) \
|
||||||
(".\\"s \
|
(".\\"s \
|
||||||
".dll")
|
".dll")
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#define VY_DLLNAME(s) \
|
#define VY_DLLNAME(s) \
|
||||||
("./lib"s \
|
("./lib"s \
|
||||||
".so")
|
".so")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef void *vy_dynlib;
|
typedef void *vy_dynlib;
|
||||||
|
@ -1,43 +1,47 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#define DbgBreak DebugBreak()
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define DbgBreak __builtin_trap()
|
||||||
|
#else
|
||||||
|
/* Not available */
|
||||||
|
#define DbgBreak
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* TODO(Kevin): Log to file, show error message box, ... */
|
/* TODO(Kevin): Log to file, show error message box, ... */
|
||||||
|
|
||||||
static void DisplayErrorBox(const char *text)
|
static bool DisplayErrorBox(const char *text) {
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WCHAR msg[256];
|
WCHAR msg[256];
|
||||||
MultiByteToWideChar(CP_UTF8,
|
vyUTF8ToWStr(text, msg, VY_ARRAY_COUNT(msg));
|
||||||
MB_PRECOMPOSED,
|
|
||||||
text,
|
#ifdef VY_ERROR_REPORT_DEBUGBREAK
|
||||||
-1,
|
return MessageBoxW(NULL, msg, L"Error", MB_OKCANCEL | MB_ICONERROR) == IDCANCEL;
|
||||||
msg,
|
#else
|
||||||
VY_ARRAY_COUNT(msg));
|
|
||||||
MessageBoxW(NULL, msg, L"Error", MB_ICONERROR);
|
MessageBoxW(NULL, msg, L"Error", MB_ICONERROR);
|
||||||
|
return false;
|
||||||
|
#endif /* VY_ERROR_REPORT_DEBUGBREAK */
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LogOut(const char *text)
|
static void LogOut(const char *text) {
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WCHAR msg[256];
|
WCHAR msg[256];
|
||||||
MultiByteToWideChar(CP_UTF8,
|
vyUTF8ToWStr(text, msg, VY_ARRAY_COUNT(msg));
|
||||||
MB_PRECOMPOSED,
|
|
||||||
text,
|
|
||||||
-1,
|
|
||||||
msg,
|
|
||||||
VY_ARRAY_COUNT(msg));
|
|
||||||
OutputDebugStringW(msg);
|
OutputDebugStringW(msg);
|
||||||
#elif defined(__linux__)
|
|
||||||
fprintf(stderr, "%s", text);
|
|
||||||
#endif
|
#endif
|
||||||
|
fprintf(stderr, "%s", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT void vyReportError(const char *subsystem, const char *fmt, ...) {
|
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");
|
at += snprintf(&buf[at], VY_ARRAY_COUNT(buf) - at - 1, "\n");
|
||||||
|
|
||||||
LogOut(buf);
|
LogOut(buf);
|
||||||
DisplayErrorBox(buf);
|
if (DisplayErrorBox(buf)) {
|
||||||
|
DbgBreak;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT void vyLog(const char *subsystem, const char *fmt, ...) {
|
VY_DLLEXPORT void vyLog(const char *subsystem, const char *fmt, ...) {
|
||||||
|
@ -29,7 +29,7 @@ vy_result vyInitFileTab(unsigned int max_files) {
|
|||||||
|
|
||||||
_file_tab.capacity = max_files;
|
_file_tab.capacity = max_files;
|
||||||
_file_tab.name_head = 0;
|
_file_tab.name_head = 0;
|
||||||
_file_tab.mutex = vyCreateMutex();
|
_file_tab.mutex = vyCreateMutex();
|
||||||
return VY_SUCCESS;
|
return VY_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,6 @@ void vyShutdownFileTab(void) {
|
|||||||
vyDestroyMutex(_file_tab.mutex);
|
vyDestroyMutex(_file_tab.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vy_file_id vyGetFileId(const char *path) {
|
vy_file_id vyGetFileId(const char *path) {
|
||||||
vy_text_span span;
|
vy_text_span span;
|
||||||
span.start = path;
|
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);
|
memcpy(_file_tab.names + _file_tab.name_head, path.start, slen);
|
||||||
_file_tab.names[_file_tab.name_head + slen - 1] = '\0';
|
_file_tab.names[_file_tab.name_head + slen - 1] = '\0';
|
||||||
_file_tab.name_offsets[at] = _file_tab.name_head;
|
_file_tab.name_offsets[at] = _file_tab.name_head;
|
||||||
_file_tab.ids[at] = fid;
|
_file_tab.ids[at] = fid;
|
||||||
_file_tab.name_head += slen;
|
_file_tab.name_head += slen;
|
||||||
break;
|
break;
|
||||||
} else if (_file_tab.ids[at] == fid) {
|
} else if (_file_tab.ids[at] == fid) {
|
||||||
|
@ -23,5 +23,4 @@ VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path);
|
|||||||
|
|
||||||
VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid);
|
VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,47 +17,41 @@ vy_renderer_api g_renderer;
|
|||||||
static vy_dynlib _renderer_lib;
|
static vy_dynlib _renderer_lib;
|
||||||
static bool _renderer_loaded = false;
|
static bool _renderer_loaded = false;
|
||||||
|
|
||||||
VY_CVAR_S(rt_Renderer,
|
VY_CVAR_S(rt_Renderer, "Select the render backend. Available options: [vk], Default: vk", "vk");
|
||||||
"Select the render backend. Available options: [vk], Default: vk",
|
|
||||||
"vk");
|
|
||||||
|
|
||||||
#ifdef VY_STATIC_LIB
|
#ifdef VY_STATIC_LIB
|
||||||
extern void VY_RENDERER_API_FN(RegisterCVars)(void);
|
extern void VY_RENDERER_API_FN(RegisterCVars)(void);
|
||||||
extern vy_result VY_RENDERER_API_FN(Init)(const vy_renderer_init_info *);
|
extern vy_result VY_RENDERER_API_FN(Init)(const vy_renderer_init_info *);
|
||||||
extern void VY_RENDERER_API_FN(Shutdown)(void);
|
extern void VY_RENDERER_API_FN(Shutdown)(void);
|
||||||
extern vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileComputePipeline)(
|
extern vy_gfx_pipeline_handle
|
||||||
const vy_compute_pipeline_info *);
|
VY_RENDERER_API_FN(CompileComputePipeline)(const vy_compute_pipeline_info *);
|
||||||
extern vy_gfx_pipeline_handle VY_RENDERER_API_FN(CompileGraphicsPipeline)(
|
extern vy_gfx_pipeline_handle
|
||||||
const vy_graphics_pipeline_info *);
|
VY_RENDERER_API_FN(CompileGraphicsPipeline)(const vy_graphics_pipeline_info *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool LoadRenderer(void) {
|
static bool LoadRenderer(void) {
|
||||||
|
|
||||||
#if !defined(VY_STATIC_LIB)
|
#if !defined(VY_STATIC_LIB)
|
||||||
#define RETRIEVE_SYMBOL(name, type) \
|
#define RETRIEVE_SYMBOL(name, type) \
|
||||||
g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vyRen" #name); \
|
g_renderer.name = (type *)vyGetSymbol(_renderer_lib, "vyRen" #name); \
|
||||||
if (!g_renderer.name) { \
|
if (!g_renderer.name) { \
|
||||||
vyReportError( \
|
vyReportError("GFX", \
|
||||||
"GFX", \
|
"Unable to retrieve renderer function %s from backend %s", \
|
||||||
"Unable to retrieve renderer function %s from backend %s", \
|
#name, \
|
||||||
#name, \
|
rt_Renderer.s); \
|
||||||
rt_Renderer.s); \
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(rt_Renderer.s, "vk") == 0) {
|
if (strcmp(rt_Renderer.s, "vk") == 0) {
|
||||||
_renderer_lib = vyOpenLib(VY_DLLNAME("vyvk"));
|
_renderer_lib = vyOpenLib(VY_DLLNAME("vyvk"));
|
||||||
if (!_renderer_lib) {
|
if (!_renderer_lib) {
|
||||||
vyReportError("GFX",
|
vyReportError("GFX", "Unable to load renderer backend: %s", VY_DLLNAME("vyvk"));
|
||||||
"Unable to load renderer backend: %s",
|
|
||||||
VY_DLLNAME("vyvk"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
RETRIEVE_SYMBOL(RegisterCVars, vy_register_renderer_cvars_fn);
|
RETRIEVE_SYMBOL(RegisterCVars, vy_register_renderer_cvars_fn);
|
||||||
RETRIEVE_SYMBOL(Init, vy_init_renderer_fn);
|
RETRIEVE_SYMBOL(Init, vy_init_renderer_fn);
|
||||||
RETRIEVE_SYMBOL(Shutdown, vy_shutdown_renderer_fn);
|
RETRIEVE_SYMBOL(Shutdown, vy_shutdown_renderer_fn);
|
||||||
RETRIEVE_SYMBOL(CompileComputePipeline, vy_compile_compute_pipeline_fn);
|
RETRIEVE_SYMBOL(CompileComputePipeline, vy_compile_compute_pipeline_fn);
|
||||||
RETRIEVE_SYMBOL(CompileGraphicsPipeline,
|
RETRIEVE_SYMBOL(CompileGraphicsPipeline, vy_compile_graphics_pipeline_fn);
|
||||||
vy_compile_graphics_pipeline_fn);
|
|
||||||
} else {
|
} else {
|
||||||
vyReportError("GFX",
|
vyReportError("GFX",
|
||||||
"Unsupported renderer backend: (%s) %s",
|
"Unsupported renderer backend: (%s) %s",
|
||||||
@ -65,12 +59,12 @@ static bool LoadRenderer(void) {
|
|||||||
rt_Renderer.s);
|
rt_Renderer.s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#undef RETRIEVE_SYMBOL
|
#undef RETRIEVE_SYMBOL
|
||||||
#else
|
#else
|
||||||
g_renderer.RegisterCVars = &vyRenRegisterCVars;
|
g_renderer.RegisterCVars = &vyRenRegisterCVars;
|
||||||
g_renderer.Init = &vyRenInit;
|
g_renderer.Init = &vyRenInit;
|
||||||
g_renderer.Shutdown = &vyRenShutdown;
|
g_renderer.Shutdown = &vyRenShutdown;
|
||||||
g_renderer.CompileComputePipeline = &vyRenCompileComputePipeline;
|
g_renderer.CompileComputePipeline = &vyRenCompileComputePipeline;
|
||||||
g_renderer.CompileGraphicsPipeline = &vyRenCompileGraphicsPipeline;
|
g_renderer.CompileGraphicsPipeline = &vyRenCompileGraphicsPipeline;
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
|
@ -60,6 +60,6 @@ void vyInitJobSystem(unsigned int worker_count) {
|
|||||||
worker_count = MAX_WORKERS;
|
worker_count = MAX_WORKERS;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < worker_count; ++i) {
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "gfx.h"
|
#include "gfx.h"
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
#include "assets.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct HINSTANCE__;
|
struct HINSTANCE__;
|
||||||
@ -38,11 +39,24 @@ typedef struct {
|
|||||||
size_t fragment_source_length;
|
size_t fragment_source_length;
|
||||||
} vy_graphics_pipeline_info;
|
} 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 void vy_register_renderer_cvars_fn(void);
|
||||||
typedef vy_result vy_init_renderer_fn(const vy_renderer_init_info *info);
|
typedef vy_result vy_init_renderer_fn(const vy_renderer_init_info *info);
|
||||||
typedef void vy_shutdown_renderer_fn(void);
|
typedef void vy_shutdown_renderer_fn(void);
|
||||||
typedef vy_gfx_pipeline_handle
|
typedef vy_gfx_pipeline_handle vy_compile_compute_pipeline_fn(const vy_compute_pipeline_info *info);
|
||||||
vy_compile_compute_pipeline_fn(const vy_compute_pipeline_info *info);
|
|
||||||
typedef vy_gfx_pipeline_handle
|
typedef vy_gfx_pipeline_handle
|
||||||
vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info);
|
vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info);
|
||||||
|
|
||||||
|
@ -4,30 +4,82 @@
|
|||||||
/* basic types and macros */
|
/* basic types and macros */
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(VY_STATIC_LIB)
|
#if defined(_MSC_VER) && !defined(VY_STATIC_LIB)
|
||||||
#define VY_DLLEXPORT __declspec(dllexport)
|
#define VY_DLLEXPORT __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
#define VY_DLLEXPORT
|
#define VY_DLLEXPORT
|
||||||
#endif
|
#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_UNUSED(x) ((void)sizeof((x)))
|
||||||
#define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0]))
|
#define VY_ARRAY_COUNT(x) (sizeof((x)) / sizeof((x)[0]))
|
||||||
|
|
||||||
#define VY_KB(n) ((n) * 1024U)
|
#define VY_KB(n) ((n)*1024U)
|
||||||
#define VY_MB(n) ((n) * 1024U * 1024U)
|
#define VY_MB(n) ((n)*1024U * 1024U)
|
||||||
#define VY_GB(n) ((n) * 1024U * 1024U * 1024U)
|
#define VY_GB(n) ((n)*1024U * 1024U * 1024U)
|
||||||
|
|
||||||
typedef unsigned int vy_result;
|
typedef unsigned int vy_result;
|
||||||
#define VY_SUCCESS 0
|
#define VY_SUCCESS 0
|
||||||
|
#define VY_UNKNOWN_ERROR (vy_result)-1
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *start;
|
const char *start;
|
||||||
unsigned int length;
|
unsigned int length;
|
||||||
} vy_text_span;
|
} 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 vyReportError(const char *subsystem, const char *fmt, ...);
|
||||||
|
|
||||||
VY_DLLEXPORT void vyLog(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
|
#endif
|
||||||
|
@ -6,8 +6,7 @@ extern vy_cvar rt_WindowWidth;
|
|||||||
extern vy_cvar rt_WindowHeight;
|
extern vy_cvar rt_WindowHeight;
|
||||||
extern vy_cvar rt_BufferManagerMemory;
|
extern vy_cvar rt_BufferManagerMemory;
|
||||||
|
|
||||||
void __RegisterRuntimeCVars(void)
|
void __RegisterRuntimeCVars(void) {
|
||||||
{
|
|
||||||
vyRegisterCVAR(&rt_Renderer);
|
vyRegisterCVAR(&rt_Renderer);
|
||||||
vyRegisterCVAR(&rt_Fullscreen);
|
vyRegisterCVAR(&rt_Fullscreen);
|
||||||
vyRegisterCVAR(&rt_WindowWidth);
|
vyRegisterCVAR(&rt_WindowWidth);
|
||||||
|
38
src/runtime/text.c
Normal file
38
src/runtime/text.c
Normal 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
|
||||||
|
}
|
@ -34,8 +34,10 @@ typedef struct vy_thread_s vy_thread;
|
|||||||
|
|
||||||
typedef void vy_thread_entry_fn(void *param);
|
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 void vyJoinThread(vy_thread *thread);
|
||||||
|
|
||||||
|
VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
struct vy_condition_var_s {
|
struct vy_condition_var_s {
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
ptrdiff_t next_reusable;
|
ptrdiff_t next_reusable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_CONDS 1024
|
#define MAX_CONDS 1024
|
||||||
vy_condition_var _conds[MAX_CONDS];
|
vy_condition_var _conds[MAX_CONDS];
|
||||||
static ptrdiff_t _first_reusable = MAX_CONDS;
|
static ptrdiff_t _first_reusable = MAX_CONDS;
|
||||||
static ptrdiff_t _next = 0;
|
static ptrdiff_t _next = 0;
|
||||||
@ -68,23 +68,21 @@ VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var) {
|
|||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
struct vy_condition_var_s {
|
struct vy_condition_var_s {
|
||||||
CRITICAL_SECTION critical_section;
|
CRITICAL_SECTION critical_section;
|
||||||
CONDITION_VARIABLE cond;
|
CONDITION_VARIABLE cond;
|
||||||
ptrdiff_t next_reusable;
|
ptrdiff_t next_reusable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_CONDS 1024
|
#define MAX_CONDS 1024
|
||||||
vy_condition_var _conds[MAX_CONDS];
|
vy_condition_var _conds[MAX_CONDS];
|
||||||
static ptrdiff_t _first_reusable = MAX_CONDS;
|
static ptrdiff_t _first_reusable = MAX_CONDS;
|
||||||
static ptrdiff_t _next = 0;
|
static ptrdiff_t _next = 0;
|
||||||
static HANDLE _guard;
|
static HANDLE _guard;
|
||||||
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
|
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
|
||||||
|
|
||||||
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
|
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) {
|
||||||
PVOID parameter,
|
|
||||||
PVOID *context) {
|
|
||||||
VY_UNUSED(initOnce);
|
VY_UNUSED(initOnce);
|
||||||
VY_UNUSED(parameter);
|
VY_UNUSED(parameter);
|
||||||
VY_UNUSED(context);
|
VY_UNUSED(context);
|
||||||
|
@ -2,24 +2,22 @@
|
|||||||
#include "threading.h"
|
#include "threading.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
struct vy_mutex_s {
|
struct vy_mutex_s {
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
ptrdiff_t next_reusable;
|
ptrdiff_t next_reusable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_MUTEX 1024
|
#define MAX_MUTEX 1024
|
||||||
static vy_mutex _mutex[MAX_MUTEX];
|
static vy_mutex _mutex[MAX_MUTEX];
|
||||||
static ptrdiff_t _first_reusable = MAX_MUTEX;
|
static ptrdiff_t _first_reusable = MAX_MUTEX;
|
||||||
static ptrdiff_t _next = 0;
|
static ptrdiff_t _next = 0;
|
||||||
static HANDLE _guard;
|
static HANDLE _guard;
|
||||||
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
|
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
|
||||||
|
|
||||||
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
|
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) {
|
||||||
PVOID parameter,
|
|
||||||
PVOID *context) {
|
|
||||||
VY_UNUSED(initOnce);
|
VY_UNUSED(initOnce);
|
||||||
VY_UNUSED(parameter);
|
VY_UNUSED(parameter);
|
||||||
VY_UNUSED(context);
|
VY_UNUSED(context);
|
||||||
@ -28,7 +26,6 @@ static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
|
|||||||
return _guard != NULL;
|
return _guard != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
|
VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
|
||||||
if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
|
if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
|
||||||
vyReportError("core", "Failed to initialize the guard mutex.");
|
vyReportError("core", "Failed to initialize the guard mutex.");
|
||||||
@ -74,13 +71,13 @@ VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
|
|||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
struct vy_mutex_s {
|
struct vy_mutex_s {
|
||||||
pthread_mutex_t handle;
|
pthread_mutex_t handle;
|
||||||
ptrdiff_t next_reusable;
|
ptrdiff_t next_reusable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_MUTEX 1024
|
#define MAX_MUTEX 1024
|
||||||
static vy_mutex _mutex[MAX_MUTEX];
|
static vy_mutex _mutex[MAX_MUTEX];
|
||||||
static ptrdiff_t _first_reusable = MAX_MUTEX;
|
static ptrdiff_t _first_reusable = MAX_MUTEX;
|
||||||
static ptrdiff_t _next = 0;
|
static ptrdiff_t _next = 0;
|
||||||
|
@ -17,7 +17,7 @@ struct vy_thread_s {
|
|||||||
bool needs_join;
|
bool needs_join;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_THREADS 256
|
#define MAX_THREADS 256
|
||||||
static vy_thread _threads[MAX_THREADS];
|
static vy_thread _threads[MAX_THREADS];
|
||||||
static ptrdiff_t _first_reusable = MAX_THREADS;
|
static ptrdiff_t _first_reusable = MAX_THREADS;
|
||||||
static ptrdiff_t _next = 0;
|
static ptrdiff_t _next = 0;
|
||||||
@ -25,9 +25,7 @@ static ptrdiff_t _next = 0;
|
|||||||
static HANDLE _guard;
|
static HANDLE _guard;
|
||||||
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
|
static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
|
||||||
|
|
||||||
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
|
static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce, PVOID parameter, PVOID *context) {
|
||||||
PVOID parameter,
|
|
||||||
PVOID *context) {
|
|
||||||
VY_UNUSED(initOnce);
|
VY_UNUSED(initOnce);
|
||||||
VY_UNUSED(parameter);
|
VY_UNUSED(parameter);
|
||||||
VY_UNUSED(context);
|
VY_UNUSED(context);
|
||||||
@ -44,7 +42,7 @@ static DWORD WINAPI win32ThreadWrapper(LPVOID arg) {
|
|||||||
return 0;
|
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)) {
|
if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
|
||||||
vyReportError("core", "Failed to initialize the guard mutex.");
|
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;
|
++_next;
|
||||||
}
|
}
|
||||||
if (thrd) {
|
if (thrd) {
|
||||||
thrd->entry = entry;
|
thrd->entry = entry;
|
||||||
thrd->param = param;
|
thrd->param = param;
|
||||||
thrd->handle = CreateThread(NULL, 0, win32ThreadWrapper, (LPVOID)thrd, 0, NULL);
|
thrd->handle = CreateThread(NULL, 0, win32ThreadWrapper, (LPVOID)thrd, 0, NULL);
|
||||||
if (thrd->handle == NULL) {
|
if (thrd->handle == NULL) {
|
||||||
vyLog("core", "Thread creation failed");
|
vyLog("core", "Thread creation failed");
|
||||||
thrd = NULL;
|
thrd = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WCHAR wname[64];
|
||||||
|
if (thrd && name && vyUTF8ToWStr(name, wname, sizeof(wname)) == VY_SUCCESS)
|
||||||
|
SetThreadDescription(thrd->handle, wname);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
vyReportError("core", "Ran out of thread objects");
|
vyReportError("core", "Ran out of thread objects");
|
||||||
}
|
}
|
||||||
@ -99,10 +102,17 @@ VY_DLLEXPORT void vyJoinThread(vy_thread *thread) {
|
|||||||
ReleaseMutex(_guard);
|
ReleaseMutex(_guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT unsigned int vyGetCPUCoreCount(void) {
|
||||||
|
SYSTEM_INFO info;
|
||||||
|
GetSystemInfo(&info);
|
||||||
|
return (unsigned int)info.dwNumberOfProcessors;
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
|
||||||
#include <pthread.h>
|
#define _GNU_SOURCE
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
struct vy_thread_s {
|
struct vy_thread_s {
|
||||||
pthread_t handle;
|
pthread_t handle;
|
||||||
@ -114,7 +124,7 @@ struct vy_thread_s {
|
|||||||
bool needs_join;
|
bool needs_join;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_THREADS 256
|
#define MAX_THREADS 256
|
||||||
static vy_thread _threads[MAX_THREADS];
|
static vy_thread _threads[MAX_THREADS];
|
||||||
static ptrdiff_t _first_reusable = MAX_THREADS;
|
static ptrdiff_t _first_reusable = MAX_THREADS;
|
||||||
static ptrdiff_t _next = 0;
|
static ptrdiff_t _next = 0;
|
||||||
@ -129,7 +139,7 @@ static void *linuxThreadWrapper(void *arg) {
|
|||||||
return NULL;
|
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;
|
vy_thread *thrd = NULL;
|
||||||
pthread_mutex_lock(&_guard);
|
pthread_mutex_lock(&_guard);
|
||||||
if (_first_reusable < MAX_THREADS) {
|
if (_first_reusable < MAX_THREADS) {
|
||||||
@ -147,11 +157,12 @@ VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) {
|
|||||||
if (thrd) {
|
if (thrd) {
|
||||||
thrd->entry = entry;
|
thrd->entry = entry;
|
||||||
thrd->param = param;
|
thrd->param = param;
|
||||||
if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) !=
|
if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) != 0) {
|
||||||
0) {
|
|
||||||
vyLog("core", "Thread creation failed");
|
vyLog("core", "Thread creation failed");
|
||||||
thrd = NULL;
|
thrd = NULL;
|
||||||
}
|
}
|
||||||
|
if (thrd && name)
|
||||||
|
pthread_setname_np(thrd->handle, name);
|
||||||
} else {
|
} else {
|
||||||
vyReportError("core", "Ran out of thread objects");
|
vyReportError("core", "Ran out of thread objects");
|
||||||
}
|
}
|
||||||
@ -169,4 +180,9 @@ VY_DLLEXPORT void vyJoinThread(vy_thread *thread) {
|
|||||||
pthread_mutex_unlock(&_guard);
|
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
|
#endif
|
||||||
|
73
src/tests/rttest.c
Normal file
73
src/tests/rttest.c
Normal 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;
|
||||||
|
}
|
@ -1,20 +1,93 @@
|
|||||||
|
#include "assetmeta.h"
|
||||||
#include "processing.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/aio.h"
|
||||||
#include "runtime/buffer_manager.h"
|
#include "runtime/buffer_manager.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
extern vy_result vyProcessPipelineFile(vy_file_id file,
|
extern vy_result vyProcessPipelineFile(vy_file_id file,
|
||||||
void *buffer,
|
void *buffer,
|
||||||
size_t size,
|
size_t size,
|
||||||
|
uint32_t flags,
|
||||||
vy_processor_output *output);
|
vy_processor_output *output);
|
||||||
|
|
||||||
extern vy_result vyProcessShaderFile(vy_file_id file,
|
extern vy_result vyProcessShaderFile(vy_file_id file,
|
||||||
void *buffer,
|
void *buffer,
|
||||||
size_t size,
|
size_t size,
|
||||||
|
uint32_t flags,
|
||||||
vy_processor_output *output);
|
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) {
|
int main(int argc, char **argv) {
|
||||||
/* Setup */
|
/* Setup */
|
||||||
if (vyInitFileTab(4096) != VY_SUCCESS) {
|
if (vyInitFileTab(4096) != VY_SUCCESS) {
|
||||||
@ -27,22 +100,37 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
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)
|
if (vyAddAssetProcessor(".pipeline", vyProcessPipelineFile) != VY_SUCCESS)
|
||||||
return 1;
|
return 1;
|
||||||
if (vyAddAssetProcessor(".shader", vyProcessShaderFile) != VY_SUCCESS)
|
if (vyAddAssetProcessor(".glsl", vyProcessShaderFile) != VY_SUCCESS)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (vyStartProcessing() != VY_SUCCESS) {
|
if (vyStartProcessing() != VY_SUCCESS) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
vyAddFileToProcessingQueue(vyAddFile("shader\\cell.pipeline"));
|
vyAddFileToProcessingQueue(vyAddFile("shader\\cell.pipeline"), 0);
|
||||||
while (1)
|
|
||||||
_sleep(10);
|
vyWaitUntilProcessingIsFinished();
|
||||||
|
|
||||||
vyStopProcessing();
|
vyStopProcessing();
|
||||||
|
|
||||||
|
vySaveAssetMeta();
|
||||||
|
|
||||||
|
vyShutdownBufferManager();
|
||||||
|
vyShutdownAIO();
|
||||||
vyShutdownFileTab();
|
vyShutdownFileTab();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
219
src/tools/assetc/assetmeta.c
Normal file
219
src/tools/assetc/assetmeta.c
Normal 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);
|
||||||
|
}
|
27
src/tools/assetc/assetmeta.h
Normal file
27
src/tools/assetc/assetmeta.h
Normal 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
|
13
src/tools/assetc/compiled.h
Normal file
13
src/tools/assetc/compiled.h
Normal 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
|
209
src/tools/assetc/description_parser.c
Normal file
209
src/tools/assetc/description_parser.c
Normal 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);
|
||||||
|
}
|
53
src/tools/assetc/description_parser.h
Normal file
53
src/tools/assetc/description_parser.h
Normal 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
|
5
src/tools/assetc/discovery.c
Normal file
5
src/tools/assetc/discovery.c
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
void DiscoverAssets(void) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
28
src/tools/assetc/options.h
Normal file
28
src/tools/assetc/options.h
Normal 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
|
90
src/tools/assetc/packages.c
Normal file
90
src/tools/assetc/packages.c
Normal 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);
|
||||||
|
}
|
13
src/tools/assetc/packages.h
Normal file
13
src/tools/assetc/packages.h
Normal 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
|
@ -10,213 +10,36 @@
|
|||||||
#include "runtime/gfx.h"
|
#include "runtime/gfx.h"
|
||||||
#include "runtime/handles.h"
|
#include "runtime/handles.h"
|
||||||
#include "runtime/runtime.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"
|
#include "processing.h"
|
||||||
|
#include "utils.h"
|
||||||
typedef enum {
|
#include "processing_flags.h"
|
||||||
VY_STMT_FORM_VALUE,
|
|
||||||
VY_STMT_FORM_LIST,
|
|
||||||
} vy_stmt_form;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int first;
|
vy_attribute_binding *uniform_bindings;
|
||||||
unsigned int count;
|
vy_attribute_binding *storage_bindings;
|
||||||
} vy_parsed_stmt_list;
|
vy_attribute_binding *texture_bindings;
|
||||||
|
|
||||||
typedef struct {
|
vy_uid vertex_shader;
|
||||||
vy_stmt_form form;
|
vy_uid fragment_shader;
|
||||||
vy_text_span attribute;
|
vy_uid compute_shader;
|
||||||
union {
|
|
||||||
vy_text_span value;
|
|
||||||
unsigned int list_index;
|
|
||||||
};
|
|
||||||
/* For lists */
|
|
||||||
unsigned int next;
|
|
||||||
} vy_parsed_stmt;
|
|
||||||
|
|
||||||
typedef struct {
|
/* TODO Fixed function settings */
|
||||||
const char *file;
|
|
||||||
const char *text;
|
|
||||||
size_t at;
|
|
||||||
size_t length;
|
|
||||||
int line;
|
|
||||||
|
|
||||||
vy_parsed_stmt *statements;
|
/* Sampler settings */
|
||||||
unsigned int statement_count;
|
|
||||||
unsigned int statement_capacity;
|
|
||||||
|
|
||||||
vy_parsed_stmt_list *statement_lists;
|
uint16_t uniform_binding_count;
|
||||||
unsigned int statement_list_count;
|
uint16_t storage_binding_count;
|
||||||
unsigned int statement_list_capacity;
|
uint16_t texture_binding_count;
|
||||||
} vy_parse_state;
|
} vy_parsed_pipeline_data;
|
||||||
|
|
||||||
static bool IsAllowedChar(char c) {
|
static void
|
||||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
|
DbgPrintShaderFile(const vy_parse_state *state, unsigned int list_index, unsigned int indent) {
|
||||||
(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) {
|
|
||||||
assert(list_index < state->statement_list_count);
|
assert(list_index < state->statement_list_count);
|
||||||
const vy_parsed_stmt_list *list = &state->statement_lists[list_index];
|
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);
|
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) {
|
static bool ParseBindingIndex(vy_text_span span, unsigned int *index) {
|
||||||
if (span.length == 0)
|
if (span.length == 0)
|
||||||
return false;
|
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');
|
unsigned int digit = (unsigned int)(span.start[at] - '0');
|
||||||
n += digit * exp;
|
n += digit * exp;
|
||||||
} else {
|
} else {
|
||||||
vyReportError("GFX",
|
vyReportError("GFX", "Unexpected non-digit character in binding index");
|
||||||
"Unexpected non-digit character in binding index");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
--at;
|
--at;
|
||||||
@ -288,15 +83,12 @@ static bool ParseBindingIndex(vy_text_span span, unsigned int *index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static vy_attribute_value ParseBindingValue(vy_text_span span) {
|
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;
|
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;
|
return VY_ATTRIBUTE_VALUE_MATERIAL_NORMAL;
|
||||||
}
|
}
|
||||||
vyReportError("GFX",
|
vyReportError("GFX", "Unsupported binding value %*.s", span.length, span.start);
|
||||||
"Unsupported binding value %*.s",
|
|
||||||
span.length,
|
|
||||||
span.start);
|
|
||||||
return VY_ATTRIBUTE_VALUE_UNDEFINED;
|
return VY_ATTRIBUTE_VALUE_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,8 +97,8 @@ static bool ParseBindings(vy_parse_state *state,
|
|||||||
const char *name,
|
const char *name,
|
||||||
const char *file_path,
|
const char *file_path,
|
||||||
vy_attribute_binding **p_bindings,
|
vy_attribute_binding **p_bindings,
|
||||||
unsigned int *p_binding_count) {
|
uint16_t *p_binding_count) {
|
||||||
const vy_parsed_stmt *bindings = FindStatement(state, root_list, name);
|
const vy_parsed_stmt *bindings = vyFindStatement(state, root_list, name);
|
||||||
if (bindings) {
|
if (bindings) {
|
||||||
if (bindings->form != VY_STMT_FORM_LIST) {
|
if (bindings->form != VY_STMT_FORM_LIST) {
|
||||||
vyReportError("GFX",
|
vyReportError("GFX",
|
||||||
@ -316,10 +108,9 @@ static bool ParseBindings(vy_parse_state *state,
|
|||||||
file_path);
|
file_path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const vy_parsed_stmt_list *binding_list =
|
const vy_parsed_stmt_list *binding_list = &state->statement_lists[bindings->list_index];
|
||||||
&state->statement_lists[bindings->list_index];
|
|
||||||
vy_attribute_binding *shader_bindings =
|
vy_attribute_binding *shader_bindings =
|
||||||
malloc(sizeof(vy_attribute_binding) * binding_list->count);
|
vyAllocBuffer(sizeof(vy_attribute_binding) * binding_list->count);
|
||||||
if (!bindings) {
|
if (!bindings) {
|
||||||
vyReportError("GFX", "Out of memory");
|
vyReportError("GFX", "Out of memory");
|
||||||
return false;
|
return false;
|
||||||
@ -329,22 +120,23 @@ static bool ParseBindings(vy_parse_state *state,
|
|||||||
unsigned int stmt_index = binding_list->first;
|
unsigned int stmt_index = binding_list->first;
|
||||||
for (unsigned int i = 0; i < binding_list->count; ++i) {
|
for (unsigned int i = 0; i < binding_list->count; ++i) {
|
||||||
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
const vy_parsed_stmt *stmt = &state->statements[stmt_index];
|
||||||
if (!ParseBindingIndex(stmt->attribute,
|
if (!ParseBindingIndex(stmt->attribute, &shader_bindings[i].index)) {
|
||||||
&shader_bindings[i].index)) {
|
vyReleaseBuffer(shader_bindings,
|
||||||
free(shader_bindings);
|
sizeof(vy_attribute_binding) * binding_list->count);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
shader_bindings[i].value = ParseBindingValue(stmt->value);
|
shader_bindings[i].value = ParseBindingValue(stmt->value);
|
||||||
if (shader_bindings[i].value == VY_ATTRIBUTE_VALUE_UNDEFINED) {
|
if (shader_bindings[i].value == VY_ATTRIBUTE_VALUE_UNDEFINED) {
|
||||||
free(shader_bindings);
|
vyReleaseBuffer(shader_bindings,
|
||||||
|
sizeof(vy_attribute_binding) * binding_list->count);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
stmt_index = stmt->next;
|
stmt_index = stmt->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
*p_bindings = shader_bindings;
|
*p_bindings = shader_bindings;
|
||||||
*p_binding_count = binding_count;
|
*p_binding_count = (uint16_t)binding_count;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
*p_bindings = NULL;
|
*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,
|
static vy_result ParseShader(vy_parse_state *state,
|
||||||
unsigned int root_list,
|
unsigned int root_list,
|
||||||
const char *name,
|
const char *name,
|
||||||
const char *file_path,
|
const char *file_path,
|
||||||
|
uint32_t processing_flags,
|
||||||
vy_uid *p_shader_uid) {
|
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) {
|
||||||
if (stmt->form != VY_STMT_FORM_VALUE) {
|
if (stmt->form != VY_STMT_FORM_LIST) {
|
||||||
vyReportError("GFX",
|
vyReportError("GFX",
|
||||||
"Expected a file name as the value of "
|
"Expected a list as the value of "
|
||||||
"\"%s\" in %s",
|
"\"%s\" in %s",
|
||||||
name,
|
name,
|
||||||
file_path);
|
file_path);
|
||||||
return VY_PROCESSING_FAILED;
|
return VY_PROCESSING_FAILED;
|
||||||
}
|
}
|
||||||
vy_file_id shader_file = vyAddFileFromSpan(stmt->value);
|
const vy_parsed_stmt_list *shader_list = &state->statement_lists[stmt->list_index];
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
*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_SUCCESS;
|
||||||
}
|
}
|
||||||
return VY_PROCESSING_FAILED;
|
return VY_PROCESSING_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static vy_result ParsePipelineFile(vy_file_id fid,
|
static uint32_t
|
||||||
const char *text,
|
ParseOptimizationLevel(vy_parse_state *state, unsigned int root_list, const char *file_path) {
|
||||||
size_t length,
|
uint32_t optimization_level;
|
||||||
vy_pipeline_data *pipeline) {
|
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:
|
/* This is the grammar for pipeline files:
|
||||||
* <stmt-list> ::= <stmt>*
|
* <stmt-list> ::= <stmt>*
|
||||||
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
|
* <stmt> ::= <attribute> ( ( <value> ';' ) | ( '{' <stmt-list> '}' ) )
|
||||||
* <attribute> ::= [:alnum:]*
|
* <attribute> ::= [:alnum:]*
|
||||||
* <value>:: = [:alnum:]* */
|
* <value>:: = [:alnum:]* */
|
||||||
const char *file_path = vyGetFilePath(fid);
|
const char *file_path = vyGetFilePath(fid);
|
||||||
vy_parse_state state = {.text = text,
|
vy_parse_state state;
|
||||||
.at = 0,
|
unsigned int root_list;
|
||||||
.length = length,
|
vy_result result = vyParseDescription(text, length, file_path, &root_list, &state);
|
||||||
.line = 1,
|
if (result != VY_SUCCESS) {
|
||||||
.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;
|
|
||||||
goto out;
|
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 */
|
/* Process shader stages */
|
||||||
pipeline->shader_stage_bitmask = 0;
|
if (ParseShader(&state,
|
||||||
vy_result shader_result;
|
root_list,
|
||||||
if ((shader_result = ParseShader(&state,
|
"vertex",
|
||||||
root_list,
|
file_path,
|
||||||
"vertex",
|
VY_SHADER_FLAG_VERTEX | optimization,
|
||||||
file_path,
|
&pipeline->vertex_shader) == VY_PROCESSING_TRY_AGAIN) {
|
||||||
&pipeline->vertex_shader)) == VY_SUCCESS) {
|
|
||||||
|
|
||||||
pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_VERTEX;
|
|
||||||
} else if (shader_result == VY_PROCESSING_TRY_AGAIN) {
|
|
||||||
result = VY_PROCESSING_TRY_AGAIN;
|
result = VY_PROCESSING_TRY_AGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((shader_result = ParseShader(&state,
|
if (ParseShader(&state,
|
||||||
root_list,
|
root_list,
|
||||||
"fragment",
|
"fragment",
|
||||||
file_path,
|
file_path,
|
||||||
&pipeline->vertex_shader)) == VY_SUCCESS) {
|
VY_SHADER_FLAG_FRAGMENT | optimization,
|
||||||
|
&pipeline->fragment_shader) == VY_PROCESSING_TRY_AGAIN) {
|
||||||
pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_FRAGMENT;
|
|
||||||
} else if (shader_result == VY_PROCESSING_TRY_AGAIN) {
|
|
||||||
result = VY_PROCESSING_TRY_AGAIN;
|
result = VY_PROCESSING_TRY_AGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((shader_result = ParseShader(&state,
|
if (ParseShader(&state,
|
||||||
root_list,
|
root_list,
|
||||||
"compute",
|
"compute",
|
||||||
file_path,
|
file_path,
|
||||||
&pipeline->vertex_shader)) == VY_SUCCESS) {
|
VY_SHADER_FLAG_COMPUTE | optimization,
|
||||||
|
&pipeline->compute_shader) == VY_PROCESSING_TRY_AGAIN) {
|
||||||
pipeline->shader_stage_bitmask |= VY_SHADER_STAGE_BIT_COMPUTE;
|
|
||||||
} else if (shader_result == VY_PROCESSING_TRY_AGAIN) {
|
|
||||||
result = VY_PROCESSING_TRY_AGAIN;
|
result = VY_PROCESSING_TRY_AGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process bindings */
|
/* Process bindings */
|
||||||
pipeline->texture_bindings = NULL;
|
pipeline->texture_bindings = NULL;
|
||||||
pipeline->texture_binding_count = 0;
|
pipeline->texture_binding_count = 0;
|
||||||
pipeline->uniform_bindings = NULL;
|
pipeline->uniform_bindings = NULL;
|
||||||
pipeline->uniform_binding_count = 0;
|
pipeline->uniform_binding_count = 0;
|
||||||
@ -528,11 +347,68 @@ out:
|
|||||||
vy_result vyProcessPipelineFile(vy_file_id file,
|
vy_result vyProcessPipelineFile(vy_file_id file,
|
||||||
void *buffer,
|
void *buffer,
|
||||||
size_t size,
|
size_t size,
|
||||||
|
uint32_t flags,
|
||||||
vy_processor_output *output) {
|
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);
|
vy_result result = ParsePipelineFile(file, buffer, size, &tmp);
|
||||||
if (result == VY_SUCCESS) {
|
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;
|
return result;
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
#ifndef VY_ASSETC_PROCESSING_H
|
#ifndef VY_ASSETC_PROCESSING_H
|
||||||
#define VY_ASSETC_PROCESSING_H
|
#define VY_ASSETC_PROCESSING_H
|
||||||
|
|
||||||
#include "runtime/file_tab.h"
|
|
||||||
#include "runtime/assets.h"
|
#include "runtime/assets.h"
|
||||||
|
#include "runtime/file_tab.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
VY_PROCESSING_FAILED = VY_SUCCESS + 1,
|
VY_PROCESSING_FAILED = VY_SUCCESS + 1,
|
||||||
@ -10,22 +10,35 @@ enum {
|
|||||||
VY_PROCESSING_TRY_AGAIN,
|
VY_PROCESSING_TRY_AGAIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vy_file_id output_file;
|
||||||
|
|
||||||
|
} vy_asset_options;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vy_uid asset_uid;
|
vy_uid asset_uid;
|
||||||
|
|
||||||
|
/* Allocated via the vyAllocBuffer API */
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
} vy_processor_output;
|
} 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 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);
|
vy_result vyStartProcessing(void);
|
||||||
void vyStopProcessing(void);
|
void vyStopProcessing(void);
|
||||||
|
|
||||||
|
vy_uid vyCalculateUID(const char *name);
|
||||||
|
|
||||||
void vyInitUIDMap(void);
|
void vyWaitUntilProcessingIsFinished(void);
|
||||||
void vyAddUIDMapping(vy_file_id fid, vy_uid uid);
|
|
||||||
vy_uid vyLookupUID(vy_file_id fid);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
20
src/tools/assetc/processing_flags.h
Normal file
20
src/tools/assetc/processing_flags.h
Normal 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
|
@ -1,17 +1,25 @@
|
|||||||
#include "processing.h"
|
#include "processing.h"
|
||||||
#include "utils.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/aio.h"
|
||||||
#include "runtime/buffer_manager.h"
|
#include "runtime/buffer_manager.h"
|
||||||
|
#include "runtime/file_tab.h"
|
||||||
|
#include "runtime/threading.h"
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vy_file_id fid;
|
vy_file_id fid;
|
||||||
|
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
/* How many times has this file been added? */
|
/* How many times has this file been added? */
|
||||||
unsigned int turn;
|
unsigned int turn;
|
||||||
} vy_file_processing_queue_entry;
|
} vy_file_processing_queue_entry;
|
||||||
@ -23,40 +31,94 @@ typedef struct {
|
|||||||
unsigned int tail;
|
unsigned int tail;
|
||||||
} vy_file_processing_queue;
|
} 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 vy_mutex *_guard;
|
||||||
static bool _keep_running;
|
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) {
|
#define MAX_PROCESSING_THREADS 16
|
||||||
if (!_guard)
|
|
||||||
_guard = vyCreateMutex();
|
|
||||||
|
|
||||||
|
#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;
|
vy_result result = VY_SUCCESS;
|
||||||
|
|
||||||
|
vyLog("ASSETC", "Adding %s to processing queue.", vyGetFilePath(file));
|
||||||
|
|
||||||
vy_file_processing_queue_entry entry = {
|
vy_file_processing_queue_entry entry = {
|
||||||
.fid = file,
|
.fid = file,
|
||||||
.turn = turn,
|
.flags = flags,
|
||||||
|
.turn = turn,
|
||||||
};
|
};
|
||||||
vyLockMutex(_guard);
|
if ((queue->head + 1) % QUEUE_LENGTH != queue->tail) {
|
||||||
if ((_processing_queue.tail + 1) % QUEUE_LENGTH != _processing_queue.head) {
|
unsigned int slot = queue->head;
|
||||||
unsigned int slot = _processing_queue.head;
|
queue->entries[slot] = entry;
|
||||||
_processing_queue.entries[slot] = entry;
|
queue->head = (queue->head + 1) % QUEUE_LENGTH;
|
||||||
_processing_queue.head = (_processing_queue.head + 1) % QUEUE_LENGTH;
|
|
||||||
} else {
|
} else {
|
||||||
vyReportError("ASSETC", "The processing queue is full!");
|
vyReportError("ASSETC", "The processing queue is full!");
|
||||||
result = 1;
|
result = 1;
|
||||||
}
|
}
|
||||||
vyUnlockMutex(_guard);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vy_result vyAddFileToProcessingQueue(vy_file_id file, uint32_t flags) {
|
||||||
|
assert(_guard != NULL);
|
||||||
|
|
||||||
vy_result vyAddFileToProcessingQueue(vy_file_id file) {
|
vyLockMutex(_guard);
|
||||||
return vyAddFileToProcessingQueueImpl(file, 1);
|
vy_result res = vyAddFileToProcessingQueueImpl(_processing_queue, file, flags, 1);
|
||||||
|
vyUnlockMutex(_guard);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define MAX_PROCESSORS 256
|
#define MAX_PROCESSORS 256
|
||||||
static vy_processor_fn *_processor_fns[MAX_PROCESSORS];
|
static vy_processor_fn *_processor_fns[MAX_PROCESSORS];
|
||||||
static const char *_processor_exts[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!");
|
vyReportError("ASSETC", "Too many asset processor functions!");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
_processor_fns[_processor_count] = fn;
|
_processor_fns[_processor_count] = fn;
|
||||||
_processor_exts[_processor_count] = file_extension;
|
_processor_exts[_processor_count] = file_extension;
|
||||||
++_processor_count;
|
++_processor_count;
|
||||||
return VY_SUCCESS;
|
return VY_SUCCESS;
|
||||||
@ -89,10 +151,10 @@ static void PopAndSwapSubmittedData(unsigned int at,
|
|||||||
*count = *count - 1;
|
*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 */
|
/* Search for a matching processor function */
|
||||||
const char *path = vyGetFilePath(entry.fid);
|
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) {
|
for (unsigned int i = 0; i < _processor_count; ++i) {
|
||||||
size_t ext_len = strlen(_processor_exts[i]);
|
size_t ext_len = strlen(_processor_exts[i]);
|
||||||
if (ext_len > path_len)
|
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];
|
const char *path_end = &path[path_len - ext_len];
|
||||||
if (memcmp(path_end, _processor_exts[i], ext_len) == 0) {
|
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_processor_output out;
|
||||||
vy_result res = _processor_fns[i](entry.fid, buffer, size, &out);
|
vy_result res = _processor_fns[i](entry.fid, buffer, size, entry.flags, &out);
|
||||||
if (res == VY_PROCESSING_FAILED) {
|
if (res == VY_SUCCESS) {
|
||||||
vyLog("ASSETC", "Failed to process file: %s", path);
|
/* 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) {
|
} else if (res == VY_PROCESSING_TRY_AGAIN) {
|
||||||
if (entry.turn < 2) {
|
if (entry.turn < MAX_TURNS) {
|
||||||
vyAddFileToProcessingQueueImpl(entry.fid, entry.turn + 1);
|
vyLockMutex(_guard);
|
||||||
|
vyAddFileToProcessingQueueImpl(_retry_queue, entry.fid, entry.flags, entry.turn + 1);
|
||||||
|
vyUnlockMutex(_guard);
|
||||||
} else {
|
} else {
|
||||||
vyLog("ASSETC",
|
vyLog("ASSETC",
|
||||||
"File '%s' took too many turns to process: %u",
|
"File '%s' took too many turns to process: %u",
|
||||||
path,
|
path,
|
||||||
entry.turn);
|
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);
|
vyLog("ASSETC", "No asset processor for file: %s", path);
|
||||||
|
return VY_UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessingThread(void *_param) {
|
static void ProcessingThread(void *_param) {
|
||||||
VY_UNUSED(_param);
|
VY_UNUSED(_param);
|
||||||
|
|
||||||
|
|
||||||
vy_file_processing_queue_entry submitted_entries[VY_LOAD_BATCH_MAX_SIZE];
|
vy_file_processing_queue_entry submitted_entries[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
vy_aio_handle submitted_handles[VY_LOAD_BATCH_MAX_SIZE];
|
vy_aio_handle submitted_handles[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
void *submitted_buffers[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;
|
bool got_entry = false;
|
||||||
do {
|
do {
|
||||||
got_entry = false;
|
got_entry = false;
|
||||||
vy_file_processing_queue_entry entry;
|
vy_file_processing_queue_entry entry = {0};
|
||||||
vyLockMutex(_guard);
|
vyLockMutex(_guard);
|
||||||
if (_processing_queue.head != _processing_queue.tail) {
|
if (_processing_queue->head != _processing_queue->tail) {
|
||||||
unsigned int next = _processing_queue.tail;
|
entry = _processing_queue->entries[_processing_queue->tail];
|
||||||
entry = _processing_queue.entries[next];
|
_processing_queue->tail = (_processing_queue->tail + 1) % QUEUE_LENGTH;
|
||||||
_processing_queue.tail =
|
got_entry = true;
|
||||||
(_processing_queue.tail + 1) % QUEUE_LENGTH;
|
} else if (load_batch.num_loads == 0) {
|
||||||
got_entry = true;
|
/* 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);
|
vyUnlockMutex(_guard);
|
||||||
|
|
||||||
|
/* Retry, if we did not get an entry */
|
||||||
if (!got_entry)
|
if (!got_entry)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -159,14 +300,15 @@ static void ProcessingThread(void *_param) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vyLog("ASSETC", "Processing %s", path);
|
||||||
|
|
||||||
size_t fsz = vyGetFileSize(path);
|
size_t fsz = vyGetFileSize(path);
|
||||||
void *dest = vyAllocBuffer(fsz);
|
void *dest = vyAllocBuffer(fsz);
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
vyLog("ASSETC",
|
vyLog("ASSETC", "Ran out of memory for loading the file: %s", path);
|
||||||
"Ran out of memory for loading the file: %s",
|
|
||||||
path);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
memset(dest, 0, fsz);
|
||||||
|
|
||||||
load_sizes[load_batch.num_loads] = fsz;
|
load_sizes[load_batch.num_loads] = fsz;
|
||||||
load_buffers[load_batch.num_loads] = dest;
|
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_batch.loads[load_batch.num_loads].offset = 0;
|
||||||
load_entries[load_batch.num_loads] = entry;
|
load_entries[load_batch.num_loads] = entry;
|
||||||
++load_batch.num_loads;
|
++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];
|
vy_aio_handle load_handles[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
if (load_batch.num_loads > 0) {
|
if (load_batch.num_loads > 0) {
|
||||||
vy_result submit_result =
|
vy_result submit_result = vySubmitLoadBatch(&load_batch, load_handles);
|
||||||
vySubmitLoadBatch(&load_batch, load_handles);
|
|
||||||
if (submit_result != VY_SUCCESS) {
|
if (submit_result != VY_SUCCESS) {
|
||||||
vyLog("ASSETC", "SubmitLoadBatch failed: %u", submit_result);
|
vyLog("ASSETC", "SubmitLoadBatch failed: %u", submit_result);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Process the previously submitted loads */
|
/* Process the previously submitted loads */
|
||||||
while (submitted_outstanding > 0) {
|
while (submitted_outstanding > 0) {
|
||||||
|
vyLockMutex(_guard);
|
||||||
|
_processing_thread_count += 1;
|
||||||
|
vyUnlockMutex(_guard);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < submitted_outstanding; ++i) {
|
for (unsigned int i = 0; i < submitted_outstanding; ++i) {
|
||||||
vy_aio_state state = vyGetAIOState(submitted_handles[i]);
|
vy_aio_state state = vyGetAIOState(submitted_handles[i]);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@ -201,6 +344,7 @@ static void ProcessingThread(void *_param) {
|
|||||||
vyLog("ASSETC",
|
vyLog("ASSETC",
|
||||||
"Loading file %s failed.",
|
"Loading file %s failed.",
|
||||||
vyGetFilePath(submitted_entries[i].fid));
|
vyGetFilePath(submitted_entries[i].fid));
|
||||||
|
vyReleaseAIO(submitted_handles[i]);
|
||||||
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
||||||
PopAndSwapSubmittedData(i,
|
PopAndSwapSubmittedData(i,
|
||||||
&submitted_outstanding,
|
&submitted_outstanding,
|
||||||
@ -227,6 +371,7 @@ static void ProcessingThread(void *_param) {
|
|||||||
ProcessLoadedFile(submitted_entries[i],
|
ProcessLoadedFile(submitted_entries[i],
|
||||||
submitted_buffers[i],
|
submitted_buffers[i],
|
||||||
submitted_sizes[i]);
|
submitted_sizes[i]);
|
||||||
|
vyReleaseAIO(submitted_handles[i]);
|
||||||
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
vyReleaseBuffer(submitted_buffers[i], submitted_sizes[i]);
|
||||||
PopAndSwapSubmittedData(i,
|
PopAndSwapSubmittedData(i,
|
||||||
&submitted_outstanding,
|
&submitted_outstanding,
|
||||||
@ -237,6 +382,10 @@ static void ProcessingThread(void *_param) {
|
|||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vyLockMutex(_guard);
|
||||||
|
_processing_thread_count -= 1;
|
||||||
|
vyUnlockMutex(_guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start new round */
|
/* 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) {
|
vy_result vyStartProcessing(void) {
|
||||||
if (!_guard)
|
if (!_guard)
|
||||||
_guard = vyCreateMutex();
|
_guard = vyCreateMutex();
|
||||||
|
|
||||||
_keep_running = true;
|
#if !FORCE_SINGLE_THREAD
|
||||||
for (unsigned int i = 0; i < NUM_PROCESSING_THREADS; ++i) {
|
_num_processing_threads = vyGetCPUCoreCount();
|
||||||
_processing_threads[i] = vySpawnThread(ProcessingThread, NULL);
|
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]) {
|
if (!_processing_threads[i]) {
|
||||||
vyReportError("ASSETC", "Failed to spawn processing thread %u!", i);
|
vyReportError("ASSETC", "Failed to spawn processing thread %u!", i);
|
||||||
_keep_running = false;
|
_keep_running = false;
|
||||||
@ -274,6 +430,35 @@ vy_result vyStartProcessing(void) {
|
|||||||
|
|
||||||
void vyStopProcessing(void) {
|
void vyStopProcessing(void) {
|
||||||
_keep_running = false;
|
_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]);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,16 +1,122 @@
|
|||||||
|
#include "assetmeta.h"
|
||||||
#include "processing.h"
|
#include "processing.h"
|
||||||
|
#include "processing_flags.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
extern vy_result vyProcessShaderFile(vy_file_id file,
|
#include "runtime/buffer_manager.h"
|
||||||
void *buffer,
|
|
||||||
size_t size,
|
#include <shaderc/shaderc.h>
|
||||||
vy_processor_output *output) {
|
#include <string.h>
|
||||||
/* Scan the first line for the shader type */
|
|
||||||
char *text = buffer;
|
static shaderc_include_result *ResolveInclude(void *user_data,
|
||||||
for (size_t i = 0; i < size; ++i) {
|
const char *requested_source,
|
||||||
if (text[i] == '\n' || text[i] == '\r') {
|
int type,
|
||||||
vyLog("ASSETC", "First Line");
|
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;
|
return VY_SUCCESS;
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
32
src/tools/assetc/uidtable.c
Normal file
32
src/tools/assetc/uidtable.c
Normal 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);
|
||||||
|
}
|
@ -5,19 +5,49 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
size_t vyGetFileSize(const char *path) {
|
size_t vyGetFileSize(const char *path) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WCHAR wpath[MAX_PATH];
|
WCHAR wpath[MAX_PATH];
|
||||||
MultiByteToWideChar(CP_UTF8,
|
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH);
|
||||||
MB_PRECOMPOSED,
|
|
||||||
path,
|
|
||||||
-1,
|
|
||||||
wpath,
|
|
||||||
MAX_PATH);
|
|
||||||
WIN32_FILE_ATTRIBUTE_DATA attribs;
|
WIN32_FILE_ATTRIBUTE_DATA attribs;
|
||||||
if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs))
|
if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs))
|
||||||
return 0;
|
return 0;
|
||||||
return (size_t)attribs.nFileSizeHigh << 32 | (size_t)attribs.nFileSizeLow;
|
return (size_t)attribs.nFileSizeHigh << 32 | (size_t)attribs.nFileSizeLow;
|
||||||
#endif
|
#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
|
||||||
}
|
}
|
@ -1,6 +1,16 @@
|
|||||||
#ifndef VY_ASSETC_UTILS_H
|
#ifndef VY_ASSETC_UTILS_H
|
||||||
#define VY_ASSETC_UTILS_H
|
#define VY_ASSETC_UTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "runtime/file_tab.h"
|
||||||
|
|
||||||
size_t vyGetFileSize(const char *path);
|
size_t vyGetFileSize(const char *path);
|
||||||
|
|
||||||
|
void vySetWorkingDirectory(const char *path);
|
||||||
|
|
||||||
|
uint64_t vyGetCurrentTimestamp(void);
|
||||||
|
|
||||||
|
uint64_t vyGetFileModificationTimestamp(vy_file_id fid);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user