2012-03-17 148 views
1

片段着色器使用两个原子计数器。它可能会或不会增加第一个,也可能不会增加第二个(但从来都不是)。然而,在修改计数器之前,它们的当前值总是被读取,然后 - 如果计数器稍后被修改 - 那些先前读取用于某些自定义逻辑的值。所有这些都发生在一个(很可能无法展开的)循环中。片段着色器中的GLSL原子计数器(和分支)

设想一个流程大致是这样的:在一些小unrollable环

  • ,说0-20(编译时解析常数)...
  • 为AC1和AC2
  • 获取计数器值
  • 检查某个值:
  • 如果x:在指数AC1,增量AC1
  • 在其他设置uimage1D_A纹理像素:在uimage1D_B设置在纹素指数(imgwidth-AC2-1),增量AC2

问题:着色器查询当前计数器值 - 它是否始终获得“最新”值?我在这里丢失了片段着色器的大规模并行性(仅针对当前代和未来的GPU和驱动程序)?

至于分支(如果x) - 我在另一个(readonly restrict uniformuimage1D纹素比作(uniformuint。所以一个操作数绝对是一个统一的标量,但另一个操作数是一个imageLoad().x,尽管图像是统一的 - 这种分支仍然是“完全并行化”的吗?你可以看到两个分支都是两个完全相同的指令。假设一个“完美优化”的GLSL编译器,这种分支可能引入一个失速?

回答

4

原子计数器是原子的。但是每个原子操作都是原子操作。

所以,如果你想确保每个着色器会从一个柜台一个独特的价值,那么每个着色器必须访问计数器atomicCounterIncrement(或Decrement,但它们都必须使用同一个)。

正确的方法做你所建议的是:

  1. 检查某个值:
  2. 如果x:
    1. atomicCounterIncrement(AC1),存储返回值。
    2. 使用存储的值作为设置uimage1D_A的texel。
  3. 其他:
    1. atomicCounterIncrement(AC2),存储返回值。
    2. 使用存储的值计算texel(imgwidth - val - 1),以便在其中将某些内容设置为uimage1D_B。

你的“获取和后来的增量”的策略是等待发生的竞争条件。没关系,如果它的“完全并行”,因为它是打破。在想知道它是否快速运行之前,您需要它来运行。

我强烈建议在尝试解决GPU问题之前,先熟悉原子和线程CPU。这是新手使用原子时常犯的一个错误。如果要成功使用GLSL原子和图像加载/存储,则需要成为线程专家(或至少是中级)。

+0

谢谢你的澄清和警告:) – metaleap 2012-03-18 23:11:22

1

由于尼科尔流星锤建议,如果你想确保你从原子计数器读永远不会被另一个内核中读取的值,你需要执行单位递增,并使用返回的值,这是任何其他内核除非他们执行atomicCounter(AC1)它检查值而不递增。你原子方式增加价值,并找回昔日的价值的那一刻,您确保其他人谁做同样只会得到增加后的值。

你似乎在做一个A-缓冲区,我很好奇,为什么你需要第二个计数器。我认为uimage1D_A是指针指向分片列表的屏幕大小的地图存储在uimage1D_B,对吗?您可以使用AC2产生一个指向uimage1D_B的一个新的未使用的内存部分,但你的AC1建议你逐渐acessing uimage1D_A,所以我可能是完全错误的:)