什么是更快的glUniform4f/glUniform4fv考虑所有类型的优化?

Eon*_*nil 11 optimization

这是签名.

glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
glUniform4fv(GLint location, GLsizei count, const GLfloat *v);
Run Code Online (Sandbox Code Playgroud)

在我看来,前者应该更快,因为值可以直接从寄存器传递而无需从内存中获取.但是,我想听到很多意见.

Mec*_*cki 26

虽然*v变体主要用于设置阵列类型的制服,但OpenGL规范明确允许您使用数组变量来设置标量值,使用一个计数.

让我引用OpenGL Spec(强调自己补充):

命令glUniform {1 | 2 | 3 | 4} {f | i} v可用于修改单个统一变量 或统一变量数组.这些命令将计数和指针传递给要加载到统一变量或统一变量数组中的值.如果修改单个统一变量的值,则应使用1的计数,并且可以使用1或更大的计数来修改整个数组或数组的一部分.

这是来自OpenGL 2.1规范,但它对于OpenGL 4.2规范读取相同.

实际上也允许相反的方式.假设您有一个类型的统一,vec3 v[2]并使用glGetUniformLocation()它查询其位置,它可能会返回6.这意味着它6实际上是位置v[0].


现在回到最初的问题:哪种变体更快?

这是不可能的.它们可能同样快,或者可能比另一个更快,这是非常依赖于实现的.实际上我希望大多数实现在另一个实现之上实现其中之一.

例如,请考虑以下代码:

void glUniform1f ( GLint location, GLfloat v0 ) {
    glUniform1fv(location, 1, &v0);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,阵列变体会更快.但是,以下变体也是可能的:

void glUniform1fv ( GLint location, GLsizei count, GLfloat * value ) {
    int i;

    for (i = 0; i < count; i++) {
        glUniform1f(location, *value);
        value++;
        location++;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,非数组变体会更快.

我个人会说(这完全是我个人的观点)早期的OpenGL实现可能已经使用非数组变体实现了数组变体,因为这是更简单的实现,在OpenGL库的其余部分几乎没有其他修改.另一方面,它也是一个慢得多的实现,因为它涉及一个现代图形适配器最不可能的循环,因此现代实现很可能在阵列变体之上实现非数组变体.

阵列变体具有其他优点.考虑以下功能:

struct v3 {
     GLfloat x;
     GLfloat y;
     GLfloat z;
};

void setUniform ( GLint location, struct v3 * vPtr ) {
    glUniform3f(location, vPtr->x, vPtr->y, vPtr->z);
}
Run Code Online (Sandbox Code Playgroud)

为了调用非数组函数而解除引用vPtr三次是相当愚蠢的,并且几乎没有比以下实现更快:

void setUniform ( GLint location, struct v3 * vPtr ) {
    glUniform3fv(location, 1, (const GLfloat *)vPtr);
}
Run Code Online (Sandbox Code Playgroud)

此外,所有阵列变体总是具有三个参数,而其他变体最多可以有五个.传递给函数需要的参数越多,当这些参数通过堆栈而不是寄存器传递时,函数调用本身就会越慢.函数调用具有的参数越多,使用混合调用方案的架构在寄存器中传递的可能性就越小.因此,通过普通CPU可以预期的纯函数调用开销,对具有更少参数的函数的调用总是比调用具有更多参数的函数更快,尽管这种差异仅在您每秒执行几千次调用时才有意义,统一值通常不是这种情况.