作为一个学校项目,我们正在开发一个使用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
可以是任何东西
答案 0 :(得分:0)
我可以完全离开这里,但在我看来mRecyclerView.layoutManager = LinearLayoutManager(this)
mRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL ,false)
你正在阅读同一段全局内存,你将它放入本地内存缓冲区tmp上面只有2行。除了您正在使用该缓冲区进行输入和输出之外,哪个不会有问题。
我看到它的方式,硬件上实际发生的是:
mRecyclerView.layoutManager = GridLayoutManager(this, spanCount)
考虑到WItem并行执行顺序未定义(硬件可能选择它想要的任何顺序),这将导致随机结果。你可以试试这个:
did_hit(ray_states[state_id])
BTW如果 tmp[l_id] = ray_states[state_id];
tmp[l_id] = ray_states[state_id];
tmp[l_id] = ray_states[state_id];
tmp[l_id] = ray_states[state_id];
tmp[l_id] = ray_states[state_id];
... local-work-size times
barrier(CLK_LOCAL_MEM_FENCE);
if (did_hit(ray_states[state_id]))
ray_states[prefix_sum[state_id]] = tmp[l_id];
if (did_hit(ray_states[state_id]))
ray_states[prefix_sum[state_id]] = tmp[l_id];
if (did_hit(ray_states[state_id]))
ray_states[prefix_sum[state_id]] = tmp[l_id];
if (did_hit(ray_states[state_id]))
ray_states[prefix_sum[state_id]] = tmp[l_id];
... again local-work-size times
只是一个简单的整数,你可以通过使参数“uint ray_states_size”直接传递它。不需要在那里找到缓冲区。
EDIT1:我的建议只有在 if (did_hit(tmp[l_id]))
ray_states[prefix_sum[state_id]] = tmp[l_id];
在每个本地工作大小的ID中没有任何重复项时才有效,否则仍会有数据竞争。所以例如如果同时为ray_states_size
- s 1和3 prefix_sum[state_id]
数组为0,且您的本地WG大小为&gt; = 4,则会有数据争用。
另外,是否有一些非常好的理由必须使用相同的缓冲区进行输入和输出?在我看来,如果你有单独的输入/输出缓冲区,它会变得复杂得多。
EDIT2:我刚注意到你说“光线总是在缓冲区中移动到比原来更小的索引”(抱歉,我错过了它)。这很好,但还不够 - 他们总是在同一个本地工作组中移动到一个较小的索引,然后任何其他光线的索引吗?如果是,那很好,但我还提到了其他的数据竞赛。