我已经在WebGL中设置了一些FPS测量代码(基于此SO答案),并且发现我的片段着色器的性能有些奇怪。该代码仅在1024x1024画布上渲染了一个四边形(或者说是两个三角形),因此所有的魔术都发生在片段着色器中。 -through):



 // some definitions

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

    // Nothing to see here...

    gl_FragColor = vec4(value, value, value, 1.0);
}
 


所以这只是呈现白色的帆布。在我的机器上,它的平均速度约为30 fps。 =“ lang-cplusplus prettyprint-override”> void main() { float seed = uSeed; float x = vPos.x; float y = vPos.y; float value = 1.0; float noise; for ( int j=0; j<10; ++j) { noise = 0.0; for ( int i=4; i>0; i-- ) { float oct = pow(2.0,float(i)); noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0; } } value = noise/2.0+0.5; gl_FragColor = vec4(value, value, value, 1.0); }

如果要运行上面的代码,我一直在使用snoise的此实现。

这样会将fps降低到大约7。这很有意义。在以下条件中:

 if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
    // same noise computation
}
 


您希望它会更快,但是仍然只有7 fps。

对于另一个测试,让我们使用以下条件过滤像素:

 if (x > 0.5 && y > 0.5) {
    // same noise computation
}
 


这将提供与以前完全相同的噪声像素数量,但是n现在我们可以恢复到近30 fps。

这是怎么回事?两种滤除第16个像素的方式不应该给出完全相同的周期数吗?为什么较慢的像素像渲染所有像素一样慢?

奖金问题:我该怎么办?如果我确实确实想只用一些昂贵的片段来散布我的画布,有什么方法可以解决这个可怕的性能?

(可以肯定的是,通过将第16个像素渲染为黑色而不是白色,我已经确认了实际的模运算根本不影响帧速率。)

#1 楼

像素被分组为小方块(大小取决于硬件),并在单个SIMD管道中一起计算。 (SIMD数组类型的结构)

此管道(根据供应商的不同,具有几种不同的名称:扭曲,波阵面)将以锁步方式对每个像素/片段执行操作。这意味着如果1个像素需要完成计算,则所有像素都将对其进行计算,而不需要结果的像素将丢弃它。其他分支将不会执行。

这意味着您计算第16个像素的第一种方法是最坏情况的分支。

如果要缩小图像尺寸,只需渲染为较小的纹理,然后进行放大。

评论


$ \ begingroup $
渲染较小的纹理并进行升采样是一种很好的方法。但是,如果由于某些原因您确实需要写入大纹理的每个第16个像素,则使用对每个第16个像素加上一个调用的计算着色器,再加上图像加载/存储以将写入分散到渲染目标中,可能是一个不错的选择。
$ \ endgroup $
–内森·里德(Nathan Reed)
15年8月16日在0:28