【问题标题】:Why Compute Shader is slowing down the rendering API calls?为什么 Compute Shader 会减慢渲染 API 调用速度?
【发布时间】:2022-01-04 06:09:01
【问题描述】:

我正在使用计算着色器来处理输入缓冲区数据并使用 imagestore() 将其存储为输出纹理。

执行计算着色器后,我有 3 个顺序渲染调用。

计算着色器代码:

#version 310 es
precision mediump image2D;
layout(std430) buffer; // Sets the default layout for SSBOs 
layout(local_size_x = 256) in; // 256 threads per work group
layout(binding = 0) readonly buffer InputBuf 
{
    uint input_buff[];
} inputbuff;
layout (rgba32f, binding = 1 ) uniform writeonly image2D out_teximg;
void main()
{
    int idx = int(gl_GlobalInvocationID.x);
    int idy = int(gl_GlobalInvocationID.y);
    unsigned int inputpix = inputbuff[1024 * idy + idx];
    // some calculation on inputpix and output is rcolor, bcolor, gcolor
    imageStore(out_teximg, ivec2(idx , idy), vec4(rcolor, bcolor, gcolor, 1.0)); 
    barrier();
};

代码:

void initCompute()
{
    glGenTextures(1, &computeOutTex);
    glGenBuffers(1, &inSSBOId);
}

uint inputBuffData = { .... }; // input buffer data
void execute_compute()
{
    // compute shader code starts...
    glUseProgram(computePgmId);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, computeOutTex);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, width, height);

    glBindImageTexture(1, computeOutTex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); // binding is 1 
    glUniform1i( glGetUniformLocation(computePgmId, "out_teximg"), 0);
    
    uint inputBuffSize = 1024 * 512 * 3;
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, inSSBOId);
    glBufferData(GL_SHADER_STORAGE_BUFFER, inputBuffSize, inputBuffData, GL_STATIC_DRAW);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER,  0 , inSSBOId); // binding is 0

    glDispatchCompute(width / 256, height, 1);
    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
    // glFinish();
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
    glBindImageTexture(1, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);  // binding is 1 
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);// binding is 0
}

int draw()
{
    glBindFramebuffer(GL_FRAMEBUFFER, m_FBOId); // Offscreen Rendering
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(compute_pgm); 
    execute_compute();

    glUseProgram(render_pgm1);
    glViewport(0,0,w,h);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, computeOutTex);
    glDrawElements(); // Render the texture data

  // 2nd draw call 
    glUseProgram(render_pgm2);
    ....
    ....
    glDrawElements();

  // 3rd draw call
    glUseProgram(render_pgm3);
    ....
    ....
    glDrawElements(); 
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind FBO
}

这里,唯一的第二次绘制调用在使用计算着色器后需要更多时间。

如果在 glMemoryBarrier() 之后调用 glFinish(),那么只有 execute_compute() 调用会变慢。 为什么计算着色器会减慢后续的绘制调用? 真的需要 glFinish() 吗?

【问题讨论】:

    标签: opengl-es shader compute-shader opengl-es-3.1


    【解决方案1】:

    计算着色器不会减慢后续的绘制调用。但是,计算着色器本身需要一些时间来执行。由于您正在设置内存屏障,因此后续的绘制必须等待。
    OpenGL 命令被缓存并且在调用时不会立即执行。 GPU 和 CPU 并行工作。 CPU 将指令发送到 GPU,GPU 会尽快处理它们。
    glFinish 让一切准备就绪,直到所有先前调用的命令都完成后才返回。 glFinish 本身并不“昂贵”。测量 CPU 上的时间似乎“代价高昂”,因为它测量的是完成之前调用的 OpenGL 命令所需的时间。
    无论如何,这里不需要glFinish。您所需要的只是内存屏障。使用内存屏障时,以下依赖于此屏障的 OpenGL 命令似乎需要更长的时间才能完成。不过他们已经不需要了,他们只需要等待屏障指示的条件满足即可。

    在您的情况下,您需要使用 GL_ALL_BARRIER_BITSGL_TEXTURE_FETCH_BARRIER_BIT,这反映了在屏障之后的纹理提取之前的不连贯内存写入(例如:图像存储)。

    【讨论】:

    • execute_compute() 在没有 glFinish() 的情况下非常快。但是第二次draw call变得非常慢。此外,计算着色器用于快速处理。但为什么它需要“昂贵”的 glFinishI() 进行同步?
    • @ArunAC glFinish 并不昂贵。请阅读答案。当glFinish 被调用时,所有之前调用的OpenGL 命令都完成了。仅调用 execute_compute 不会完成计算着色器的执行。您只需测量 CPU 时间。这只是设置 OpenGL 指令的时间,而不是它需要在 GPU 上运行的时间。 glFinish 这里不需要。您所需要的只是适当的内存屏障。
    • 感谢 Rabbid76。我会调查的。
    猜你喜欢
    • 2019-04-04
    • 2014-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多