Vulkan教程 - 10 创建图形管线

2024-08-21 19:58

本文主要是介绍Vulkan教程 - 10 创建图形管线,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在我们完成管线创建之前,我们需要告诉Vulkan渲染将要用到的帧缓冲附件的信息。我们需要明确有多少颜色和深度缓冲,每个又有多少采样以及它们的内容应该如何通过渲染操作来进行处理。所有这些信息都包装在渲染通道(render pass)对象中,我们就创建一个新的方法createRenderPass,在initVulkan中调用它,且它在createGraphicsPipeline之前。

        我们这里仅有一个颜色缓冲附件,就是来自交换链的一个图像。

void createRenderPass() {VkAttachmentDescription colorAttachment = {};colorAttachment.format = swapChainImageFormat;colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
}

        颜色附件的format参数应该和交换链图像的格式匹配,且我们目前没有多重采样,所以就保持一个采样。

colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;

        loadOp和storeOp决定了在渲染前后和附件中的数据如何交互。loadOp有如下可选项:

        VK_ATTACHMENT_LOAD_OP_LOAD:保存附件当前存在的上下文;

        VK_ATTACHMENT_LOAD_OP_CLEAR:开始的时候清除值使其变成一个常数;

        VK_ATTACHMENT_LOAD_OP_DONT_CARE:当前存在的上下文是未定义的,且我们也不关心它们。

        我们这里将会在绘制新的帧之前使用清除操作来清除帧缓冲到黑色。storeOp只有两个可选项:

        VK_ATTACHMENT_STORE_OP_STORE:渲染内容将会被存储到内存且能后续读出;

        VK_ATTACHMENT_STORE_OP_DONT_CARE:渲染操作之后帧缓冲的内容会是未定义的;

        我们想要看到屏幕上渲染的三角形,所以我们选用存储操作:

colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

        loadOp和storeOp应用到颜色和深度数据,stencilLoadOp和stencilStoreOp应用到模板数据。我们的应用不会对模板缓冲做什么处理,所以加载和存储结果是无关的。

colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

        Vulkan中的材质和帧缓冲通过有特定像素格式的VkImage对象表示,但是内存中像素的布局可以改变,改变的依据是你要和图像做什么操作。

        几个最常用的布局如下:

        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:图像用作颜色附件;

        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:图像呈现到交换链;

        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:图像用作内存复制操作的目的地 。

        我们当前要知道的是,图像需要转移到特定的布局,该布局适合我们将要进行的操作。

        initialLayout明确了在渲染通道开始之前图像将会拥有的布局。finalLayout明确了当渲染通道完成后要自动转移到的布局。用VK_IMAGE_LAYOUT_UNDEFINED作为initialLayout表示我们不关心之前图像布局。需要注意到该特殊值的一点是,图像内容不保证被保留,但是这没什么影响,因为我们反正还是要清除它的。我们想要图像为使用交换链渲染后的呈现准备就绪,所以用VK_IMAGE_LAYOUT_PRESENT_SRC_KHR作为finalLayout。

        单渲染通道可以由多个子通道组成。子通道是依赖于之前通道的帧缓冲内容的后续渲染操作,例如一个接一个应用的一系列后期处理效果。如果你把这些渲染操作合并成一个渲染通道,那么Vulkan能够重新对这些操作排序,并保存内存带宽以便更好地提升性能。我们的第一个三角形就还是用单个子通道。

        每个子通道引用一个或多个附件,这些引用就是些类似下面的VkAttachmentReference结构体:

VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        attachment参数表明通过附件描述数组的索引来确定引用哪一个附件。我们的数组是由单个VkAttachmentDescription组成,因此索引就是0。布局参数表明了子通道使用该引用的时候,我们想让附件用的布局。当子通道开启的时候,Vulkan将会自动将附件转移到该布局。我们打算使用附件来当作一个颜色缓冲,VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL布局会给我们最好的性能,就和它的名字的意思一样。

        子通道使用VkSubpassDescription结构体来描述:

VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;

        Vulkan将来可能支持计算子通道,所以我们必须显式说明这个是图形子通道。下面我们指明到颜色附件的引用:

subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;

        附件的索引就在片段着色器中用 layout(location = 0) out vec4 outColor直接引用。接着其他类型的可以被子通道引用的附件如下:

        pInputAttachments:从着色器读取的附件;

        pResolveAttachments:用于多重采样颜色附件的附件;

        pDepthStencilAttachment:用于深度和模板数据的附件;

        pPreserveAttachments:不是给这个子通道用的附件,但是数据又必须保存。

        现在附件和引用它的基础子通道已经都说过了,我们需要创建渲染通道了。创建一个新的类成员来存储VkRenderPass对象,就放在pipelineLayout变量上边:

VkRenderPass renderPass;

        渲染通道对象就可以根据VkRenderPassCreateInfo结构体信息创建,VkAttachmentReference对象通过数组索引引用附件:

VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {throw std::runtime_error("failed to create render pass!");
}

        如管线布局一样,渲染通道也是整个程序生命周期中都被引用的,所以在最后的cleanup中清理,紧跟管线布局清理之后调用:

vkDestroyRenderPass(device, renderPass, nullptr);

        现在我们能把前面章节所有的结构体和对象都组合起来创建图形管线了!现在回顾下我们都有哪些对象:

        着色器阶段:着色器模块定义了图形管线可编程阶段的功能;

        固定管线状态:所有的结构体定义了管线的固定功能阶段,例如输入组装,光栅器,视口和颜色混合;

        管线布局:由着色器引用的可以在绘制时更新的统一和可压入的值;

        渲染通道:由管线阶段引用的附件以及它们的用法。

        所有这些组合完整定义了图形管线的功能,所以我们现在开始填充VkGraphicsPipelineCreateInfo结构体,就在createGraphicsPipeline的末尾处。

VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;

        上面通过引用VkPipelineShaderStageCreateInfo进行起步,然后我们引用所有的结构体描述固定管线阶段:

pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr;  // optional
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr;  // optional

        之后是管线布局,它是一个Vulkan句柄而不是一个结构体指针:

pipelineInfo.layout = pipelineLayout;

        设置好到渲染通道的引用以及子通道的索引:

pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;

        本管线也可以使用别的渲染通道而不一定是这个特定的实例,但是要和renderPass兼容。兼容的要求是要在这里描述的,只是本教程不用而已。

pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;  // optional
pipelineInfo.basePipelineIndex = -1;  // optional

        实际上还有两个参数,basePipelineHandle和basePipelineIndex。Vulkan允许你通过派生一个已有的管线来创建新的图形管线。管线派生的想法是因为如果二者有很多相同的功能,这样比建立管线更加节省开销,而且从同一个父对象切换管线也会更快。你可以通过basePipelineHandle指明一个已存在管线的句柄,或者引用另一个管线,也就是要通过basePipelineIndex加上索引来创建的管线。现在只有一个管线,我们就指定一个空句柄和无效的索引。这些值仅仅在VkGraphicsPipelineCreateInfo的flags字段的VK_PIPELINE_CREATE_DERIVATIVE_BIT标记也指定的情况下才有用。

        最后一步,通过创建一个类成员来保存VkPipeline对象:

VkPipeline graphicsPipeline;

        最终可以创建图形管线了:

if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {throw std::runtime_error("failed to create graphics pipeline!");
}

        vkCreateGraphicsPipelines实际有更多的参数,它设计的时候就是接收多个VkGraphicsPipelineCreateInfo对象然后一次调用就会创建多个VkPipeline对象。

        第二个参数,我们已经传了VK_NULL_HANDLE,引用了一个可选的VkPipelineCache对象。管线缓冲可被用于存储和重用管线创建有关的数据,横跨多次vkCreateGraphicsPipelines调用,如果存储到了文件甚至横跨程序执行。这让极大提高管线传将速度成为可能,以后管线缓冲章节再深究。

        图形管线是所有常用绘制操作都要的,所以它也应该在程序结束的时候清理掉:

vkDestroyPipeline(device, graphicsPipeline, nullptr);

        这个就写在cleanup方法的第一行。现在运行程序,确认所有这些艰苦的工作能够最终成功创建管线。我们现在离看到屏幕显示东西已经很近了(其实快一百三十页了,居然还没看到三角形),下面的章节会设置来自交换链的真正的的帧缓冲并准备绘制命令。

这篇关于Vulkan教程 - 10 创建图形管线的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1094133

相关文章

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

电脑提示d3dx11_43.dll缺失怎么办? DLL文件丢失的多种修复教程

《电脑提示d3dx11_43.dll缺失怎么办?DLL文件丢失的多种修复教程》在使用电脑玩游戏或运行某些图形处理软件时,有时会遇到系统提示“d3dx11_43.dll缺失”的错误,下面我们就来分享超... 在计算机使用过程中,我们可能会遇到一些错误提示,其中之一就是缺失某个dll文件。其中,d3dx11_4

Linux下在线安装启动VNC教程

《Linux下在线安装启动VNC教程》本文指导在CentOS7上在线安装VNC,包含安装、配置密码、启动/停止、清理重启步骤及注意事项,强调需安装VNC桌面以避免黑屏,并解决端口冲突和目录权限问题... 目录描述安装VNC安装 VNC 桌面可能遇到的问题总结描js述linux中的VNC就类似于Window

Go语言编译环境设置教程

《Go语言编译环境设置教程》Go语言支持高并发(goroutine)、自动垃圾回收,编译为跨平台二进制文件,云原生兼容且社区活跃,开发便捷,内置测试与vet工具辅助检测错误,依赖模块化管理,提升开发效... 目录Go语言优势下载 Go  配置编译环境配置 GOPROXYIDE 设置(VS Code)一些基本

Windows环境下解决Matplotlib中文字体显示问题的详细教程

《Windows环境下解决Matplotlib中文字体显示问题的详细教程》本文详细介绍了在Windows下解决Matplotlib中文显示问题的方法,包括安装字体、更新缓存、配置文件设置及编码調整,并... 目录引言问题分析解决方案详解1. 检查系统已安装字体2. 手动添加中文字体(以SimHei为例)步骤

Ubuntu 24.04启用root图形登录的操作流程

《Ubuntu24.04启用root图形登录的操作流程》Ubuntu默认禁用root账户的图形与SSH登录,这是为了安全,但在某些场景你可能需要直接用root登录GNOME桌面,本文以Ubuntu2... 目录一、前言二、准备工作三、设置 root 密码四、启用图形界面 root 登录1. 修改 GDM 配

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

Java JDK1.8 安装和环境配置教程详解

《JavaJDK1.8安装和环境配置教程详解》文章简要介绍了JDK1.8的安装流程,包括官网下载对应系统版本、安装时选择非系统盘路径、配置JAVA_HOME、CLASSPATH和Path环境变量,... 目录1.下载JDK2.安装JDK3.配置环境变量4.检验JDK官网下载地址:Java Downloads