操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Visual Studio 2017


到目前为止,我们了解到Vulkan是一个与平台特性无关联的API集合。它不能直接与窗口系统进行交互。为了将渲染结果呈现到屏幕,需要建立Vulkan与窗体系统之间的连接,我们需要使用WSI(窗体系统集成)扩展。在本小节中,我们将讨论第一个,即VK_KHR_surface。它暴露了VkSurfaceKHR,它代表surface的一个抽象类型,用以呈现渲染图像使用。我们程序中将要使用到的surface是由我们已经引入的GLFW扩展及其打开的相关窗体支持的。简单来说surface就是Vulkan与窗体系统的连接桥梁。

VK_KHR_surface扩展是一个instance级扩展,我们目前为止已经启用过它,它包含在glfwGetRequiredInstanceExtensions返回的列表中。该列表还包括将在接下来几小节中使用的一些其他WSI扩展。

需要在instance创建之后立即创建窗体surface,因为它会影响物理设备的选择。之所以在本小节将surface创建逻辑纳入讨论范围,是因为窗体surface对于渲染、呈现方式是一个比较大的课题,如果过早的在创建物理设备加入这部分内容,会混淆基本的物理设备设置工作。另外窗体surface本身对于Vulkan也是非强制的。Vulkan允许这样做,不需要同OpenGL一样必须要创建窗体surface。

Window surface creation


现在开始着手创建窗体surface,在类成员debugCallback下加入成员变量surface。

VkSurfaceKHR surface;

虽然VkSurfaceKHR对象及其用法与平台无关联,但创建过程需要依赖具体的窗体系统的细节。比如,在Windows平台中,它需要WIndows上的HWND和HMODULE句柄。因此针对特定平台提供相应的扩展,在Windows上为VK_KHR_win32_surface,它自动包含在glfwGetRequiredInstanceExtensions列表中。

我们将会演示如何使用特定平台的扩展来创建Windows上的surface桥,但是不会在教程中实际使用它。使用GLFW这样的库避免了编写没有任何意义的跨平台相关代码。GLFW实际上通过glfwCreateWindowSurface很好的处理了平台差异性。当然了,比较理想是在依赖它们帮助我们完成具体工作之前,了解一下背后的实现是有帮助的。

因为一个窗体surface是一个Vulkan对象,它需要填充VkWin32SurfaceCreateInfoKHR结构体,这里有两个比较重要的参数: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窗体对象获取原始的HWND。GetModuleHandle函数返回当前进程的HINSTANCE句柄。

填充完结构体之后,可以利用vkCreateWin32SurfaceKHR创建surface桥,和之前获取创建、销毁DebugReportCallEXT一样,这里同样需要通过instance获取创建surface用到的函数。这里涉及到的参数分别为instance, surface创建的信息,自定义分配器和最终保存surface的句柄变量。

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,使用X11界面窗体系统,可以通过vkCreateXcbSurfaceKHR函数建立连接。

glfwCreateWindowSurface函数根据不同平台的差异性,在实现细节上会有所不同。我们现在将其整合到我们的程序中。从initVulkan中添加一个函数createSurface,安排在createInstnace和setupDebugCallback函数之后。

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

GLFW没有使用结构体,而是选择非常直接的参数传递来调用函数。

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

参数是VkInstance,GLFW窗体的指针,自定义分配器和用于存储VkSurfaceKHR变量的指针。对于不同平台统一返回VkResult。GLFW没有提供专用的函数销毁surface,但是可以简单的通过Vulkan原始的API完成:

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

最后请确保surface的清理是在instance销毁之前完成。

Querying for presentation support


虽然Vulkan的实现支持窗体集成功能,但是并不意味着系统中的每一个物理设备都支持它。因此,我们需要扩展isDeviceSuitable函数,确保设备可以将图像呈现到我们创建的surface。由于presentation是一个队列的特性功能,因此解决问题的方法就是找到支持presentation的队列簇,最终获取队列满足surface创建的需要。

实际情况是,支持graphics命令的的队列簇和支持presentation命令的队列簇可能不是同一个簇。因此,我们需要修改QueueFamilyIndices结构体,以支持差异化的存储。

struct QueueFamilyIndices {
int graphicsFamily = -;
int presentFamily = -; bool isComplete() {
return graphicsFamily >= && presentFamily >= ;
}
};

接下来,我们修改findQueueFamilies函数来查找具备presentation功能的队列簇。函数中用于检查的核心代码是vkGetPhysicalDeviceSurfaceSupportKHR,它将物理设备、队列簇索引和surface作为参数。在VK_QUEUE_GRAPHICS_BIT相同的循环体中添加函数的调用:

VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);

然后之需要检查布尔值并存储presentation队列簇的索引:

if (queueFamily.queueCount >  && presentSupport) {
indices.presentFamily = i;
}

需要注意的是,为了支持graphics和presentation功能,我们实际环境中得到的可能是同一个队列簇,也可能不同,为此在我们的程序数据结构及选择逻辑中,将按照均来自不同的队列簇分别处理,这样便可以统一处理以上两种情况。除此之外,出于性能的考虑,我们也可以通过添加逻辑明确的指定物理设备所使用的graphics和presentation功能来自同一个队列簇。

Creating the presentation queue


剩下的事情是修改逻辑设备创建过程,在于创建presentation队列并获取VkQueue的句柄。添加保存队列句柄的成员变量:

VkQueue presentQueue;

接下来,我们需要多个VkDeviceQueueCreateInfo结构来创建不同功能的队列。一个优雅的方式是针对不同功能的队列簇创建一个set集合确保队列簇的唯一性:

#include <set>

...

QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; float queuePriority = 1.0f;
for (int queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = ;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}

同时还要修改VkDeviceCreateInfo指向队列集合:

createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();

如果队列簇相同,那么我们之需要传递一次索引。最后,添加一个调用检索队列句柄:

vkGetDeviceQueue(device, indices.presentFamily, , &presentQueue);

在这个例子中,队列簇是相同的,两个句柄可能会有相同的值。在下一个章节中我们会看看交换链,以及它们如何使我们能够将图像呈现给surface。

获取工程代码 GitHub checkout

Vulkan Tutorial 07 Window surface的更多相关文章

  1. [译]Vulkan教程(07)物理设备和队列家族

    [译]Vulkan教程(07)物理设备和队列家族 Selecting a physical device 选择一个物理设备 After initializing the Vulkan library ...

  2. Vulkan Tutorial 18 重构交换链

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 现在我们已经成功的在屏幕上绘制出三角形,但是在某些情况下, ...

  3. Vulkan Tutorial 开发环境搭建之Windows

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 相信很多人在开始学习Vulkan开发的起始阶段都会在开发环境的配置上下一些功夫,那么 ...

  4. Vulkan Tutorial 02 编写Vulkan应用程序框架原型

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 General structure 在上一节中,我们创建了一个正确配置.可运行的的V ...

  5. Vulkan Tutorial 08 交换链

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 在这一章节,我们了解一下将渲染图像提交到屏幕的基本机制.这种机制成为交换链,并且需要 ...

  6. Vulkan Tutorial 17 Rendering and presentation

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Setup 这一章节会把之前的所有内容进行整合.我们将会编写drawFrame函数, ...

  7. Vulkan Tutorial 23 Descriptor layout and buffer

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们现在可以将任意属性传递给每个顶点的顶点着色器使用.但是 ...

  8. Vulkan Tutorial 28 Depth buffering

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 到目前为止,我们所使用的几何图形为3D,但仍然完全扁平的. ...

  9. Vulkan Tutorial 04 理解Validation layers

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 What are validation layers? Vulkan API的设计核 ...

随机推荐

  1. ORACLE中关于外键缺少索引的探讨和总结

    在ORACLE数据库中,定义外键约束时,ORACLE是不会自动创建对应索引的,必须手动在外键约束相关的列上创建索引.那么外键字段上是否有必要创建索引呢?如果有必要的话,巡检时,如何找出外键字段上没有创 ...

  2. 关于mpu6050的几个很好的帖子

    最近在研究6050,真是很磨人啊,这个小东西还挺复杂,一个读取程序竟然需要600多行. 这几天连查资料找到了几个很好的帖子,要是以后有人看到这篇帖子,可以避免误入歧途,也可以省去很多时间. 1.阿西莫 ...

  3. CF #365 703D. Mishka and Interesting sum

    题目描述 D. Mishka and Interesting sum的意思就是给出一个数组,以及若干询问,每次询问某个区间[L, R]之间所有出现过偶数次的数字的异或和. 这个东西乍看很像是经典问题, ...

  4. 关于Java中Arrays.sort()方法TLE

    最近一直在练用Java写题,今天无意发现一道很简单的二分题(链接),我一开始是直接开int[]数组调用Arrays.sort()去排序,没想到TLE了,原来是因为jdk中对于int[]的排序是使用快速 ...

  5. Android性能优化——之防止内存泄露

    又是好久没有写博客了,一直都比较忙,最近终于有时间沉淀和整理一下最近学到和解决的一些问题. 最近进行技术支持的时候,遇到了几个崩溃的问题,都是OOM异常,一般OOM异常给人的感觉应该是加载大图片造成的 ...

  6. 开发Angular库的简单指导(译)

    1. 最近工作上用到Angular,需要查阅一些英文资料,虽然英文非常烂,但是种种原因又不得不硬着头皮上,只是每次看英文都很费力,因此决定将一些比较重要的特别是需要反复阅读的资料翻译一下,以节约再次阅 ...

  7. python基础教程第二版 第一章

    1.模块导入python以增强其功能的扩展:三种方式实现 (1). >>> Import math >>> math.floor(32.9) 32.0 #按照 模块 ...

  8. Akka(1):Actor - 靠消息驱动的运算器

    Akka是由各种角色和功能的Actor组成的,工作的主要原理是把一项大的计算任务分割成小环节,再按各环节的要求构建相应功能的Actor,然后把各环节的运算托付给相应的Actor去独立完成.Akka是个 ...

  9. 一条sql语句引发mysql不停创建临时表的问题解决..coping to tmp table on desk

    (不懂临时表的先看 MySQL临时表 ) 首先,临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间.因此在不同的连接中可以创建同名的临时表,并且操作属于本连接的临时表.     ...

  10. Bash 的若干基本问题

    Bash 的若干基本问题   这里介绍一些bash启动前.后的问题,以及一些使用bash需要注意的基本问题.     1.Bash的介绍   Bash是一种Shell程序,它是一般的Linux系统中的 ...