我想使用OpenCL加速渲染光线跟踪图像,但是我注意到Wikipedia页面声称Open CL中禁止递归。这是真的?由于我在光线追踪时大量使用递归,因此需要大量的重新设计才能从速度上受益。阻止递归的基本限制是什么?周围有什么办法吗?

评论

GPU以不同的方式工作。 (某些体系结构)没有全局“程序堆栈”的概念,因此在其中不能进行递归函数调用。 OpenCL可能采用最低的公分母,因此完全不允许它在GPU之间保持可移植性。较新的CUDA硬件似乎已引入了对递归的支持:stackoverflow.com/q/3644809/1198654

我最近建议在《一个周末》中使用Raytracing一书的ray_color函数的非递归实现,这可能有助于您入门:github.com/RayTracing/raytracing.github.io/issues/624

#1 楼

这主要是因为并非所有GPU都可以支持函数调用,即使支持,函数调用也可能非常慢或存在栈深度很小等限制。到处都有函数调用,但是在正常情况下,编译器会内联100%内联它们。 GPU执行的机器代码包含分支和循环,但没有函数调用。但是,由于明显的原因,无法内联递归函数调用。 (除非某些参数是编译时常量,否则编译器可以折叠它们并内联整个调用树。)

为了实现真正的函数调用,您需要一个堆栈。在大多数情况下,着色器代码根本不使用堆栈-GPU具有大型寄存器文件,并且着色器可以将所有数据始终保留在寄存器中。使堆栈难以工作是因为(a)您需要大量的堆栈空间才能同时提供所有可能发生的所有扭曲,并且(b)GPU内存系统已针对大量批处理进行了优化内存事务以实现高吞吐量,但这是以等待时间为代价的,所以我的猜测是像保存/还原本地变量这样的堆栈操作会非常慢。在GPU上并不是太有用,因为在编译器中内联所有内容变得更加有意义。因此,GPU架构师并没有专注于使其快速发展。如果将来对高效的硬件级调用有需求,可能会做出一些不同的权衡,但是(与工程中的所有内容一样)它将在其他地方产生成本。

就光线追踪而言,人们通常处理此类事情的方式是通过创建正在被追踪的光线队列。无需递归,而是将射线添加到队列中,并在高层的某个位置具有循环,该循环不断进行处理,直到所有队列为空。但是,如果您从经典的递归raytracer开始,则确实需要对渲染代码进行大量重组。有关更多信息,请阅读Wavefront路径跟踪,这是一篇不错的文章。

评论


$ \ begingroup $
我不愿意分享这种秘密的调味料,但是我很幸运,它具有固定的最大跳出计数并具有固定大小的堆栈(以及具有固定迭代次数的循环)来处理此问题。另外(这是真正的秘密调味酱imo!)我的材料是反射性的或折射性的,但绝不能是两者兼有的,这使得光线反弹时不会裂开。所有这些的最终结果是递归类型的raytraced渲染,但是通过固定大小的迭代而不是递归。
$ \ endgroup $
–艾伦·沃尔夫(Alan Wolfe)
2015年8月29日在3:03



$ \ begingroup $
像尾递归吗?
$ \ endgroup $
– Tanmay Patil
15年8月31日在20:34

$ \ begingroup $
因为尾递归函数可以转换为迭代函数,所以不需要堆栈即可执行尾递归。 OpenCL编译器是否不会自动执行此操作?
$ \ endgroup $
–安德森·格林(Anderson Green)
19年7月6日在18:46

$ \ begingroup $
如果每个光线的颜色贡献可以线性建模,也可以使用while循环实现光线跟踪算法。这将消除对调用堆栈的需求,从而有效消除对着色器代码中的硬编码跳出计数的需求。然后您可以从主机程序设置跳出计数
$ \ endgroup $
– Kaan E.
20-05-28在5:07