diff --git a/.gitmodules b/.gitmodules
index 79e4283c..6603aede 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -16,3 +16,6 @@
[submodule "external/Vulkan-Headers"]
path = external/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers
+[submodule "external/SPIRV-Headers"]
+ path = external/SPIRV-Headers
+ url = https://github.com/KhronosGroup/SPIRV-Headers
diff --git a/OpenCloud.sln b/OpenCloud.sln
index 845b4bc0..9f3514ae 100644
--- a/OpenCloud.sln
+++ b/OpenCloud.sln
@@ -72,6 +72,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{CBDD
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "volk", "external\volk.vcxproj", "{4CCD9127-5EE0-4D86-97A6-545D1F227804}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vk", "code\vk\vk.vcxproj", "{CFE73B5E-826E-44F9-88B6-F3EF24954890}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -151,6 +153,12 @@ Global
{4CCD9127-5EE0-4D86-97A6-545D1F227804}.FastDebug|x64.Build.0 = FastDebug|x64
{4CCD9127-5EE0-4D86-97A6-545D1F227804}.Release|x64.ActiveCfg = Release|x64
{4CCD9127-5EE0-4D86-97A6-545D1F227804}.Release|x64.Build.0 = Release|x64
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}.Debug|x64.ActiveCfg = Debug|x64
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}.Debug|x64.Build.0 = Debug|x64
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}.FastDebug|x64.ActiveCfg = FastDebug|x64
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}.FastDebug|x64.Build.0 = FastDebug|x64
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}.Release|x64.ActiveCfg = Release|x64
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -167,6 +175,7 @@ Global
{123DEE7E-D3F8-4A2A-95F3-5D4E8EA26FB3} = {B7584972-373B-4199-A285-5CCD599277EE}
{7E35C3E7-B0A9-407A-A27D-E552B43602C2} = {5B4BAF44-C9E2-449A-86B5-DE883F2E2919}
{4CCD9127-5EE0-4D86-97A6-545D1F227804} = {B7584972-373B-4199-A285-5CCD599277EE}
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890} = {5B4BAF44-C9E2-449A-86B5-DE883F2E2919}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {81C91EE3-8C0D-44FC-A3A6-F4D80A915E56}
diff --git a/code/graph/api.h b/code/graph/api.h
new file mode 100644
index 00000000..00e39b19
--- /dev/null
+++ b/code/graph/api.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace graph
+{
+ class api
+ {
+ public:
+ api() = default;
+ virtual ~api() = default;
+ };
+}
\ No newline at end of file
diff --git a/code/graph/graph.vcxproj b/code/graph/graph.vcxproj
index be510ccc..84e685f0 100644
--- a/code/graph/graph.vcxproj
+++ b/code/graph/graph.vcxproj
@@ -19,6 +19,7 @@
+
diff --git a/code/vk/api.cc b/code/vk/api.cc
new file mode 100644
index 00000000..b9c2442f
--- /dev/null
+++ b/code/vk/api.cc
@@ -0,0 +1,69 @@
+#include "common/log.h"
+
+#include "vk/api.h"
+#include "vk/helpers.h"
+
+set_log_channel("vk");
+
+namespace vk
+{
+ api::api() = default;
+ api::~api() = default;
+
+ VkInstance api::create_instance()
+ {
+ const auto api_version = vk::enumerate_instance_version();
+
+ const VkApplicationInfo app =
+ {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pNext = VK_NULL_HANDLE,
+ .pApplicationName = "Open Cloud",
+ .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
+ .pEngineName = "Open Cloud Engine",
+ .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
+ .apiVersion = api_version.value_or(VK_API_VERSION_1_0)
+ };
+
+ log_info("Api Version: {}.{}", VK_VERSION_MAJOR(app.apiVersion), VK_API_VERSION_MINOR(app.apiVersion));
+
+ static constexpr std::array validation_features =
+ {
+ VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
+ VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT,
+ VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
+ };
+
+ VkValidationFeaturesEXT validation_ext =
+ {
+ .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
+ .pNext = VK_NULL_HANDLE,
+ .enabledValidationFeatureCount = static_cast(validation_features.size()),
+ .pEnabledValidationFeatures = validation_features.data(),
+ .disabledValidationFeatureCount = 0,
+ .pDisabledValidationFeatures = VK_NULL_HANDLE
+ };
+
+ VkInstanceCreateInfo create_info =
+ {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pNext = VK_NULL_HANDLE,
+ .flags = 0,
+ .pApplicationInfo = &app,
+ };
+
+ vk::append_structure_to_chain(&create_info, &validation_ext);
+
+ VkInstance instance{ VK_NULL_HANDLE };
+ const VkResult res = vkCreateInstance(&create_info, VK_NULL_HANDLE, &instance);
+
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to create instance: {}", res);
+
+ return VK_NULL_HANDLE;
+ }
+
+ return instance;
+ }
+}
\ No newline at end of file
diff --git a/code/vk/api.h b/code/vk/api.h
new file mode 100644
index 00000000..4fd2a0f9
--- /dev/null
+++ b/code/vk/api.h
@@ -0,0 +1,21 @@
+#pragma once
+#include
+
+#include
+
+#include "graph/api.h"
+
+#include "common/strings.h"
+#include "common/dictionary.h"
+
+namespace vk
+{
+ class api : public graph::api
+ {
+ public:
+ api();
+ ~api();
+
+ static VkInstance create_instance();
+ };
+}
\ No newline at end of file
diff --git a/code/vk/helpers.cc b/code/vk/helpers.cc
new file mode 100644
index 00000000..0319327f
--- /dev/null
+++ b/code/vk/helpers.cc
@@ -0,0 +1,161 @@
+#include "vk/helpers.h"
+
+#include "common/log.h"
+
+set_log_channel("vk");
+
+namespace vk
+{
+ std::string_view result_to_string(VkResult res)
+ {
+ constexpr common::dictionary mapping =
+ {
+ { VK_SUCCESS, "VK_SUCCESS" },
+ { VK_NOT_READY, "VK_NOT_READY" },
+ { VK_TIMEOUT, "VK_TIMEOUT" },
+ { VK_EVENT_SET, "VK_EVENT_SET" },
+ { VK_EVENT_RESET, "VK_EVENT_RESET" },
+ { VK_INCOMPLETE, "VK_INCOMPLETE" },
+ { VK_ERROR_OUT_OF_HOST_MEMORY, "VK_ERROR_OUT_OF_HOST_MEMORY" },
+ { VK_ERROR_OUT_OF_DEVICE_MEMORY, "VK_ERROR_OUT_OF_DEVICE_MEMORY" },
+ { VK_ERROR_INITIALIZATION_FAILED, "VK_ERROR_INITIALIZATION_FAILED" },
+ { VK_ERROR_DEVICE_LOST, "VK_ERROR_DEVICE_LOST" },
+ { VK_ERROR_MEMORY_MAP_FAILED, "VK_ERROR_MEMORY_MAP_FAILED" },
+ { VK_ERROR_LAYER_NOT_PRESENT, "VK_ERROR_LAYER_NOT_PRESENT" },
+ { VK_ERROR_EXTENSION_NOT_PRESENT, "VK_ERROR_EXTENSION_NOT_PRESENT" },
+ { VK_ERROR_FEATURE_NOT_PRESENT, "VK_ERROR_FEATURE_NOT_PRESENT" },
+ { VK_ERROR_INCOMPATIBLE_DRIVER, "VK_ERROR_INCOMPATIBLE_DRIVER" },
+ { VK_ERROR_TOO_MANY_OBJECTS, "VK_ERROR_TOO_MANY_OBJECTS" },
+ { VK_ERROR_FORMAT_NOT_SUPPORTED, "VK_ERROR_FORMAT_NOT_SUPPORTED" },
+ { VK_ERROR_FRAGMENTED_POOL, "VK_ERROR_FRAGMENTED_POOL" },
+ { VK_ERROR_UNKNOWN, "VK_ERROR_UNKNOWN" },
+ { VK_ERROR_OUT_OF_POOL_MEMORY, "VK_ERROR_OUT_OF_POOL_MEMORY" },
+ { VK_ERROR_INVALID_EXTERNAL_HANDLE, "VK_ERROR_INVALID_EXTERNAL_HANDLE" },
+ { VK_ERROR_FRAGMENTATION, "VK_ERROR_FRAGMENTATION" },
+ { VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS" },
+ { VK_PIPELINE_COMPILE_REQUIRED, "VK_PIPELINE_COMPILE_REQUIRED" },
+ { VK_ERROR_SURFACE_LOST_KHR, "VK_ERROR_SURFACE_LOST_KHR" },
+ { VK_ERROR_NATIVE_WINDOW_IN_USE_KHR, "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR" },
+ { VK_SUBOPTIMAL_KHR, "VK_SUBOPTIMAL_KHR" },
+ { VK_ERROR_OUT_OF_DATE_KHR, "VK_ERROR_OUT_OF_DATE_KHR" },
+ { VK_ERROR_INCOMPATIBLE_DISPLAY_KHR, "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR" },
+ { VK_ERROR_VALIDATION_FAILED_EXT, "VK_ERROR_VALIDATION_FAILED_EXT" },
+ { VK_ERROR_INVALID_SHADER_NV, "VK_ERROR_INVALID_SHADER_NV" },
+ { VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR, "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR" },
+ { VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR, "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR" },
+ { VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR, "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR" },
+ { VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR, "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR" },
+ { VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR, "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR" },
+ { VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR, "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR" },
+ { VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT, "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT" },
+ { VK_ERROR_NOT_PERMITTED_KHR, "VK_ERROR_NOT_PERMITTED_KHR" },
+ { VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT, "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT" },
+ { VK_THREAD_IDLE_KHR, "VK_THREAD_IDLE_KHR" },
+ { VK_THREAD_DONE_KHR, "VK_THREAD_DONE_KHR" },
+ { VK_OPERATION_DEFERRED_KHR, "VK_OPERATION_DEFERRED_KHR" },
+ { VK_OPERATION_NOT_DEFERRED_KHR, "VK_OPERATION_NOT_DEFERRED_KHR" },
+ { VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT, "VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT" }
+ };
+
+ return mapping.find_or(res, "VK_ERROR_UNKNOWN");
+ }
+
+ std::optional enumerate_instance_version()
+ {
+ u32 instance_version{ 0 };
+
+ if (!vkEnumerateInstanceVersion)
+ {
+ log_warn("vkEnumerateInstanceVersion not implemented");
+
+ return std::nullopt;
+ }
+
+ const VkResult res = vkEnumerateInstanceVersion(&instance_version);
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to get instance version: {}", res);
+
+ return std::nullopt;
+ }
+
+ return instance_version;
+ }
+
+ std::vector enumerate_instance_layer_properties()
+ {
+ u32 prop_count;
+
+ VkResult res = vkEnumerateInstanceLayerProperties(&prop_count, VK_NULL_HANDLE);
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to enumerate instance layer properties (pass 1): {}", res);
+
+ return {};
+ }
+
+ std::vector props{ prop_count };
+
+ res = vkEnumerateInstanceLayerProperties(&prop_count, props.data());
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to enumerate instance layer properties (pass 2): {}", res);
+
+ return {};
+ }
+
+ return props;
+ }
+
+ std::vector enumerate_instance_extension_properties()
+ {
+ u32 prop_count;
+
+ VkResult res = vkEnumerateInstanceExtensionProperties(VK_NULL_HANDLE, &prop_count, VK_NULL_HANDLE);
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to enumerate instance extension properties (pass 1): {}", res);
+
+ return {};
+ }
+
+ std::vector props{ prop_count };
+
+ res = vkEnumerateInstanceExtensionProperties(VK_NULL_HANDLE, &prop_count, props.data());
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to enumerate instance extension properties (pass 2): {}", res);
+
+ return {};
+ }
+
+ return props;
+ }
+
+ std::vector enumerate_physical_devices(VkInstance instance)
+ {
+ assert_panic(instance != VK_NULL_HANDLE);
+
+ u32 device_count;
+
+ VkResult res = vkEnumeratePhysicalDevices(instance, &device_count, VK_NULL_HANDLE);
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to enumerate physical devices (pass 1): {}", res);
+
+ return {};
+ }
+
+ std::vector devices{ device_count };
+
+ res = vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to enumerate physical devices (pass 2): {}", res);
+
+ return {};
+ }
+
+ return devices;
+ }
+}
\ No newline at end of file
diff --git a/code/vk/helpers.h b/code/vk/helpers.h
new file mode 100644
index 00000000..910a3f33
--- /dev/null
+++ b/code/vk/helpers.h
@@ -0,0 +1,67 @@
+#pragma once
+#include
+#include
+
+#include
+#include
+
+#include "common/dictionary.h"
+#include "common/strings.h"
+
+namespace vk
+{
+ // convert a VkResult to a human readable string
+ std::string_view result_to_string(VkResult res);
+
+ // get the api version for the instance
+ // returns nullopt on failure or 1.0 spec
+ std::optional enumerate_instance_version();
+
+ // get instance layers
+ std::vector enumerate_instance_layer_properties();
+
+ // get instance extensions
+ std::vector enumerate_instance_extension_properties();
+
+ // get physical devices
+ std::vector enumerate_physical_devices(VkInstance instance);
+
+ // append a structure to a vk structure chain
+ template
+ void append_structure_to_chain(head_structure_type* head, type* structure)
+ {
+ VkBaseOutStructure* current = reinterpret_cast(head);
+
+ while (current->pNext)
+ {
+ const auto next = current->pNext;
+
+ // we already have this structure in the chain
+ if (next->sType == structure->sType)
+ {
+ return;
+ }
+
+ current = next;
+ }
+
+ current->pNext = reinterpret_cast(structure);
+ }
+}
+
+template<>
+struct fmt::formatter : formatter
+{
+ auto format(VkResult res, format_context& ctx)
+ {
+ constexpr std::string_view tpl =
+ {
+ "VkResult({})"
+ };
+
+ return fmt::format_to(ctx.out(), tpl, vk::result_to_string(res));
+ }
+};
+
+#define VK_SUCCEEDED(res) res == VK_SUCCESS
+#define VK_FAILED(res) res != VK_SUCCESS
diff --git a/code/vk/loader.cc b/code/vk/loader.cc
new file mode 100644
index 00000000..8b82cb01
--- /dev/null
+++ b/code/vk/loader.cc
@@ -0,0 +1,37 @@
+#include "vk/loader.h"
+#include "vk/helpers.h"
+
+#include "common/log.h"
+
+set_log_channel("vk");
+
+namespace vk
+{
+ bool load_base_library()
+ {
+ const auto res = volkInitialize();
+
+ if (VK_FAILED(res))
+ {
+ log_error("Failed to load vulkan library: {}", res);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ bool load_instance_functions(VkInstance instance)
+ {
+ volkLoadInstance(instance);
+
+ return true;
+ }
+
+ bool load_device_functions(VkDevice device)
+ {
+ volkLoadDevice(device);
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/code/vk/loader.h b/code/vk/loader.h
new file mode 100644
index 00000000..4919dd7a
--- /dev/null
+++ b/code/vk/loader.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include
+
+namespace vk
+{
+ // load the vulkan library
+ bool load_base_library();
+
+ // load function pointers for the instance
+ bool load_instance_functions(VkInstance instance);
+
+ // load function pointers for the device
+ bool load_device_functions(VkDevice device);
+}
\ No newline at end of file
diff --git a/code/vk/vk.exports.props b/code/vk/vk.exports.props
new file mode 100644
index 00000000..e69de29b
diff --git a/code/vk/vk.vcxproj b/code/vk/vk.vcxproj
new file mode 100644
index 00000000..434da607
--- /dev/null
+++ b/code/vk/vk.vcxproj
@@ -0,0 +1,33 @@
+
+
+
+
+
+ {CFE73B5E-826E-44F9-88B6-F3EF24954890}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/external/SPIRV-Headers b/external/SPIRV-Headers
new file mode 160000
index 00000000..b8b9eb86
--- /dev/null
+++ b/external/SPIRV-Headers
@@ -0,0 +1 @@
+Subproject commit b8b9eb8640c8c0107ba580fbcb10f969022ca32c
diff --git a/external/spirv-headers.exports.props b/external/spirv-headers.exports.props
new file mode 100644
index 00000000..e619c6c0
--- /dev/null
+++ b/external/spirv-headers.exports.props
@@ -0,0 +1,8 @@
+
+
+
+
+ $(SolutionDir)external\SPIRV-Headers\include;%(AdditionalIncludeDirectories)
+
+
+
diff --git a/external/volk.exports.props b/external/volk.exports.props
index 9ec71675..88ccb444 100644
--- a/external/volk.exports.props
+++ b/external/volk.exports.props
@@ -1,5 +1,11 @@
+
+
+ $(SolutionDir)external\volk;%(AdditionalIncludeDirectories)
+
+
+
{4CCD9127-5EE0-4D86-97A6-545D1F227804}