0%

【Piccolo代码解读】渲染系统(三)帧调试

上一节了解了 Piccolo 中两种不同的渲染路径的具体实现,这一节我们使用 RenderDoc 帧调试工具来验证整个渲染流程。

1 回顾渲染路径

Piccolo 引擎默认的渲染路径是延迟渲染,因此我们再次回顾延迟渲染路径:

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
void RenderPipeline::deferredRender(std::shared_ptr<RHI> rhi, std::shared_ptr<RenderResourceBase> render_resource)
{
VulkanRHI* vulkan_rhi = static_cast<VulkanRHI*>(rhi.get());
RenderResource* vulkan_resource = static_cast<RenderResource*>(render_resource.get());

vulkan_resource->resetRingBufferOffset(vulkan_rhi->m_current_frame_index);

vulkan_rhi->waitForFences();

vulkan_rhi->resetCommandPool();

bool recreate_swapchain =
vulkan_rhi->prepareBeforePass(std::bind(&RenderPipeline::passUpdateAfterRecreateSwapchain, this));
if (recreate_swapchain)
{
return;
}

static_cast<DirectionalLightShadowPass*>(m_directional_light_pass.get())->draw();

static_cast<PointLightShadowPass*>(m_point_light_shadow_pass.get())->draw();

ColorGradingPass& color_grading_pass = *(static_cast<ColorGradingPass*>(m_color_grading_pass.get()));
FXAAPass& fxaa_pass = *(static_cast<FXAAPass*>(m_fxaa_pass.get()));
ToneMappingPass& tone_mapping_pass = *(static_cast<ToneMappingPass*>(m_tone_mapping_pass.get()));
UIPass& ui_pass = *(static_cast<UIPass*>(m_ui_pass.get()));
CombineUIPass& combine_ui_pass = *(static_cast<CombineUIPass*>(m_combine_ui_pass.get()));

static_cast<MainCameraPass*>(m_main_camera_pass.get())
->draw(color_grading_pass,
fxaa_pass,
tone_mapping_pass,
ui_pass,
combine_ui_pass,
vulkan_rhi->m_current_swapchain_image_index);

vulkan_rhi->submitRendering(std::bind(&RenderPipeline::passUpdateAfterRecreateSwapchain, this));
}

首先是平行光的 Shadow Pass,然后是点光源的 Shadow Pass,最后是相机的 Pass,其中相机的 Pass 又包含很多 Subpass,也就是 draw() 函数中的流程:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
void MainCameraPass::draw(ColorGradingPass& color_grading_pass,
FXAAPass& fxaa_pass,
ToneMappingPass& tone_mapping_pass,
UIPass& ui_pass,
CombineUIPass& combine_ui_pass,
uint32_t current_swapchain_image_index)
{
{
VkRenderPassBeginInfo renderpass_begin_info {};
renderpass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderpass_begin_info.renderPass = m_framebuffer.render_pass;
renderpass_begin_info.framebuffer = m_swapchain_framebuffers[current_swapchain_image_index];
renderpass_begin_info.renderArea.offset = {0, 0};
renderpass_begin_info.renderArea.extent = m_vulkan_rhi->m_swapchain_extent;

VkClearValue clear_values[_main_camera_pass_attachment_count];
clear_values[_main_camera_pass_gbuffer_a].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
clear_values[_main_camera_pass_gbuffer_b].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
clear_values[_main_camera_pass_gbuffer_c].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
clear_values[_main_camera_pass_backup_buffer_odd].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clear_values[_main_camera_pass_backup_buffer_even].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clear_values[_main_camera_pass_post_process_buffer_odd].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clear_values[_main_camera_pass_post_process_buffer_even].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clear_values[_main_camera_pass_depth].depthStencil = {1.0f, 0};
clear_values[_main_camera_pass_swap_chain_image].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
renderpass_begin_info.clearValueCount = (sizeof(clear_values) / sizeof(clear_values[0]));
renderpass_begin_info.pClearValues = clear_values;

m_vulkan_rhi->m_vk_cmd_begin_render_pass(
m_vulkan_rhi->m_current_command_buffer, &renderpass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
}

if (m_vulkan_rhi->isDebugLabelEnabled())
{
VkDebugUtilsLabelEXT label_info = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, NULL, "BasePass", {1.0f, 1.0f, 1.0f, 1.0f}};
m_vulkan_rhi->m_vk_cmd_begin_debug_utils_label_ext(m_vulkan_rhi->m_current_command_buffer, &label_info);
}

drawMeshGbuffer();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

drawDeferredLighting();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

drawBillboardParticle();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

tone_mapping_pass.draw();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

color_grading_pass.draw();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

if (m_enable_fxaa) fxaa_pass.draw();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

VkClearAttachment clear_attachments[1];
clear_attachments[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clear_attachments[0].colorAttachment = 0;
clear_attachments[0].clearValue.color.float32[0] = 0.0;
clear_attachments[0].clearValue.color.float32[1] = 0.0;
clear_attachments[0].clearValue.color.float32[2] = 0.0;
clear_attachments[0].clearValue.color.float32[3] = 0.0;
VkClearRect clear_rects[1];
clear_rects[0].baseArrayLayer = 0;
clear_rects[0].layerCount = 1;
clear_rects[0].rect.offset.x = 0;
clear_rects[0].rect.offset.y = 0;
clear_rects[0].rect.extent.width = m_vulkan_rhi->m_swapchain_extent.width;
clear_rects[0].rect.extent.height = m_vulkan_rhi->m_swapchain_extent.height;
m_vulkan_rhi->m_vk_cmd_clear_attachments(m_vulkan_rhi->m_current_command_buffer,
sizeof(clear_attachments) / sizeof(clear_attachments[0]),
clear_attachments,
sizeof(clear_rects) / sizeof(clear_rects[0]),
clear_rects);

drawAxis();

ui_pass.draw();

m_vulkan_rhi->m_vk_cmd_next_subpass(m_vulkan_rhi->m_current_command_buffer, VK_SUBPASS_CONTENTS_INLINE);

combine_ui_pass.draw();

m_vulkan_rhi->m_vk_cmd_end_render_pass(m_vulkan_rhi->m_current_command_buffer);
}

可以看到这些 Subpass 按照顺序依次是:

  • drawMeshGbuffer()
  • drawDeferredLighting()
  • drawBillboardParticle()
  • tone_mapping_pass.draw()
  • color_grading_pass.draw()
  • if (m_enable_fxaa) fxaa_pass.draw()
  • drawAxis()
  • ui_pass.draw()
  • combine_ui_pass.draw()

2 帧调试

常用的图形调试工具有 Intel GPA、Nvidia Nsight 和 RenderDoc。他们大概有以下一些特点:

  • Intel GPA 简单易用,用于 PC 调试
  • Nsight 功能强大复杂,用于 PC 调试
  • RenderDoc 兼容性强,用于PC、移动设备、主机等

接下来我们使用 RenderDoc 来抓取引擎运行时的一帧并查看这一帧的渲染流程。

image-20220725102303105

左侧 Event 列表即为当前帧的绘制流程。

我们知道 Vulkan 中进行渲染流程其实就是向 Command Buffer 中提交命令,Command Buffer 记录命令后最后统一提交给设备的 Queue 去执行,执行完毕后渲染就完成了,最后送显(Present)就将画面显示在了屏幕上。

因此每一帧的绘制都以 vkBeginCommandBuffer 开始,以vkEndCommandBuffer 结束,命令记录完之后调用 vkQueueSubmit 提交给设备执行,最后调用 vkQueuePresentKHR 送显。

image-20220725104007201

接下来就是一系列的渲染指令,首先是 Colour Pass #1 为平行光阴影绘制,如下图绘制了平行光的 Shadow Map:

image-20220725104117496

然后是 Colour Pass #2 点光源的 Shadow Map,但因为我们的场景目前还没有点光源,因此 Shadow Map 没有内容:
image-20220725104259284

接下来 Colour Pass #3 对应主相机 Pass 中的drawMeshGbuffer(),逐模型逐 Mesh 的绘制了 G-buffer:

image-20220725104756924

image-20220725104804231

image-20220725104814678

image-20220725104908630

右侧的 output 列表可以看到 G-Buffer 中有哪些内容,包括法线 Buffer、颜色 Buffer、深度 Buffer 等:

image-20220725105037213

image-20220725105051150

接下来按照代码流程切换到了下一个 Subpass,因此先发送了命令 vkCmdNextSubpass,然后调用了 drawDeferredLighting() 进行延迟光照渲染:

image-20220725105257799

接下来继续切换下一个 Subpass,进行粒子特效及参与介质的渲染 drawBillboardParticle(),但该功目前还未实现,因此继续切换 Next Subpass 来到后处理的部分。

后处理首先进行了色调映射 tone_mapping_pass.draw() 得到了更加鲜明的结果:

image-20220725105510219

然后是切换下一个 Subpass 来到 color_grading_pass.draw(),但目前的 LUT 颜色映射没有对颜色进行变换,因此没有进行操作:

image-20220725105621484

然后是 fxaa 抗锯齿,默认也没有开启,因此也没有进行操作,之后是绘制选中目标的坐标轴 drawAxis()

image-20220725105747376

然后是绘制 UI 界面 ui_pass.draw()

image-20220725105838076

最后是将 UI 界面和渲染的图像结合起来的 combine_ui_pass.draw()

image-20220725105924846

这样就得到了我们引擎的一帧画面。

RenderDoc 还可以查看每个渲染命令绑定的 Pipeline,以及 Pipeline 绑定的资源 DescriptorSets 等:

image-20220725110403129

还可以看到每个 Pass 的输入纹理,纹理可以是我们准备的资源,也可以是上一个 Pass 的渲染结果,比如 color grading pass 的输入是色调映射的渲染结果和 LUT 颜色查找表:

image-20220725110623171

其他功能在之后用到的时候再继续深入探索。

---- 本文结束 知识又增加了亿点点!----

文章版权声明 1、博客名称:LycTechStack
2、博客网址:https://lz328.github.io/LycTechStack.github.io/
3、本博客的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系博主进行删除处理。
4、本博客所有文章版权归博主所有,如需转载请标明出处。