我有一系列计算着色器,它们会生成一个索引网格,最后一个这样写生成的索引,如下所示:

void addTriangle (uint i0, uint i1, uint i2) {
    uint ic = atomicCounterIncrement(indirectIndexCount);
    meshIndices[ic*3+0] = i0;
    meshIndices[ic*3+1] = i1;
    meshIndices[ic*3+2] = i2;
}


生成网格后,它使用glDrawElementsIndirect绘制。上面代码中的indirectIndexCount是GL_DRAW_INDIRECT_BUFFER中位置0处的atomic_uint计数器(请参阅名为DrawElementsIndirectCommand的结构)。现在该计数器显然太小了三倍,因为每个三角形仅增加了一次。目前,我在发出绘画调用之前将其乘以3。

(目前,这是通过映射缓冲区并在CPU上乘以完成的,这当然是胡说八道,但是表明整个事情基本上都能正常工作。一切都正确绘制。我可以通过调用一个1x1x1x1x1x1计算着色器来做到这一点,但这似乎只不过稍微傻了一点。)

如何摆脱这种多余的乘法步骤?

由于每当生成具有可变索引数的网格时,这似乎是一个明显的问题,我想一定有一些我忽略的简单解决方案?

评论

您能将3原子加到计数器上而不是递增吗?

不可以,原子计数器只能查询,增加(增加1)或减少(增加1)。请参阅opengl Wiki中的文章。它们与Images上的atomic Add等不同。您只有8个(或大约8个),并且它们在极频繁地访问时(例如,在生成(并计数)成千上万个...事物时)应该会快得多。

是的,我想您必须将其从“原子计数器”转换为SSBO中的变量。看看这实际上是否更慢(取决于硬件),可能会很有趣。除此之外,我唯一想到的就是按照您所说的做,然后运行计算着色器将值乘以3。

嗨,cupe,可以看看某处的代码吗?附:您是否也在检查多个条目?

#1 楼

如果可以使用glsl 4.3+(或glsl ES 3.1),则可以使用atomicAdd

下一个选项是在计算中生成所有顶点之后使用barrier(),然后将其值乘以计数器:

main(){

    // generate vertices

    barrier();
    if(gl_localInvocationID == vec3(0)){
        indirectCount = atomicCounter(indirectIndexCount)*3;
    }
}


这模拟运行另一个1x1x1计算着色器来乘以索引。

否则,您可以使用第二个原子来保存顶点数:

void addTriangle (uint i0, uint i1, uint i2) {
    uint ic = atomicCounterIncrement(indirectIndex);
    meshIndices[ic*3+0] = i0;
    meshIndices[ic*3+1] = i1;
    meshIndices[ic*3+2] = i2;
    atomicCounterIncrement(indirectIndexCount);
    atomicCounterIncrement(indirectIndexCount);
    atomicCounterIncrement(indirectIndexCount);
}


indirectIndexindirectIndexCount均被初始化为0,而indirectIndexCount则传递给glDrawElementsIndirect

评论


$ \ begingroup $
我喜欢第二个想法,因为它明确表明我需要两个不同的东西:一种原子添加三角形的方法以及一种获取最终索引数的方法。不必是相同的机制。是的,我也会尝试atomicAdd。正如Nathan在上面的评论中所建议的那样,很有趣的是看看它是否真的更慢。我将报告调查结果。
$ \ endgroup $
–杯子
2015年10月13日14:33

$ \ begingroup $
可以确认在nvidia kepler上的这种使用情况下,atomicAdd确实并不慢(〜1.5k索引:0.1ms-可能需要更大的大小才能实现有意义的基准测试)。将间接命令缓冲区绑定为SSBO并写入位置0的uint可以正常工作。有时候,应该先尝试简单的解决方案...
$ \ endgroup $
–杯子
2015年10月13日在22:15