多次调用 cv::ogl::Texture2D.copyFrom() 会导致 cv::Exception (-219)

Multiple calls to cv::ogl::Texture2D.copyFrom() results in cv::Exception (-219)

我在 HMD 上每帧渲染两个视图,现在有点复杂,因为我使用 OpenCV 加载图像和处理中间结果,其余的是 OpenGL,但我仍然希望它能工作。我正在使用 OpenCV 3.1,如果您有任何帮助,我们将不胜感激,即使您只是提供一些建议。

申请详情:

每个视图(左眼和右眼)我将四张图像作为 cv::Mat 并将它们复制到四个 cv::ogl::Texture2D 对象中。然后我将这些纹理绑定到活动的 OpenGL 纹理以配置我的着色器并绘制到帧缓冲区。我再次读取帧缓冲区的像素 (glReadPixels()) 作为 cv::Mat 并进行一些后处理。这个 cv::Mat ("synthView") 被复制到另一个 cv::ogl::Texture2D,它在 2D 屏幕空间四边形上为视图呈现。

这是我为每次调用 cv::ogl::Texture2D 对象 记录的一些控制台输出。没有实际代码!

// First iteration for my left eye view
colorImageTexture[LEFT].copyFrom(imageLeft, true); //view1
colorImageTexture[RIGHT].copyFrom(imageRight, true); //view1
depthImageTexture[LEFT].copyFrom(depthLeft, true); //view1
depthImageTexture[RIGHT].copyFrom(depthRight, true); //view1

colorImageTexture[i].bind(); //left
depthImageTexture[i].bind(); //left
colorImageTexture[i].bind(); //right
depthImageTexture[i].bind(); //right

synthesizedImageTexture.copyFrom(synthView, true); //frame0, left_eye done

// Second iteration for my right eye view, reusing colorImageTexture[LEFT] the first time
colorImageTexture[LEFT].copyFrom(imageLeft, true); //view2 // cv::Exception!

当我捕获到异常并使用 Oculus DK2 而不是 CV1 时,代码正在运行。如您所见,我可以 运行 通过一个渲染视图,但尝试渲染第二个视图将在 gl::Buffer::unbind(ogl::Buffer::PIXEL_UNPACK_BUFFER 处的 copyFrom 方法中抛出异常).

异常发生在所有ogl::Texture2D对象都被使用一次后,第一个得到"reused",也就是说不会调用ogl::Texture2D::在 copyFrom() 函数中创建(...)!


详情cv::Exception:

code: -219

err: The specified operation is not allowed in the current state

func: cv::ogl::Buffer::unbind

file: C:\SDKs\opencv3.1\sources\modules\core\src\opengl.cpp

调用堆栈详细信息:

cv::ogl::Texture2D::copyFrom(const cv::_InputArray &arr, bool autoRelease);

从我的调用中调用,调用

ogl::Buffer::unbind(ogl::Buffer::PIXEL_UNPACK_BUFFER);

其中,有一个 OpenGL 调用

gl::BindBuffer(target, 0); // target is "ogl::Buffer::PIXEL_UNPACK_BUFFER"

直接调用 CV_CheckGlError();之后,抛出 cv::exception。 HAVE_OPENGL 显然没有在我的代码中定义。 GL 错误是 GL_INVALID_OPERATION。

根据glBindBuffer的specification

void glBindBuffer(GLenum target,
                  GLuint buffer);

While a non-zero buffer object name is bound, GL operations on the target to which it is bound affect the bound buffer object, and queries of the target to which it is bound return state from the bound buffer object. While buffer object name zero is bound, as in the initial state, attempts to modify or query state on the target to which it is bound generates an GL_INVALID_OPERATION error.

如果我理解正确,gl::BindBuffer(target, 0) 会导致此错误,因为 buffer 参数为 0 并且我以某种方式更改了目标。我不确定目标到底是什么,但也许我的 glReadPixels() 会干扰它?


有人可以指出正确的方向来摆脱这个异常吗?我只是使用示例 OpenCV 代码来构建我的代码。


更新:我的着色器代码可以触发异常。如果我只是输出未投影的坐标或 vec4(0,0,0,1.0f),程序会因为异常而中断。否则,它会继续,但我无法在我的网格上看到我的颜色纹理。

根据您问题中的信息,我认为问题在于对像素缓冲区对象 (PBO) 的异步写入。我相信您的代码正在尝试将缓冲区绑定到 0(取消绑定缓冲区),但该缓冲区仍由它之前的异步调用写入。

克服这个问题的一种方法是使用同步对象。定义一个同步对象并使用 glFenceSync() 或 glWaitSync()。如果您等待缓冲区完成它们的操作,这将对性能产生负面影响。这是关于 sync object.

的一些信息

检查此问题以获取有关在哪里使用 fence sync objects 的信息。

另一种方法是使用多个缓冲区并在它们之间切换连续帧,这将降低在解除绑定时缓冲区仍在使用的可能性。

实际答案是来自 OpenCV 的代码使用 glGetError() 检查错误。如果您不在代码中执行此操作,cv::ogl::Texture2D::copyFrom() 代码将捕获错误并抛出异常。