DirectX12(D3D12)基础教程(十二)——多线程+多显卡渲染及水彩画效果和标准简化版高斯模糊_d3d12多线程

使用键盘空格键可以控制水彩画效果的开关,Tab键控制高斯模糊效果的开关,Q键控制水彩画的随机采样半径,E键控制量化Bit数。

本章全部代码已经上传至GitHub:11-MultiThreadAndAdapter

2、多线程+多显卡整体渲染架构:

为了使大家能够彻底掌握和理解本章示例代码中的核心内容,首先来看一下这一次组合多线程+多显卡渲染后的程序整体逻辑。注意在这里依旧没有使用任何C++类封装,代码风格依然是C-Style,除了方便大家阅读代码外,更是为了让大家从整体上把握和理解多线程+多显卡渲染的整体框架。或者更贴切的说,是为了让大家能够从“上帝视角”来理解整个框架。

OK一图胜千言,下图中详细展示了本次示例的执行顺序框架:

上图只是展示了本章示例程序的大致执行逻辑顺序,特别要注意的是,图中先后顺序,并不是说GPU的过程与CPU的过程是串行执行的,本质上其实是CPU往GPU的命令队列中写入GPU命令的顺序而已,而CPU在调用了ExecuteCommandLists之后,CPU和GPU是真并行执行的。同时图中没有明显标识出Draw Call的调用,在D3D12的异步渲染框架中,其实Draw Call只是变成了一个命令记录而已,子线程录制命令列表最后一般都会写入一个Draw Call命令,同时每遍后处理最后也会写入一个Draw Call命令。通过前面的学习,这点各位应该不难理解。没有单独标出Draw Call的目的,是让大家不至于再混淆认为Draw Call时GPU就开始工作了,是耗时的调用。主要是为了让各位牢记或者说习惯现在D3D12中,实质上是调用ExecuteCommadLists之后,GPU才开始工作而已。

最终从图中可以看出,这个示例程序的框架其实与多线程渲染的框架大同小异,控制辅助显卡后处理的工作实质都是主线程单独完成的,因为到这一步,实质上CPU已经不用再做过多的工作了,所以一般也就不用CPU多线程来提高效率了。

当程序运行后主显卡和辅助显卡就是如下图形式的错帧运行了:

这个图在之前第八章教程中已经出现过了。需要注意的是,其中的时间值不是我们这个例子中的时间值,这里只是展示原理,我只是偷懒没有再重绘这个图了。

在我们实际例子中,辅助显卡主要是运行后处理,因为后处理基本上就是不断地处理屏幕像素大小的一张纹理而已,工作量基本上是固定的,所以其性能也是可以预期的。在本例中高斯模糊在沿用没有优化的“九宫格”方式的情况下,性能开销如下图:

从上图可以看出,未经优化的水彩画和高斯模糊后处理占用了UHD630显卡几乎50%的性能,这已经是非常明显的开销了。经过优化处理后,性能得到明显改善,优化后的性能开销如下图所示:

从上图可以看出,我们几乎优化了近30%的性能,实际运行中UHD630的性能开销平均大概稳定在20%左右。这样综合评估下来,因为我们的后处理实质上差不多是3Pass处理:一遍水彩画,然后分开水平垂直两个方向的高斯模糊处理,所以这样算来,如果让UHD630满负载运行后处理的大概可以进行15 Pass的后处理,这对复杂甚至更高分辨率的后处理来说已经足够了(当然需要实际的效果测试,并且打开Shader编译优化)。

这也就是为什么我一直热衷于类似我笔记本这种配置情况下的异构多显卡渲染架构的根本原因了。因为现代场景渲染中,后处理其实占用了很大一部分,但其工作量又通常比较固定,所以放到如UHD630这样的核显上来运行是再合适不过了,这样就可以将主显卡从多遍(Pass)后处理中解放出来,从而全力进行复杂的3D场景渲染。这样就尽可能大的发挥了系统的全部性能,并且可以明显看到画质的提升。

3、本章主要Shader

从前一章开始,本系列教程的重点就放到了Shader上来,因此关于本章示例代码中的C++代码部分我就不过多啰嗦了,除非有非常重要的需要注意的地方,大家可以自行从GitHub上下载阅读学习。

在这一章的示例中,由于使用了多遍的后处理,并且多次使用了渲染到纹理的技巧,所以综合下来Shader程序就比较多了。主要有下面几个Shader:

其中MultiThreadAndAdapter.hlsl文件中,主要是进行常规的基本的3D物体渲染,也就是第一遍渲染的Shader程序。例子中只是进行了简单的物体坐标系变换到视锥体空间的变换操作,以及最简单的Sample纹理得到物体像素点基本颜色的操作,这些没什么新奇的,有兴趣的可以自行加入光照、材质、法线纹理等内容,做出基本的光照模型,全当练手。因为这不是我们本章的重点,so 我就没有搞那么复杂了。这里友情提示一下,可以什么都学,但是千万别学我的偷懒!

其余的四个hlsl文件中,主要就是三遍后处理的Shader代码了。因为后处理实际都是针对已经被渲染成2D纹理图像的离屏表面进行的,本质上其实主要是运行Pixel Shader进行图像处理,而所有VS阶段只是简单的绘制一个全屏的矩形,并且不用带坐标系变换操作,所以我将Vertex Shader部分单独抽取出来,放在一个统一的QuadVS.hlsl中,这样后处理的三个PSO对象可以共用这个相同的Vertex Shader代码。其详细代码如下:

struct PSInput
{
    float4 m_v4Pos   : SV_POSITION;
    float2 m_v2UV : TEXCOORD0;
};

PSInput VSMain(float4 v4Pos : POSITION, float2 v2UV : TEXCOORD0)
{
    PSInput stResult;
    stResult.m_v4Pos =v4Pos;
    stResult.m_v2UV = v2UV;
    return stResult;
}

这看上去实在是太简单了,仅仅就是简单的值传递操作。而程序中就是传递了一个归一化的单位矩形的顶点,原样输出就是了。

接着11-WaterColourPS.hlsl中和上一章代码中的水彩画特效大同小异,只是将对应的Pixel Shader代码搬到了这个单独的文件中而已。算法的过程就不啰嗦了,大家可以参考前一章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值