Glsl 奇怪的性能下降,由单个for循环引起

Glsl 奇怪的性能下降,由单个for循环引起,glsl,Glsl,我目前正在linux上编写一个OpenGL 3.1(GLSL版本330)应用程序(NVIDIA 360M卡,带有313.0 nv驱动程序),大约有15k行。我的问题是,在我的一个顶点着色器中,我可以通过对代码进行最小的更改来体验drastical性能下降,而这些更改实际上应该是无操作的 例如: //使用此解决方案,我的程序以3-5 fps的速度运行 对于(int i=0;i

我目前正在linux上编写一个OpenGL 3.1(GLSL版本330)应用程序(NVIDIA 360M卡,带有313.0 nv驱动程序),大约有15k行。我的问题是,在我的一个顶点着色器中,我可以通过对代码进行最小的更改来体验drastical性能下降,而这些更改实际上应该是无操作的

例如:

//使用此解决方案,我的程序以3-5 fps的速度运行
对于(int i=0;i<4;++i){
vout.shadowCoord[i]=uShadowCP[i]*w_pos;
}
//但它以30+fps的速度运行
vout.shadowCoord[0]=uShadowCP[0]*w_pos;
您的影子合作社[1]=uShadowCP[1]*w_pos;
vout.shadowCoord[2]=uShadowCP[2]*w_pos;
您的影子合作社[3]=uShadowCP[3]*w_pos;
//这也适用于30+fps
vec4阴影坐标[4];
对于(int i=0;i<4;++i){
影子坐标[i]=uShadowCP[i]*w_pos;
}
对于(int i=0;i<4;++i){
你的影子坐标[i]=影子坐标[i];
}

或考虑如下:

uniform int uNumUsedShadowMaps=4;//编辑:我在原始问题中称之为“随机统一”
//每秒8帧
对于(int i=0;i
请参见此处出现此问题的整个着色器代码:


像任何想法一样,我们都会很感激,是什么导致了这些。你得到了这样的下降,因为你在循环的每一次迭代中都运行min(random_uniform,4)。通常情况下,这在CPU上并不重要,但是你在gpu上运行这个程序,它在每个顶点上运行。因此,即使在一个由4个元素组成的数组上运行min通常是一个便宜的计算,但当您为每个顶点调用它4次时,它的计算速度很快

考虑这样做:

 int check = min(random_uniform,4);
 for(int i = 0; i < check; ++i) {
     vout.shadowCoord[i] = vec4(1.0);
 }
int check=min(随机均匀,4);
对于(int i=0;i
这将为每个顶点运行一次min(random_uniform,4),而不是4次


另一个问题可能是随机制服。这是如何创建的?找到最小值有什么意义?

我终于找到了问题的根源,并找到了解决方案

但是在开始正确的解决方案之前,请让我粘贴最简单的着色器代码,使用它,我可以复制这个“bug”

顶点着色器:

#版本330
vec3 CountPosition();//它是如何实现的。
均匀mat4投影矩阵,uCameraMatrix;
输出顶点数据{
vec3 c_pos,w_pos;
vec4阴影坐标[4];
}vout;
void main(){
vout.w_pos=CountPosition();
vout.c_pos=(uCameraMatrix*vec4(vout.w_pos,1.0)).xyz;
vec4 w_pos=vec4(vout.w_pos,1.0);
//每秒20帧
对于(int i=0;i<4;++i){
vout.shadowCoord[i]=uShadowCP[i]*w_pos;
}
//每秒50帧
vout.shadowCoord[0]=uShadowCP[0]*w_pos;
您的影子合作社[1]=uShadowCP[1]*w_pos;
vout.shadowCoord[2]=uShadowCP[2]*w_pos;
您的影子合作社[3]=uShadowCP[3]*w_pos;
gl_位置=投影矩阵*vec4(vout.c_位置,1.0);
}

片段着色器:

#版本330
在顶点数据中{
vec3 c_pos,w_pos;
vec4阴影坐标[4];
}vin;
输出vec4 frag_颜色;
void main(){
frag_color=vec4(1.0);
}

有趣的是,只需对顶点着色器进行最小的修改,就可以使两种解决方案都以50 fps的速度工作。主功能应修改为如下所示:

void main(){
vec4 w_pos=vec4(CountPosition(),1.0);
vec4 c_pos=uCameraMatrix*w_pos;
vout.w_pos=vec3(w_pos);
vout.c_pos=vec3(c_pos);
//每秒50帧
对于(int i=0;i<4;++i){
vout.shadowCoord[i]=uShadowCP[i]*w_pos;
}
//每秒50帧
vout.shadowCoord[0]=uShadowCP[0]*w_pos;
您的影子合作社[1]=uShadowCP[1]*w_pos;
vout.shadowCoord[2]=uShadowCP[2]*w_pos;
您的影子合作社[3]=uShadowCP[3]*w_pos;
gl_位置=投影矩阵*c_位置;
}
不同之处在于,上面的代码从着色器中读取变量,而下面的代码将这些值保存在临时变量中,并且只写入变量

结论是: 读取着色器的输出变量通常被视为一种优化,以减少一个临时变量,或者至少我在互联网上的许多地方见过它。尽管有前面的事实,读取输出变量实际上可能是无效的OpenGL操作,并且可能使GL进入未定义状态,在这种状态下,代码中的随机更改可能会触发不好的事情

关于这一点,最好的一点是,没有说任何关于从一个变量中读取的内容,而这个变量是以前写入的。可能是因为我不该这么做


附言


还要注意的是,原始代码中的第二个示例可能看起来完全不同,但在这个小代码段中,它的工作原理完全相同,如果读取out varying,则在for循环中使用
i
作为条件,它会变得非常慢,但是如果只写入out varying,它在性能上没有任何变化,
i
one也能以50 fps的速度工作。

实际上整个着色器相当复杂,它进行了十几次texel提取,进行了大约500次向量操作,我的应用程序以30+fps的速度运行。这段代码可能只计算了几纳秒,因此非常感谢,但它仍然以8 fps的速度运行。random_uniform是可以影响此对象的阴影投射者的数量。实际上,阴影坐标是通过变换世界空间位置来计算的,矩阵来自一个统一的坐标系。此对象附近最多可以有4个阴影施法者,但可能会更低。我用了那套制服,所以如果只有一个阴影施法者在场,我可以跳过