第一行
VulkanCore::Context::enableDefaultFeatures();
看了下,主要是设置要启动的vulkan1.2特性
void Context::enableDefaultFeatures() {
// do we need these for defaults?
enable12Features_.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
enable12Features_.shaderStorageImageArrayNonUniformIndexing = VK_TRUE;
// enable12Features_.descriptorBindingUniformBufferUpdateAfterBind =
// VK_TRUE, // This
// // makes creating a device on a 1060 fail
enable12Features_.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;
enable12Features_.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE;
enable12Features_.descriptorBindingUpdateUnusedWhilePending = VK_TRUE;
enable12Features_.descriptorBindingPartiallyBound = VK_TRUE;
enable12Features_.descriptorBindingVariableDescriptorCount = VK_TRUE;
enable12Features_.descriptorIndexing = VK_TRUE;
enable12Features_.runtimeDescriptorArray = VK_TRUE;
}
而enable12Features_本身是一个static变量
static VkPhysicalDeviceVulkan12Features enable12Features_;
VkPhysicalDeviceVulkan12Features Context::enable12Features_ = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
};
context的构建函数
调用的代码是
std::vector<std::string> validationLayers;
#ifdef _DEBUG
validationLayers.push_back("VK_LAYER_KHRONOS_validation");
#endif
VulkanCore::Context context(
(void*)glfwGetWin32Window(window_),
validationLayers, // layers
{
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME,
}, // instance extensions
{VK_KHR_SWAPCHAIN_EXTENSION_NAME}, // device extensions
VK_QUEUE_GRAPHICS_BIT, // request a graphics queue only
true);
函数签名是
explicit Context(void* window, const std::vector<std::string>& requestedLayers,
const std::vector<std::string>& requestedInstanceExtensions,
const std::vector<std::string>& requestedDeviceExtensions,
VkQueueFlags requestedQueueTypes, bool printEnumerations = false,
bool enableRayTracing = false, const std::string& name = "");
结合一下看,第一个参数是window,第二个是requestedLayers,实际传入的要用到了layer的字符串VK_LAYER_KHRONOS_validation, 也就是validation layer验证层。然后传入了instance extension和device extension,需要用的queue的类型,这里是Graphics Queue,最后一个传入的bool量应该是用来控制是否打印一些信息。
构建函数内容
首先是 VK_CHECK(volkInitialize()),这个volk是一个库,用来加载vulkan函数地址
enabledLayers_ = util::filterExtensions(enumerateInstanceLayers(), requestedLayers);
enabledLayers_ 是一个 std::unordered_set<std::string> enabledLayers_
。
enumerateInstanceLayers()
函数内容如下
std::vector<std::string> Context::enumerateInstanceLayers(bool printEnumerations) {
uint32_t instanceLayerCount{0};
VK_CHECK(vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
std::vector<VkLayerProperties> layers(instanceLayerCount);
VK_CHECK(vkEnumerateInstanceLayerProperties(&instanceLayerCount, layers.data()));
std::vector<std::string> returnValues;
std::transform(
layers.begin(), layers.end(), std::back_inserter(returnValues),
[](const VkLayerProperties& properties) { return properties.layerName; });
if (printEnumerations) {
std::cerr << "Found " << instanceLayerCount << " available layer(s)" << std::endl;
for (const auto& layer : returnValues) {
std::cerr << "\t" << layer << std::endl;
}
}
return returnValues;
}
就是遍历了一遍可用的instance layer,然后把名字存下来返回
std::unordered_set<std::string> filterExtensions(
std::vector<std::string> availableExtensions,
std::vector<std::string> requestedExtensions) {
std::sort(availableExtensions.begin(), availableExtensions.end());
std::sort(requestedExtensions.begin(), requestedExtensions.end());
std::vector<std::string> result;
std::set_intersection(availableExtensions.begin(), availableExtensions.end(),
requestedExtensions.begin(), requestedExtensions.end(),
std::back_inserter(result));
return std::unordered_set<std::string>(result.begin(), result.end());
}
这个函数看上去就是把可用的extension和request的extension使用std::set_intersection算法做一个交集,把结果返回.
下一行也类似
enabledInstanceExtensions_ = util::filterExtensions(enumerateInstanceExtensions(), requestedInstanceExtensions);
这么看来 validation layer也是一个instanceExtension
然后是
// Transform list of enabled Instance layers from std::string to const char*
std::vector<const char*> instanceLayers(enabledLayers_.size());
std::transform(enabledLayers_.begin(), enabledLayers_.end(), instanceLayers.begin(),
std::mem_fn(&std::string::c_str));
std::mem_fn 是 C++11 引入的一个工具,用于将成员函数指针转换为能够对对象实例进行调用的可调用对象。这种可调用对象可以像普通函数一样使用,也可以与标准库中的许多算法和函数结合使用。
在 std::mem_fn(&std::string::c_str) 的例子中:
&std::string::c_str 是 std::string 类的成员函数指针,指向 c_str() 函数。
std::mem_fn(&std::string::c_str) 将这个成员函数指针转换为一个可调用对象。
举个例子
std::vector<std::string> words = {"hello", "world", "C++"};
// Create a mem_fn object for std::string::c_str
auto c_str_fn = std::mem_fn(&std::string::c_str);
for (const auto& word : words) {
// Use the callable object to call c_str on each word
const char* c_str = c_str_fn(word);
std::cout << c_str << '\n';
}
hello
world
C++
创建VkInstance
std::vector<VkValidationFeatureEnableEXT> validationFeaturesEnabled;
const VkValidationFeaturesEXT features = {
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
.enabledValidationFeatureCount =
static_cast<uint32_t>(validationFeaturesEnabled.size()),
.pEnabledValidationFeatures = validationFeaturesEnabled.data(),
};
const VkInstanceCreateInfo instanceInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = &features,
.pApplicationInfo = &applicationInfo_,
.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size()),
.ppEnabledLayerNames = instanceLayers.data(),
.enabledExtensionCount = static_cast<uint32_t>(instanceExtensions.size()),
.ppEnabledExtensionNames = instanceExtensions.data(),
};
VK_CHECK(vkCreateInstance(&instanceInfo, nullptr, &instance_))
// 看起来是为了这个创建的instance加载函数之类的操作
volkLoadInstance(instance_);
这一段就是创建了VkInstance
创建Debug工具
#if defined(VK_EXT_debug_utils)
if (enabledInstanceExtensions_.contains(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
const VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
#if defined(VK_EXT_device_address_binding_report)
| VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT
#endif
,
.pfnUserCallback = &debugMessengerCallback,
.pUserData = nullptr,
};
VK_CHECK(
vkCreateDebugUtilsMessengerEXT(instance_, &messengerInfo, nullptr, &messenger_));
}
#endif
创建Surface
// Surface
#if defined(VK_USE_PLATFORM_WIN32_KHR) && defined(VK_KHR_win32_surface)
if (enabledInstanceExtensions_.contains(VK_KHR_WIN32_SURFACE_EXTENSION_NAME)) {
if (window != nullptr) {
const VkWin32SurfaceCreateInfoKHR ci = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.hinstance = GetModuleHandle(NULL),
.hwnd = (HWND)window,
};
VK_CHECK(vkCreateWin32SurfaceKHR(instance_, &ci, nullptr, &surface_));
}
}
#endif
创建PhysicalDevice
然后是创建Physical device,这里的Physical device不是VkPhysicalDevice,自己又包了一层
// Find all physical devices in the system and choose one
physicalDevice_ = choosePhysicalDevice(
enumeratePhysicalDevices(requestedDeviceExtensions, enableRayTracing),
requestedDeviceExtensions);
看看Physical device的构造函数
PhysicalDevice::PhysicalDevice(VkPhysicalDevice device, VkSurfaceKHR surface,
const std::vector<std::string>& requestedExtensions,
bool printEnumerations, bool enableRayTracing)
: physicalDevice_{device} {
if (!enableRayTracing) {
descIndexFeature_.pNext = &meshShaderFeature_;
}
// Features
vkGetPhysicalDeviceFeatures2(physicalDevice_, &features_);
// Properties
vkGetPhysicalDeviceProperties2(physicalDevice_, &properties_);
// Get memory properties
vkGetPhysicalDeviceMemoryProperties2(physicalDevice_, &memoryProperties_);
// Enumerate queues
{
uint32_t queueFamilyCount{0};
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice_, &queueFamilyCount, nullptr);
queueFamilyProperties_.resize(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice_, &queueFamilyCount,
queueFamilyProperties_.data());
}
// Enumerate extensions
{
uint32_t propertyCount{0};
VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice_, nullptr,
&propertyCount, nullptr));
std::vector<VkExtensionProperties> properties(propertyCount);
VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice_, nullptr,
&propertyCount, properties.data()));
std::transform(properties.begin(), properties.end(), std::back_inserter(extensions_),
[](const VkExtensionProperties& property) {
return std::string(property.extensionName);
});
enabledExtensions_ = util::filterExtensions(extensions_, requestedExtensions);
}
if (surface != VK_NULL_HANDLE) {
enumerateSurfaceFormats(surface);
enumerateSurfaceCapabilities(surface);
enumeratePresentationModes(surface);
}
}
然后确保永远request Graphics queue
physicalDevice_.reserveQueues(requestedQueueTypes | VK_QUEUE_GRAPHICS_BIT, surface_)
具体为
ASSERT(requestedQueueTypes > 0, "Requested queue types is empty");
// We only share queues with presentation, a vulkan queue can support multiple
// operations (such as graphics, compute, sparse, transfer etc), however in
// that case that queue can be used only through one thread This code ensures
// we treat each queue as independent and it can only be used either for
// graphics/compute/transfer or sparse, this helps us with multithreading,
// however if the device only have one queue for all, then because of this
// code we may not be able to create compute/transfer queues
for (uint32_t queueFamilyIndex = 0;
queueFamilyIndex < queueFamilyProperties_.size() && requestedQueueTypes != 0;
++queueFamilyIndex)
{
if (!presentationFamilyIndex_.has_value() && surface != VK_NULL_HANDLE)
{
VkBool32 supportsPresent{VK_FALSE};
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice_, queueFamilyIndex, surface,
&supportsPresent);
if (supportsPresent == VK_TRUE) {
presentationFamilyIndex_ = queueFamilyIndex;
presentationQueueCount_ = queueFamilyProperties_[queueFamilyIndex].queueCount;
}
}
if (!graphicsFamilyIndex_.has_value() &&
(requestedQueueTypes & queueFamilyProperties_[queueFamilyIndex].queueFlags) &
VK_QUEUE_GRAPHICS_BIT)
{
graphicsFamilyIndex_ = queueFamilyIndex;
graphicsQueueCount_ = queueFamilyProperties_[queueFamilyIndex].queueCount;
requestedQueueTypes &= ~VK_QUEUE_GRAPHICS_BIT;
continue;
}
if (!computeFamilyIndex_.has_value() &&
(requestedQueueTypes & queueFamilyProperties_[queueFamilyIndex].queueFlags) &
VK_QUEUE_COMPUTE_BIT)
{
computeFamilyIndex_ = queueFamilyIndex;
computeQueueCount_ = queueFamilyProperties_[queueFamilyIndex].queueCount;
requestedQueueTypes &= ~VK_QUEUE_COMPUTE_BIT;
continue;
}
if (!transferFamilyIndex_.has_value() &&
(requestedQueueTypes & queueFamilyProperties_[queueFamilyIndex].queueFlags) &
VK_QUEUE_TRANSFER_BIT)
{
transferFamilyIndex_ = queueFamilyIndex;
transferQueueCount_ = queueFamilyProperties_[queueFamilyIndex].queueCount;
requestedQueueTypes &= ~VK_QUEUE_TRANSFER_BIT;
continue;
}
if (!sparseFamilyIndex_.has_value() &&
(requestedQueueTypes & queueFamilyProperties_[queueFamilyIndex].queueFlags) &
VK_QUEUE_SPARSE_BINDING_BIT)
{
sparseFamilyIndex_ = queueFamilyIndex;
sparseQueueCount_ = queueFamilyProperties_[queueFamilyIndex].queueCount;
requestedQueueTypes &= ~VK_QUEUE_SPARSE_BINDING_BIT;
continue;
}
}
ASSERT(graphicsFamilyIndex_.has_value() || computeFamilyIndex_.has_value() ||
transferFamilyIndex_.has_value() || sparseFamilyIndex_.has_value(),
"No suitable queue(s) found");
ASSERT(surface == VK_NULL_HANDLE || presentationFamilyIndex_.has_value(),
"No queues with presentation capabilities found");
}
看一下上面这个函数的话,其实就是一个for循环遍历所有的queue family Properties,然后选择满足条件的queue,存下来他的queueFamily Index和count
创建Device
DeviceQueue
std::vector<std::vector<float>> prioritiesForAllFamilies(familyIndices.size());
for (size_t index = 0; const auto& [queueFamilyIndex, queueCount] : familyIndices)
{
prioritiesForAllFamilies[index] = std::vector<float>(queueCount, 1.0f);
queueCreateInfos.emplace_back(VkDeviceQueueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = queueFamilyIndex,
.queueCount = queueCount,
.pQueuePriorities = prioritiesForAllFamilies[index].data(),
});
++index;
}
const VkDeviceCreateInfo dci = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = featureChain.firstNextPtr(),
.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()),
.pQueueCreateInfos = queueCreateInfos.data(),
.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size()),
.ppEnabledLayerNames = instanceLayers.data(),
.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()),
.ppEnabledExtensionNames = deviceExtensions.data(),
};
VK_CHECK(vkCreateDevice(physicalDevice_.vkPhysicalDevice(), &dci, nullptr, &device_));
顺便看一下这个featureChain,学习一下c++
template <size_t CHAIN_SIZE = 10>
class VulkanFeatureChain {
public:
VulkanFeatureChain() = default;
MOVABLE_ONLY(VulkanFeatureChain);
auto& pushBack(auto nextVulkanChainStruct) {
ASSERT(currentIndex_ < CHAIN_SIZE, "Chain is full");
data_[currentIndex_] = nextVulkanChainStruct;
auto& next = std::any_cast<decltype(nextVulkanChainStruct)&>(data_[currentIndex_]);
next.pNext = std::exchange(firstNext_, &next);
currentIndex_++;
return next;
}
[[nodiscard]] void* firstNextPtr() const { return firstNext_; };
private:
std::array<std::any, CHAIN_SIZE> data_;
VkBaseInStructure* root_ = nullptr;
int currentIndex_ = 0;
void* firstNext_ = VK_NULL_HANDLE;
};
VulkanFeatureChain<> featureChain; // 生成一个10个大小(默认大小的VulkanFeatureChain)
VulkanFeatureChain的pushBack,可以看到是给一个 std::array<std::any, CHAIN_SIZE> data_;里面push数据。std::any是17引入的类型,可以保存任意类型的对象,可以通过type()方法获取类型信息,any_cast方法提取对象
std::any a = 42; // 存储一个整数
a = std::string("Hello, world!"); // 存储一个字符串
// 检查当前存储的类型
if (a.type() == typeid(std::string)) {
std::cout << "Stored a string: " << std::any_cast<std::string>(a) << '\n';
} else if (a.type() == typeid(int)) {
std::cout << "Stored an int: " << std::any_cast<int>(a) << '\n';
}
firstNext_是一个void*
auto& next = std::any_cast<decltype(nextVulkanChainStruct)&>(data_[currentIndex_]);
next.pNext = std::exchange(firstNext_, &next);
currentIndex_++;
std::exchange(firstNext_, &next):
std::exchange 是一个标准库函数,它将 firstNext_ 的当前值返回,并把 firstNext_ 改为新值 &next。
换句话说,该函数执行以下两步操作:
返回 firstNext_ 的当前值。
将 firstNext_ 设置为 &next。
next.pNext = ...:
next.pNext 表示一个结构体成员,用于指向链中的下一个结构体(假设 nextVulkanChainStruct 有一个名为 pNext 的成员)。
将 next.pNext 设置为 std::exchange(firstNext_, &next) 的返回值,即原先 firstNext_ 指向的地址。
综上所述,整行代码的作用是:
更新链表头:将当前结构体 next 的地址保存到 firstNext_ 中,使其成为新的链表头。
链接链表:将当前结构体的 pNext 成员指向之前的链表头,这样就将当前结构体添加到了链表的头部。
举例说明
假设我们有一个链表,初始为空,当我们依次插入 StructA, StructB, StructC 时,操作如下:
插入 StructA:
firstNext_ 初始为 VK_NULL_HANDLE。
next.pNext 被设置为 firstNext_,即 VK_NULL_HANDLE。
然后 firstNext_ 被更新为 &StructA。
插入 StructB:
firstNext_ 当前为 &StructA。
next.pNext(即 StructB.pNext)被设置为 firstNext_,即 &StructA。
然后 firstNext_ 被更新为 &StructB。
插入 StructC:
firstNext_ 当前为 &StructB。
next.pNext(即 StructC.pNext)被设置为 firstNext_,即 &StructB。
然后 firstNext_ 被更新为 &StructC。
最终,链表的形态会是 StructC -> StructB -> StructA,其中 firstNext_ 指向 StructC,而每个结构体的 pNext 成员指向下一个结构体。
看完只能说高手和我的差距太大了
然后又是一大段获取Device Queue的代码
if (physicalDevice_.graphicsFamilyIndex().has_value())
{
if (physicalDevice_.graphicsFamilyCount() > 0) {
// 一个家族中有多个队列
graphicsQueues_.resize(physicalDevice_.graphicsFamilyCount(), VK_NULL_HANDLE);
for (int i = 0; i < graphicsQueues_.size(); ++i) {
vkGetDeviceQueue(device_, physicalDevice_.graphicsFamilyIndex().value(),
uint32_t(i), &graphicsQueues_[i]);
}
}
}
if (physicalDevice_.computeFamilyIndex().has_value())
{
if (physicalDevice_.computeFamilyCount() > 0) {
computeQueues_.resize(physicalDevice_.computeFamilyCount(), VK_NULL_HANDLE);
for (int i = 0; i < computeQueues_.size(); ++i) {
vkGetDeviceQueue(device_, physicalDevice_.computeFamilyIndex().value(),
uint32_t(i), &computeQueues_[i]);
}
}
}
if (physicalDevice_.transferFamilyIndex().has_value()) {
if (physicalDevice_.transferFamilyCount() > 0) {
transferQueues_.resize(physicalDevice_.transferFamilyCount(), VK_NULL_HANDLE);
for (int i = 0; i < transferQueues_.size(); ++i) {
vkGetDeviceQueue(device_, physicalDevice_.transferFamilyIndex().value(),
uint32_t(i), &transferQueues_[i]);
}
}
}
if (physicalDevice_.sparseFamilyIndex().has_value()) {
if (physicalDevice_.sparseFamilyCount() > 0) {
sparseQueues_.resize(physicalDevice_.sparseFamilyCount(), VK_NULL_HANDLE);
for (int i = 0; i < sparseQueues_.size(); ++i) {
vkGetDeviceQueue(device_, physicalDevice_.sparseFamilyIndex().value(),
uint32_t(i), &sparseQueues_[i]);
}
}
}
if (physicalDevice_.presentationFamilyIndex().has_value()) {
vkGetDeviceQueue(device_, physicalDevice_.presentationFamilyIndex().value(), 0,
&presentationQueue_);
}
最后是创建vma内存分配器的
createMemoryAllocator();
可算是看完了