前言
上一章深入分析了vsg::CommandGraph的创建过程及其通过子场景遍历实现Vulkan命令录制的机制。本章将在该基础上,进一步探讨Vulkan命令录制中的核心封装------vsg::RenderGraph。作为渲染流程的关键组件,RenderGraph封装了vkCmdBeginRenderPass和vkCmdEndRenderPass的核心功能,RecordTraversal会在这两个关键调用之间遍历并处理其子节点。同时本章内容将涵盖vsg::FrameBuffer(封装VkFramebuffer)、vsg::RenderPass(封装VkRenderPass)和vsg::ImageView(封装VkImageView)等重要概念。
目录
- 1 vsg::RenderPass
- 2 vsg::FrameBuffer
- 3 vsg::RenderGraph
1 vsg::RenderPass
vsg::RenderPass 是 VulkanSceneGraph (VSG) 框架中的核心类,它封装了 Vulkan 的渲染通道(Render Pass),用于定义渲染过程中帧缓冲区附件的结构和组织方式。
1.1 VkRenderPass的创建
颜色附件描述:
cpp
VkAttachmentDescription colorAttachment
{
0, VK_FORMAT_B8G8R8A8_SRGB,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR, // Load/Store
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoad/Store
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR // finalLayout
};
VkAttachmentReference colorAttachmentRef
{
0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
子通道描述:
cpp
VkSubpassDescription subpass
{
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
0, nullptr, // pInputAttachments
1, &colorAttachmentRef, // pColorAttachments
nullptr, // pResolveAttachments
nullptr, // pDepthStencilAttachment
0, nullptr, // pPreserveAttachments
};
渲染通道创建信息:
cpp
VkRenderPassCreateInfo renderPassInfo
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
nullptr, 0,
1, &colorAttachment, // pAttachments
1, &subpass, // pSubpasses
0, nullptr // pDependencies
};
创建渲染通道:
cpp
VkRenderPass renderPass = nullptr;
VkResult result = vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass);
if (result != VK_SUCCESS)
{
printf("Failed to create render-pass.\n");
return 1;
}
1.2 vsg::RenderPass的创建
在5.1章(vulkanscenegraph显示倾斜模型(5.1)-窗口创建-CSDN博客)中深入探讨了窗口的创建过程,vsg中窗口相关的基类为vsg::Window,RenderPass的创建与窗口密切相关,如下为vsg::Window中初始化vsg::RenderPass相关代码:
cpp
void Window::_initRenderPass()
{
if (!_device) _initDevice();
bool requiresDepthRead = (_traits->depthImageUsage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0;
if (_framebufferSamples == VK_SAMPLE_COUNT_1_BIT)
{
_renderPass = vsg::createRenderPass(_device, _imageFormat.format, _depthFormat, requiresDepthRead);
}
else
{
_renderPass = vsg::createMultisampledRenderPass(_device, _imageFormat.format, _depthFormat, _framebufferSamples, requiresDepthRead);
}
}
在场景创建的过程中,vsg::RenderGraph 在初始化时会获取或创建 vsg::RenderPass 对象,vsg::RenderPass有三个来源,示例代码如下:
cpp
RenderPass* RenderGraph::getRenderPass()
{
if (renderPass)
{
return renderPass;
}
else if (framebuffer)
{
return framebuffer->getRenderPass();
}
else if (window)
{
return window->getOrCreateRenderPass();
}
return nullptr;
}
2 vsg::FrameBuffer
vsg::FrameBuffer封装了VkFramebuffer,用作与窗口关联的渲染目标或用于渲染到纹理。
2.1 VkFrameBuffer的创建
VkFrameBuffer的创建分为VkImageView的创建与VkFrameBuffer的创建两个过程。
获取所有图像:从VkSwapChain中获取VkImage分两步调用vkGetSwapchainImagesKHR函数:首先传入nullptr作为第四个参数以获取交换链中的图像数量,然后再次调用该函数来实际获取所有VkImage对象。
cpp
uint32_t imageCount = 0;
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
std::vector<VkImage> swapChainImages(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
创建图像视图:在vulkan中,图像视图提供了对图像的访问接口,图像视图的创建示例代码如下:
cpp
VkImageViewCreateInfo createInfo
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
nullptr, 0,
nullptr, VK_IMAGE_VIEW_TYPE_2D,
VK_FORMAT_B8G8R8A8_SRGB,
{ VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
};
std::vector<VkImageView> swapchainImageViews(swapChainImages.size());
for (size_t i = 0; i < swapchainImageViews.size(); ++i)
{
createInfo.image = swapChainImages[i];
VkResult result = vkCreateImageView(
app.getDevice(), &createInfo, nullptr, &swapchainImageViews[i]);
if (result != VK_SUCCESS)
{
printf("Failed to create image view: %zd.\n", i);
return 1;
}
}
创建VkFramebuffer:下述代码表示为交换链(Swapchain)的每个图像视图(VkImageView)创建一个帧缓冲(VkFramebuffer),并建立它们与渲染流程(VkRenderPass)的关联,其中1024和768分别表示对应影像的宽高。VkRenderPass 定义了渲染流程的附件结构和子流程(Subpass),但不绑定具体图像,VkFramebuffer 将 VkRenderPass 的抽象附件绑定到具体的 VkImageView,使渲染操作可以实际写入图像。
cpp
std::vector<VkFramebuffer> framebuffers(swapchainImageViews.size());
for (size_t i = 0; i < framebuffers.size(); ++i)
{
VkImageView attachments[] = { swapchainImageViews[i] };
VkFramebufferCreateInfo framebufferInfo
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
nullptr, 0,
renderPass,
1, attachments,
1024, 768, 1
};
vkCreateFramebuffer(device, &framebufferInfo, nullptr, &framebuffers[i]);
}
2.2 vsg::FrameBuffer的封装
在5.2章(vulkanscenegraph显示倾斜模型(5.2)-交换链-CSDN博客)中深入探讨了交换链的创建过程,与交换链创建紧密相关的有VkImageView与VkFramebuffer的创建。
vsg中Window.cpp文件中372-339行代码如下,为Window::buildSwapchain()函数中,创建vsg::ImageView与vsg::Framebuffer的部分。
cpp
auto& imageViews = _swapchain->getImageViews();
_availableSemaphore = vsg::Semaphore::create(_device, _traits->imageAvailableSemaphoreWaitFlag);
size_t initial_indexValue = imageViews.size();
for (size_t i = 0; i < imageViews.size(); ++i)
{
vsg::ImageViews attachments;
if (_multisampleImageView)
{
attachments.push_back(_multisampleImageView);
}
attachments.push_back(imageViews[i]);
if (_multisampleDepthImageView)
{
attachments.push_back(_multisampleDepthImageView);
}
attachments.push_back(_depthImageView);
ref_ptr<Framebuffer> fb = Framebuffer::create(_renderPass, attachments, _extent2D.width, _extent2D.height, 1);
ref_ptr<Semaphore> ias = vsg::Semaphore::create(_device, _traits->imageAvailableSemaphoreWaitFlag);
//_frames.push_back({multisampling ? _multisampleImageView : imageViews[i], fb, ias});
_frames.push_back({imageViews[i], fb, ias});
_indices.push_back(initial_indexValue);
}
如下代码为vsg中Framebuffer的构造函数,在构造函数中实现了VkFramebuffer的创建。
cpp
Framebuffer::Framebuffer(ref_ptr<RenderPass> renderPass, const ImageViews& attachments, uint32_t width, uint32_t height, uint32_t layers) :
_device(renderPass->device),
_renderPass(renderPass),
_attachments(attachments),
_width(width),
_height(height),
_layers(layers)
{
auto deviceID = _device->deviceID;
std::vector<VkImageView> vk_attachments;
for (auto& attachment : attachments)
{
vk_attachments.push_back(attachment->vk(deviceID));
}
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.flags = 0;
framebufferInfo.renderPass = *_renderPass;
framebufferInfo.attachmentCount = static_cast<uint32_t>(vk_attachments.size());
framebufferInfo.pAttachments = vk_attachments.data();
framebufferInfo.width = width;
framebufferInfo.height = height;
framebufferInfo.layers = layers;
if (VkResult result = vkCreateFramebuffer(*_device, &framebufferInfo, nullptr, &_framebuffer); result != VK_SUCCESS)
{
throw Exception{"Error: vsg::Framebuffer::create(...) Failed to create VkFramebuffer.", result};
}
}
3 vsg::RenderGraph
本章在上章(vulkanscenegraph显示倾斜模型(5.5)-CommandGraph的创建-CSDN博客)场景图构建基础上,继续深入vsg::RenderGraph的创建:

vsg::RenderGraph中通过访问者模式(accept函数)实现场景的遍历,进而实现命令的录制,其核心过程如下(RenderGraph.cpp中146-151行代码):
cpp
vkCmdBeginRenderPass(vk_commandBuffer, &renderPassInfo, contents);
// traverse the subgraph to place commands into the command buffer.
traverse(recordTraversal);
vkCmdEndRenderPass(vk_commandBuffer);
文末:本章在上一篇文章的基础上,继续深入vsg场景图中另一关键的节点vsg::RenderGraph,RecordTraversal遍历场景图过程中,RenderGraph的子节点会在vkCmdBeginRenderPass和vkCmdEndRenderPass之间被遍历访问。同时本章介绍了vsg::FrameBuffer(封装VkFramebuffer)、vsg::RenderPass(封装VkRenderPass)和vsg::ImageView(封装VkImageView)三个关键概念,vsg::RenderPass定义渲染流程,vsg::ImageView提供图像资源访问接口,vsg::FrameBuffer将二者绑定为具体渲染目标。下章将介绍vulkan图形渲染过程中另一关键对象VkPipeline,以及vsg对其的封装vsg::GraphicsPipeline。