Vulkan图像管线(1)

一、简介
在介绍Vulkan的图像管线前,我们先来了解下Vulkan的Surface相关内容。

二、Vulkan Window Surface
我们了解到Vulkan是一个与平台特性无关联的API集合。它不能直接与窗口系统进行交互,为了将渲染结果呈现到屏幕,需要建立Vulkan与窗体系统之间的连接,我们需要使用WSI(窗体系统集成)扩展。
VK_KHR_surface扩展是一个instance级扩展,它包含在glfwGetRequiredInstanceExceptions返回的列表中。需要在instance创建之后立即创建窗体surface,因为它会影响物理设备的选择,另外窗体surface本身对于Vulkan也是非强制的,Vulkan允许这样做,不需要同OpenGL一样必须要创建窗体surface。

创建Window Surface:
VkSurfaceKHR surface;
因为一个窗体surface是一个Vulkan对象,它需要填充VkWin32SurfaceCreateInfoKHR结构体,这里有两个比较重要的参数:hwnd和hinstance:
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = glfwGetWin32Window(window);
createInfo.hinstance = GetModuleHandle(nullptr);
glfwGetWin32Window函数用于从GLFW窗体对象获取原始的HWND,GetModuleHandle函数返回当前进程的HINSATNCE句柄。
填充完结构体之后,可以利用vkCreateWin32SurfaceKHR创建surface桥,和之前获取创建、销毁DebugReportCallEXT一样,这里同样需要通过instance获取创建surface用到的函数。这里涉及到的参数分别为instance、surface创建的信息,自定义分配器和最终保存surface的句柄变量。
Auto CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)vkGetInstanceProcAddr(instance,”vkCreate Win32SurfzceKHR”);
if(!CreateWin32SurfaceKHR || CreateWin32SurfaceKHR(instance,&createInfo,nullptr,&surface)!=VK_SUCCESS){
throw std::runtime_error(“failed to create window surface!”);
}
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_erroe(“failed to create window surface!”);
}
}
最后依然是函数销毁:
void cleanup(){

vkDestroySurfaceKHR(instance,surface,nullptr);
vkDestroyInstance(instance,nullptr);

}

查询演示支持:
支持graphics命令的的队列簇和支持presentation命令的队列簇可能不是同一个簇。因此,我们需要修改QueueFamilyIndices结构体,以支持差异化的存储:
struct QueueFamilyIndices{
int graphicsFamily = -1;
int presentFamily = -1;
bool isComplate(){
return graphicsFamily >= 0 && presentFamily >= 0;
}
};
接下来,我们修改findQueueFamilies函数来查找具备presentation功能的队列簇。函数中用于检查的核心代码是vkGetPhysicalDeviceSurfaceSupportKHR,它将物理设备、队列簇索引和surface作为参数。在VK_QUEUE_GRAPHICS_BIT相同的循环体中添加函数的调用:
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
然后之需要检查布尔值并存储presentation队列簇的索引:
if (queueFamily.queueCount > 0 && presentSupport) {
indices.presentFamily = i;
}
为了支持graphics和presentation功能,我们实际环境中得到的可能是同一个队列簇,也可能不同,为此在我们的程序数据结构及选择逻辑中,将按照均来自不同的队列簇分别处理,这样便可以统一处理以上两种情况。除此之外,出于性能的考虑,我们也可以通过添加逻辑明确的指定物理设备所使用的graphics和presentation功能来自同一个队列簇。
在这里插入图片描述
创建演示队列:
VkQueue presentQueue;
接下来,我们需要多个VkDeviceQueueCreateInfo结构来创建不同功能的队列。一个优雅的方式是针对不同功能的队列簇创建一个set集合确保队列簇的唯一性:
#include

QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

std::vector queueCreateInfos;
std::set 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 = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}

同时还要修改VkDeviceCreateInfo指向队列集合:
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();

如果队列簇相同,那么我们之需要传递一次索引。最后,添加一个调用检索队列句柄:
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);

发布了146 篇原创文章 · 获赞 28 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_38498942/article/details/103699764