OpenGL compute shader-странные результаты

Я пытаюсь реализовать многопроходный вычислительный шейдер для обработки изображений. В каждом проходе есть входное изображение и выходное изображение. Входное изображение следующего прохода-это выход предыдущих.

Это первый раз, когда я использую compute shader в OpenGL, поэтому могут возникнуть некоторые проблемы с моей настройкой. Я использую коврик OpenCV в качестве контейнера для операций чтения/копирования.

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

инициализации:

//texture init
glGenTextures(1, &feedbackTexture_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, feedbackTexture_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

glGenTextures(1, &resultTexture_);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D, resultTexture_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

// shader init
computeShaderID = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(computeShaderID, 1, &computeShaderSourcePtr, &computeShaderLength);
glCompileShader(computeShaderID);
programID = glCreateProgram();
glAttachShader(programID, computeShaderID);
glLinkProgram(programID);
glDeleteShader(computeShaderID);

Шейдер Код:

//shader code (simple invert)
#version 430
layout (local_size_x = 1, local_size_y = 1) in;

layout (location = 0, binding = 0, /*format*/ rgba32f) uniform readonly image2D inImage;
layout (location = 1, binding = 1, /*format*/ rgba32f) uniform writeonly image2D resultImage;

uniform writeonly image2D image;

void main()
{
    // Acquire the coordinates to the texel we are to process.
    ivec2 texelCoords = ivec2(gl_GlobalInvocationID.xy);

    // Read the pixel from the first texture.
    vec4 pixel = imageLoad(inImage, texelCoords);

    pixel.rgb = 1. - pixel.rgb;

    imageStore(resultImage, texelCoords, pixel);
}

использование:

cv::Mat image = loadImage().clone();
cv::Mat result(image.rows,image.cols,image.type());
// These get the appropriate enums used by glTexImage2D
GLenum internalformat = GLUtils::getMatOpenGLImageFormat(image);
GLenum format = GLUtils::getMatOpenGLFormat(image);
GLenum type = GLUtils::getMatOpenGLType(image);

int dispatchX = 1;
int dispatchY = 1;

for ( int i = 0; i < shaderPasses_.size(); ++i)
{
    // Update textures
    glBindTexture(GL_TEXTURE_2D, feedbackTexture_);
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, result.cols, result.rows, 0, format, type, result.data);
    glBindTexture(GL_TEXTURE_2D, resultTexture_);
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, image.cols, image.rows, 0, format, type, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glClear(GL_COLOR_BUFFER_BIT);
    std::shared_ptr<Shader> shaderPtr = shaderPasses_[i];
    // Enable shader
    shaderPtr->enable();
    {
        // Bind textures
        // location = 0, binding = 0
        glUniform1i(0,0);
        // binding = 0
        glBindImageTexture(0, feedbackTexture_, 0, GL_FALSE, 0, GL_READ_ONLY, internalformat);
        // location = 1, binding = 1
        glUniform1i(1,1);
        // binding = 1
        glBindImageTexture(1, resultTexture_, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalformat);

        // Dispatch rendering
        glDispatchCompute((GLuint)image.cols/dispatchX,(GLuint)image.rows/dispatchY,1);
        // Barrier will synchronize
        glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
    }
    // disable shader
    shaderPtr->disable();

    // Here result is now the result of the last pass.
}

иногда я получаю странные результаты (глюковые текстуры, частично визуализированные текстуры), также первый пиксель (при 0,0) иногда не записывается. Я все правильно настроил или мне чего-то не хватает? Кажется, что этот метод с текстурами действительно медленный, есть ли альтернатива, которая повысит производительность?

Edit1: изменен флаг memorybarrier.

3 ответов


glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

Это неправильный барьер. Барьер указывает как вы собираетесь получить доступ к данным после бессвязный доступ. Если вы пытаетесь прочитать текстуру с помощью glGetTexImage, вы должны использовать GL_TEXTURE_UPDATE_BARRIER_BIT.


я не уверен на 100%, что это исправит вашу проблему или нет; но я не вижу ничего, по-видимому, неправильно с вашими флагами для инициализации настроек текстуры. Когда я сравнивал ваш код с моим проектом, мое внимание привлек порядок вызовов API. В вашем источнике у вас есть такой порядок:

glGenTextures(...);    // Generate
glActiveTexture(...);  // Set Active
glBindTexture(...);    // Bind Texture
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Mipmap Setting
glTexParameteri(...);  // Mipmap Setting
glBindTexture(...);    // Bind / Unbind

и вы повторяете это для каждой текстуры, за исключением передачи переменной текстуры и увеличения значения id.

я не знаю, сделает ли это разница, но в моем движке и следуя логическому пути, который я установил; попробуйте сделать это в этом порядке и посмотреть, имеет ли это значение

glGenTextures(...);    // Generate
glBindTexture(...);    // Bind Texture
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Wrap Setting
glTexParameteri(...);  // Mipmap Setting
glTexParameteri(...);  // Mipmap Setting

glActiveTexture(...);  // Set Active
glBindTexture(...);    // Bind / Unbind

я не использую вычислительные шейдеры, но в моем движке у меня есть несколько классов, которые управляют разными вещами. У меня есть хранилище активов, которое сохранит все активы в базе данных памяти, включая текстуры для изображений, у меня есть класс ShaderManager для управления различными шейдерами, который в настоящее время использует только вершину и фрагментный шейдеры. Он будет считывать и компилировать файлы шейдеров, создавать программы шейдеров, устанавливать атрибуты и униформы, связывать программы и запускать шейдеры. Я использую пакетный процесс, в котором у меня есть класс batch и класс batch manager для рендеринга различных типов примитивов. Поэтому, когда я проходил через свое решение и следовал пути или потоку логики, это то, что я видел в своем коде.

это был класс AssetStorage, который настраивал свойства для текстуры, и он вызывал эти вызовы API в этом порядке в своем


я мог бы решить эту проблему, наконец!

проблема лежала в конструкторе cv::Mat. Следующая строка создает только заголовок для cv:: Mat:

cv::Mat result(image.rows,image.cols,image.type());

Это тут выделить данные, но это делает не инициализируйте эти данные, поэтому я получил эти странные результаты. Это был мусор в памяти.

используя любую функцию, которая выделяет и инициализирует эти данные решает вопрос:

cv::Mat::zeros
cv::Mat::ones
cv::Mat::create