本篇博客继续学习Vulkan,主要是基础代码学习记录。
先来一个通用的结构:
#include
#include
#include
#include
#include
class HelloTriangleApplication {
public:
void run() {
initVulkan();
mainLoop();
cleanup();
}
private:
void initVulkan() {
}
void mainLoop() {
}
void cleanup() {
}
};
int main() {
HelloTriangleApplication app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
先从SDK中包含一个vulkan.h的头文件,stdexcept和iostream用于展示错误信息,functional头文件用于lambda函数,也就是资源管理部分的。cstdlib提供了EXIT_SUCCESS和EXIT_FAILURE宏。该程序包装成一个类,我们可以在其中存放Vulkan对象和私有类成员,还可以添加函数来初始化,这会在initVulkan函数中被调用。一切准备就绪,我们进入主循环开始渲染frames,我们会填充主循环,让它包含一个直到窗口被关闭前不断迭代的循环。窗口关闭后,mainLoop返回,在cleanup方法中销毁资源。以后我们会每章添加一些函数以便initVulkan调用,私有类成员中也会添加Vulkan对象,然后最终在cleanup中清除。
Vulkan对象要么是vkCreateXXX要么是vkAllocateXXX创建,不需要的时候,用vkDestroyXXX或者vkFreeXXX来释放。为了更好地开展工作,还要集成GLFW,把一开始的vulkan头文件包含那一行替换为:
#define GLFW_INCLUDE_VULKAN
#include
这样GLFW就会包含自己的定义并自动加载Vulkan的头。现在对前面的框架添加一个initWindow方法,该方法第一步应该是glfwInit,也就是初始化GLFW库。因为GLFW一开始设计的就是创建OpenGL上下文,那么我们要告诉它你不要这个样子:
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
接着,窗口大小修改会要特殊操作,所以暂且禁用:
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
然后创建窗口:
GLFWwindow* window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr);
这里就是创建800*600的窗口,第四个参数指定在哪个屏幕显示,最后一个知识OpenGL相关所以这里忽略。为了让应用不断运行,直到出现错误或者窗口被关闭,需要向mainLoop中添加事件循环如下:
void mainLoop() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
最后,如果关闭了,需要清理,代码如下:
void cleanup() {
glfwDestroyWindow(window);
glfwTerminate();
}
最后完整的代码如下:
#define GLFW_INCLUDE_VULKAN
#include
#include
#include
#include
#include
const int WIDTH = 800;
const int HEIGHT = 600;
class HelloTriangleApplication {
public:
void run() {
initWindow();
initVulkan();
mainLoop();
cleanup();
}
private:
GLFWwindow* window;
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}
void initVulkan() {
}
void mainLoop() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
void cleanup() {
glfwDestroyWindow(window);
glfwTerminate();
}
};
int main() {
HelloTriangleApplication app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
下面准备创建Vulkan实例,实例是程序和Vulkan库之间的连接,创建实例需要对驱动提供一些自己程序的详细信息。需要写一个createInstance的方法,而我们需要提供的信息则是VkApplicationInfo:
void initVulkan() {
createInstance();
}
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;
}
还要再添加一个类成员来保存实例:
VkInstance instance;
另外,为了提供充足的信息来创建实例,我们还需要提供VkInstanceCreateInfo。
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
由于Vulkan是平台无关的,所以需要一个扩展来和窗口系统交互。GLFW有许多内置的函数能返回这些扩展,我们需要将其传给前面创建的结构体:
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
后两个参数表明了所需的全局扩展。最后还要两个成员,表明启用全局验证层,后面的章节会深入学习,所以这里留空:
createInfo.enabledLayerCount = 0;
现在可以创建Vulkan实例了:
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
基本上所有Vulkan方法返回的VkResult不是VK_SUCCESS就是错误码。现在就可以运行起来了,得到一个空窗口,没有报错即可。
可以通过vkEnumerateInstanceExtensionProperties函数在创建实例前获取支持的扩展列表,它需要一个指向存储扩展个数的变量,另外还需要一组VkExtensionProperties来存储这些扩展的详情。分配数组之前要确定这些扩展有多少个:
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector extensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
然后就能打印支持的扩展了:
std::cout << "available extensions:" << std::endl;
for (const auto& extension : extensions) {
std::cout << "\t" << extension.extensionName << std::endl;
}
得到的输出如下:
available extensions:
VK_EXT_debug_report
VK_EXT_display_surface_counter
VK_KHR_get_physical_device_properties2
VK_KHR_get_surface_capabilities2
VK_KHR_surface
VK_KHR_win32_surface
VK_KHR_device_group_creation
VK_KHR_external_fence_capabilities
VK_KHR_external_memory_capabilities
VK_KHR_external_semaphore_capabilities
VK_NV_external_memory_capabilities
VK_EXT_debug_utils
最终,完整的代码如下:
#define GLFW_INCLUDE_VULKAN
#include
#include
#include
#include
#include
const int WIDTH = 800;
const int 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, "Vulkan", nullptr, nullptr);
}
void initVulkan() {
createInstance();
}
void mainLoop() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
void cleanup() {
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;
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector extensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
std::cout << "available extensions:" << std::endl;
for (const auto& extension : extensions) {
std::cout << "\t" << extension.extensionName << std::endl;
}
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;
}