OpenGL 顶点着色器在 Linux 上速度很快,但在 Windows 上非常慢
OpenGL vertex shader is fast on Linux, but extremely slow on Windows
为了绘制信号的功率谱密度(与热图非常相似),我使用了这个顶点着色器程序。它接收每个顶点的功率值,取对数以 dB 为单位显示结果,在颜色图数组范围内归一化,并为顶点分配颜色。
#version 130
uniform float max_val;
uniform float min_val;
uniform int height;
attribute float val; // power-spectral-density value assigned to each vertex
// colormap values
const float r[512] = float[]( /* red values come here */ );
const float g[512] = float[]( /* green values come here */ );
const float b[512] = float[]( /* blue values come here */ );
void main() {
// set vertex position based on its ID
int x = gl_VertexID / height;
int y = gl_VertexID - x * height;
gl_Position = gl_ModelViewProjectionMatrix * vec4(x, y, -1.0, 1.0);
float e = log(max_val / min_val);
float d = log(val / min_val);
// set color
int idx = int(d * (512 - 1) / e); // find normalized index that falls in range [0, 512)
gl_FrontColor = vec4(r[idx], g[idx], b[idx], 1.0); // set color
}
对应的C++代码在这里:
QOpenGLShaderProgram glsl_program;
// initialization code is omitted
glsl_program.bind();
glsl_program.setUniformValue(vshader_max_uniform, max_val);
glsl_program.setUniformValue(vshader_min_uniform, min_val);
glsl_program.setUniformValue(vshader_height_uniform, max_colormap_height);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, colormap); // colormap is a vector that contains value of power at each vertex
glDrawElements(GL_TRIANGLE_STRIP, vertices_length, GL_UNSIGNED_INT, nullptr); // vertex_length is size of colormap
glDisableVertexAttribArray(0);
glsl_program.release();
此程序在 Linux 上运行速度足够快。但是在 Windows 中,它非常慢并且需要很多 CPU 时间。如果我更改这行 GLSL:
// int idx = int(d * (512 - 1) / e);
int idx = 0;
然后该应用程序在 Windows 上也能快速运行。所以,这一定是GLSL代码的问题。
我该如何解决?
你在那里所做的属于片段着色器,而不是顶点着色器。然后您将颜色查找 table 和光谱密度数据作为纹理提交。虽然顶点设置没有那么那么昂贵,但它会带来一定的开销,通常您希望用尽可能少的顶点覆盖尽可能多的像素。
还要学习对数计算规则(例如log(a/b) = log(a) - log(b)
),避免在整个绘制调用中进行统一计算,并在主机上预先计算。
/* vertex shader */
#version 130
varying vec2 pos;
void main() {
// set vertex position based on its ID
// To fill the viewport, we need just three vertices
// of a rectangular triangle of with and height 2
pos.x = gl_VertexID % 2;
pos.y = gl_VertexID / 2;
// screen position is controlled using glViewport/glScissor
gl_Position = vec4(2*pos, 0, 1.0);
}
-
/* fragment shader */
#version 130
varying vec2 pos;
uniform sampler2D values;
uniform sampler1D colors;
uniform float log_min;
uniform float log_max;
void main() {
float val = texture2D(values, pos).x;
float e = log_max - log_min;
float d = (log(val) - log_min) / e;
gl_FragColor = vec4(texture1D(colors, d).rgb, 1.0); // set color
}
在 GLSL 的更高版本中,一些关键字发生了变化。使用 in
和 out
而不是 varying
定义变量,并且纹理访问函数已统一以涵盖所有采样器类型。
glsl_program.bind();
glsl_program.setUniformValue(vshader_log_max_uniform, log(max_val));
glsl_program.setUniformValue(vshader_log_min_uniform, log(min_val));
// specify where to draw in window pixel coordinates.
glEnable(GL_SCISSOR_TEST);
glViewport(x, y, width, height);
glScissor(x, y, width, height);
glBindTexture(GL_TEXTURE_2D, values_texture);
glTexSubImage2D(GL_TEXTURE_2D, ..., spectral_density_data);
glDrawArrays(GL_TRIANGLES, 0, 3);
glsl_program.release();
为了绘制信号的功率谱密度(与热图非常相似),我使用了这个顶点着色器程序。它接收每个顶点的功率值,取对数以 dB 为单位显示结果,在颜色图数组范围内归一化,并为顶点分配颜色。
#version 130
uniform float max_val;
uniform float min_val;
uniform int height;
attribute float val; // power-spectral-density value assigned to each vertex
// colormap values
const float r[512] = float[]( /* red values come here */ );
const float g[512] = float[]( /* green values come here */ );
const float b[512] = float[]( /* blue values come here */ );
void main() {
// set vertex position based on its ID
int x = gl_VertexID / height;
int y = gl_VertexID - x * height;
gl_Position = gl_ModelViewProjectionMatrix * vec4(x, y, -1.0, 1.0);
float e = log(max_val / min_val);
float d = log(val / min_val);
// set color
int idx = int(d * (512 - 1) / e); // find normalized index that falls in range [0, 512)
gl_FrontColor = vec4(r[idx], g[idx], b[idx], 1.0); // set color
}
对应的C++代码在这里:
QOpenGLShaderProgram glsl_program;
// initialization code is omitted
glsl_program.bind();
glsl_program.setUniformValue(vshader_max_uniform, max_val);
glsl_program.setUniformValue(vshader_min_uniform, min_val);
glsl_program.setUniformValue(vshader_height_uniform, max_colormap_height);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, colormap); // colormap is a vector that contains value of power at each vertex
glDrawElements(GL_TRIANGLE_STRIP, vertices_length, GL_UNSIGNED_INT, nullptr); // vertex_length is size of colormap
glDisableVertexAttribArray(0);
glsl_program.release();
此程序在 Linux 上运行速度足够快。但是在 Windows 中,它非常慢并且需要很多 CPU 时间。如果我更改这行 GLSL:
// int idx = int(d * (512 - 1) / e);
int idx = 0;
然后该应用程序在 Windows 上也能快速运行。所以,这一定是GLSL代码的问题。
我该如何解决?
你在那里所做的属于片段着色器,而不是顶点着色器。然后您将颜色查找 table 和光谱密度数据作为纹理提交。虽然顶点设置没有那么那么昂贵,但它会带来一定的开销,通常您希望用尽可能少的顶点覆盖尽可能多的像素。
还要学习对数计算规则(例如log(a/b) = log(a) - log(b)
),避免在整个绘制调用中进行统一计算,并在主机上预先计算。
/* vertex shader */
#version 130
varying vec2 pos;
void main() {
// set vertex position based on its ID
// To fill the viewport, we need just three vertices
// of a rectangular triangle of with and height 2
pos.x = gl_VertexID % 2;
pos.y = gl_VertexID / 2;
// screen position is controlled using glViewport/glScissor
gl_Position = vec4(2*pos, 0, 1.0);
}
-
/* fragment shader */
#version 130
varying vec2 pos;
uniform sampler2D values;
uniform sampler1D colors;
uniform float log_min;
uniform float log_max;
void main() {
float val = texture2D(values, pos).x;
float e = log_max - log_min;
float d = (log(val) - log_min) / e;
gl_FragColor = vec4(texture1D(colors, d).rgb, 1.0); // set color
}
在 GLSL 的更高版本中,一些关键字发生了变化。使用 in
和 out
而不是 varying
定义变量,并且纹理访问函数已统一以涵盖所有采样器类型。
glsl_program.bind();
glsl_program.setUniformValue(vshader_log_max_uniform, log(max_val));
glsl_program.setUniformValue(vshader_log_min_uniform, log(min_val));
// specify where to draw in window pixel coordinates.
glEnable(GL_SCISSOR_TEST);
glViewport(x, y, width, height);
glScissor(x, y, width, height);
glBindTexture(GL_TEXTURE_2D, values_texture);
glTexSubImage2D(GL_TEXTURE_2D, ..., spectral_density_data);
glDrawArrays(GL_TRIANGLES, 0, 3);
glsl_program.release();