Render Passes and Framebuffers

Render Passes and Framebuffers

[TOC]

介绍

内容

  • Specifying attachment descriptions
  • Specifying subpass descriptions
  • Specifying dependencies between subpasses
  • Creating a render pass
  • Creating a framebuffer
  • Preparing a render pass for geometry rendering and postprocess subpasses
  • Preparing a render pass and a framebuffer with color and depth attachments
  • Beginning a render pass
  • Progressing to the next subpass
  • Ending a render pass
  • Destroying a framebuffer
  • Destroying a render pass

dc在render passes中组织.一个render pass是subpasses的集合.subpass描述images 资源(color,depth/stencil,input attachment)如何被使用:layouts是什么,在subpasses间layouts如何变换,何时向attachments渲染或合适从里面读数据,renderpass介绍后它们的内容是否有用,或它们的suage是否只被限制在一个render pass里.

存储在渲染过程中的上述数据只是一个general description或metadata.在rendering process中真实的resources为framebuffers.通过他们,定义了rendering atatchments的image views.

我们需要提前准备这些信息,在我们能issue(record)rendering commands前.有了这些信息,驱动能高效控制drawing process,限制rendering的memory 数量,或者给某些attachments使用非常快的cache,提高更多性能.

接下来讨论如何组织renderpasses和subpasses的drawing操作.以及如何准备RT,创建framebuffers–用作attachments的image views.

descriptor

specifying attachments descriptions

一个render pass是一组资源的集合(images)叫做attachments,用于rendering操作.分为color,depth/stencil,input,或者attachments.在创建render pass前,需要描述所有的attachmetns.
创建一组attachment descriptions.这个数组的indices之后也用于subpass descriptions.类似,创建一个framebuffer和指明每个attachment使用哪个image resources,定义了一个列表其每个元素对应于attachment descriptions数组.

通常绘制一个几何体,至少需要一个color attachment.可能还需要depth attachment(如果开启depth test).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
std::vector<VkAttachmentDescription> attachments_descriptions = 
{
{
0,
VK_FORMAT_R8G8B8A8_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
{
0,
VK_FORMAT_D16_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
}
};

在之前的例子,指明了两个attachments:一个R8G8B8A8_UNORM和一个D16_UNORM格式的.二者在render pass开始都需要clear.(类似glClear).当render pass完成时,我们想保持第一个attachment的内容,单不想第二个attachment的内容.二者都指明一个UNDEFINED intial layout–总能用于一个initial/old layout–意味着当memory barrier set up 时我们不需要images内容.

final layout的内容依赖于在render pass后我们如何使用image.如果我们直接向一个swapchain image渲染且想显示到屏幕上,我们需要PRESENT_SRC layout.对于depth attachment,如果render pass之后不想用depth component(通常为true),需要需要再render pass的最后的subpass set the same layout value as specified.

也有可能一个render pass不用任何attachments.此时不需要指明attachment descriptions但这种情况很少.

pass

Specifying subpass descriptions

再render pass中的操作被组织再subpasses中.每个subpass表示rendering commands(a subset of render pass’s attachments are used)的一个stage或一个phase.

一个render pass总需要至少一个subpass—当开始一个render pass时自动允许的.对于每个subpass,需要准备一个description.

为了减少参数,定义一个自定义结构体.它是vulkan头文件中定义的vksubpassDescription结构的简化版本.

1
2
3
4
5
6
7
8
9
10
11
struct SubpassParameters {
VkPipelineBindPoint PipelineType;//定义了pipeline type(graphic,compute.)
std::vector<VkAttachmentReference> InputAttachments;
std::vector<VkAttachmentReference> ColorAttachments;
//指明哪些color attachments需要在subpass结束时resolved(从多采样图像更改为非多采样/单采样图像)
std::vector<VkAttachmentReference> ResolveAttachments;
//如果用了,指明哪个attachment用于depth and/or stencil attachment.
VkAttachmentReference const * DepthStencilAttachment;
//一组不用于subpass但其内容在整个subpass中需要preserved
std::vector<uint32_t> PreserveAttachments;
};

再总结一下.

vulkan的render pass至少要有一个subpass,subpass参数定义在一组VkSubpassDescription中,每个这样的元素描述了attachments在关联的subpass中如何使用的.他们是分开的input,color,resolve,preserved attachments和单个entry for depth/stencil attachments的列表.所有成员都可能为空,在这种情况下,子类中不使用相应类型的attachment.

刚刚描述的列表中的每个条目都是再attachment descriptions中为render pass指明的attachments的列表的引用.此外,每个条目都指定了一个布局,其中图像应该在子类期间出现.驱动程序自动执行到指定布局的转换.

下面是使用子类参数类型的自定义结构指定子类定义的代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
subpass_descriptions.clear();
for( auto & subpass_description : subpass_parameters ) {
subpass_descriptions.push_back( {
0,
subpass_description.PipelineType,
static_cast<uint32_t>(subpass_description.InputAttachments.size()),
subpass_description.InputAttachments.data(),
static_cast<uint32_t>(subpass_description.ColorAttachments.size()),
subpass_description.ColorAttachments.data(),
subpass_description.ResolveAttachments.data(),
subpass_description.DepthStencilAttachment,
static_cast<uint32_t>(subpass_description.PreserveAttachments.size()),
subpass_description.PreserveAttachments.data()
} );
}

下面是一个使用一个color attachment:a depth/stencil attachment的一个subpass的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
VkAttachmentReference depth_stencil_attachment = {
1,//index
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
std::vector<SubpassParameters> subpass_parameters = {
{
VK_PIPELINE_BIND_POINT_GRAPHICS,
{},
{
{
0,//index
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
}
},
{},
&depth_stencil_attachment,
{}
}
};

specifying dependencies between subpasses

当subpass有依赖关系时需要指明subpass dependencies.

定义subpass dependencies与memory barrier相似.

指明subpass之间(或subpass和render pass之后/之前的commands之间)与设置image memory barrier相似.当位于某个subpass的commands以来另一个subpass时需要这样做.不需要设置layout transitions的dependencies.它们时根据render pass attachment和subpass descriptions自动进行的,但如果两个subpass中attachment都是只读的,就不需要指明dependency了.

在render pass建立image memory barriers也需要subpass dependencies.但不能”self-dependency”(source和dest的index相同).但如果给已有subpass定义了一个这样的dependency,我们能record一个memory barrier.其他情况,source subpass index必须比target subpass index小(除了VK_SUBPASS_EXTERNAL)

下例,准备了两个subpass之间的dependency–第一个绘制geometry到color和depth attachments,第二个使用color data做后处理.

1
2
3
4
5
6
7
8
9
10
11
std::vector<VkSubpassDependency> subpass_dependencies = {
{
0,//第一个subpass
1,//第二个subpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,//stage for 0
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,//access mask for 0
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,//access mask for 1
VK_DEPENDENCY_BY_REGION_BIT;//第一个subpass在一个坐标写一个值,第二个subpass在相同坐标读取到相同的值
}
};

VK_DEPENDENCY_BY_REGION_BIT我们这样做时,我们不应该假设区域大于单个像素,因为在不同的硬件平台上,区域的大小可能不同.

creating a render pass

后处理等操作需要在subpasses里对这些操作排序.指明所有需要的attachments的descriptions,所有组织操作的 subpasses,还有这些操作间必要的dependencies.这些数据准备好后,能创建render pass了.

vkCreateRenderPass

render pass创建最重要的部分是准备数据.descriptions.所有用到的attachments和subpasses和subpasses间的dependencies的descriptions.

1
2
3
4
SpecifyAttachmentsDescriptions( attachments_descriptions );
std::vector<VkSubpassDescription> subpass_descriptions;
SpecifySubpassDescriptions( subpass_parameters, subpass_descriptions );
SpecifyDependenciesBetweenSubpasses( subpass_dependencies );

在创建render pass时作为参数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
VkRenderPassCreateInfo render_pass_create_info = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
nullptr,
0,
static_cast<uint32_t>(attachments_descriptions.size()),
attachments_descriptions.data(),
static_cast<uint32_t>(subpass_descriptions.size()),
subpass_descriptions.data(),
static_cast<uint32_t>(subpass_dependencies.size()),
subpass_dependencies.data()
};
VkResult result = vkCreateRenderPass( logical_device,
&render_pass_create_info, nullptr, &render_pass );
if( VK_SUCCESS != result ) {
std::cout << "Could not create a render pass." << std::endl;
return false;
}
return true;

为了让render pass正常工作,有关用于所有已定义attachments的特定资源的此类信息存储在framebuffers中。

creating a framebuffer

framebuffers和render passes一起使用.它们指明了render pass里与之关联的attachments使用什么image resources.也定义了可渲染区域的尺寸.

framebuffer总是和render passes一起创建,它们定义了用于渲染过程中指定的attachments的特定image subresources,因此这两种对象类型应相互对应.

当创建frame buffer,提供一个render pass object能使用这个fb.但也不限于用这个特定的render pass使用它.所有可以兼容的render passes都可以用它.

什么是兼容的(compatible)render passes?第一,相同数量的subpasses.每个subpass有compatible的input,color,resolve,depth/stencil attachments.但是,需要记住确保特定区域外的pixels/fragments是未定义的.为此需要在创建pipeline时或者设置对应动态状态时指明一些参数(viewport和scissor test)(相关间:preparing view port and scissor test state c8,graphics and compute piplines ,setting a dynamic viewport and scissors state c9,command recording and drawing).

当开始render pass,使用framebuffer时,需要确保images在framebuffer中指明的subresources不用于其他目的.换句话说,如果使用了image作为framebuffer attachment,就不能降至用于其他render pass.

创建framebuffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
VkFramebufferCreateInfo framebuffer_create_info = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
nullptr,
0,
render_pass,
static_cast<uint32_t>(attachments.size()),
attachments.data(),
width,
height,
layers
};
VkResult result = vkCreateFramebuffer( logical_device,
&framebuffer_create_info, nullptr, &framebuffer );
if( VK_SUCCESS != result ) {
std::cout << "Could not create a framebuffer." << std::endl;
return false;
}
return true;

preparing a render pass for geometry rendering and postprocess subpasses

介绍两个subpasses的例子.第一个是有两个attachments–color和depth.第二个从第一个color attachment读取数据并render到另一个color attachment(swapchain image,能显示到屏幕上).

3个attachments

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
std::vector<VkAttachmentDescription> attachments_descriptions = {
{
0,
VK_FORMAT_R8G8B8A8_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
},
{
0,
VK_FORMAT_D16_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
},
{
0,
VK_FORMAT_R8G8B8A8_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
};

第一个是第一个subpass的color attachment和第二个subpass读取数据的.第二个attachment是depth数据;第三个是第二个subpass的color attachment.在render pass之后不需要第一个和第二个attachments了,需要给它们的store operations指明VK_ATTACHMENT_STORE_OP_DONT_CARE.render pass一开始也不需要它们的内容,所以知名一个未定义的layout.也clear三个attachments.

定义2个subpasses:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
VkAttachmentReference depth_stencil_attachment = {
1,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
std::vector<SubpassParameters> subpass_parameters = {
// #0 subpass
{
VK_PIPELINE_BIND_POINT_GRAPHICS,
{},
{
{
0,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
}
},
{},
&depth_stencil_attachment,
{}
},
// #1 subpass
{
VK_PIPELINE_BIND_POINT_GRAPHICS,
{
{
0,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
},
{
{
2,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
}
},
{},
nullptr,
{}
}
};

最后定义两个subpasses关于第一个attachment的dependency,一个时color attachment,另一个是input attachment.然后创建render pass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::vector<VkSubpassDependency> subpass_dependencies = {
{
0,
1,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
VK_DEPENDENCY_BY_REGION_BIT
}
};
if( !CreateRenderPass( logical_device, attachments_descriptions,
subpass_parameters, subpass_dependencies, render_pass ) ) {
return false;
}
return true;

reparing a rener pass and a framebuffer with color and depth attachments

3D场景渲染除了需要color attachment还需要给depth testing准备depth attachment.

本节介绍给color和depth数据创建images,创建只有单个subpass(向color和dpeth attachments渲染)的render pass.创建在render pass attachments中使用这两个images的framebuffer.

1.想在render pass里作为RT,usages为COLOR_ATTACHMENT / DEPTH_STENCIL_ATTACHMENT

2.向在render pass之后作为sample 用的textures,usage:SAMPLED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if( !Create2DImageAndView( physical_device, logical_device,
VK_FORMAT_R8G8B8A8_UNORM, { width, height }, 1, 1, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, color_image, color_image_memory_object,
color_image_view ) ) {
return false;
}
if( !Create2DImageAndView( physical_device, logical_device,
VK_FORMAT_D16_UNORM, //format
{ width, height }, 1, 1, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,//usage
VK_IMAGE_ASPECT_DEPTH_BIT, depth_image, depth_image_memory_object,
depth_image_view ) ) {
return false;
}

然后将2个attachment指给render pass,他们在render pass开始都clear,render pass之后内容都保持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
std::vector<VkAttachmentDescription> attachments_descriptions = {
{
0,
VK_FORMAT_R8G8B8A8_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
},
{
0,
VK_FORMAT_D16_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
}
};

下一步是定义一个subpass,将第一个attachment作为color,第二个作为depth/stencil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
VkAttachmentReference depth_stencil_attachment = {
1,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
std::vector<SubpassParameters> subpass_parameters = {
{
VK_PIPELINE_BIND_POINT_GRAPHICS,
{},
{
{
0,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
}
},
{},
&depth_stencil_attachment,
{}
}
};

最后定义subpass和render pass后执行的commands的dependency.

这有必要,因为不想在render pass里正在写数据时其他commands就开始才能够images里读取数据.也创建renderpass和framebufer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
std::vector<VkSubpassDependency> subpass_dependencies = {
{
0,
VK_SUBPASS_EXTERNAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT,
0
}
};
if( !CreateRenderPass( logical_device, attachments_descriptions,
subpasses_parameters, subpasses_dependencies, render_pass ) ) {
return false;
}
if( !CreateFramebuffer( logical_device, render_pass,
{ color_image_view,depth_image_view },
width, height, 1, framebuffer ) ) {
return false;
}
return true;

beginning a render pass

创建好render pass和frame buffer且准备开始recording commands绘制geometry,需要一个开始render pass的record操作.这个操作同时也自动开始它的第一个subbpass.在这完成前VkRenderPassBeginInfo:准备就绪

1
2
3
4
5
6
7
8
9
VkRenderPassBeginInfo render_pass_begin_info = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
nullptr,
render_pass,
framebuffer,
render_area,
static_cast<uint32_t>(clear_values.size()),
clear_values.data()
};

clearing values的数组至少要和attachments对应的元素个数一样.只需要给需要clear的提供值.可以提供nullptr.

当开始render pass,需要提供render area的dimentsions.可以和frame buffer的dimension一样也可以更小.我们要确保渲染将局限于指定的区域,否则超出此范围的像素可能会变得未定义.

1
2
vkCmdBeginRenderPass( command_buffer, &render_pass_begin_info,
subpass_contents );

progressing to the next subpass

一个render pass里record的commands分散在subpasses里.当给定subpass里的commands集已经recorded且想给另一个subpass record commands,需要switch(or progress)到下一个subpass.

同一个render pass.

在此操作过程中,将执行适当的layout transitions,并引入memory和执行依赖(与meemory arriers里类似).都是驱动自动进行的,因此如果需要,新的subpass能按照创建render pass指明的方式使用attachemnts.移到下一个subpass也performs multisample resolve operations on specified color attachments.

subpass里的commands可以直接在command buffer里record,也可以在第二个command bufer里间接执行.(Commands in the subpass can be recorded directly, by inlining them in the command buffer, or indirectly by executing a secondary command buffer.)

1
vkCmdNextSubpass( command_buffer, subpass_contents );

ending a render pass

1
vkCmdEndRenderPass( command_buffer );

在一个command bufffer里record这个函数会执行多个操作.引入执行和内存依赖性(如内存屏障中的那些),并执行图像布局转换——将图像从为最后一个子类指定的布局转换为最终布局的值(请参阅指定附件描述方法).Also multisample resolving is performed on color attachments for which resolving was specified in the last subpass. dditionally, for attachments whose contents should be preserved after the render pass, attachment data may be transferred from the cache to the image’s memory.

destroy

destroy a framebuffer

1
2
3
4
if( VK_NULL_HANDLE != framebuffer ) {
vkDestroyFramebuffer( logical_device, framebuffer, nullptr );
framebuffer = VK_NULL_HANDLE;
}

在销毁它前需要确保commands不会继续执行

destroying a render pass

1
2
3
4
if( VK_NULL_HANDLE != render_pass ) {
vkDestroyRenderPass( logical_device, render_pass, nullptr );
render_pass = VK_NULL_HANDLE;
}