HLSL 中的流量控制
Flow Control in HLSL
我最近阅读了这篇关于光线行进云的论文(注意它是一个 PDF,以防你不想要它:http://www.diva-portal.org/smash/get/diva2:1223894/FULLTEXT01.pdf)作者在其中继续优化(第 22 页)。
通过重投影算法。
他表示,通过每帧仅对所有像素的 1/16 进行光线行进(选定像素在 4x4 网格中跳跃)并重新投影其余部分,他获得了大约 10 倍的性能提升。
我现在也尝试在 Unreal Engine 4(自定义 HLSL 着色器)中实现它,并且现在我得到了光线行进和重新投影。
然而,我实际上只停留在 运行 必要像素上的光线行进。据我所知,HLSL 中的任何分支都会计算分支的两侧,其中一个将被丢弃。
因此,我不能在像素着色器中做类似伪代码的事情:
如果(!PixelReprojection){ return 0;}
else { return Raymarch(...); }
因为即使对于重新投影的像素,它也会计算 Raymarch。
我没有看到任何其他方法来实现这个...
HLSL 中是否有任何类型的分支允许这样做?它不能是静态的,因为经过光线行进和重新投影的像素每帧都会发生变化。
据我所知,我真的很好奇作者是如何在 GPU 上编写代码时实现性能十倍提高的。
如有任何意见,我将不胜感激。
此致,
美食家
TLDR: 在您的 if 语句前面使用属性 [branch]
。
As far as I´m aware with any branching in HLSL both sides of the branch will be calculated and one will be thrown away
这实际上并不完全正确。是的,一个分支可以展平,也就是说两边都按照你描述的那样计算,但也可以不展平(称为动态分支).
现在,不展平分支有一些缺点:如果同一个 wave 中的两个线程在分支中采用不同的路径,则必须生成第二个 wave,因为一个 wave 中的所有线程都必须 运行相同的代码(因此一些线程将被移动到新生成的 wave 中)。因此,在这种情况下,很多线程都是 "disabled"(这意味着它们 运行 与它们 wave 中的其他线程具有相同的代码,但实际上并未将任何内容写入内存)。尽管如此,这种动态分支可能仍然比 运行 在分支的两侧都快,但这取决于实际代码。
甚至可以通过智能着色器设计来消除这个缺点(即确保走分支一侧的线程在同一个波浪中,所以波浪内部不会发生分歧。然而,这需要一些知识底层硬件,如波形大小等)
在任何情况下:如果没有另外说明,HLSL 编译器会自行决定分支是使用动态分支还是展平。但是,可以通过 adding an attribute 对 if 语句强制执行两种方式之一,例如:
//Enforce dynamic branching:
[branch]
if (...) { ... }
else { ... }
//Enforce flattening of the branch:
[flatten]
if (...) { ... }
else { ... }
我最近阅读了这篇关于光线行进云的论文(注意它是一个 PDF,以防你不想要它:http://www.diva-portal.org/smash/get/diva2:1223894/FULLTEXT01.pdf)作者在其中继续优化(第 22 页)。 通过重投影算法。 他表示,通过每帧仅对所有像素的 1/16 进行光线行进(选定像素在 4x4 网格中跳跃)并重新投影其余部分,他获得了大约 10 倍的性能提升。
我现在也尝试在 Unreal Engine 4(自定义 HLSL 着色器)中实现它,并且现在我得到了光线行进和重新投影。 然而,我实际上只停留在 运行 必要像素上的光线行进。据我所知,HLSL 中的任何分支都会计算分支的两侧,其中一个将被丢弃。 因此,我不能在像素着色器中做类似伪代码的事情: 如果(!PixelReprojection){ return 0;} else { return Raymarch(...); } 因为即使对于重新投影的像素,它也会计算 Raymarch。
我没有看到任何其他方法来实现这个... HLSL 中是否有任何类型的分支允许这样做?它不能是静态的,因为经过光线行进和重新投影的像素每帧都会发生变化。 据我所知,我真的很好奇作者是如何在 GPU 上编写代码时实现性能十倍提高的。
如有任何意见,我将不胜感激。
此致, 美食家
TLDR: 在您的 if 语句前面使用属性 [branch]
。
As far as I´m aware with any branching in HLSL both sides of the branch will be calculated and one will be thrown away
这实际上并不完全正确。是的,一个分支可以展平,也就是说两边都按照你描述的那样计算,但也可以不展平(称为动态分支).
现在,不展平分支有一些缺点:如果同一个 wave 中的两个线程在分支中采用不同的路径,则必须生成第二个 wave,因为一个 wave 中的所有线程都必须 运行相同的代码(因此一些线程将被移动到新生成的 wave 中)。因此,在这种情况下,很多线程都是 "disabled"(这意味着它们 运行 与它们 wave 中的其他线程具有相同的代码,但实际上并未将任何内容写入内存)。尽管如此,这种动态分支可能仍然比 运行 在分支的两侧都快,但这取决于实际代码。
甚至可以通过智能着色器设计来消除这个缺点(即确保走分支一侧的线程在同一个波浪中,所以波浪内部不会发生分歧。然而,这需要一些知识底层硬件,如波形大小等)
在任何情况下:如果没有另外说明,HLSL 编译器会自行决定分支是使用动态分支还是展平。但是,可以通过 adding an attribute 对 if 语句强制执行两种方式之一,例如:
//Enforce dynamic branching:
[branch]
if (...) { ... }
else { ... }
//Enforce flattening of the branch:
[flatten]
if (...) { ... }
else { ... }