创建窗口表面

VkSurfaceKHR surface;

尽管VkSurfaceKHR对象是平台无关的,但它的创建依赖窗口系统。比如,在Windows系统上,它的创建需要HWND和HMODULE。存在一个叫做VK_KHR_win32_surface的Windows平台特有扩展,用于处理与Windows系统窗口交互有关的问题,这一扩展也被包含在了glfwGetRequiredInstanceExtensions函数获取的扩展列表中。

接下来,我们将会演示如何使用这一Windows系统特有扩展来创建表面,但对于之后的章节,我们不会使用这一特定平台扩展,而是直接使用GLFW库来完成相关操作。我们可以使用GLFW库的glfwCreateWindowSurface函数来完成表面创建。这里演示如何使用这一平台特定扩展,是出于学习目的,让读者能明白我们使用的GLFW库在背后究竟做了什么。

我们需要填写VkWin32SurfaceCreateInfoKHR结构体来完成VkSurfaceKHR对象的创建。这一结构体包含了两个非常重要的成员:hwnd和hinstance。它们分别对应Windows系统的窗口句柄和进程实例句柄:

VkWin32SurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = glfwGetWin32Window(window);
createInfo.hinstance = GetModuleHandle(nullptr);

glfwGetWin32Window函数可以获取GLFW窗口对象的Windows平台窗口句柄。GetModuleHandle函数可以获取当前进程的实例句柄。

vkCreateWin32SurfaceKHR函数需要我们自己加载。加载后使用Vulkan实例,要创建的表面信息,自定义内存分配器和要存储表面对象的内存地址为参数调用:

auto CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");

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

其它平台的处理方式与之类似,比如Linux平台,可以通过vkCreateXcbSurfaceKHR函数完成表面创建的工作。

GLFW库的glfwCreateWindowSurface函数在不同平台的实现是不同的,可以跨平台使用。现在,我们将它集成到我们的应用程序中。添加一个叫做createSurface的函数,然后在initVulkan函数中在Vulkan实例创建和setupDebugCallback调用之后调用它:

void initVulkan() {
    createInstance();
    setupDebugCallback();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
}

void createSurface() {

}

glfwCreateWindowSurface函数的参数非常直白:

void createSurface() {
    if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
        throw std::runtime_error("failed to create window surface!");
    }
}

它的参数依次是VkInstance对象,GLFW窗口指针,自定义内存分配器,存储返回的VkSurfaceKHR对象的内存地址。调用后,它会返回VkResult来指示创建是否成功。表面在应用程序退出需要被清理,GLFW并没有提供清除表面的函数,我们可以自己调用vkDestroySurfaceKHR函数完成这一工作:

void cleanup() {
        ...
    vkDestroySurfaceKHR(instance, surface, nullptr);
    vkDestroyInstance(instance, nullptr);
        ...
}

需要注意,表面对象的清除需要在Vulkan实例被清除之前完成。