【发布时间】:2021-04-10 02:16:04
【问题描述】:
我最近一直在苦苦挣扎,因为我不得不更改我不久前编写的一些代码以在 Qt 和 OpenGl 中进行图像处理以支持多线程。
问题是我想用它在一组图像上应用批量过滤器, 我正在使用 openMP 来执行这样的多线程:
void batchProcess(QVector<QImage> &images)
{
#pragma omp parallel
{
#pragma omp for schedule(dynamic) nowait
for (int i = 1; i < images.count(); i++)
{
images[i] = ImageProcessing::processImage(images[i], _vertexShader, _fragmentShader, _textureVar, _vertexPosVar, _textureCoordVar);
}
}
}
ImageProcessing::processImage() 函数如下所示:
QImage ImageProcessing::processImage(const QImage &source, const QString &vertexShader, const QString &fragmentShader, const QString &textureVar, const QString &vertexPosVar, const QString &textureCoordVar)
{
QOpenGLContext context;
if(!context.create())
return source;
QOffscreenSurface surface;
surface.setFormat(context.format());
surface.create();
if(!surface.isValid())
return source;
if(!context.makeCurrent(&surface))
return source;
QOpenGLFramebufferObject fbo(source.size());
context.functions()->glViewport(0, 0, source.width(), source.height());
QOpenGLShaderProgram program(&context);
if (!program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader))
return source;
if (!program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader))
return source;
if (!program.link())
return source;
if (!program.bind())
return source;
QOpenGLTexture texture(QOpenGLTexture::Target2D);
texture.setData(source);
texture.bind();
if(!texture.isBound())
return source;
VertexData vertices[] =
{
{{ -1.0f, +1.0f }, { 0.0f, 1.0f }}, // top-left
{{ +1.0f, +1.0f }, { 1.0f, 1.0f }}, // top-right
{{ -1.0f, -1.0f }, { 0.0f, 0.0f }}, // bottom-left
{{ +1.0f, -1.0f }, { 1.0f, 0.0f }} // bottom-right
};
GLuint indices[] =
{
0, 1, 2, 3
};
QOpenGLBuffer vertexBuf(QOpenGLBuffer::VertexBuffer);
QOpenGLBuffer indexBuf(QOpenGLBuffer::IndexBuffer);
if(!vertexBuf.create())
return source;
if(!indexBuf.create())
return source;
if(!vertexBuf.bind())
return source;
vertexBuf.allocate(vertices, 4 * sizeof(VertexData));
if(!indexBuf.bind())
return source;
indexBuf.allocate(indices, 4 * sizeof(GLuint));
int offset = 0;
program.enableAttributeArray(vertexPosVar.toLatin1().data());
program.setAttributeBuffer(vertexPosVar.toLatin1().data(), GL_FLOAT, offset, 2, sizeof(VertexData));
offset += sizeof(QVector2D);
program.enableAttributeArray(textureCoordVar.toLatin1().data());
program.setAttributeBuffer(textureCoordVar.toLatin1().data(), GL_FLOAT, offset, 2, sizeof(VertexData));
program.setUniformValue(textureVar.toLatin1().data(), 0);
context.functions()->glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, Q_NULLPTR);
return fbo.toImage(false);
}
OpenMP 被禁用时它工作正常,但当我启用它时,我收到以下错误:
试图在 gui 线程之外创建基于 QWindow 的 QOffscreenSurface。预计会失败。
根据文档,这是预期的行为,因为QOffscreenSurface 在某些平台上只是隐藏的QWindow,这意味着它只能从主线程创建或销毁。
所以为了在主线程中创建QOffscreenSurface,我遵循了这个answer 并实现了以下类
class OpenGlFilterPrivate : public QObject
{
Q_OBJECT
public:
enum CustomEventTypes
{
CreateSurface = QEvent::User,
CreateContext = QEvent::User + 1,
CreateProgram = QEvent::User + 2
};
void moveToMainThread()
{
moveToThread(QApplication::instance()->thread());
}
virtual bool event(QEvent *event )
{
switch (event->type())
{
case CreateSurface: // m_surface (create an offscreen surface from the main thread)
m_surface = QSharedPointer<QOffscreenSurface>::create();
m_surface->setFormat(m_context->format());
m_surface->create();
break;
case CreateContext: // m_context (create an openGl context from the main thread)
m_context = QSharedPointer<QOpenGLContext>::create();
break;
case CreateProgram: // m_shaderProgram (create an openGl shader program from the main thread)
m_shaderProgram = QSharedPointer<QOpenGLShaderProgram>::create(&*m_context);
}
return false;
}
QSharedPointer<QOpenGLContext> m_context;
QSharedPointer<QOffscreenSurface> m_surface;
QSharedPointer<QOpenGLShaderProgram> m_shaderProgram;
};
现在我不是直接在我的processImage 函数中创建QOffscreenSurface,而是执行以下操作:
OpenGlFilterPrivate openGlFilterPrivate;
openGlFilterPrivate.moveToMainThread();
QCoreApplication::postEvent(&openGlFilterPrivate, new QEvent(QEvent::Type(OpenGlFilterPrivate::CreateSurface)));
QSharedPointer<QOffscreenSurface> surface = openGlFilterPrivate.m_surface;
它有效,但现在我不断收到以下消息:
QObject::~QObject: 定时器不能从另一个线程停止
然后我的应用程序立即崩溃。
我知道任何QObject 内部都包含QTimer,这就是导致问题的原因,但我想不出任何其他方法来解决这个问题。
有人可以帮忙吗?
我需要做的就是使用openGl 或任何其他硬件加速方法应用一些图像处理过滤器,同时能够以thread-safe 方式进行。
- 我认为它可以使用
QPainter使用的相同方法来完成,它是线程安全的,据我所知它是使用OpenGL 进行硬件加速的。但我找不到任何关于如何做这件事的资源。
【问题讨论】:
标签: c++ multithreading qt opengl