SwapChain:交换链
VkSwapChain
是Vulkan中管理图像呈现的关键组件之一。它负责处理窗口表面的图像,以便在屏幕上进行呈现。它包含一个或多个VkImage图像对象(双缓冲对应2张图像,三缓冲对应有3张)。VkImage是通过VkImageView来访问的。
VkImageView
图像视图,用于指定图像的格式、范围以及如何访问图像的方式。实际上,imageView并没有直接存储图像数据,你可以将其比喻为一个"窗口",你可以透过这个窗口看到图像的一部分,同时指定这个窗口的属性,比如窗口的大小、位置等。在这个比喻中,图像数据是窗户背后的实际场景。
创建流程大致为:
- 填充
VkSwapchainCreateInfoKHR
结构体,通过PFN_vkCreateSwapchainKHR
来创建交换链对象,并通过PFN_vkGetSwapchainImagesKHR
来获取交换链的图像 - 为每个图像创建一个
VkImageView
FrameBuffer:帧缓冲
framebuffer 是由一组附件(attachments)组成的,这些附件可以包括颜色附件、深度附件等。
注意: 在vulkan中,framebuffer的附件是image view
通常,当你进行渲染时,至少需要一个颜色附件,而这个颜色附件可以是与 swapchain 关联的 imageView。这是因为最终渲染的图像需要显示在屏幕上,而 swapchain的imageView就是用来呈现到屏幕上的,因此作为渲染的最终目标位置。
如果你有其他需要渲染的内容,例如深度、法线等,你可以在 framebuffer 中包含额外的附件。这样,渲染过程可以将结果输出到这些附件,做离屏渲染而不仅仅是直接显示在屏幕上。
总体而言,framebuffer 的附件设置是根据你的渲染需求而定的,但通常会包括至少一个与 swapchain 相关的颜色附件。
RenderPass:渲染通道
RenderPass定义了渲染操作的结构和执行顺序。它规定了图形渲染管线在一个FrameBuffer中执行的全部步骤。RenderPass包含多个Subpass,每个Subpass都描述了一部分渲染操作,可以包括顶点着色、片段着色、深度测试等。Subpass之间可以有依赖关系,确保它们按正确的顺序执行。
renderpass不直接引用附件的实际资源。它定义了这个流程中,需要的附件的数量、类型、格式等信息,但并没有指定附件的具体内容或内存。具体的附件资源是在创建 Framebuffer(帧缓冲)时绑定到 Render Pass 中的。
创建了renderpass对象之后,需要将其绑定到具体的FrameBuffer对象上(通过在VkFramebufferCreateInfo
中进行指定)
SwapChain、FrameBuffer与RenderPass的整体关系:
SwapChain提供了可以呈现到屏幕上的图像和图像视图,FrameBuffer关联了这些图像视图,并指定了RenderPass,RenderPass定义了渲染操作的步骤和依赖关系。
实例(G-Bbuffer):延迟渲染的offscreen渲染阶段的framebuffer的创建流程,
struct FrameBufferAttachment {
VkImage image;
VkDeviceMemory mem;
VkImageView view; // 真正绑定到VkFrameBuffer的是view
VkFormat format;
};
struct FrameBuffer {
int32_t width, height;
VkFramebuffer frameBuffer;
FrameBufferAttachment position, normal, albedo;
FrameBufferAttachment depth;
VkRenderPass renderPass;
} offScreenFrameBuf;
void prepareOffscreenFramebuffer()
{
offScreenFrameBuf.width = FB_DIM;
offScreenFrameBuf.height = FB_DIM;
// Color attachments
// (World space) Positions
createAttachment(
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
&offScreenFrameBuf.position);
// (World space) Normals
createAttachment(
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
&offScreenFrameBuf.normal);
// Albedo (color)
createAttachment(
VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
&offScreenFrameBuf.albedo);
// Depth attachment
// Find a suitable depth format
VkFormat attDepthFormat;
VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(physicalDevice, &attDepthFormat);
assert(validDepthFormat);
createAttachment(
attDepthFormat,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
&offScreenFrameBuf.depth);
// 需要单独的subpass来渲染到G-Buffer中
// Set up separate renderpass with references to the color and depth attachments
std::array<VkAttachmentDescription, 4> attachmentDescs = {};
// Init attachment properties
for (uint32_t i = 0; i < 4; ++i)
{
attachmentDescs[i].samples = VK_SAMPLE_COUNT_1_BIT;
attachmentDescs[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachmentDescs[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachmentDescs[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachmentDescs[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
if (i == 3) // 第4个放深度模板附件
{
attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
else
{
attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
}
// Formats
attachmentDescs[0].format = offScreenFrameBuf.position.format;
attachmentDescs[1].format = offScreenFrameBuf.normal.format;
attachmentDescs[2].format = offScreenFrameBuf.albedo.format;
attachmentDescs[3].format = offScreenFrameBuf.depth.format;
std::vector<VkAttachmentReference> colorReferences;
colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
colorReferences.push_back({ 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
colorReferences.push_back({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
VkAttachmentReference depthReference = {};
depthReference.attachment = 3;
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.pColorAttachments = colorReferences.data();
subpass.colorAttachmentCount = static_cast<uint32_t>(colorReferences.size());
subpass.pDepthStencilAttachment = &depthReference;
// Use subpass dependencies for attachment layout transitions
std::array<VkSubpassDependency, 2> dependencies;
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.pAttachments = attachmentDescs.data();
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescs.size());
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 2;
renderPassInfo.pDependencies = dependencies.data();
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offScreenFrameBuf.renderPass));
// 注意绑定到framebuffer的附件,是VkImageView
std::array<VkImageView,4> attachments;
attachments[0] = offScreenFrameBuf.position.view;
attachments[1] = offScreenFrameBuf.normal.view;
attachments[2] = offScreenFrameBuf.albedo.view;
attachments[3] = offScreenFrameBuf.depth.view;
VkFramebufferCreateInfo fbufCreateInfo = {};
fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbufCreateInfo.pNext = NULL;
fbufCreateInfo.renderPass = offScreenFrameBuf.renderPass;
fbufCreateInfo.pAttachments = attachments.data();
fbufCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
fbufCreateInfo.width = offScreenFrameBuf.width;
fbufCreateInfo.height = offScreenFrameBuf.height;
fbufCreateInfo.layers = 1;
VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offScreenFrameBuf.frameBuffer));
// Create sampler to sample from the color attachments
VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo();
sampler.magFilter = VK_FILTER_NEAREST;
sampler.minFilter = VK_FILTER_NEAREST;
sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampler.addressModeV = sampler.addressModeU;
sampler.addressModeW = sampler.addressModeU;
sampler.mipLodBias = 0.0f;
sampler.maxAnisotropy = 1.0f;
sampler.minLod = 0.0f;
sampler.maxLod = 1.0f;
sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &colorSampler));
}