Проблемы с барьером памяти при написании и чтении изображения OpenGL - PullRequest
0 голосов
/ 23 октября 2018

У меня проблема с попыткой чтения изображения из фрагментного шейдера, сначала я записываю изображение в шейдерную схему A (я просто рисую синим цветом на изображении), затем я читаю из другой программы B шейдера для отображенияизображение, но читающая часть не получает нужный цвет, я получаю черное изображение

неожиданный результат

Это код моего приложения:

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, или если я поставил неправильные биты барьера, или если я поместил барьеры в коде в неправильную часть

Вершинный шейдер fили обе шейдерные программы следующие:

#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;
}

и в моей функции инициализации:

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 голосов
/ 24 октября 2018
glEnable(GL_DEPTH_TEST);

Как я и подозревал.

Тот факт, что фрагментный шейдер не записывает вывод цвета, не означает, что эти фрагменты не будут влиять на буфер глубины .Если фрагмент проходит тест глубины и маска записи глубины включена (при условии, что не задействовано никакое другое состояние), он обновит буфер глубины с глубиной текущего фрагмента (и буфер цвета с неинициализированными значениями, ноэто другое дело).

Поскольку вы рисуете одну и ту же геометрию оба раза, фрагменты второго рендеринга получат те же значения глубины, что и соответствующие фрагменты первого рендеринга.Но функция глубины по умолчанию GL_LESS.Поскольку любое значение не меньше самого себя, это означает, что все фрагменты второго рендеринга не проходят проверку глубины .

И, следовательно, они не обрабатываются.

Так что просто отключите 1020 * тест глубины.И пока вы это делаете, отключите запись цвета для вашего прохода "записи" рендеринга, поскольку вы не записываете в буферы цвета.


Теперь вы делаетеПравильно нужен барьер памяти между двумя вызовами отрисовки.Но вам нужен только GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, поскольку именно так вы и читаете данные (через загрузку / сохранение изображений, а не через сэмплеры).

...