Cha*_*lie 1 opengl performance textures glsl
我用OpenGL 3
和PyOpenGL
。
我有大约 50,000 (53'490) 个顶点,每个顶点都有 199 个vec3
属性来确定它们的位移。不可能将此数据存储为常规顶点属性,因此我使用纹理。
问题是:非并行化C
函数计算顶点位移的速度与计算顶点位移的速度一样快GLSL
,在某些情况下甚至更快。我已经检查过:问题是纹理读取,我不明白如何优化它。
我写了两个不同的着色器。一种在约 0.09 秒内计算出新模型,另一种在约 0.12 秒内计算出新模型(包括属性分配,这两种情况都是相同的)。
两个着色器都以
#version 300 es
in vec3 vin_position;
out vec4 vin_pos;
uniform mat4 rotation_matrix;
uniform float coefficients[199];
uniform sampler2D principal_components;
Run Code Online (Sandbox Code Playgroud)
速度较快的是
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;
}
Run Code Online (Sandbox Code Playgroud)
较慢的那一个
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;
}
Run Code Online (Sandbox Code Playgroud)
它们之间的主要区别思想:
x
纹理坐标的分量我认为,对齐的数据访问速度会更快。
coefficients
数组从帧到帧改变,否则没有问题:我可以预先计算模型并且很高兴为什么数据访问速度不快?
因为 GPU 并不神奇。GPU 通过并行执行计算来提高性能。执行 100 万次纹素提取,无论如何发生,都不会很快。
如果您使用这些纹理的结果来进行照明计算,它会显得很快,因为照明计算的成本将被内存获取的延迟隐藏。您正在获取提取的结果,进行乘法/加法,然后进行另一次提取。那很慢。
是否能够将纹理块与顶点链接起来?
即使有(也没有),那又有什么帮助呢?GPU并行执行操作。这意味着同时处理多个顶点,每个顶点访问 200 个纹理。
因此,提高性能的方法就是使每个纹理访问保持一致。也就是说,相邻的顶点将访问相邻的纹理像素,从而使纹理获取的缓存效率更高。但无法知道哪些顶点将被视为“邻居”。纹理混合布局依赖于实现,因此即使您确实知道顶点处理的顺序,您也无法调整纹理以充分利用它。
最好的方法是放弃顶点着色器和纹理访问,转而使用计算着色器和 SSBO。这样,您就可以通过设置工作组大小来直接了解访问的位置。借助 SSBO,您可以以任何方式布置阵列,从而为每个波前提供最佳的访问位置。
但这样的事情就相当于在裂开的伤口上贴上创可贴。
我怎样才能提高它的性能?
停止进行如此多的纹理获取。
我是认真的。虽然有多种方法可以降低您正在做的事情的成本,但最有效的解决方案是更改您的算法,以便它不需要做那么多工作。
您的算法看起来可疑地像通过“姿势”调色板进行顶点变形,系数指定应用于每个姿势的权重。如果是这种情况,那么大多数系数很可能为 0 或小到可以忽略不计。如果是这样,那么您就浪费了大量时间访问纹理,结果却将它们的贡献化为乌有。
如果大多数系数都是 0,那么最好的办法是选择一些任意的小数字作为可以影响结果的最大系数数。例如,8。您将 8 个索引和系数的数组作为制服发送到着色器。然后您遍历该数组,仅获取 8 次。而且你可能只需要 4 个就可以逃脱惩罚。