NazaraEngine/src/Nazara/VulkanRenderer/Vulkan.cpp

630 lines
23 KiB
C++

// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Vulkan renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/VulkanRenderer/Vulkan.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Utility/Utility.hpp>
#include <Nazara/VulkanRenderer/Config.hpp>
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/Utils/CallOnExit.hpp>
#include <array>
#include <unordered_set>
#include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz
{
namespace
{
struct AvailableVulkanLayer
{
VkLayerProperties layerProperties;
std::unordered_map<std::string, std::size_t> extensionByName;
std::vector<VkExtensionProperties> extensionList;
};
void EnumerateVulkanLayers(std::vector<AvailableVulkanLayer>& availableLayers, std::unordered_map<std::string, std::size_t>& layerByName)
{
std::vector<VkLayerProperties> layerList;
if (Vk::Loader::EnumerateInstanceLayerProperties(&layerList))
{
for (VkLayerProperties& layerProperties : layerList)
{
std::size_t layerIndex = availableLayers.size();
auto& layerData = availableLayers.emplace_back();
layerData.layerProperties = layerProperties;
if (Vk::Loader::EnumerateInstanceExtensionProperties(&layerData.extensionList, layerProperties.layerName))
{
for (VkExtensionProperties& extProperty : layerData.extensionList)
layerData.extensionByName.emplace(extProperty.extensionName, layerData.extensionByName.size());
}
layerByName.emplace(layerProperties.layerName, layerIndex);
}
}
}
}
RenderDeviceInfo Vulkan::BuildRenderDeviceInfo(const Vk::PhysicalDevice& physDevice)
{
RenderDeviceInfo deviceInfo;
deviceInfo.name = physDevice.properties.deviceName;
deviceInfo.features.anisotropicFiltering = physDevice.features.samplerAnisotropy;
deviceInfo.features.computeShaders = true;
deviceInfo.features.depthClamping = physDevice.features.depthClamp;
deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid;
deviceInfo.features.storageBuffers = true;
deviceInfo.features.textureReadWithoutFormat = physDevice.features.shaderStorageImageReadWithoutFormat;
deviceInfo.features.textureReadWrite = true;
deviceInfo.features.textureWriteWithoutFormat = physDevice.features.shaderStorageImageWriteWithoutFormat;
deviceInfo.features.unrestrictedTextureViews = true;
deviceInfo.limits.maxComputeSharedMemorySize = physDevice.properties.limits.maxComputeSharedMemorySize;
deviceInfo.limits.maxComputeWorkGroupCount = { physDevice.properties.limits.maxComputeWorkGroupCount[0], physDevice.properties.limits.maxComputeWorkGroupCount[1], physDevice.properties.limits.maxComputeWorkGroupCount[2] };
deviceInfo.limits.maxComputeWorkGroupSize = { physDevice.properties.limits.maxComputeWorkGroupSize[0], physDevice.properties.limits.maxComputeWorkGroupSize[1], physDevice.properties.limits.maxComputeWorkGroupSize[2] };
deviceInfo.limits.maxComputeWorkGroupInvocations = physDevice.properties.limits.maxComputeWorkGroupInvocations;
deviceInfo.limits.maxStorageBufferSize = physDevice.properties.limits.maxStorageBufferRange;
deviceInfo.limits.maxUniformBufferSize = physDevice.properties.limits.maxUniformBufferRange;
deviceInfo.limits.minStorageBufferOffsetAlignment = physDevice.properties.limits.minStorageBufferOffsetAlignment;
deviceInfo.limits.minUniformBufferOffsetAlignment = physDevice.properties.limits.minUniformBufferOffsetAlignment;
switch (physDevice.properties.deviceType)
{
case VK_PHYSICAL_DEVICE_TYPE_CPU:
deviceInfo.type = RenderDeviceType::Software;
break;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
deviceInfo.type = RenderDeviceType::Dedicated;
break;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
deviceInfo.type = RenderDeviceType::Integrated;
break;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
deviceInfo.type = RenderDeviceType::Virtual;
break;
default:
NazaraWarning("Device " + deviceInfo.name + " has handled device type (0x" + NumberToString(physDevice.properties.deviceType, 16) + ')');
[[fallthrough]];
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
deviceInfo.type = RenderDeviceType::Unknown;
break;
}
return deviceInfo;
}
Vk::Instance& Vulkan::GetInstance()
{
return s_instance;
}
const std::vector<Vk::PhysicalDevice>& Vulkan::GetPhysicalDevices()
{
return s_physDevices;
}
const Vk::PhysicalDevice& Vulkan::GetPhysicalDeviceInfo(VkPhysicalDevice physDevice)
{
for (const Vk::PhysicalDevice& info : s_physDevices)
{
if (info.physDevice == physDevice)
return info;
}
// This cannot happen if physDevice is valid, as we retrieved every physical device
NazaraInternalError("Invalid physical device: " + PointerToString(physDevice));
static Vk::PhysicalDevice dummy;
return dummy;
}
bool Vulkan::Initialize(UInt32 targetApiVersion, RenderAPIValidationLevel validationLevel, const ParameterList& parameters)
{
NazaraAssert(!IsInitialized(), "Vulkan is already initialized");
// Initialize module here
if (!Vk::Loader::Initialize())
{
NazaraError("Failed to load Vulkan API, it may be not installed on your system");
return false;
}
CallOnExit onExit(Vulkan::Uninitialize);
std::string appName = parameters.GetStringParameter("VkAppInfo_OverrideApplicationName").GetValueOr("Another application made with Nazara Engine");
std::string engineName = parameters.GetStringParameter("VkAppInfo_OverrideEngineName").GetValueOr("Nazara Engine - Vulkan Renderer");
UInt32 appVersion = SafeCast<UInt32>(parameters.GetIntegerParameter("VkAppInfo_OverrideApplicationVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0)));
UInt32 engineVersion = SafeCast<UInt32>(parameters.GetIntegerParameter("VkAppInfo_OverrideEngineVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0)));
if (auto result = parameters.GetIntegerParameter("VkAppInfo_OverrideAPIVersion"))
targetApiVersion = SafeCast<UInt32>(result.GetValue());
if (Vk::Loader::vkEnumerateInstanceVersion)
{
UInt32 supportedApiVersion;
Vk::Loader::vkEnumerateInstanceVersion(&supportedApiVersion);
targetApiVersion = std::min(targetApiVersion, supportedApiVersion);
}
else
// vkEnumerateInstanceVersion is available from Vulkan 1.1, fallback to 1.0 if not supported
targetApiVersion = VK_API_VERSION_1_0;
VkApplicationInfo appInfo = {
VK_STRUCTURE_TYPE_APPLICATION_INFO,
nullptr,
appName.c_str(),
appVersion,
engineName.c_str(),
engineVersion,
targetApiVersion
};
VkInstanceCreateFlags createFlags = SafeCast<VkInstanceCreateFlags>(parameters.GetIntegerParameter("VkInstanceInfo_OverrideCreateFlags").GetValueOr(0));
std::vector<const char*> enabledLayers;
std::vector<AvailableVulkanLayer> availableLayers;
std::unordered_map<std::string, std::size_t> availableLayerByName;
EnumerateVulkanLayers(availableLayers, availableLayerByName);
if (auto result = parameters.GetBooleanParameter("VkInstanceInfo_OverrideEnabledLayers"); !result.GetValueOr(false))
{
//< Nazara default layers goes here
if (validationLevel != RenderAPIValidationLevel::None)
{
// Enable Vulkan validation if available in debug mode
if (availableLayerByName.count("VK_LAYER_KHRONOS_validation"))
enabledLayers.push_back("VK_LAYER_KHRONOS_validation");
else if (availableLayerByName.count("VK_LAYER_LUNARG_standard_validation"))
enabledLayers.push_back("VK_LAYER_LUNARG_standard_validation");
}
}
std::vector<const char*> enabledExtensions;
std::vector<std::string> additionalLayers; // Just to keep the String alive
if (long long customLayerCount = parameters.GetIntegerParameter("VkInstanceInfo_EnabledLayerCount").GetValueOr(0) > 0)
{
additionalLayers.reserve(customLayerCount);
for (long long i = 0; i < customLayerCount; ++i)
{
std::string parameterName = "VkInstanceInfo_EnabledLayer" + NumberToString(i);
if (auto result = parameters.GetStringParameter(parameterName))
{
additionalLayers.emplace_back(std::move(result).GetValue());
enabledLayers.push_back(additionalLayers.back().c_str());
}
else
NazaraWarning("Parameter " + parameterName + " expected");
}
}
// Get supported extension list
std::unordered_set<std::string> availableExtensions;
std::vector<VkExtensionProperties> extensionList;
if (Vk::Loader::EnumerateInstanceExtensionProperties(&extensionList))
{
for (VkExtensionProperties& extProperty : extensionList)
availableExtensions.insert(extProperty.extensionName);
}
if (auto result = parameters.GetBooleanParameter("VkInstanceInfo_OverrideEnabledExtensions"); !result.GetValueOr(false))
{
enabledExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
#ifdef VK_USE_PLATFORM_ANDROID_KHR
enabledExtensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#endif
#ifdef VK_USE_PLATFORM_XCB_KHR
enabledExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
enabledExtensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
enabledExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#endif
#ifdef VK_USE_PLATFORM_METAL_EXT
enabledExtensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
#endif
if (availableExtensions.count(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
enabledExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (availableExtensions.count(VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
std::vector<std::string> additionalExtensions; // Just to keep the String alive
if (long long customLayerCount = parameters.GetIntegerParameter("VkInstanceInfo_EnabledExtensionCount").GetValueOr(0) > 0)
{
additionalExtensions.reserve(customLayerCount);
for (int i = 0; i < customLayerCount; ++i)
{
std::string parameterName = "VkInstanceInfo_EnabledExtension" + NumberToString(i);
if (auto result = parameters.GetStringParameter(parameterName))
{
additionalLayers.emplace_back(std::move(result).GetValue());
enabledExtensions.push_back(additionalExtensions.back().c_str());
}
else
NazaraWarning("Parameter " + parameterName + " expected");
}
}
VkInstanceCreateInfo instanceInfo = {};
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
#ifdef NAZARA_DEBUG
// Handle VK_LAYER_KHRONOS_validation extended features
VkValidationFeaturesEXT features = {};
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
std::array<VkValidationFeatureEnableEXT, 1> enabledFeatures = {
//VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
//VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT,
//VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
};
auto validationIt = std::find_if(enabledLayers.begin(), enabledLayers.end(), [&](const char* layerName)
{
return std::strcmp(layerName, "VK_LAYER_KHRONOS_validation") == 0;
});
if (validationIt != enabledLayers.end())
{
auto layerIt = availableLayerByName.find("VK_LAYER_KHRONOS_validation");
assert(layerIt != availableLayerByName.end());
auto& validationLayer = availableLayers[layerIt->second];
if (validationLayer.extensionByName.find(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) != validationLayer.extensionByName.end())
{
enabledExtensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
features.enabledValidationFeatureCount = UInt32(enabledFeatures.size());
features.pEnabledValidationFeatures = enabledFeatures.data();
instanceInfo.pNext = &features;
}
}
#endif
instanceInfo.flags = createFlags;
instanceInfo.pApplicationInfo = &appInfo;
instanceInfo.enabledExtensionCount = UInt32(enabledExtensions.size());
instanceInfo.ppEnabledExtensionNames = enabledExtensions.data();
instanceInfo.enabledLayerCount = UInt32(enabledLayers.size());
instanceInfo.ppEnabledLayerNames = enabledLayers.data();
if (!s_instance.Create(validationLevel, instanceInfo))
{
NazaraError("Failed to create instance: " + TranslateVulkanError(s_instance.GetLastErrorCode()));
return false;
}
std::vector<VkPhysicalDevice> physDevices;
if (!s_instance.EnumeratePhysicalDevices(&physDevices))
{
NazaraError("Failed to enumerate physical devices");
return false;
}
s_physDevices.reserve(physDevices.size());
for (VkPhysicalDevice physDevice : physDevices)
{
Vk::PhysicalDevice deviceInfo;
if (!s_instance.GetPhysicalDeviceQueueFamilyProperties(physDevice, &deviceInfo.queueFamilies))
{
NazaraWarning("Failed to query physical device queue family properties for " + std::string(deviceInfo.properties.deviceName) + " (0x" + NumberToString(deviceInfo.properties.deviceID, 16) + ')');
continue;
}
deviceInfo.physDevice = physDevice;
deviceInfo.features = s_instance.GetPhysicalDeviceFeatures(physDevice);
deviceInfo.memoryProperties = s_instance.GetPhysicalDeviceMemoryProperties(physDevice);
deviceInfo.properties = s_instance.GetPhysicalDeviceProperties(physDevice);
std::vector<VkExtensionProperties> extensions;
if (s_instance.GetPhysicalDeviceExtensions(physDevice, &extensions))
{
for (auto& extProperty : extensions)
deviceInfo.extensions.emplace(extProperty.extensionName);
}
else
NazaraWarning("Failed to query physical device extensions for " + std::string(deviceInfo.properties.deviceName) + " (0x" + NumberToString(deviceInfo.properties.deviceID, 16) + ')');
s_physDevices.emplace_back(std::move(deviceInfo));
}
if (s_physDevices.empty())
{
NazaraError("No valid physical device found");
return false;
}
s_initializationParameters = parameters;
onExit.Reset();
NazaraNotice("Initialized: Vulkan module");
return true;
}
bool Vulkan::IsInitialized()
{
return s_instance.IsValid();
}
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const RenderDeviceFeatures& enabledFeatures)
{
Nz::ErrorFlags errFlags(ErrorMode::ThrowException, true);
// Find a queue that supports graphics operations
UInt32 graphicsQueueNodeIndex = UINT32_MAX;
UInt32 transfertQueueNodeFamily = UINT32_MAX;
for (UInt32 i = 0; i < deviceInfo.queueFamilies.size(); i++)
{
if (deviceInfo.queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsQueueNodeIndex = i;
break;
}
}
for (UInt32 i = 0; i < deviceInfo.queueFamilies.size(); i++)
{
if (deviceInfo.queueFamilies[i].queueFlags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT)) //< Compute and graphics queue implicitly support transfer operations
{
transfertQueueNodeFamily = i;
if (transfertQueueNodeFamily != graphicsQueueNodeIndex)
break;
}
}
std::array<QueueFamily, 2> queuesFamilies = {
{
{ graphicsQueueNodeIndex, 1.f },
{ transfertQueueNodeFamily, 1.f }
}
};
return CreateDevice(deviceInfo, enabledFeatures, queuesFamilies.data(), queuesFamilies.size());
}
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const RenderDeviceFeatures& enabledFeatures, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex)
{
Nz::ErrorFlags errFlags(ErrorMode::ThrowException, true);
// Find a queue that supports graphics operations
UInt32 graphicsQueueNodeIndex = UINT32_MAX;
UInt32 presentQueueNodeIndex = UINT32_MAX;
UInt32 transferQueueNodeFamily = UINT32_MAX;
for (UInt32 i = 0; i < deviceInfo.queueFamilies.size(); i++)
{
bool supportPresentation = false;
if (!surface.GetSupportPresentation(deviceInfo.physDevice, i, &supportPresentation))
NazaraWarning("Failed to get presentation support of queue family #" + NumberToString(i));
if (deviceInfo.queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
if (supportPresentation)
{
// Queue family support both graphics and presentation to our surface, choose it
graphicsQueueNodeIndex = i;
presentQueueNodeIndex = i;
break;
}
else if (graphicsQueueNodeIndex == UINT32_MAX)
graphicsQueueNodeIndex = i;
}
else if (supportPresentation)
presentQueueNodeIndex = i;
}
if (graphicsQueueNodeIndex == UINT32_MAX)
{
// A Vulkan device without graphics support may technically exists but I've yet to see one
NazaraError("Device does not support graphics operations");
return {};
}
if (presentQueueNodeIndex == UINT32_MAX)
{
// On multi-GPU systems, it's very possible to have surfaces unsupported by some
NazaraError("Device cannot present this surface");
return {};
}
// Search for a transfer queue (first one being different to the graphics one)
for (UInt32 i = 0; i < deviceInfo.queueFamilies.size(); i++)
{
// Transfer bit is not mandatory if compute and graphics bits are set (as they implicitly support transfer)
if (deviceInfo.queueFamilies[i].queueFlags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT))
{
transferQueueNodeFamily = i;
if (transferQueueNodeFamily != graphicsQueueNodeIndex)
break;
}
}
assert(transferQueueNodeFamily != UINT32_MAX);
std::array<QueueFamily, 3> queuesFamilies = {
{
{graphicsQueueNodeIndex, 1.f},
{presentQueueNodeIndex, 1.f},
{transferQueueNodeFamily, 1.f}
}
};
*graphicsFamilyIndex = graphicsQueueNodeIndex;
*presentableFamilyIndex = presentQueueNodeIndex;
*transferFamilyIndex = transferQueueNodeFamily;
return CreateDevice(deviceInfo, enabledFeatures, queuesFamilies.data(), queuesFamilies.size());
}
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const RenderDeviceFeatures& enabledFeatures, const QueueFamily* queueFamilies, std::size_t queueFamilyCount)
{
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
queueCreateInfos.reserve(queueFamilyCount);
for (std::size_t i = 0; i < queueFamilyCount; ++i)
{
const QueueFamily& queueFamily = queueFamilies[i];
auto it = std::find_if(queueCreateInfos.begin(), queueCreateInfos.end(), [&] (const VkDeviceQueueCreateInfo& createInfo)
{
return createInfo.queueFamilyIndex == queueFamily.familyIndex;
});
if (it == queueCreateInfos.end())
{
VkDeviceQueueCreateInfo createInfo = {
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0, // VkDeviceQueueCreateFlags flags;
queueFamily.familyIndex, // uint32_t queueFamilyIndex;
1, // uint32_t queueCount;
&queueFamily.priority // const float* pQueuePriorities;
};
queueCreateInfos.emplace_back(createInfo);
}
}
std::vector<const char*> enabledLayers;
std::vector<const char*> enabledExtensions;
if (auto result = s_initializationParameters.GetBooleanParameter("VkDeviceInfo_OverrideEnabledLayers"); !result.GetValueOr(false))
{
//< Nazara default layers goes here
}
std::vector<std::string> additionalLayers; // Just to keep the string alive
if (long long customLayerCount = s_initializationParameters.GetIntegerParameter("VkDeviceInfo_EnabledLayerCount").GetValueOr(0))
{
additionalLayers.reserve(customLayerCount);
for (long long i = 0; i < customLayerCount; ++i)
{
std::string parameterName = "VkDeviceInfo_EnabledLayer" + NumberToString(i);
if (auto value = s_initializationParameters.GetStringViewParameter(parameterName))
{
additionalLayers.emplace_back(std::move(value).GetValue());
enabledLayers.push_back(additionalLayers.back().c_str());
}
else
NazaraWarning("Parameter " + parameterName + " expected");
}
}
if (auto result = s_initializationParameters.GetBooleanParameter("VkDeviceInfo_OverrideEnabledExtensions"); !result.GetValueOr(false))
{
// Swapchain extension is required for rendering
enabledExtensions.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
auto EnableIfSupported = [&](const char* extName)
{
if (deviceInfo.extensions.count(extName))
enabledExtensions.emplace_back(extName);
};
// VMA extensions
EnableIfSupported(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
if (deviceInfo.properties.apiVersion < VK_API_VERSION_1_1)
{
EnableIfSupported(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
EnableIfSupported(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
}
}
std::vector<std::string> additionalExtensions; // Just to keep the String alive
if (long long customExtCount = s_initializationParameters.GetIntegerParameter("VkDeviceInfo_EnabledExtensionCount").GetValueOr(0))
{
for (long long i = 0; i < customExtCount; ++i)
{
std::string parameterName = "VkDeviceInfo_EnabledExtension" + NumberToString(i);
if (auto value = s_initializationParameters.GetStringViewParameter(parameterName))
{
additionalExtensions.emplace_back(std::move(value).GetValue());
enabledExtensions.push_back(additionalExtensions.back().c_str());
}
else
NazaraWarning("Parameter " + parameterName + " expected");
}
}
VkPhysicalDeviceFeatures deviceFeatures = {};
if (enabledFeatures.anisotropicFiltering)
deviceFeatures.samplerAnisotropy = VK_TRUE;
if (enabledFeatures.depthClamping)
deviceFeatures.depthClamp = VK_TRUE;
if (enabledFeatures.nonSolidFaceFilling)
deviceFeatures.fillModeNonSolid = VK_TRUE;
VkDeviceCreateInfo createInfo = {
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
nullptr,
0,
UInt32(queueCreateInfos.size()),
queueCreateInfos.data(),
UInt32(enabledLayers.size()),
enabledLayers.data(),
UInt32(enabledExtensions.size()),
enabledExtensions.data(),
&deviceFeatures
};
std::shared_ptr<VulkanDevice> device = std::make_shared<VulkanDevice>(s_instance, enabledFeatures, BuildRenderDeviceInfo(deviceInfo));
if (!device->Create(deviceInfo, createInfo))
{
NazaraError("Failed to create Vulkan Device: " + TranslateVulkanError(device->GetLastErrorCode()));
return {};
}
return device;
}
void Vulkan::Uninitialize()
{
// Uninitialize module here
s_instance.Destroy();
Vk::Loader::Uninitialize();
}
std::vector<Vk::PhysicalDevice> Vulkan::s_physDevices;
Vk::Instance Vulkan::s_instance;
ParameterList Vulkan::s_initializationParameters;
}
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Core/AntiWindows.hpp>
#endif