为什么纹理查找比直接计算慢得多?

时间:2013-12-15 06:30:55

标签: performance opengl oculus

我正在研究oculus Rift失真着色器的OpenGL实现。着色器通过获取输入纹理坐标(包含先前渲染的场景的纹理)并使用失真系数对其进行变换,然后使用变换的纹理来确定片段颜色来工作。

我希望通过预先计算失真并将其存储在第二个纹理中来提高性能,但结果实际上比直接计算慢

直接计算版看起来基本上是这样的:

float distortionFactor(vec2 point) {
    float rSq = lengthSquared(point);
    float factor =  (K[0] + K[1] * rSq + K[2] * rSq * rSq + K[3] * rSq * rSq * rSq);
    return factor;
}

void main()
{
    vec2 distorted = vRiftTexCoord * distortionFactor(vRiftTexCoord);
    vec2 screenCentered = lensToScreen(distorted);
    vec2 texCoord = screenToTexture(screenCentered);
    vec2 clamped = clamp(texCoord, ZERO, ONE);
    if (!all(equal(texCoord, clamped))) {
        vFragColor = vec4(0.5, 0.0, 0.0, 1.0);
        return;
    }
    vFragColor = texture(Scene, texCoord);
}

其中K是以制服传递的vec4。

另一方面,置换贴图查找如下所示:

void main() {
    vec2 texCoord = vTexCoord;
    if (Mirror) {
        texCoord.x = 1.0 - texCoord.x;
    }
    texCoord = texture(OffsetMap, texCoord).rg;
    vec2 clamped = clamp(texCoord, ZERO, ONE);
    if (!all(equal(texCoord, clamped))) {
        discard;
    }
    if (Mirror) {
        texCoord.x = 1.0 - texCoord.x;
    }
    FragColor =  texture(Scene, texCoord);
}

还有一些其他操作可用于校正纵横比和计算镜头偏移,但它们非常简单。期望它优于简单的纹理查找是否合理?

3 个答案:

答案 0 :(得分:12)

GDDR内存具有相当高的延迟性,现代GPU架构具有大量的数字运算功能。它曾经是另一种方式,GPU无法进行计算,通过从立方体图中提取,规范化的成本更低。

请注意,您这里没有进行 常规 纹理查找,而是 依赖 查找,毫不奇怪。由于您从中获取的位置取决于另一次获取的结果,因此无法预先获取/有效缓存(有效的延迟隐藏策略)着色器所需的内存。这不是简单的纹理查找。"

此外,除了执行从属纹理查找外,您的第二个着色器还包含discard关键字。这将有效地消除在许多硬件上进行早期深度测试的可能性。

老实说,我不明白为什么你要"优化"将distortionFactor (...)函数放入查找中。它使用 平方长度 ,所以你甚至没有处理sqrt,只是一堆乘法和加法。

答案 1 :(得分:6)

Andon M. Coleman已经解释了发生了什么。基本上内存带宽和更重要的内存延迟是现代GPU的主要瓶颈,因此在大约2007年到今天之间建立的外观简单计算通常比纹理查找更快。

实际上,内存访问模式对效率有如此大的影响,稍微重新排列访问模式并确保正确对齐可以轻松地提供1000倍的性能提升(BT; DT,但这是CUDA编程)。然而,依赖查找不一定是性能杀手:如果依赖纹理坐标查找与控制器纹理是单调的,那么它通常不会那么糟糕。


话虽如此,你有没有听说过Horner's Method?你可以重写

float factor =  (K[0] + K[1] * rSq + K[2] * rSq * rSq + K[3] * rSq * rSq * rSq);

琐事

float factor =  K[0]  + rSq * (K[1] + rSq * (K[2] + rSq * K[3]) );

为您节省一些操作。

答案 2 :(得分:0)

GPU是大规模并行的,可以在一个时钟周期内计算多达1000个结果。内存读取始终是顺序的。如果需要f.e. 5个时钟用于计算乘法,可以在5个时钟周期内计算1000个结果。如果必须用f.e顺序读取数据。每个时钟周期10个数据集,需要100个时钟周期而不是5个来获取数据。数字只是随机让你明白:)