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