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

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

Я впервые использую вычислительный шейдер в OpenGL, поэтому могут возникнуть некоторые проблемы с моей настройкой.
Я использую Mat 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: Изменен флаг барьера памяти.

6

Решение

glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

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

5

Другие решения

Я не уверен на 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 для управления различными шейдерами, который в настоящее время использует только вершинные и фрагментные шейдеры. Он будет читать и компилировать файлы шейдеров, создавать программы шейдеров, устанавливать атрибуты и униформу, связывать программы и запускать шейдеры. Я использую пакетный процесс, где у меня есть класс пакетной обработки и класс диспетчера пакетной обработки, для рендеринга различных типов примитивов. Поэтому, когда я проходил свое решение и следовал пути или логике, это то, что я видел в своем коде.

Это был класс AssetStorage, который настраивал свойства для текстур, и он вызывал эти вызовы API в этом порядке в пределах своих add() функция для добавления текстур в память.

 glGenTextures(...);
glBindTextures(...);
glTexParameteri(...);
glTexParameteri(...);
glTexParameteri(...);
glTexParameteri(...);

Затем AssetStorage также вызывал их

glPixelStorei(...);
glTexImage2D(...)

И функция для добавления текстур в AssetStorage, наконец, возвращает пользовательскую структуру объекта TextureInfo.

Когда я проверил мой пакетный класс под его render() вызов функции — это то место, где он вызывал функцию ShaderManager, чтобы установить униформу для использования текстур, затем вызывал функцию ShaderManager, чтобы установить текстуру, а затем снова установить униформу, если текстура содержала альфа-канал. В классе ShaderManger для setTexture() функция это где glActiveTexture() а также glBindTexture() наконец-то звонят.

Итак, вкратце попробуйте переместить ваш glActiveTexture() позвонить, чтобы быть между последним glTexParameter() и последнее glBindTexture() призывает к обеим текстурам. Я думаю, что это также должно прийти после этих двух звонков glPixelStorei() & glTexImage2D() из-за того, что вы хотите сделать текстуру активной, как вы собираетесь ее визуализировать.

Как я упоминал ранее, я не уверен на 100%, является ли это основной причиной вашей проблемы, но я верю, что стоит попробовать, чтобы посмотреть, поможет ли это вам или нет. Пожалуйста, дайте мне знать, что произойдет, если вы попробуете это. Я хотел бы знать для себя, влияет ли порядок этих вызовов API на это. Я попробовал бы это в моем собственном решении, но я не хочу ломать свои классы или проект, потому что это в настоящее время работает должным образом.

Как примечание, единственное, что имеет флажки для ваших настроек текстуры, находится внутри разделов wrap / repeat. Вы могли бы вместо этого попробовать использовать GL_REPEAT за первые два glTexParameteri() звонки вместо использования GL_CLAMP_TO_EDGE и дайте мне знать, что вы придумали, вам не нужно беспокоиться о настройках mipmap для последних двух glTexParameteri() звонки, потому что кажется, что вы не используете mipmaps из настроек, которые вы используете.

3

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

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

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

Это ДЕЛАЕТ распределять данные, но это НЕ инициализировать эти данные, поэтому я получил эти странные результаты. Это был мусор в памяти.

Используя любую функцию, которая выделяет А ТАКЖЕ инициализация этих данных решает проблему:

cv::Mat::zeros
cv::Mat::ones
cv::Mat::create
3
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector