Vulkan实战之Instance

本文介绍了如何使用VulkanAPI创建实例,包括初始化Vulkan库、填充VkApplicationInfo结构体、设置VkInstanceCreateInfo以及处理全局扩展。同时,文章还提到了检查扩展支持的方法,以及在程序结束时如何正确销毁Vulkan实例。最后,提供了完整的创建实例代码示例。

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

创建实例(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实例

### C++ 实战项目示例 以下是几个常见的 C++ 实战项目的示例及其代码片段,涵盖了异步编程、图形界面开发、数据结构实现等多个领域。 --- #### 1. **基于 C++20 的无栈协程 (Lazy)** 此部分展示了如何利用现代 C++ 特性构建简单的异步任务处理机制。 ```cpp #include <coroutine> #include <iostream> struct Task { struct promise_type; using handle_type = std::coroutine_handle<promise_type>; bool await_ready() const noexcept { return false; } void await_resume() const noexcept {} auto await_suspend(std::coroutine_handle<> h) noexcept { _handle = handle_type::from_promise(h.promise()); return _handle; } private: handle_type _handle; struct promise_type { Task get_return_object() { return Task{this}; } std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void unhandled_exception() {} void return_void() {} }; }; Task asyncFunction() { std::cout << "Start coroutine\n"; co_await std::suspend_always{}; std::cout << "Resume coroutine\n"; } int main() { asyncFunction(); } ``` 上述代码实现了基本的无栈协程功能[^1],适合用于轻量级的任务调度场景。 --- #### 2. **屏幕捕捉技术** 以下是一个使用 Windows API 和 GDI 函数捕获屏幕图像的例子: ```cpp #include <windows.h> #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") void CaptureScreen(const char* filename) { HDC hdcScreen = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdcScreen); int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, screenWidth, screenHeight); SelectObject(hdcMem, hBitmap); BitBlt(hdcMem, 0, 0, screenWidth, screenHeight, hdcScreen, 0, 0, SRCCOPY); Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); Gdiplus::Bitmap bitmap(hBitmap, NULL); CLSID clsid; GetEncoderClsid(L"image/jpeg", &clsid); // JPEG 编码器 bitmap.Save(filename, &clsid, NULL); DeleteObject(hBitmap); ReleaseDC(NULL, hdcScreen); DeleteDC(hdcMem); Gdiplus::GdiplusShutdown(gdiplusToken); } // 辅助函数:获取编码器 CLSID bool GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT numEncoders = 0; UINT size = 0; Gdiplus::GetImageEncodersSize(&numEncoders, &size); if (size == 0) return false; Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size)); if (!pImageCodecInfo) return false; Gdiplus::GetImageEncoders(numEncoders, size, pImageCodecInfo); for (UINT j = 0; j < numEncoders; ++j) { if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) { *pClsid = pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return true; } } free(pImageCodecInfo); return false; } int main() { CaptureScreen("screenshot.jpg"); return 0; } ``` 该代码演示了如何在 VC 开发环境中通过 GDI+ 库保存屏幕截图到文件中[^2]。 --- #### 3. **C++ 数据结构实战 - 双向链表** 这是一个双向链表的简单实现,支持节点插入和删除操作。 ```cpp #include <iostream> template<typename T> class Node { public: T data; Node<T>* prev; Node<T>* next; Node(T value) : data(value), prev(nullptr), next(nullptr) {} }; template<typename T> class DoublyLinkedList { public: Node<T>* head; Node<T>* tail; DoublyLinkedList() : head(nullptr), tail(nullptr) {} ~DoublyLinkedList() { while (head != nullptr) { RemoveHead(); } } void InsertTail(T value) { Node<T>* newNode = new Node<T>(value); if (tail == nullptr) { head = tail = newNode; } else { tail->next = newNode; newNode->prev = tail; tail = newNode; } } void RemoveHead() { if (head == nullptr) return; Node<T>* temp = head; head = head->next; if (head != nullptr) head->prev = nullptr; delete temp; if (head == nullptr) tail = nullptr; } void PrintList() { Node<T>* current = head; while (current != nullptr) { std::cout << current->data << " "; current = current->next; } std::cout << "\n"; } }; int main() { DoublyLinkedList<int> list; list.InsertTail(10); list.InsertTail(20); list.InsertTail(30); list.PrintList(); list.RemoveHead(); list.PrintList(); return 0; } ``` 这段代码展示了一个完整的双向链表实现,包括插入和删除方法[^5]。 --- #### 4. **Vulkan 图形渲染入门** 下面是一段 Vulkan 渲染管线初始化的基础代码: ```cpp #include <vulkan/vulkan.hpp> vk::UniqueInstance createVulkanInstance() { vk::ApplicationInfo appInfo( "MyApp", VK_MAKE_VERSION(1, 0, 0), "No Engine", VK_MAKE_VERSION(1, 0, 0), VK_API_VERSION_1_0 ); vk::InstanceCreateInfo instanceInfo({}, &appInfo); return vk::createInstanceUnique(instanceInfo); } int main() { try { auto instance = createVulkanInstance(); std::cout << "Vulkan Instance created successfully.\n"; } catch (const vk::SystemError& err) { std::cerr << "Failed to create Vulkan instance: " << err.what() << '\n'; } return 0; } ``` 这是 Vulkan-Hpp 绑定的一个简单例子,适用于初学者学习 Vulkan API[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三哥编程分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值