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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

沁恒CH32在MounRiver Studio上环境配置以及使用详细教程

目录 1.  RISC-V简介 2.  CPU架构现状 3.  MounRiver Studio软件下载 4.  MounRiver Studio软件安装 5.  MounRiver Studio软件介绍 6.  创建工程 7.  编译代码 1.  RISC-V简介         RISC就是精简指令集计算机(Reduced Instruction SetCom