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
架构的特点
Vulkan tutorial:
Render passes in Vulkan describe the type of images that are used during rendering operations, how they will be used, and how their contents should be treated. In our initial triangle rendering application, we’ll tell Vulkan that we will use a single image as color target and that we want it to be cleared to a solid color right before the drawing operation.
(Render pass
主要标志如何和 image
交互,是作为颜色,深度,模板附件还是其他作用等)
Note:可以理解为 RenderPass
定义了对 vkImage
的操作关系
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;`
附件描述
附件描述(Attachement Description
) 定义了在渲染过程中使用的图像资源,包括它们的格式,样本数、加载和存储操作等
比如定义一个深度附件和模板附件:
// 定义颜色附件描述
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; // 最终布局
附件说明
RenderPass
是通过附件(Attachment
)的形式描述图像资源,包括颜色附件(Color Attachment
)、深度/模板附件(Depth/Stencil Attachment
)、用于多重采样的的解析附件(Resolve Attachment
)和输入附件(Input Attachment
)等
-
颜色附件
是片段着色器的输出目标,存储渲染输出的颜色信息,通常对应屏幕或者离屏渲染目标 -
深度和模板附件
用于存储深度和模板的值,用于模板和深度测试 -
解析附件
用于多重采样抗锯齿的颜色附件解析,将多重采样的颜色附件解析为单采样目标 -
输入附件
允许子通道读取前一子通道的附件数据,主要用于多子通道渲染技术,可以高效的利用片上缓存
子通道和子通道描述
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
RenderPass
的创建流程可以用下图表示:
参考代码
// 定义颜色附件描述
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!");
}