Vulkan实战之Instance

news/2024/11/23 0:55:27/

文章目录

      • 创建实例(**Creating an instance**)
      • 检查扩展支持(**Checking for extension support**)
      • 销毁清除(**Cleaning up**)
      • 最终代码

创建实例(Creating an instance)

您需要做的第一件事是通过创建一个实例来初始化Vulkan库。实例是应用程序和Vulkan库之间的连接,创建它需要向驱动程序指定有关应用程序的一些详细信息。

首先添加一个createInstance函数,并在initVulkan函数中调用它。

void initVulkan() {createInstance();
}

另外,添加一个数据成员来保存实例的句柄:

private:
VkInstance instance;

现在,要创建一个实例,我们首先必须用有关应用程序的一些信息填充一个结构体。这些数据在技术上是可选的,但它可能为驱动程序提供一些有用的信息,以便优化我们的特定应用程序(例如,因为它使用了具有某些特殊行为的知名图形引擎)。这个结构体叫做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;
}

如前所述,Vulkan中的许多结构要求您在sType成员中显式指定类型。这也是具有pNext成员的众多结构之一,该成员可以在将来指向扩展信息。我们在这里使用值初始化将它保留为nullptr。

Vulkan中的许多信息都是通过结构体而不是函数参数传递的,我们必须再填写一个结构体来为创建实例提供足够的信息。这个结构体不是可选的,它告诉Vulkan驱动程序我们想要使用哪些全局扩展和验证层。这里的全局意味着它们适用于整个程序,而不是特定的设备,这将在接下来的几章中变得清晰。

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

前两个参数很简单。接下来的两层指定所需的全局扩展。正如在概述章节中提到的,Vulkan是一个平台无关的API,这意味着您需要一个扩展来与窗口系统接口。GLFW有一个方便的内置函数,它返回它需要做的扩展,我们可以把它传递给结构体:

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

结构体的最后两个成员确定要启用的全局验证层。我们将在下一章中更深入地讨论这些,所以现在先把这些留空。

createInfo.enabledLayerCount = 0;

现在我们已经指定了Vulkan创建实例所需的所有内容,我们最终可以发出vkCreateInstance调用:

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

正如你将看到的,在Vulkan中对象创建函数参数遵循的一般模式是:

  • 指向具有创建信息的结构体的指针
  • 指向自定义分配器回调函数的指针,在本教程中总是nullptr
  • 指向存储新对象句柄的变量的指针

如果一切顺利,则实例的句柄存储在VkInstance类成员中。几乎所有的Vulkan函数都返回VkResult类型的值,要么是VK_SUCCESS,要么是错误代码。要检查实例是否成功创建,我们不需要存储结果,只需要检查成功值即可:

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

现在运行程序以确保成功创建了实例。

检查扩展支持(Checking for extension support)

如果您查看vkCreateInstance文档,那么您将看到其中一个可能的错误代码是VK_ERROR_EXTENSION_NOT_PRESENT。我们可以简单地指定我们需要的扩展,并在错误代码返回时终止。这对于像窗口系统接口这样的基本扩展来说是有意义的,但是如果我们想要检查可选功能呢?

要在创建实例之前检索支持的扩展列表,可以使用vkEnumerateInstanceExtensionProperties函数。它接受一个指向存储扩展数量的变量的指针和一个存储扩展详细信息的VkExtensionProperties数组。它还有一个可选的第一个参数,允许我们通过特定的验证层过滤扩展,现在我们将忽略它。

为了分配一个数组来保存扩展细节,我们首先需要知道有多少个扩展。您可以通过将后一个参数保留为空来请求扩展的数量:

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

现在分配一个数组来保存扩展细节(include ):

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

最后我们可以查询扩展的详细信息:

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

每个VkExtensionProperties结构体都包含扩展的名称和版本。我们可以用一个简单的for循环列出它们(\t是用于缩进的制表符):

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

如果您想提供有关Vulkan支持的一些细节,可以将此代码添加到createInstance函数中。作为一个挑战,尝试创建一个函数来检查glfwGetRequiredInstanceExtensions返回的所有扩展是否都包含在支持的扩展列表中。

销毁清除(Cleaning up)

VkInstance应该只在程序退出之前销毁。它可以在清理时用vkDestroyInstance函数销毁:

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

vkDestroyInstance函数的参数很简单。正如前一章所提到的,Vulkan中的分配和释放函数有一个可选的allocator回调,我们将通过传递nullptr来忽略它。我们将在接下来的章节中创建的所有其他Vulkan资源都应该在销毁实例之前清理干净。

在实例创建之后继续执行更复杂的步骤之前,应该通过检查验证层来评估我们的调试选项。接下来介绍验证层的使用。

最终代码

//01_create_instance.cpp
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>#include <vulkan/vulkan.h>
#include <iostream>
#include <stdexcept>
#include <cstdlib>const int WIDTH = 800;
const int HEIGHT = 600;class HelloTriangleApplication {
public:void run() {initWindow();initVulkan();mainLoop();cleanup();}private:void initWindow() {glfwInit();glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);}void initVulkan() {createInstance();}void mainLoop() {}void cleanup() {vkDestroyInstance(instance, nullptr);glfwDestroyWindow(window);glfwTerminate();}VkInstance instance;GLFWwindow* window;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;VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);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;
}

运行后将产生一个glfw窗口,并创建了一个instance实例


http://www.ppmy.cn/news/59474.html

相关文章

Android9.0 原生系统SystemUI下拉状态栏和通知栏视图之锁屏通知布局

1.前言 在9.0的系统rom定制化开发中,对于系统原生systemui的锁屏界面的功能也是非常重要的,所以在锁屏页面布局中,也是有通知栏布局的,所以接下来对于息屏亮屏 通知栏布局的相关流程分析,看下亮屏后锁屏页面做了哪些功能 2.原生系统SystemUI下拉状态栏和通知栏视图之锁…

总结一下vue的关键字和用处

Vue.js 是一个轻量级的 JavaScript 框架&#xff0c;用于构建用户界面和单页面应用程序。下面是一些 Vue.js 中的关键字和它们的用途&#xff1a; v-bind&#xff1a;用于动态绑定属性和事件监听器。例如&#xff0c;可以用 v-bind 绑定一个元素的属性&#xff08;如&#xff…

node.js的核心模块

node的核心模块由一些精简而高效的库组成 文章目录 全局对象全局对象和全局变量processcosole utilutils.inheritsutils.inspect 事件机制事件发射器error 事件继承EventEmitter 文件系统访问fs.readFile(filename,[encoding],[callback(err,data)])fs.readFileSync(filename,…

Python小姿势 - ## Python中的迭代器与生成器

Python中的迭代器与生成器 在Python中&#xff0c;迭代是一个非常重要的概念&#xff0c;迭代器和生成器是迭代的两种最常见的形式。那么&#xff0c;迭代器与生成器有何不同呢&#xff1f; 首先&#xff0c;我们先来了解一下迭代器。 迭代器是一种对象&#xff0c;它可以记住遍…

Kubernetes云原生实战05 一键安装三主三从高可用集群

大家好,我是飘渺。 今天咱们继续更新Kubernetes云原生实战系列,本节文章会使用Kubekey安装一个三主三从的高可用集群。 在开始之前请分别登录kubernets集群节点,分别修改节点的主机名,跟第一章中提到的节点规划表保持一致。 hostnamectl set-hostname k8s-master1 hostn…

Activiti7流程操作详解

一、Activiti流程操作步骤 定义流程&#xff0c;按照BPMN的规范&#xff0c;使用流程定义工具&#xff0c;用流程符号把整个流程描述出来 部署流程&#xff0c;把画好的流程定义文件&#xff0c;加载到数据库中&#xff0c;生成表的数据 启动流程&#xff0c;使用java代码来操…

日志打印最佳实践

日志打印最佳实践 标题为什么要记录日志? 打印调试&#xff1a;用日志来记录变量或者某一段逻辑&#xff0c;记录程序运行的流程&#xff0c;即程序运行了哪些代码&#xff0c;方便排查逻辑问题。 问题定位&#xff1a;程序出异常或者出故障时快速的定位问题&#xff0c;方…

Django之定时任务django-crontab

Django之定时任务django-crontab crontab安装django-crontab注册应用定时时间格式定时时间示例设置定时任务符号方法解决crontab中文问题管理定时任务注意 crontab Django可以使用第三方库如django-crontab来实现定时任务的调度。该库允许使用类似于crontab文件格式的语法指定任…