587 lines
19 KiB
C++
587 lines
19 KiB
C++
// Copyright (C) 2020 Jérôme Leclercq
|
|
// 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/CallOnExit.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 <array>
|
|
#include <unordered_set>
|
|
#include <Nazara/VulkanRenderer/Debug.hpp>
|
|
|
|
namespace Nz
|
|
{
|
|
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: " + String::Pointer(physDevice));
|
|
|
|
static Vk::PhysicalDevice dummy;
|
|
return dummy;
|
|
}
|
|
|
|
bool Vulkan::Initialize(UInt32 targetApiVersion, 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);
|
|
|
|
String appName = "Another application made with Nazara Engine";
|
|
String engineName = "Nazara Engine - Vulkan Renderer";
|
|
|
|
UInt32 appVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
UInt32 engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
|
|
parameters.GetStringParameter("VkAppInfo_OverrideApplicationName", &appName);
|
|
parameters.GetStringParameter("VkAppInfo_OverrideEngineName", &engineName);
|
|
|
|
bool bParam;
|
|
long long iParam;
|
|
|
|
if (parameters.GetIntegerParameter("VkAppInfo_OverrideAPIVersion", &iParam))
|
|
targetApiVersion = static_cast<UInt32>(iParam);
|
|
|
|
if (parameters.GetIntegerParameter("VkAppInfo_OverrideApplicationVersion", &iParam))
|
|
appVersion = static_cast<UInt32>(iParam);
|
|
|
|
if (parameters.GetIntegerParameter("VkAppInfo_OverrideEngineVersion", &iParam))
|
|
engineVersion = static_cast<UInt32>(iParam);
|
|
|
|
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.GetConstBuffer(),
|
|
appVersion,
|
|
engineName.GetConstBuffer(),
|
|
engineVersion,
|
|
targetApiVersion
|
|
};
|
|
|
|
VkInstanceCreateFlags createFlags = 0;
|
|
|
|
if (parameters.GetIntegerParameter("VkInstanceInfo_OverrideCreateFlags", &iParam))
|
|
createFlags = static_cast<VkInstanceCreateFlags>(iParam);
|
|
|
|
std::vector<const char*> enabledLayers;
|
|
std::vector<const char*> enabledExtensions;
|
|
|
|
if (!parameters.GetBooleanParameter("VkInstanceInfo_OverrideEnabledLayers", &bParam) || !bParam)
|
|
{
|
|
//< Nazara default layers goes here
|
|
}
|
|
|
|
std::vector<String> additionalLayers; // Just to keep the String alive
|
|
if (parameters.GetIntegerParameter("VkInstanceInfo_EnabledLayerCount", &iParam))
|
|
{
|
|
additionalLayers.reserve(iParam);
|
|
for (long long i = 0; i < iParam; ++i)
|
|
{
|
|
Nz::String parameterName = "VkInstanceInfo_EnabledLayer" + String::Number(i);
|
|
Nz::String layer;
|
|
if (parameters.GetStringParameter(parameterName, &layer))
|
|
{
|
|
additionalLayers.emplace_back(std::move(layer));
|
|
enabledLayers.push_back(additionalLayers.back().GetConstBuffer());
|
|
}
|
|
else
|
|
NazaraWarning("Parameter " + parameterName + " expected");
|
|
}
|
|
}
|
|
|
|
// Get 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 (!parameters.GetBooleanParameter("VkInstanceInfo_OverrideEnabledExtensions", &bParam) || !bParam)
|
|
{
|
|
enabledExtensions.push_back("VK_KHR_surface");
|
|
|
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
|
enabledExtensions.push_back("VK_KHR_android_surface");
|
|
#endif
|
|
|
|
#ifdef VK_USE_PLATFORM_MIR_KHR
|
|
enabledExtensions.push_back("VK_KHR_mir_surface");
|
|
#endif
|
|
|
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
|
enabledExtensions.push_back("VK_KHR_xcb_surface");
|
|
#endif
|
|
|
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
|
enabledExtensions.push_back("VK_KHR_xlib_surface");
|
|
#endif
|
|
|
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
|
enabledExtensions.push_back("VK_KHR_wayland_surface");
|
|
#endif
|
|
|
|
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
|
enabledExtensions.push_back("VK_KHR_win32_surface");
|
|
#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<String> additionalExtensions; // Just to keep the String alive
|
|
if (parameters.GetIntegerParameter("VkInstanceInfo_EnabledExtensionCount", &iParam))
|
|
{
|
|
additionalExtensions.reserve(iParam);
|
|
for (int i = 0; i < iParam; ++i)
|
|
{
|
|
Nz::String parameterName = "VkInstanceInfo_EnabledExtension" + String::Number(i);
|
|
Nz::String extension;
|
|
if (parameters.GetStringParameter(parameterName, &extension))
|
|
{
|
|
additionalExtensions.emplace_back(std::move(extension));
|
|
enabledExtensions.push_back(additionalExtensions.back().GetConstBuffer());
|
|
}
|
|
else
|
|
NazaraWarning("Parameter " + parameterName + " expected");
|
|
}
|
|
}
|
|
|
|
VkInstanceCreateInfo instanceInfo = {
|
|
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
nullptr,
|
|
createFlags,
|
|
&appInfo,
|
|
UInt32(enabledLayers.size()),
|
|
enabledLayers.data(),
|
|
UInt32(enabledExtensions.size()),
|
|
enabledExtensions.data()
|
|
};
|
|
|
|
if (!s_instance.Create(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 " + String(deviceInfo.properties.deviceName) + " (0x" + String::Number(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 " + String(deviceInfo.properties.deviceName) + " (0x" + String::Number(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)
|
|
{
|
|
Nz::ErrorFlags errFlags(ErrorFlag_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, queuesFamilies.data(), queuesFamilies.size());
|
|
}
|
|
|
|
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex)
|
|
{
|
|
Nz::ErrorFlags errFlags(ErrorFlag_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 #" + String::Number(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, queuesFamilies.data(), queuesFamilies.size());
|
|
}
|
|
|
|
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, 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;
|
|
|
|
bool bParam;
|
|
long long iParam;
|
|
|
|
if (!s_initializationParameters.GetBooleanParameter("VkDeviceInfo_OverrideEnabledLayers", &bParam) || !bParam)
|
|
{
|
|
//< Nazara default layers goes here
|
|
}
|
|
|
|
std::vector<String> additionalLayers; // Just to keep the String alive
|
|
if (s_initializationParameters.GetIntegerParameter("VkDeviceInfo_EnabledLayerCount", &iParam))
|
|
{
|
|
additionalLayers.reserve(iParam);
|
|
for (long long i = 0; i < iParam; ++i)
|
|
{
|
|
Nz::String parameterName = "VkDeviceInfo_EnabledLayer" + String::Number(i);
|
|
Nz::String layer;
|
|
if (s_initializationParameters.GetStringParameter(parameterName, &layer))
|
|
{
|
|
additionalLayers.emplace_back(std::move(layer));
|
|
enabledLayers.push_back(additionalLayers.back().GetConstBuffer());
|
|
}
|
|
else
|
|
NazaraWarning("Parameter " + parameterName + " expected");
|
|
}
|
|
}
|
|
|
|
if (!s_initializationParameters.GetBooleanParameter("VkDeviceInfo_OverrideEnabledExtensions", &bParam) || !bParam)
|
|
{
|
|
// 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);
|
|
EnableIfSupported(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
EnableIfSupported(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
|
}
|
|
|
|
std::vector<String> additionalExtensions; // Just to keep the String alive
|
|
if (s_initializationParameters.GetIntegerParameter("VkDeviceInfo_EnabledExtensionCount", &iParam))
|
|
{
|
|
for (long long i = 0; i < iParam; ++i)
|
|
{
|
|
Nz::String parameterName = "VkDeviceInfo_EnabledExtension" + String::Number(i);
|
|
Nz::String extension;
|
|
if (s_initializationParameters.GetStringParameter(parameterName, &extension))
|
|
{
|
|
additionalExtensions.emplace_back(std::move(extension));
|
|
enabledExtensions.push_back(additionalExtensions.back().GetConstBuffer());
|
|
}
|
|
else
|
|
NazaraWarning("Parameter " + parameterName + " expected");
|
|
}
|
|
}
|
|
|
|
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(),
|
|
nullptr
|
|
};
|
|
|
|
std::shared_ptr<VulkanDevice> device = std::make_shared<VulkanDevice>(s_instance);
|
|
if (!device->Create(deviceInfo, createInfo))
|
|
{
|
|
NazaraError("Failed to create Vulkan Device: " + TranslateVulkanError(device->GetLastErrorCode()));
|
|
return {};
|
|
}
|
|
|
|
s_devices.emplace_back(device);
|
|
|
|
return device;
|
|
}
|
|
|
|
std::shared_ptr<VulkanDevice> Vulkan::SelectDevice(const Vk::PhysicalDevice& deviceInfo)
|
|
{
|
|
for (auto it = s_devices.begin(); it != s_devices.end();)
|
|
{
|
|
const auto& devicePtr = *it;
|
|
if (devicePtr->GetPhysicalDevice() == deviceInfo.physDevice)
|
|
return devicePtr;
|
|
}
|
|
|
|
return CreateDevice(deviceInfo);
|
|
}
|
|
|
|
std::shared_ptr<VulkanDevice> Vulkan::SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex)
|
|
{
|
|
// First, try to find a device compatible with that surface
|
|
for (auto it = s_devices.begin(); it != s_devices.end();)
|
|
{
|
|
const auto& devicePtr = *it;
|
|
if (devicePtr->GetPhysicalDevice() == deviceInfo.physDevice)
|
|
{
|
|
const std::vector<Vk::Device::QueueFamilyInfo>& queueFamilyInfo = devicePtr->GetEnabledQueues();
|
|
UInt32 graphicsQueueFamilyIndex = UINT32_MAX;
|
|
UInt32 presentableQueueFamilyIndex = UINT32_MAX;
|
|
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
|
|
{
|
|
bool supported = false;
|
|
if (surface.GetSupportPresentation(deviceInfo.physDevice, queueInfo.familyIndex, &supported) && supported)
|
|
{
|
|
if (presentableQueueFamilyIndex == UINT32_MAX || queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
|
|
{
|
|
presentableQueueFamilyIndex = queueInfo.familyIndex;
|
|
if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
|
|
{
|
|
*graphicsFamilyIndex = queueInfo.familyIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (graphicsQueueFamilyIndex == UINT32_MAX)
|
|
{
|
|
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
|
|
{
|
|
if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
|
|
{
|
|
*graphicsFamilyIndex = queueInfo.familyIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (presentableQueueFamilyIndex != UINT32_MAX)
|
|
{
|
|
*presentableFamilyIndex = presentableQueueFamilyIndex;
|
|
|
|
UInt32 transferQueueNodeFamily = UINT32_MAX;
|
|
// Search for a transfer queue (first one being different to the graphics one)
|
|
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
|
|
{
|
|
// Transfer bit is not mandatory if compute and graphics bits are set (as they implicitly support transfer)
|
|
if (queueInfo.flags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT))
|
|
{
|
|
transferQueueNodeFamily = queueInfo.familyIndex;
|
|
if (transferQueueNodeFamily != *graphicsFamilyIndex)
|
|
break;
|
|
}
|
|
}
|
|
assert(transferQueueNodeFamily != UINT32_MAX);
|
|
|
|
*transferFamilyIndex = transferQueueNodeFamily;
|
|
|
|
return devicePtr;
|
|
}
|
|
}
|
|
|
|
++it;
|
|
}
|
|
|
|
// No device had support for that surface, create one
|
|
return CreateDevice(deviceInfo, surface, graphicsFamilyIndex, presentableFamilyIndex, transferFamilyIndex);
|
|
}
|
|
|
|
void Vulkan::Uninitialize()
|
|
{
|
|
// Uninitialize module here
|
|
s_devices.clear();
|
|
s_instance.Destroy();
|
|
|
|
Vk::Loader::Uninitialize();
|
|
}
|
|
|
|
std::vector<std::shared_ptr<VulkanDevice>> Vulkan::s_devices;
|
|
std::vector<Vk::PhysicalDevice> Vulkan::s_physDevices;
|
|
Vk::Instance Vulkan::s_instance;
|
|
ParameterList Vulkan::s_initializationParameters;
|
|
}
|
|
|