Performance 如何提高OpenGL着色器中的纹理访问性能? 条件

Performance 如何提高OpenGL着色器中的纹理访问性能? 条件,performance,opengl,glsl,textures,Performance,Opengl,Glsl,Textures,我使用opengl3和PyOpenGL 我有大约50000(53'490)个顶点,每个顶点都有199个决定其位移的属性。不可能将这些数据存储为常规顶点属性,所以我使用纹理 问题是:非并行化C函数计算顶点位移的速度与GLSL一样快,在某些情况下甚至更快。我已经检查过了:问题是纹理读取,我不知道如何优化它 我编写了两个不同的着色器。一个在~0.09s内计算新模型,另一个在~0.12s内计算新模型(包括属性分配,这两种情况下是相等的) 代码 两个着色器都以 #version 300 es in ve

我使用
opengl3
PyOpenGL

我有大约50000(53'490)个顶点,每个顶点都有199个决定其位移的属性。不可能将这些数据存储为常规顶点属性,所以我使用纹理

问题是:非并行化
C
函数计算顶点位移的速度与
GLSL
一样快,在某些情况下甚至更快。我已经检查过了:问题是纹理读取,我不知道如何优化它

我编写了两个不同的着色器。一个在~0.09s内计算新模型,另一个在~0.12s内计算新模型(包括属性分配,这两种情况下是相等的)

代码 两个着色器都以

#version 300 es

in vec3 vin_position;

out vec4 vin_pos;

uniform mat4 rotation_matrix;

uniform float coefficients[199];

uniform sampler2D principal_components;
越快越好

void main(void) {
    int c_pos = gl_VertexID;
    int texture_size = 8192;
    ivec2 texPos = ivec2(c_pos % texture_size, c_pos / texture_size);
    vec4 tmp = vec4(0.0);
    for (int i = 0; i < 199; i++) {
        tmp += texelFetch(principal_components, texPos, 0) * coefficients[i];
        c_pos += 53490;
        texPos = ivec2(c_pos % texture_size, c_pos / texture_size);
    }
    gl_Position = rotation_matrix
        * vec4(vin_position + tmp.xyz, 246006.0);
    vin_pos = gl_Position;
}
void主管道(void){
int c_pos=gl_VertexID;
int纹理_大小=8192;
ivec2 texPos=ivec2(c_pos%纹理大小,c_pos/纹理大小);
vec4-tmp=vec4(0.0);
对于(int i=0;i<199;i++){
tmp+=texelFetch(主分量,texPos,0)*系数[i];
c_pos+=53490;
texPos=ivec2(c_pos%纹理大小,c_pos/纹理大小);
}
gl\u位置=旋转矩阵
*vec4(vin_位置+tmp.xyz,246006.0);
vin\u位置=gl\u位置;
}
慢一点的

void main(void) {
    int texture_size = 8192;
    int columns = texture_size - texture_size % 199;
    int c_pos = gl_VertexID * 199;
    ivec2 texPos = ivec2(c_pos % columns, c_pos / columns);
    vec4 tmp = vec3(0.0);
    for (int i = 0; i < 199; i++) {
        tmp += texelFetch(principal_components, texPos, 0) * coefficients[i];
        texPos.x++;
    }
    gl_Position = rotation_matrix
        * vec4(vin_position + tmp.xyz, 246006.0);
    vin_pos = gl_Position;
}
void主管道(void){
int纹理_大小=8192;
int columns=纹理大小-纹理大小%199;
int c_pos=gl_VertexID*199;
ivec2 texPos=ivec2(c_pos%列,c_pos/列);
vec4-tmp=vec3(0.0);
对于(int i=0;i<199;i++){
tmp+=texelFetch(主分量,texPos,0)*系数[i];
texPos.x++;
}
gl\u位置=旋转矩阵
*vec4(vin_位置+tmp.xyz,246006.0);
vin\u位置=gl\u位置;
}
它们之间的主要区别是:

  • 在第一种情况下,顶点的属性按以下方式存储:
    • 所有顶点的第一个属性
    • 所有顶点的第二个属性
    • 所有顶点的最后属性
  • 在第二种情况下,顶点的属性以另一种方式存储:
    • 第一个顶点的所有属性
    • 第二个顶点的所有属性
    • 最后一个顶点的所有属性
  • 同样在第二个示例中,数据是对齐的,因此每个顶点的所有属性仅存储在一行中。这意味着,如果我知道某个顶点的第一个属性的行和列,我只需要增加纹理坐标的
    x
    分量
我认为,对齐的数据将被更快地访问

问题
  • 为什么不能更快地访问数据
  • 如何提高it的性能
  • 是否能够将纹理块与顶点链接
  • 是否有关于GPU(英特尔HD、nVidia GeForce)中缓存的相关文章中的数据对齐建议
笔记
  • 系数
    数组随着帧的变化而变化,否则就没有问题了:我可以预先计算模型并感到高兴
为什么不能更快地访问数据

因为GPU并不神奇。GPU通过并行执行计算来获得性能。执行100万个texel回迁,无论它是如何发生的,都不会很快

如果您使用这些纹理的结果进行照明计算,它会显示得很快,因为照明计算的成本会被内存提取的延迟所隐藏。您正在获取一次提取的结果,进行乘法/加法运算,然后进行另一次提取。太慢了

是否能够将纹理块与顶点链接

即使有(也没有),那又有什么帮助呢?GPU并行执行操作。这意味着同时处理多个顶点,每个顶点访问200个纹理

因此,有助于提高性能的是使每个纹理访问连贯一致。也就是说,相邻顶点将访问相邻的texel,从而使纹理获取的缓存效率更高。但无法知道哪些顶点将被视为“邻居”。而且纹理旋转布局依赖于实现,因此即使您知道顶点处理的顺序,也无法调整纹理以利用其局部优势

最好的方法是放弃顶点着色器和纹理访问,转而使用计算着色器和SSBO。这样,通过设置工作组大小,您就可以直接了解访问的位置。使用SSBO,您可以以任何方式排列阵列,为每个波前提供最佳访问位置

但是像这样的事情就相当于把创可贴贴在一个裂开的伤口上

如何提高it的性能

停止这么多纹理提取

我是认真的。虽然有一些方法可以降低您所做工作的成本,但最有效的解决方案是更改您的算法,使其不需要做那么多工作

您的算法看起来很像通过“姿势”选项板进行的顶点变形,系数指定应用于每个姿势的权重。如果是这样的话,那么大多数系数要么为0,要么小到可以忽略不计,这是很有可能的。如果是这样的话,那么你就是在浪费大量的时间访问纹理,只是为了将它们的贡献转化为零


如果大多数系数为0,那么最好选择一些任意的小数值作为可能影响结果的最大系数数。例如,8。将包含8个索引和系数的数组作为一致性发送到着色器。然后遍历该数组,只获取8次。您可能只需要4个就可以解决问题。

性能差异可能是由于着色器的执行方式不同。通常,一组着色器在PAR中执行相同的命令。