gpt4 book ai didi

c++ - 我如何创建一个线程安全的 QOffscreenSurface 供 OpenGl 使用

转载 作者:行者123 更新时间:2023-12-03 12:58:19 28 4
gpt4 key购买 nike

我最近一直在苦苦挣扎,因为我不得不更改我不久前编写的一些代码以在 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 时它工作正常已禁用,但是当我启用它时,出现以下错误:

Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures.


根据文档,这是预期的行为,因为 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;
};
现在而不是直接创建一个 QOffscreenSurface在我的 processImage功能我执行以下操作:
OpenGlFilterPrivate openGlFilterPrivate;
openGlFilterPrivate.moveToMainThread();
QCoreApplication::postEvent(&openGlFilterPrivate, new QEvent(QEvent::Type(OpenGlFilterPrivate::CreateSurface)));
QSharedPointer<QOffscreenSurface> surface = openGlFilterPrivate.m_surface;
它有效,但现在我不断收到以下消息:

QObject::~QObject: Timers cannot be stopped from another thread


然后我的应用程序立即崩溃。
我知道任何 QObject包含 QTimer在内部,这就是导致问题的原因,但我想不出任何其他方法来解决这个问题。
有人能帮忙吗?
我需要做的就是使用 openGl 应用一些图像处理过滤器或任何其他硬件加速方法,同时有能力在 thread-safe 中做到这一点道路。
  • 我认为可以使用与 QPainter 相同的方法来完成,它是线程安全的,据我所知,它是使用 OpenGL 进行硬件加速的。但我找不到关于如何做这样的事情的任何资源。
  • 最佳答案

    哦,我的,OpenMP 和 OpenGL 可能不能很好地混合。 OpenMP 创建多线程的方式并没有固定在 OpenGL 实现可以访问的方面。它很可能使用常规 POSIX 线程或 Windows native 线程。但它也可以是其他任何东西,在同一地址空间内创建额外的任务。
    我认为创建一个由 N 个线程组成的池会更加健壮和容易,每个线程都有自己的共享 OpenGL 上下文,然后使用工作窃取来安排任务;使 OpenGL 在工作集之间的线程上保持最新状态。
    使用现代 OpenGL 非常方便,甚至不需要窗口或类似的可绘制对象来使上下文成为当前状态。 glXMakeContextCurrentwglMakeContextCurrent将接受 drawables/HDC 的 Nil 参数。因此,您可以对帧缓冲区对象和所有其他内容执行 OpenGL 操作,唯一需要注意的是,没有主要的默认帧缓冲区。
    随意使用这些代码片段来创建帮助器上下文。

    #include "wglu_context.h"
    #include <windows.h>
    #include <GL/gl.h>
    #include <GL/wglext.h>

    #include <stdio.h>
    #include <stddef.h>
    #include <stdint.h>
    #include <assert.h>

    static char const *wgl_create_context_attribs_name = "wglCreateContextAttribsARB";

    static
    int wglu_get_proc_address(char const *name, PROC *pproc)
    {
    int rc= 0;
    *pproc = wglGetProcAddress(name);
    fprintf(stderr, "%s: %p\n", name, (void*)*pproc );
    if( !(*pproc) ){
    rc= GetLastError();
    fprintf(stderr,
    "error wglGetProcAddress('%s'): 0x%x\n",
    name, rc);
    }
    if( rc ){ fprintf(stderr, "%s: %d\n", __func__, rc); }
    return rc;
    }

    /* -----------------------------------------------------------------------
    * Create a OpenGL context of desired version and share it */
    int wglu_create_context_with_sharing(
    int major, int minor, int profile,
    HDC surface,
    HGLRC share_context,
    HGLRC *out_context )
    {
    int rc= 0;
    int attribs[8] = {0};
    size_t i_attrib = 0;
    HGLRC context = 0;

    HDC const save_surface = wglGetCurrentDC();
    HGLRC const save_context = wglGetCurrentContext();
    if( save_surface != surface
    || save_context != share_context
    ){
    wglMakeCurrent(surface, share_context);
    }

    PFNWGLCREATECONTEXTATTRIBSARBPROC wgl_create_context_attribs;
    if( (rc= wglu_get_proc_address(
    wgl_create_context_attribs_name,
    (PROC*)&wgl_create_context_attribs))
    ){
    goto fail;
    }

    if( major ){
    attribs[i_attrib++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
    attribs[i_attrib++] = major;
    }
    if( minor ){
    attribs[i_attrib++] = WGL_CONTEXT_MINOR_VERSION_ARB;
    attribs[i_attrib++] = minor;
    }
    if( profile ){
    attribs[i_attrib++] = WGL_CONTEXT_PROFILE_MASK_ARB;
    attribs[i_attrib++] = profile;
    }
    attribs[i_attrib] = attribs[i_attrib+1] = 0;

    context = wgl_create_context_attribs(surface, *share_context, attribs);
    if( !context ){
    rc= GetLastError();
    fprintf(stderr,
    "error %s(surface=0x%x, share_context=0x%x, attribs=0x%p): %x\n",
    wgl_create_context_attribs_name,
    (uintptr_t)surface, (uintptr_t)share_context,
    attribs,
    rc );
    goto fail;
    }

    if( !(wglMakeCurrent(surface, context)) ){
    rc= GetLastError();
    fprintf(stderr,
    "error %s(surface=0x%x, contest=0x%x): 0x%x\n",
    "wglMakeCurrent",
    (uintptr_t)surface, (uintptr_t)context,
    rc );
    goto fail;
    }
    assert( context == wglGetCurrentContext() );
    fprintf(stderr,
    "GL_VENDOR = %s\n"
    "GL_RENDERER = %s\n"
    "GL_VERSION = %s\n",
    glGetString(GL_VENDOR),
    glGetString(GL_RENDERER),
    glGetString(GL_VERSION) );

    if( !(wglMakeCurrent(NULL, NULL)) ){
    rc= GetLastError();
    fprintf(stderr,
    "error %s(0, 0): 0x%x\n",
    "wglMakeCurrent",
    (uintptr_t)surface, (uintptr_t)context,
    rc );
    goto fail;
    }
    if( !(wglShareLists(context, share_context)) ){
    rc= GetLastError();
    fprintf(stderr,
    "error %s(context=0x%x, share_context=0x%x): 0x%x\n",
    "wglShareLists",
    (uintptr_t)context, (uintptr_t)share_context,
    rc );
    goto fail;
    }

    wglMakeCurrent(save_surface, save_context);

    if( !rc ){
    if( out_context ){ *out_context = context; }
    } else {
    if( context ){ wglDeleteContext(context); }
    }

    if( rc ){ fprintf(stderr, "%s: %d\n", __func__, rc); }
    return rc;
    }
    #include <GL/glxext.h>

    #include <stdio.h>
    #include <stddef.h>
    #include <stdint.h>
    #include <assert.h>
    #include <errno.h>
    #include <dlfcn.h>

    static char const *glX_create_context_attribs_name = "glXCreateContextAttribsARB";

    typedef void (*PROC)();

    static
    int glxu_get_proc_address(char const *name, PROC *pproc)
    {
    int rc= 0;
    static PROC (*glX_get_proc_address)(char const*) = NULL;
    if( !glX_get_proc_address ){
    *(void**)(&glX_get_proc_address) = dlsym(RTLD_DEFAULT, "glXGetProcAddress");
    }
    if( !glX_get_proc_address ){
    rc = -EFAULT;
    } else {
    *pproc = glX_get_proc_address(name);
    fprintf(stderr, "%s: %p\n", name, (void*)*pproc );
    if( !(*pproc) ){
    rc= -ENOENT;
    fprintf(stderr, "error glXGetProcAddress('%s'): so such function\n", name);
    }
    }
    if( rc ){ fprintf(stderr, "%s: %d\n", __func__, rc); }
    return rc;
    }

    static
    int glxu_get_fbconfig_for_xid(
    Display *display,
    GLXFBConfigID const id,
    GLXFBConfig *out_fbconfig )
    {
    static GLXFBConfig* (*glX_get_fb_configs)(Display*,int,int*) = NULL;
    static int (*glX_get_fb_config_attrib)(Display*,GLXFBConfig,int,int*) = NULL;
    if( !glX_get_fb_configs ){
    *(void**)(&glX_get_fb_configs) = dlsym(RTLD_DEFAULT, "glXGetFBConfigs");
    }
    if( !glX_get_fb_config_attrib ){
    *(void**)(&glX_get_fb_config_attrib) = dlsym(RTLD_DEFAULT, "glXGetFBConfigAttrib");
    }

    int rc = 0;
    int n_configs = 0;
    /* we always assume to operate on screen 0, since hardly any X connection
    * encountered these days actually operates on multiple screens. */
    if( !glX_get_fb_configs || !glX_get_fb_config_attrib ){
    rc = -EFAULT;
    } else {
    GLXFBConfig *const fbconfig = glX_get_fb_configs(display, 0, &n_configs);
    for(int i=0; !rc && i < n_configs; ++i){
    unsigned int qry_id;
    rc= glX_get_fb_config_attrib(display, fbconfig[i], GLX_FBCONFIG_ID, &qry_id);
    if( !rc && id == qry_id ){
    *out_fbconfig = fbconfig[i];
    break;
    }
    }
    }
    return rc;
    }

    Display *glxu_get_current_display(void)
    {
    static Display* (*glX_get_current_display)(void) = NULL;
    if( !glX_get_current_display ){
    *(void**)(&glX_get_current_display) = dlsym(RTLD_DEFAULT, "glXGetCurrentDisplay");
    }
    if( !glX_get_current_display ){
    return NULL;
    }
    return glX_get_current_display();
    }

    GLXDrawable glxu_get_current_drawable(void)
    {
    static GLXDrawable (*glX_get_current_drawable)(void) = NULL;
    if( !glX_get_current_drawable ){
    *(void**)(&glX_get_current_drawable) = dlsym(RTLD_DEFAULT, "glXGetCurrentDrawable");
    }
    if( !glX_get_current_drawable ){
    return 0;
    }
    return glX_get_current_drawable();
    }

    /* -----------------------------------------------------------------------
    * Create a OpenGL context of desired version and share it */
    int glxu_create_context_with_sharing(
    int major, int minor, int profile,
    Display *display,
    GLXDrawable surface,
    GLXContext share_context,
    GLXContext *out_context )
    {
    int rc= 0;
    int attribs[8] = {0};
    size_t i_attrib = 0;
    GLXContext context = 0;
    unsigned int fbconfigxid = 0;
    GLXFBConfig fbconfig = 0;

    static GLXContext (*glX_get_current_context)(void) = NULL;
    static void (*glX_query_drawable)(Display*,GLXDrawable,int,unsigned*) = NULL;
    static Bool (*glX_make_current)(Display*,Drawable,GLXContext) = NULL;
    static void (*glX_destroy_context)(Display*,GLXContext) = NULL;
    if( !glX_get_current_context ){
    *(void**)(&glX_get_current_context) = dlsym(RTLD_DEFAULT, "glXGetCurrentContext");
    }
    if( !glX_query_drawable ){
    *(void**)(&glX_query_drawable) = dlsym(RTLD_DEFAULT, "glXQueryDrawable");
    }
    if( !glX_make_current ){
    *(void**)(&glX_make_current) = dlsym(RTLD_DEFAULT, "glXMakeCurrent");
    }
    if( !glX_destroy_context ){
    *(void**)(&glX_destroy_context) = dlsym(RTLD_DEFAULT, "glXDestroyContext");
    }

    if( !glX_get_current_context || !glX_query_drawable
    || !glX_make_current || !glX_destroy_context
    ){
    return -EFAULT;
    }

    Display *const save_display = glxu_get_current_display();
    GLXDrawable const save_surface = glxu_get_current_drawable();
    GLXContext const save_context = glX_get_current_context();

    if( !display ){ display = save_display; }
    if( !surface ){ surface = save_surface; }
    if( !share_context ){ share_context = save_context; }

    if( save_display != display
    || save_surface != surface
    || save_context != share_context
    ){
    fprintf(stderr, ".....\n");
    if( !(glX_make_current(display, surface, share_context)) ){
    rc = -1;
    goto fail;
    }
    }

    PFNGLXCREATECONTEXTATTRIBSARBPROC glX_create_context_attribs;
    if( (rc= glxu_get_proc_address(
    glX_create_context_attribs_name,
    (PROC*)&glX_create_context_attribs))
    ){
    rc = -2;
    goto fail;
    }

    glX_query_drawable(display, surface, GLX_FBCONFIG_ID, &fbconfigxid);
    glxu_get_fbconfig_for_xid(display, fbconfigxid, &fbconfig);

    if( major ){
    attribs[i_attrib++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
    attribs[i_attrib++] = major;
    }
    if( minor ){
    attribs[i_attrib++] = GLX_CONTEXT_MINOR_VERSION_ARB;
    attribs[i_attrib++] = minor;
    }
    if( profile ){
    attribs[i_attrib++] = GLX_CONTEXT_PROFILE_MASK_ARB;
    attribs[i_attrib++] = profile;
    }
    attribs[i_attrib] = attribs[i_attrib+1] = 0;

    context = glX_create_context_attribs(display, fbconfig, share_context, True, attribs);
    if( !context ){
    rc= -3;
    fprintf(stderr,
    "error %s(surface=0x%p, share_context=0x%p, attribs=0x%p): %x\n",
    glX_create_context_attribs_name,
    (void*)surface, (void*)share_context,
    attribs,
    rc );
    goto fail;
    }

    if( !(glX_make_current(display, surface, context)) ){
    rc= -4;
    fprintf(stderr,
    "error %s(surface=0x%p, contest=0x%p): 0x%x\n",
    "wglMakeCurrent",
    (void*)surface, (void*)context,
    rc );
    goto fail;
    }
    assert( context == glX_get_current_context() );
    fprintf(stderr,
    "GL_VENDOR = %s\n"
    "GL_RENDERER = %s\n"
    "GL_VERSION = %s\n",
    glGetString(GL_VENDOR),
    glGetString(GL_RENDERER),
    glGetString(GL_VERSION) );

    fail:
    glX_make_current(display, save_surface, save_context);

    if( !rc ){
    if( out_context ){ *out_context = context; }
    } else {
    if( context ){ glX_destroy_context(display, context); }
    }

    if( rc ){ fprintf(stderr, "%s: %d\n", __func__, rc); }
    return rc;
    }

    Bool glxu_make_context_current(
    Display* display,
    GLXDrawable read, GLXDrawable draw,
    GLXContext ctx )
    {
    static Bool (*glX_make_context_current)(Display*,Drawable,Drawable,GLXContext) = NULL;
    if( !glX_make_context_current ){
    *(void**)(&glX_make_context_current) = dlsym(RTLD_DEFAULT, "glXMakeContextCurrent");
    }
    if( !glX_make_context_current ){
    return False;
    }
    return glX_make_context_current(display, read, draw, ctx);
    }

    关于c++ - 我如何创建一个线程安全的 QOffscreenSurface 供 OpenGl 使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65560109/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com