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}