Vulkan 编程指南 - 实例 (Vulkan Tutorial / Instance / 01_instance_creation.cpp)

2023-11-27 22:20

本文主要是介绍Vulkan 编程指南 - 实例 (Vulkan Tutorial / Instance / 01_instance_creation.cpp),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Vulkan 编程指南 - 实例 (Vulkan Tutorial / Instance / 01_instance_creation.cpp)

仅供个人学习、研究使用,建议大家阅读 Vulkan Tutorial

Drawing a triangle / Setup / Instance
https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance

Vulkan Tutorial
https://vulkan-tutorial.com/

GitHub Repository
https://github.com/Overv/VulkanTutorial

1 Instance (实例)

1.1 Creating an instance

The very first thing you need to do is initialize the Vulkan library by creating an instance. The instance is the connection between your application and the Vulkan library and creating it involves specifying some details about your application to the driver.
我们首先创建一个实例来初始化 Vulkan 库。这个实例是你的应用程序和 Vulkan 库之间的连接,创建它需要向驱动指定一些关于你的应用程序的信息。

Start by adding a createInstance function and invoking it in the initVulkan function.

void initVulkan() {createInstance();
}

Additionally add a data member to hold the handle to the instance (添加了一个存储实例句柄的私有成员):

private:
VkInstance instance;

Now, to create an instance we’ll first have to fill in a struct with some information about our application. This data is technically optional, but it may provide some useful information to the driver in order to optimize our specific application (e.g. because it uses a well-known graphics engine with certain special behavior).
现在,为了创建一个实例,我们首先要在一个结构中填写关于我们应用程序的一些信息。这些数据在技术上是可选的,但填写的信息可能会作为驱动程序的优化依据,让驱动程序进行一些特殊的优化。例如,应用程序使用了某个引擎,驱动程序对这个引擎有一些特殊处理,这时就可能有很大的优化提升。

This struct is called VkApplicationInfo:

void createInstance() {VkApplicationInfo appInfo{};appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;appInfo.pApplicationName = "Hello Triangle";appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);appInfo.pEngineName = "No Engine";appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);appInfo.apiVersion = VK_API_VERSION_1_0;
}

As mentioned before, many structs in Vulkan require you to explicitly specify the type in the sType member. This is also one of the many structs with a pNext member that can point to extension information in the future. We’re using value initialization here to leave it as nullptr.
之前提到,Vulkan 的很多结构体需要我们显式地在 sType 成员变量中指定结构体的类型。此外,许多 Vulkan 结构体还有一个 pNext 成员变量,用来指向未来可能扩展的参数信息,现在,我们并没有使用它,将其设置为 nullptr

A lot of information in Vulkan is passed through structs instead of function parameters and we’ll have to fill in one more struct to provide sufficient information for creating an instance. This next struct is not optional and tells the Vulkan driver which global extensions and validation layers we want to use. Global here means that they apply to the entire program and not a specific device, which will become clear in the next few chapters.
Vulkan 倾向于通过结构体传递信息,我们需要填写一个或多个结构体来提供足够的信息创建 Vulkan 实例。下面的这个结构体是必须的,它告诉 Vulkan 的驱动程序需要使用的全局扩展和校验层。全局是指这里的设置对于整个应用程序都有效,而不仅仅对一个设备有效。

VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;

The first two parameters are straightforward. The next two layers specify the desired global extensions. As mentioned in the overview chapter, Vulkan is a platform agnostic API, which means that you need an extension to interface with the window system. GLFW has a handy built-in function that returns the extension(s) it needs to do that which we can pass to the struct.

上面代码中填写得两个参数非常直白,不用多解释。接下来,我们需要指定需要的全局扩展。正如在概述章节中提到的,Vulkan 是一个与平台无关的 API,这意味着你需要一个扩展来与窗口系统对接。GLFW 有一个方便的内置函数,可以返回它所需要的扩展,我们可以将其传递给结构。

uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;

The last two members of the struct determine the global validation layers to enable. We’ll talk about these more in-depth in the next chapter, so just leave these empty for now.
结构体的最后两个成员变量用来指定全局校验层。在这里,我们将其设置为 0,不使用它。

createInfo.enabledLayerCount = 0;

We’ve now specified everything Vulkan needs to create an instance and we can finally issue the vkCreateInstance call.
填写完所有必要的信息,我们就可以调用 vkCreateInstance 函数来创建 Vulkan 实例。

VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);

As you’ll see, the general pattern that object creation function parameters in Vulkan follow is:

  • Pointer to struct with creation info (一个包含了创建信息的结构体指针)
  • Pointer to custom allocator callbacks, always nullptr in this tutorial (一个自定义的分配器回调函数,在本教程,我们没有使用自定义的分配器,总是将它设置为 nullptr)
  • Pointer to the variable that stores the handle to the new object (一个指向新对象句柄存储位置的指针)

If everything went well then the handle to the instance was stored in the VkInstance class member. Nearly all Vulkan functions return a value of type VkResult that is either VK_SUCCESS or an error code. To check if the instance was created successfully, we don’t need to store the result and can just use a check for the success value instead.
如果一切顺利,我们创建的实例的句柄就被存储在了类的 VkInstance 成员变量中。几乎所有 Vulkan 函数调用都会返回一个 VkResult 来反应函数调用的结果,它的值可以是 VK_SUCCESS 表示调用成功,或是一个错误代码,表示调用失败。为了检测实例是否创建成功,我们可以直接将创建函数在条件语句中使用,不需要存储它的返回值。

if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {throw std::runtime_error("failed to create instance!");
}

Now run the program to make sure that the instance is created successfully.

1.2 Encountered VK_ERROR_INCOMPATIBLE_DRIVER

If using MacOS with the latest MoltenVK sdk, you may get VK_ERROR_INCOMPATIBLE_DRIVER returned from vkCreateInstance. According to the Getting Start Notes. Beginning with the 1.3.216 Vulkan SDK, the VK_KHR_PORTABILITY_subset extension is mandatory.
从 Vulkan SDK 1.3.216 开始,VK_KHR_PORTABILITY_subset 扩展是强制性的。

To get over this error, first add the VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR bit to VkInstanceCreateInfo struct’s flags, then add VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME to instance enabled extension list.

Typically the code could be like this:

...std::vector<const char*> requiredExtensions;for(uint32_t i = 0; i < glfwExtensionCount; i++) {requiredExtensions.emplace_back(glfwExtensions[i]);
}requiredExtensions.emplace_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;createInfo.enabledExtensionCount = (uint32_t) requiredExtensions.size();
createInfo.ppEnabledExtensionNames = requiredExtensions.data();if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {throw std::runtime_error("failed to create instance!");
}

1.3 Checking for extension support (检测扩展支持)

If you look at the vkCreateInstance documentation then you’ll see that one of the possible error codes is VK_ERROR_EXTENSION_NOT_PRESENT. We could simply specify the extensions we require and terminate if that error code comes back. That makes sense for essential extensions like the window system interface, but what if we want to check for optional functionality?
如果读者看过 vkCreateInstance 函数的官方文档,可能会知道它返回的中一个错误代码 VK_ERROR_EXTENSION_NOT_PRESENT。我们可以利用这个错误代码在扩展不能满足时直接结束我们的程序,这对于像窗口系统这种必要的扩展来说非常适合。但有时,我们请求的扩展可能是非必须的,有了很好,没有的话,程序仍然可以运行。

To retrieve a list of supported extensions before creating an instance, there’s the vkEnumerateInstanceExtensionProperties function. It takes a pointer to a variable that stores the number of extensions and an array of VkExtensionProperties to store details of the extensions. It also takes an optional first parameter that allows us to filter extensions by a specific validation layer, which we’ll ignore for now.
为了在创建实例之前检索支持的扩展列表,有一个 vkEnumerateInstanceExtensionProperties 函数。它需要一个存储扩展数量的变量指针和一个 VkExtensionProperties 的数组来存储扩展的细节。它还需要一个可选的第一个参数,允许我们通过一个特定的验证层来过滤扩展,我们现在将忽略这个参数。

To allocate an array to hold the extension details we first need to know how many there are. You can request just the number of extensions by leaving the latter parameter empty:
我们首先需要知道扩展的数量,以便分配合适的数组大小来存储信息。可以通过下面的代码来获取扩展的数量。

uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);

Now allocate an array to hold the extension details (include <vector>):

std::vector<VkExtensionProperties> extensions(extensionCount);

Finally we can query the extension details.
最后,我们可以查询扩展的详细信息。

vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());

Each VkExtensionProperties struct contains the name and version of an extension. We can list them with a simple for loop (\t is a tab for indentation).
每个 VkExtensionProperties 结构包含一个扩展的名称和版本。

std::cout << "available extensions:\n";for (const auto& extension : extensions) {std::cout << '\t' << extension.extensionName << '\n';
}

You can add this code to the createInstance function if you’d like to provide some details about the Vulkan support. As a challenge, try to create a function that checks if all of the extensions returned by glfwGetRequiredInstanceExtensions are included in the supported extensions list.
如果你想提供一些关于 Vulkan 支持的细节,你可以在 createInstance 函数中加入这段代码。尝试创建一个函数,检查由 glfwGetRequiredInstanceExtensions 返回的所有扩展是否包含在支持的扩展列表中。

1.4 Cleaning up

The VkInstance should be only destroyed right before the program exits. It can be destroyed in cleanup with the vkDestroyInstance function.
VkInstance 应该只在程序退出前销毁,它可以在 cleanup 中用 vkDestroyInstance 函数销毁。

void cleanup() {vkDestroyInstance(instance, nullptr);glfwDestroyWindow(window);glfwTerminate();
}

The parameters for the vkDestroyInstance function are straightforward. As mentioned in the previous chapter, the allocation and deallocation functions in Vulkan have an optional allocator callback that we’ll ignore by passing nullptr to it. All of the other Vulkan resources that we’ll create in the following chapters should be cleaned up before the instance is destroyed.
vkDestroyInstance 函数的参数非常直白。之前提到,Vulkan 对象的分配和清除函数都有一个可选的分配器回调参数,在本教程,我们没有自定义的分配器,所以,将其设置为 nullptr。除了 Vulkan 实例,其余我们使用 Vulkan 创建的对象也需要被清除,且应该在 Vulkan 实例清除之前被清除。

Before continuing with the more complex steps after instance creation, it’s time to evaluate our debugging options by checking out validation layers.

01_instance_creation.cpp
https://github.com/Overv/VulkanTutorial/blob/main/code/01_instance_creation.cpp

01_instance_creation.cpp

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>#include <iostream>
#include <stdexcept>
#include <cstdlib>const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;class HelloTriangleApplication {
public:void run() {initWindow();initVulkan();mainLoop();cleanup();}private:GLFWwindow* window;VkInstance instance;void initWindow() {glfwInit();glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);window = glfwCreateWindow(WIDTH, HEIGHT, "yongqiang", nullptr, nullptr);}void initVulkan() {createInstance();}void mainLoop() {while (!glfwWindowShouldClose(window)) {glfwPollEvents();}}void cleanup() {vkDestroyInstance(instance, nullptr);glfwDestroyWindow(window);glfwTerminate();}void createInstance() {VkApplicationInfo appInfo{};appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;appInfo.pApplicationName = "Hello Triangle";appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);appInfo.pEngineName = "No Engine";appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);appInfo.apiVersion = VK_API_VERSION_1_0;VkInstanceCreateInfo createInfo{};createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;createInfo.pApplicationInfo = &appInfo;uint32_t glfwExtensionCount = 0;const char** glfwExtensions;glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);createInfo.enabledExtensionCount = glfwExtensionCount;createInfo.ppEnabledExtensionNames = glfwExtensions;createInfo.enabledLayerCount = 0;if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {throw std::runtime_error("failed to create instance!");}}
};int main() {HelloTriangleApplication app;try {app.run();} catch (const std::exception& e) {std::cerr << e.what() << std::endl;return EXIT_FAILURE;}return EXIT_SUCCESS;
}

Makefile

CFLAGS = -std=c++17 -O2
LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXiVulkanTest: instance_creation_01.cppg++ $(CFLAGS) -o VulkanTest instance_creation_01.cpp $(LDFLAGS).PHONY: test cleantest: VulkanTest./VulkanTestclean:rm -f VulkanTest
yongqiang@yongqiang:~/vulkan_workspace/instance_creation_01$ make clean
rm -f VulkanTest
yongqiang@yongqiang:~/vulkan_workspace/instance_creation_01$
yongqiang@yongqiang:~/vulkan_workspace/instance_creation_01$ make test
g++ -std=c++17 -O2 -o VulkanTest instance_creation_01.cpp -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
./VulkanTest
yongqiang@yongqiang:~/vulkan_workspace/instance_creation_01$
yongqiang@yongqiang:~/vulkan_workspace/instance_creation_01$ ls
Makefile  VulkanTest  instance_creation_01.cpp
yongqiang@yongqiang:~/vulkan_workspace/instance_creation_01$

在这里插入图片描述

References

https://yongqiang.blog.csdn.net/
Vulkan Tutorial https://vulkan-tutorial.com/
Vulkan 教程 https://geek-docs.com/vulkan/vulkan-tutorial/vulkan-tutorial-index.html

这篇关于Vulkan 编程指南 - 实例 (Vulkan Tutorial / Instance / 01_instance_creation.cpp)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/428561

相关文章

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow