Vulkan【2】创建Vulkan实例

本文介绍了如何使用Vulkan API创建Vulkan实例,这是Vulkan应用程序的基础步骤。文章详细解析了vkCreateInstance函数及其参数,包括VkInstanceCreateInfo和VkApplicationInfo结构体的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建Vulkan实例

本节的源代码是 01-init_instance.cpp

Vulkan程序的第一步是创建一个Vulkan实例。

当你阅读到本节的时候,可以在LunarG Vulkan 示例代码仓库中 API-Samples文件夹下找到01-init_instance.cpp

Vulkan实例

Vulkan API使用 vkInstance 对象 来存储 所有每个应用的状态。应用程序必须在执行任何其他Vulkan操作之前创建一个Vulkan实例。

基本的Vulkan架构看起来是这样的:

这里写图片描述

上面的图显示了一个Vulkan应用程序链接到一个通常被称为加载器(Loader)的Vulkan库。创建实例会初始化装载器。加载器还加载并初始化低级图形驱动程序,通常由GPU硬件的供应商提供。

请注意,图中有一些层(Layer),这些层也被加载器加载。层通常用于验证,通常是由驱动执行的错误检查。在Vulkan中,驱动程序比OpenGL等其他API要轻量得多,部分原因是它将功能验证委托给验证层。
层是可选的,每次应用程序创建一个实例时都可以选择性地装载。

Vulkan层超出了本教程的范围和示例的进展。因此,本教程不进一步介绍这些层。关于图层的进一步信息可以在下面的网址找到
LunarG LunarXchange website.

vkCreateInstance

看下 01-init_instance.cpp 代码然后找到vkCreateInstance 函数调用,函数原型如下:

    VkResult vkCreateInstance(
        const VkInstanceCreateInfo*                 pCreateInfo,
        const VkAllocationCallbacks*                pAllocator,
        VkInstance*                                 pInstance);

一点一点来看:

VkResult -
这是函数的返回状态。你可能想要打开“vulkan.h”的头文件。当我们遇到这些定义时,请检查其中的一些定义。你可以在“include”目录中找到这个文件

VkInstanceCreateInfo -
这个结构包含了用来创建实例的额外的信息。由于这是一个很重要的一项,您稍后将更详细得了解它。

VkAllocationCallbacks -
分配主机内存的函数都有一个参数,允许应用程序执行自己的主机内存管理。否则,Vulkan使用默认系统内存管理设施。应用程序可能想要管理自己的主机内存,例如,为了记录内存分配。

本示例不使用这个特性,因此您将始终看到NULL被传递到这个和其他函数的参数中。

VkInstance -
这是函数返回的句柄如果实例创建成功。
这是一个不可见的句柄,所以不要试图去引用它。
许多Vulkan函数创建对象返回句柄,它们以这种方式创建的对象。

详解VkInstanceCreateInfo结构体

创建对象的Vulkan函数通常有一个Vk_Object_CreateInfo参数。
找到初始化这个结构的代码:

    typedef struct VkInstanceCreateInfo {
        VkStructureType             sType;
        const void*                 pNext;
        VkInstanceCreateFlags       flags;
        const VkApplicationInfo*    pApplicationInfo;
        uint32_t                    enabledLayerCount;
        const char* const*          ppEnabledLayerNames;
        uint32_t                    enabledExtensionCount;
        const char* const*          ppEnabledExtensionNames;
    } VkInstanceCreateInfo;

前两个成员通常出现在许多Vulkan的CreateInfo结构中。

sType -
“sType”字段表示结构的类型。在本例中,您将其设置为VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,因为它是一个VkInstanceCreateInfo 结构。这可能看起来是多余的,因为只有这种类型的结构可以是作为vkCreateInstance()的第一个参数传递。但它有一些价值,原因如下:
* 一个驱动程序、验证层或结构的其他使用者可以执行一个简单的有效性检查,如果“sType”不像预期的那样,可能会请求失败。
* 通过没有完全定义到接口参数类型化的接口,可以通过无类型(void)指针传递给消费者。例如,如果一个驱动程序支持实例创建的扩展,它可以查看通过无类型pNext指针(接下来讨论)传递的这样一个结构。在这种情况下,sType将被设置为扩展识别的值。

由于这个成员总是结构中的第一个成员,所以使用者可以很容易地确定结构的类型并决定如何处理它。

pNext -
你会经常将pNext设置为NULL。这个空指针有时用于在类型化结构中传递特定于扩展的信息,其中sType被设置为一个扩展定义的值。正如上面所讨论的,扩展可以分析在这个pNext指针链中传递的任何结构,以找到它们识别的结构。

flags -
目前还没有定义,所以将其设置为零。

pApplicationInfo -
这是一个指向另一个结构的指针,你需要设置它。你一会儿就会了解到。

enabledLayerCountppEnabledLayerNames -
本教程中的示例不使用层,因此这些成员被清空。

enabledExtensionCountppEnabledExtensionNames -
本教程中的示例不使用扩展。稍后,另一个示例将显示扩展的用法。

VkApplicationInfo结构

该结构为Vulkan的实现提供了关于应用程序的一些基本信息:

    typedef struct VkApplicationInfo {
        VkStructureType    sType;
        const void*        pNext;
        const char*        pApplicationName;
        uint32_t           applicationVersion;
        const char*        pEngineName;
        uint32_t           engineVersion;
        uint32_t           apiVersion;
    } VkApplicationInfo;

sType and pNext -
含义和vkInstanceCreateInfo 结构中的一样.

pApplicationName, applicationVersion, pEngineName, engineVersion -
如果需要的话,这些是应用程序可以提供的自由字段。
工具、加载器、层或驱动程序的一些实现在调试或收集时使用这些字段来提供信息,或者信息报告等。
驱动甚至有可能改变它们的行为,取决于正在运行的应用程序。

apiVersion -
这个字段表示用于编译应用程序的Vulkan API的主版本、子版本和补丁版本。如果你使用的是Vulkan 1.0,那么主版本是1,而子版本应该是0。使用来自vulkan.hVK_API_VERSION_1_0 宏来完成,补丁级别为0。补丁版本的差异不会影响只在补丁版本上不同的版本之间的完整兼容性。通常,您应该将这个字段设置为VK_API_VERSION_1_0,除非您有充分的理由不这样做。

回到代码上

一旦结构被填充,样例应用程序就会创建实例:

    VkInstance inst;
    VkResult res;

    res = vkCreateInstance(&inst_info, NULL, &inst);
    if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
        std::cout << "cannot find a compatible Vulkan ICD\n";
        exit(-1);
    } else if (res) {
        std::cout << "unknown error\n";
        exit(-1);
    }

    vkDestroyInstance(inst, NULL);

在上面的代码中,应用程序根据结果快速检查可能出现的错误并报告它,或者其他一些错误。请注意,成功(VK_SUCCESS)的值为0,许多应用程序将非零结果解释为一个错误。

最后,应用在退出前销毁这个对象。

现在您已经创建了一个Vulkan实例,现在是时候发现您的Vulkan实例可用的图形设备了。

© Copyright 2016 LunarG, Inc

<think>嗯,我现在要了解如何在Vulkan创建窗口表面。我之前学过一些Vulkan的基础知识,知道它和OpenGL不同,需要自己管理很多底层的东西。窗口表面应该和渲染到屏幕有关吧?那具体步骤是怎样的呢? 首先,我记得Vulkan是一个跨平台的API,所以创建窗口表面可能涉及到不同操作系统的处理。比如,Windows用HWND,Linux可能有X11或Wayland之类的。不过Vulkan应该提供了统一的接口来处理这些差异。可能要用到像VkSurfaceKHR这样的对象。 那具体步骤呢?可能需要先创建一个Vulkan实例(instance),然后根据不同的平台创建表面。比如在Windows上,可能需要调用vkCreateWin32SurfaceKHR函数。不过这些函数是不是扩展的一部分?所以可能需要启用相应的扩展,比如VK_KHR_surface和VK_KHR_win32_surface。 对了,创建窗口表面应该在实例创建之后,物理设备和逻辑设备之前吗?或者是在实例创建的时候就需要启用这些扩展?比如在创建实例时,要检查并启用所需的窗口表面扩展。比如,对于Windows来说,实例扩展需要包括VK_KHR_surface和VK_KHR_win32_surface。其他平台如Linux可能需要不同的扩展,比如VK_KHR_xlib_surface或VK_KHR_wayland_surface。 然后,创建表面需要用到平台相关的信息,比如Windows的HINSTANCE和HWND。这些信息应该由窗口库(比如GLFW或SDL)提供,或者自己手动获取。假设用户没有使用这些库,可能需要自己调用系统API来获取这些句柄。 不过GLFW这样的库应该已经封装了创建表面的过程,比如glfwCreateWindowSurface函数,这样可以跨平台处理。但如果是手动创建的话,步骤可能更复杂。比如,在Windows上,先填写VkWin32SurfaceCreateInfoKHR结构体,包含hinstance和hwnd,然后调用vkCreateWin32SurfaceKHR函数,得到VkSurfaceKHR对象。 另外,创建表面之后,需要检查物理设备是否支持呈现(presentation)到该表面。这涉及到队列族的支持情况,比如检查vkGetPhysicalDeviceSurfaceSupportKHR函数来确定某个队列族是否能向表面提交图像。 可能的大致步骤是: 1. 创建Vulkan实例,启用必要的窗口表面扩展。 2. 获取平台的窗口句柄(如HWND、Xlib的Window等)。 3. 使用平台相关的函数创建VkSurfaceKHR。 4. 检查物理设备是否支持该表面,选择合适的队列族。 需要注意的是,不同平台的创建方式不同,所以代码可能需要条件编译或者根据平台选择不同的路径。另外,错误处理也很重要,比如检查扩展是否可用,表面创建是否成功。 还有,Vulkan中的表面创建涉及到KHR扩展,这些扩展可能需要在实例创建时显式启用。例如,在VkInstanceCreateInfo的ppEnabledExtensionNames中加入相应的扩展名称。 可能的问题:如果忘记启用扩展,创建表面时会失败。或者,如果传递的窗口句柄无效,也会导致错误。此外,创建表面后需要销毁,避免资源泄漏,比如使用vkDestroySurfaceKHR函数。 总结一下,创建窗口表面的关键点包括:实例扩展的启用、平台特定的表面创建函数、正确的窗口句柄传递,以及后续的物理设备支持检查。</think>在Vulkan创建窗口表面需要结合平台特定的API和Vulkan扩展。以下是分步说明: --- ### **1. 创建Vulkan实例时启用扩展** 在初始化Vulkan实例时,必须启用以下扩展(以Windows为例): - **实例扩展**: - `VK_KHR_surface`(通用表面支持) - 平台相关扩展(如Windows的`VK_KHR_win32_surface`) 示例代码片段: ```cpp const char* extensions[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME, // 其他平台替换为对应扩展 }; VkInstanceCreateInfo instanceInfo = {}; instanceInfo.enabledExtensionCount = 2; instanceInfo.ppEnabledExtensionNames = extensions; // ...其他参数 vkCreateInstance(&instanceInfo, nullptr, &instance); ``` --- ### **2. 获取平台窗口句柄** 需要从窗口系统获取原生句柄: - **Windows**:`HINSTANCE`(应用程序实例)和`HWND`(窗口句柄) - **Linux/X11**:`Display*`和`Window` - **GLFW/SDL**:若使用库(如GLFW),可通过`glfwGetWin32Window(window)`获取句柄。 --- ### **3. 创建窗口表面** 使用平台特定的Vulkan函数创建`VkSurfaceKHR`对象。 #### **Windows示例**: ```cpp #include <vulkan/vulkan_win32.h> VkWin32SurfaceCreateInfoKHR surfaceInfo = {}; surfaceInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; surfaceInfo.hinstance = hInstance; // 从系统或窗口库获取 surfaceInfo.hwnd = hWnd; // 窗口句柄 VkSurfaceKHR surface; VkResult result = vkCreateWin32SurfaceKHR( instance, &surfaceInfo, nullptr, &surface ); if (result != VK_SUCCESS) { // 处理错误 } ``` #### **使用GLFW简化**: ```cpp glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // 禁用OpenGL GLFWwindow* window = glfwCreateWindow(...); VkSurfaceKHR surface; VkResult err = glfwCreateWindowSurface( instance, window, nullptr, &surface ); if (err != VK_SUCCESS) { // 处理错误 } ``` --- ### **4. 检查物理设备支持** 创建表面后,需验证物理设备(GPU)是否支持呈现到该表面: ```cpp // 获取物理设备的队列族属性 uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); // 检查每个队列族是否支持呈现 for (uint32_t i = 0; i < queueFamilyCount; i++) { VkBool32 presentSupport = false; vkGetPhysicalDeviceSurfaceSupportKHR( physicalDevice, i, surface, &presentSupport ); if (presentSupport) { // 记录支持呈现的队列族索引 } } ``` --- ### **5. 销毁表面** 程序退出时需清理资源: ```cpp vkDestroySurfaceKHR(instance, surface, nullptr); ``` --- ### **关键注意事项** 1. **平台差异**:不同操作系统需启用不同的扩展(如Linux的`VK_KHR_xlib_surface`)。 2. **扩展验证**:调用`vkEnumerateInstanceExtensionProperties`确认扩展可用。 3. **错误处理**:检查`vkCreateXXXSurfaceKHR`的返回值,处理`VK_ERROR_EXTENSION_NOT_PRESENT`等错误。 通过以上步骤,即可在Vulkan中成功创建窗口表面,为后续交换链和渲染操作奠定基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值