创建vulkan context部分代码

第一行

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();
可算是看完了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值