OpenGL计算着色器-奇怪的结果
OpenGL compute shader - strange results
我正在尝试实现一个用于图像处理的多路径计算着色器。在每个过程中都有一个输入图像和一个输出图像。下一次的输入图像是前一次的输出图像。
这是我第一次在OpenGL中使用计算着色器,所以我的设置可能有一些问题。我使用OpenCV的Mat作为容器来读取/复制操作。
代码中有一些部分与问题无关,所以我没有包括在内。其中一些部分包括加载图像或初始化上下文。
初始化:
//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(有时也没有被写入。我是正确设置了所有内容,还是缺少了什么?看起来这种纹理的方法真的很慢,有没有其他方法可以提高性能?
第1版:更改了memorybarrier标志
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
这是错误的障碍。屏障指定在非相干访问之后,您打算如何访问数据。如果要使用glGetTexImage
读取纹理,则必须使用GL_TEXTURE_UPDATE_BARRIER_BIT
。
我终于可以解决这个问题了!
问题出在cv::Mat的构造函数上。以下行仅为cv::Mat:创建一个标题
cv::Mat result(image.rows,image.cols,image.type());
它确实分配数据,但它没有初始化数据,这就是为什么我得到这些奇怪的结果。那是记忆中的垃圾。
使用任何分配AND的函数初始化该数据可以解决问题:
cv::Mat::zeros
cv::Mat::ones
cv::Mat::create
我不能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类正在为纹理设置属性,并在其add()
函数中按此顺序调用这些API调用,以便将纹理添加到内存中。
glGenTextures(...);
glBindTextures(...);
glTexParameteri(...);
glTexParameteri(...);
glTexParameteri(...);
glTexParameteri(...);
然后AssetStorage也调用了
glPixelStorei(...);
glTexImage2D(...)
将纹理添加到AssetStorage的函数最终将返回TextureInfo对象的自定义结构。
当我在render()
函数调用下检查Batch Class时,它就是在这里调用ShaderManager的函数来设置统一使用纹理,然后调用ShaderManagement的函数来设定纹理,如果纹理包含alpha通道,则再次设定统一。在setTexture()
函数的ShaderManger类中,这是glActiveTexture()
和glBindTexture()
最终被调用的地方。
因此,简而言之,尝试将glActiveTexture()
调用移动到两个纹理的最后一个glTexParameter()
和最后一个glBindTexture()
调用之间。我认为它也应该在这两次通话之后出现glPixelStorei()
&glTexImage2D()
,因为您希望在即将渲染纹理时使其处于活动状态。
正如我之前提到的,我不能100%确定这是否是你问题的根本原因,但我相信值得尝试一下,看看它是否对你有帮助。请告诉我如果你尝试这个会发生什么。我想知道这些API调用的顺序是否对它有任何影响。我会在自己的解决方案中尝试它,但我不想破坏我的类或项目,因为它目前工作正常。
需要注意的是,纹理设置的唯一标志是在包裹/重复部分。您可以尝试在前两个glTexParameteri()
调用中使用GL_REPEAT
,而不是使用GL_CLAMP_TO_EDGE
,并让我知道您的想法,您不必担心最后两个glTexParameteri()
调用的mipmap设置,因为您使用的设置中似乎没有使用mipmap。
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 计算幂级数的数学结果不正确
- 当我的 if 条件计算结果为 false 时,我的 else 块将不会执行
- 为什么组合的上限和下限比较的计算结果总是为 true?
- 为什么'typeid(x) == typeid(y)'的计算结果为 true,其中 'x' 和 'y' 分别是 T 和 T& 类型的 id-表达式?
- 在Javascript和C++中计算MD5哈希的不同结果
- 为什么 Lisp 中 1000 阶乘的计算如此之快(并显示正确的结果)?
- 我们如何并行运行算法的 n 个实例并以有效的方式计算结果函数的平均值?
- Fmod 函数清楚地输出一个预期的双精度值,但 if(fmod == 预期的双精度值)的计算结果不是 true
- WebGL2 和 C++ 上的浮点计算结果不同
- 术语的计算结果不是采用0个参数的函数
- C++编译器能在编译时计算出文字的除法结果吗
- 错误 C2064:术语的计算结果不是采用 3 个参数的函数
- 在C++中,是否可以编写一个条件的lambda,即只是一个计算结果为真或假的条件
- C++ 函数指针的计算结果为 1
- 为什么 02000 的计算结果为 1024
- 为什么这两个相似的代码计算行列式的结果不同?
- C++ openmp 并行计算计算错误的结果
- 如何让编译器忽略这个计算结果为 false 的 if-constexpr?
- 从文本文件中读取并使用 sizeof 结果计算整数的数量到 nan