Vulkan 学习(11)---- Vulkan RenderPass 创建
目录
- RenderPass 概述
- RenderPass 创建
- 附件描述
- 子通道和子通道描述
- 子通道依赖
- 参考代码
RenderPass 概述
Vulkan
渲染通道(RenderPass
) 定义了整个渲染管线一次执行的过程,包括了渲染所使用的所有资源和操作的描述(比如指定渲染管线的渲染目标,告诉管线要渲染到哪里)
RenderPass 本质上是一个渲染流程的完整描述(管理渲染流程),包括如何渲染这些数据的元数据和指令,但是不包括实际的数据(图像),通过与 FrameBuffer
结合来获取实际的图像数据
在Vulkan
编程,RenderPass
是必不可少的,它必须包含一个或者多个子通道(SubPass)
每个子通道表示一个渲染阶段,并且都是使用 RenderPass
中定义的资源描述这个阶段的渲染的步骤
RenderPass
是通过附件(Attachment
)的形式描述图像资源,包括颜色附件(Color Attachment
)、深度/模板附件(Depth/Stencil Attachment
)、用于多重采样的的解析附件(Resolve Attachment
)和输入附件(Input Attachment
)等,
RenderPass
和 FrameBuffer
的关系密切,FrameBuffer
代表了 RenderPass
使用的具体内存集合,定义了 RenderPass
中每个 ImageView
与附件的对应关系
RenderPass
使得开发者能够更精细的控制渲染过程,优化性能,同时适应现代GPU
架构的特点
RenderPass 创建
vkCreateRenderPass
的函数原型如下:
VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass(
VkDevice device,
const VkRenderPassCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkRenderPass* pRenderPass);
关键结构 VkRenderPassCreateInfo
的结构:
typedef struct VkRenderPassCreateInfo {
VkStructureType sType;
const void* pNext;
VkRenderPassCreateFlags flags;
uint32_t attachmentCount; // 附件数量
const VkAttachmentDescription* pAttachments; // 附件描述
uint32_t subpassCount; // 子通道数量
const VkSubpassDescription* pSubpasses; // 子通道描述
uint32_t dependencyCount; // 子通道依赖数量
const VkSubpassDependency* pDependencies; // 子通道依赖描述
} VkRenderPassCreateInfo;`
附件描述
附件描述定义了在渲染过程中使用的图像资源,包括它们的格式,样本数、加载和存储操作等
比如定义一个深度附件和模板附件:
// 定义颜色附件描述
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = VK_FORMAT_R8G8B8A8_UNORM; // 交换链图像格式
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; // 采样数
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // 在渲染前清除附件
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // 在渲染后存储附件内容
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // 不关心模板加载操作
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // 不关心模板存储操作
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // 初始布局
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // 最终布局
// 定义深度模板附件描述
VkAttachmentDescription depthAttachment = {};
depthAttachment.format = VK_FORMAT_R8G8B8A8_UNORM;
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; // 采样数
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // 在渲染前清除附件
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // 渲染后不需要存储附件内容
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // 不关心模板加载操作
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // 不关心模板存储操作
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // 初始布局
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // 最终布局
子通道和子通道描述
VkSubpassDescription
是用于定义子通道的结构体,每个子通道表示一个渲染阶段:
typedef struct VkSubpassDescription {
VkSubpassDescriptionFlags flags;// 子通道描述的附加标志,目前必须为0
VkPipelineBindPoint pipelineBindPoint; // 管线绑定点,必须是 VK_PIPELINE_BIND_POINT_GRAPHICS
uint32_t inputAttachmentCount;// 输入附件的数量
const VkAttachmentReference* pInputAttachments; // 输入附件数组
uint32_t colorAttachmentCount; // 颜色附件数量
const VkAttachmentReference* pColorAttachments; // 颜色附件数组
const VkAttachmentReference* pResolveAttachments; // 解析附件数组
const VkAttachmentReference* pDepthStencilAttachment; // 深度附件数组
uint32_t preserveAttachmentCount;
const uint32_t* pPreserveAttachments;
} VkSubpassDescription;
子通道描述了渲染管道一个阶段及其输入输出附件:
// 定义颜色附件引用
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // 绑定到第一个附件描述
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 最佳颜色附件布局
// 定义深度模板附件引用
VkAttachmentReference depthAttachmentRef = {};
depthAttachmentRef.attachment = 1; // 绑定到第二个附件描述
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // 最佳深度模板附件布局
// 定义子通道描述
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; // 图形管线绑定点
subpass.colorAttachmentCount = 1; // 颜色附件数量
subpass.pColorAttachments = &colorAttachmentRef; // 颜色附件引用
subpass.pDepthStencilAttachment = &depthAttachmentRef; // 深度模板附件引用
子通道依赖
TBD
参考代码
// 定义颜色附件描述
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = VK_FORMAT_R8G8B8A8_UNORM; // 交换链图像格式
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; // 采样数
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // 在渲染前清除附件
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // 在渲染后存储附件内容
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // 不关心模板加载操作
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // 不关心模板存储操作
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // 初始布局
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // 最终布局
// 定义深度模板附件描述
VkAttachmentDescription depthAttachment = {};
//depthAttachment.format = findDepthFormat(physicalDevice); // 深度模板格式
depthAttachment.format = VK_FORMAT_R8G8B8A8_UNORM;
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; // 采样数
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // 在渲染前清除附件
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // 渲染后不需要存储附件内容
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // 不关心模板加载操作
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // 不关心模板存储操作
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // 初始布局
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // 最终布局
// 定义颜色附件引用
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0; // 绑定到第一个附件描述
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 最佳颜色附件布局
// 定义深度模板附件引用
VkAttachmentReference depthAttachmentRef = {};
depthAttachmentRef.attachment = 1; // 绑定到第二个附件描述
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // 最佳深度模板附件布局
// 定义子通道描述
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; // 图形管线绑定点
subpass.colorAttachmentCount = 1; // 颜色附件数量
subpass.pColorAttachments = &colorAttachmentRef; // 颜色附件引用
subpass.pDepthStencilAttachment = &depthAttachmentRef; // 深度模板附件引用
// 定义子通道依赖
// 子通道依赖数组,用于布局转换
//std::array<VkSubpassDependency, 2> dependencies;
VkSubpassDependency dependencies[2];
// 第一个依赖关系
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; // 外部到第一个子通道的依赖
dependencies[0].dstSubpass = 0; // 目标子通道索引为0,即第一个子通道
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; // 源阶段掩码:早期和晚期片段测试阶段
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; // 目标阶段掩码:早期和晚期片段测试阶段
dependencies[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // 源访问掩码:深度模板附件写入
dependencies[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; // 目标访问掩码:深度模板附件写入和读取
dependencies[0].dependencyFlags = 0; // 无额外依赖标志
// 第二个依赖关系
dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL; // 外部到第一个子通道的依赖
dependencies[1].dstSubpass = 0; // 目标子通道索引为0,即第一个子通道
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // 源阶段掩码:颜色附件输出阶段
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // 目标阶段掩码:颜色附件输出阶段
dependencies[1].srcAccessMask = 0; // 源访问掩码:无特定访问类型
dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; // 目标访问掩码:颜色附件写入和读取
dependencies[1].dependencyFlags = 0; // 无额外依赖标志
// 创建 RenderPass
VkAttachmentDescription attachments[] = { colorAttachment, depthAttachment }; // 定义附件数组
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 2; // 附件数量
renderPassInfo.pAttachments = attachments; // 附件描述
renderPassInfo.subpassCount = 1; // 子通道数量
renderPassInfo.pSubpasses = &subpass; // 子通道描述
renderPassInfo.dependencyCount = 2; // 子通道依赖数量
renderPassInfo.pDependencies = dependencies; // 子通道依赖描述
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
throw std::runtime_error("failed to create render pass!");
}