作为一个学校项目,我们 4 正在使用 OpenCL 开发并行光线追踪器。这是我们第一个使用 OpenCL 的项目,所以我们可能对它有一些不理解。
我们正在尝试实现并行缓冲区压缩以删除完成的光线,或者没有与任何东西碰撞的光线,以便下一次迭代处理更少的数据。基本上,我们有一个缓冲区,可以s_ray_states
根据需要进行渲染、跟踪、获取碰撞数据、压缩缓冲区,这样就只有与其中的对象发生碰撞的光线,然后对它们进行着色。
所以我们有一个缓冲区uint *prefix_sum
,其中包含每个s_ray_state
必须移动到缓冲区中的索引,s_ray_state *ray_states
以减少发送到着色内核的光线数量,以及跟踪/着色内核的下一次迭代。
可悲的是,ray_sort
下面的内核似乎无法正常工作,我们验证了输入prefix_sum
数据,这是 100% 正确的,对于ray_states
缓冲区也是如此,但我们在输出中得到了不需要的数据。
我们正在启动一个工作组(全局工作大小 = 局部工作大小),光线总是在缓冲区中移动到比其原始索引更小的索引。我们设置了障碍,并且正在使用s_ray_state *tmp
缓冲区来防止并行执行写入彼此的数据,但它似乎不起作用,即使移除障碍我们也会得到相同的结果。
我们俩都在努力了 4 天,已经向其他学生寻求帮助,但似乎没有人能够弄清楚哪里出了问题。我们可能对障碍/内存栅栏的理解不足以确保这实际上可以工作。
我们已经尝试让单个工作组中的单个工作项对整个数组进行排序,这很有效,甚至可以提供更好的性能。
下面的代码应该工作吗?以我们对 OpenCL 的理解,它应该可以工作,我们做了很多研究,但从未真正得到任何明确的答案。
kernel void ray_sort(
global read_only uint *prefix_sum,
global read_write struct s_ray_state *ray_states,
global read_only uint *ray_states_size,
local read_write struct s_ray_state *tmp
)
{
int l_size = get_local_size(0);
int l_id = get_local_id(0);
int group_id = -1;
int group_nb = *ray_states_size / l_size;
int state_id;
while (++group_id < group_nb)
{
state_id = group_id * l_size + l_id;
tmp[l_id] = ray_states[state_id];
barrier(CLK_LOCAL_MEM_FENCE);
if (did_hit(tmp[l_id]))
ray_states[prefix_sum[state_id]] = tmp[l_id];
barrier(CLK_GLOBAL_MEM_FENCE);
}
}
ray_states
长度是ray_states_size
prefix_sum
包含每个ray_states
元素必须移动到的索引
tmp
是大小的本地缓冲区local_work_size
local_work_size
=global_work_size
did_hit()
如果射线击中一个物体,则返回 1,否则返回 0
我们期望ray_states
元素被移动到包含在prefix_sum
示例:每个都ray_states[id]
被移动到prefix_sum[id]
索引中
ray_states
prefix_sum: 0 | 0 | 1 | 1 | 2 | 3 | 3 | 3 | 4
did_hit(ray_states[id]): 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0
did_hit(output[id]): 1 | 1 | 1 | 1 | X | X | X | X | X
X
s 可以是任何东西