用于写入和读取图像OpenGL的内存屏障问题

时间:2018-10-23 18:36:04

标签: c++ opengl glsl

我在尝试从片段着色器读取图像时遇到问题,首先我在着色器图A中将图像写入图像(只是在图像上涂成蓝色),然后从另一个着色器程序B读取以显示图片,但阅读部分无法获得正确的颜色,我得到的是黑色图片

Unexpected result

这是我的应用程序代码:

void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
    std::cout << "GL CALLBACK: type = " << std::hex << type << ", severity = " << std::hex << severity << ", message = " << message << "\n"
    << (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "") << std::endl;
}

class ImgRW
    : public Core
{
public:
    ImgRW()
        : Core(512, 512, "JFAD")
    {}

virtual void Start() override
{
    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(MessageCallback, nullptr);

    shader_w = new Shader("w_img.vert", "w_img.frag");
    shader_r = new Shader("r_img.vert", "r_img.frag");

    glGenTextures(1, &space);
    glBindTexture(GL_TEXTURE_2D, space);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 512, 512);
    glBindImageTexture(0, space, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);

    glGenVertexArrays(1, &vertex_array);
    glBindVertexArray(vertex_array);
}

virtual void Update() override
{
    shader_w->use(); // writing shader
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

    shader_r->use(); // reading shader
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

virtual void End() override
{
    delete shader_w;
    delete shader_r;

    glDeleteTextures(1, &space);

    glDeleteVertexArrays(1, &vertex_array);
}

private:
    Shader* shader_w;
    Shader* shader_r;

GLuint vertex_array;

GLuint space;
};

#if 1
CORE_MAIN(ImgRW)
#endif

这些是我的片段着色器:

写图像 代码glsl:

#version 430 core

layout (binding = 0, rgba32f) uniform image2D img;

out vec4 out_color;

void main()
{
    imageStore(img, ivec2(gl_FragCoord.xy), vec4(0.0f, 0.0f, 1.0f, 1.0f));
}

从图像读取 代码glsl:

#version 430 core

layout (binding = 0, rgba32f) uniform image2D img;

out vec4 out_color;

void main()
{
    vec4 color = imageLoad(img, ivec2(gl_FragCoord.xy));
    out_color = color;
}

获得正确结果的唯一方法是,如果我更改了绘图命令的顺序并且不需要内存障碍,例如这样(在上述“更新功能”中):

shader_r->use(); // reading shader
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

shader_w->use(); // writing shader
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

我不知道问题是否出在图形卡或驱动程序上,或者我是否缺少启用memoryBarriers的某种标志,或者我是否放置了错误的屏障位,或者是否在代码中放置了屏障错误的部分

两个着色器程序的顶点着色器是下一个:

#version 430 core

void main()
{
    vec2 v[4] = vec2[4]
    (
        vec2(-1.0, -1.0),
        vec2( 1.0, -1.0),
        vec2(-1.0,  1.0),
        vec2( 1.0,  1.0)
    );

    vec4 p = vec4(v[gl_VertexID], 0.0, 1.0);
    gl_Position = p;
}

在我的init函数中是:

void Window::init()
{
    glfwInit();
    window = glfwCreateWindow(getWidth(), getHeight(), name, nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
    glfwSetCursorPosCallback(window, cursorPosCallback);

    //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    assert(gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) && "Couldn't initilaize OpenGL");

    glEnable(GL_DEPTH_TEST);
}

在函数运行中,我调用了我的开始,更新和结束函数

void Core::Run()
{
    std::cout << glGetString(GL_VERSION) << std::endl;

    Start();

    float lastFrame{ 0.0f };

    while (!window.close())
    {
        float currentFrame = static_cast<float>(glfwGetTime());
        Time::deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        glViewport(0, 0, getWidth(), getHeight());
        glClearBufferfv(GL_COLOR, 0, &color[0]);
        glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0);

        Update();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    End();
}

1 个答案:

答案 0 :(得分:2)

glEnable(GL_DEPTH_TEST);

我怀疑。

仅仅因为片段着色器不写颜色输出并不意味着这些片段不会影响深度缓冲区。如果片段通过了深度测试并且深度写掩码处于打开状态(假设不涉及其他状态),它将 使用当前片段的深度(以及未初始化值的颜色缓冲区)更新深度缓冲区,但这是另一回事。

由于两次都绘制相同的几何图形,因此第二个渲染的片段将获得与第一个渲染中相应片段相同的深度值。但是默认的深度函数是GL_LESS。由于任何值都不小于其自身,这意味着第二次渲染中的所有片段均未通过深度测试。

因此,它们不会被渲染。

所以只需关闭 深度测试。而且在进行此操作时,turn off color writes用于“书写”渲染阶段,因为您没有写入颜色缓冲区。


现在,您确实确实需要两个绘图调用之间的内存屏障。但是您只需要GL_SHADER_IMAGE_ACCESS_BARRIER_BIT,因为这就是您通过数据加载/存储(而不是采样器)读取数据的方式。