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

时间:2016-08-14 10:22:13

标签: performance opengl glsl textures

条件

我使用OpenGL 3PyOpenGL

我有~5万(53'490)个顶点,每个顶点都有199 vec3个属性决定它们的位移。将这些数据存储为常规顶点属性是不可能的,因此我使用纹理。

问题是:非并行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 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;
}

他们之间差异的主要观点:

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

我想,可以更快地访问对齐的数据。

问题

  • 为什么数据访问速度不快?
  • 如何提高性能呢?
  • 是否有能力将纹理块与顶点链接?
  • 是否有关于数据一致性的建议,有关GPU中缓存的相关文章(Intel HD,nVidia GeForce)?

注释

  • coefficients数组从一帧到另一帧更改,否则没有问题:我可以预先计算模型并开心

1 个答案:

答案 0 :(得分:3)

  

为什么数据访问速度不快?

因为GPU并不神奇。 GPU通过并行执行计算来获得性能。无论如何发生,执行100万个纹素提取都不会很快。

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

  

是否有能力将纹理块与顶点链接?

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

那么有助于提高性能的每个纹理访问是连贯的。也就是说,相邻顶点将访问相邻纹素,从而使纹理获得更高效的缓存。但是没有办法知道将考虑哪些顶点&#34;邻居&#34;。纹理混合布局依赖于实现,所以即使您确实知道了顶点处理的顺序,也无法调整纹理以获得局部优势。

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

但是这样的事情相当于将创可贴放在一个巨大的伤口上。

  

如何提高它的性能?

停止进行如此多的纹理提取。

我是完全认真的。虽然有很多方法可以降低您正在做的事情的成本,但最有效的解决方案是更改您的算法,以便它不需要做那么多工作。

您的算法看起来像是通过&#34; pose&#34;的调色板进行顶点变形,其系数指定了应用于每个姿势的权重。如果是这种情况,那么你的大多数系数都是0或者可以忽略不计的几率都很高。如果是这样,那么你浪费大量的时间访问纹理只是为了将他们的贡献转化为任何东西。

如果你的大部分系数都是0,那么最好的办法是为可能影响结果的最大系数数选择一些任意的数。例如,8。您将8个索引和系数的数组作为制服发送到着色器。然后你走了那个数组,只取了8次。你可能只能用4来逃脱。