销毁QOffscreenSurface子类时程序崩溃的原因排查
问题:Qt离屏渲染析构时删除QOpenGLContext触发断言错误
我使用QOffscreenSurface和QOpenGLContext实现纹理离屏渲染,调用OffscreenRender::paint()后可以在纹理tbo中心渲染矩形,但在析构函数中调用delete openGLContext;时,Qt会触发断言错误:ASSERT: "d_1_0_Core->refs.load()" in file opengl\qopenglfunctions_4_3_core.cpp, line 93。移除该delete语句后程序运行正常,求原因。
相关代码
OffscreenRender.h
class OffscreenRender:public QOffscreenSurface,protected QOpenGLFunctions_4_3_Core { Q_OBJECT public: explicit OffscreenRender(QObject *parent = 0); virtual ~OffscreenRender(); void paint(); GLuint* getTBO(); QOpenGLContext *getCurrentContext(){return openGLContext;} private: void initialGL(); private: bool glInitialized = false; QOpenGLContext *openGLContext = nullptr; GLuint proGen = 0, vaoGen = 0, vboGen = 0, tbo = 0, fbo = 0; };
OffscreenRender.cpp
OffscreenRender::OffscreenRender(QObject *parent) { setParent(parent); QSurfaceFormat surfaceFormat; surfaceFormat.setMajorVersion(4); surfaceFormat.setMinorVersion(3); surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); openGLContext = new QOpenGLContext(); openGLContext->setFormat(surfaceFormat); QOpenGLContext *share = QOpenGLContext::globalShareContext(); openGLContext->setShareContext(share); if(openGLContext->create()){ setFormat(surfaceFormat); create(); if(!isValid()) qDebug("Unable to create the Offscreen surface"); initialGL(); } else{ qDebug("Unable to create the context"); } } OffscreenRender::~OffscreenRender(){ openGLContext->makeCurrent(this); if(glIsProgram(proGen)){ glDeleteProgram(proGen); } if(glIsVertexArray(vaoGen)){ glDeleteVertexArrays(1,&vaoGen); } if(glIsBuffer(vboGen)){ glDeleteBuffers(1,&vboGen); } if(glIsTexture(tbo)){ glDeleteTextures(1,&tbo); } if(glIsFramebuffer(fbo)){ glDeleteFramebuffers(1,&fbo); } openGLContext->doneCurrent(); if(openGLContext){ delete openGLContext; } } void OffscreenRender::paint(){ if(glInitialized){ openGLContext->makeCurrent(this); glViewport(0, 0, 100, 100); glClearColor(1,0,1,1); glUseProgram(proGen); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glDrawBuffer(GL_COLOR_ATTACHMENT0); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(vaoGen); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); openGLContext->doneCurrent(); } } GLuint *OffscreenRender::getTBO(){return &tbo;} void OffscreenRender::initialGL(){ openGLContext->makeCurrent(this); if(!initializeOpenGLFunctions()){ qDebug("Unable to initialize the functions"); return; } proGen = loadShaders(); glGenVertexArrays(1, &vaoGen); glBindVertexArray(vaoGen); glGenBuffers(1, &vboGen); glBindBuffer(GL_ARRAY_BUFFER,vboGen); int nBytes = sizeof(float) * 16; glBufferData(GL_ARRAY_BUFFER,nBytes,vertexCord,GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, NULL); glEnableVertexAttribArray(0); glActiveTexture(GL_TEXTURE0); glGenTextures(1, &tbo); glBindTexture(GL_TEXTURE_2D, tbo); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 100, 100); glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tbo, 0); openGLContext->doneCurrent(); glInitialized = true; }
原因分析与解决方法
核心原因
你的OffscreenRender类直接继承了QOpenGLFunctions_4_3_Core,这个类的内部实现会与当前绑定的QOpenGLContext建立关联并维护引用计数。当你在析构函数中先调用openGLContext->doneCurrent()再删除上下文时,QOpenGLFunctions_4_3_Core的实例(即你的OffscreenRender对象)还未销毁,其内部资源仍持有对已销毁上下文的引用,导致引用计数不匹配,触发断言。
解决方法
最稳妥的方式是用组合替代继承,避免QOpenGLFunctions_4_3_Core的生命周期与类本身强绑定,手动控制其销毁顺序:
- 修改头文件,将继承改为组合:
class OffscreenRender:public QOffscreenSurface { Q_OBJECT public: explicit OffscreenRender(QObject *parent = 0); virtual ~OffscreenRender(); void paint(); GLuint* getTBO(); QOpenGLContext *getCurrentContext(){return openGLContext;} private: void initialGL(); private: bool glInitialized = false; QOpenGLContext *openGLContext = nullptr; QOpenGLFunctions_4_3_Core *glFuncs = nullptr; // 改为组合方式 GLuint proGen = 0, vaoGen = 0, vboGen = 0, tbo = 0, fbo = 0; };
- 初始化GL函数对象,所有GL调用改为通过该对象调用:
void OffscreenRender::initialGL(){ openGLContext->makeCurrent(this); glFuncs = new QOpenGLFunctions_4_3_Core(); if(!glFuncs->initializeOpenGLFunctions()){ qDebug("Unable to initialize the functions"); delete glFuncs; glFuncs = nullptr; return; } proGen = loadShaders(); glFuncs->glGenVertexArrays(1, &vaoGen); glFuncs->glBindVertexArray(vaoGen); glFuncs->glGenBuffers(1, &vboGen); glFuncs->glBindBuffer(GL_ARRAY_BUFFER,vboGen); int nBytes = sizeof(float) * 16; glFuncs->glBufferData(GL_ARRAY_BUFFER,nBytes,vertexCord,GL_STATIC_DRAW); glFuncs->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, NULL); glFuncs->glEnableVertexAttribArray(0); glFuncs->glActiveTexture(GL_TEXTURE0); glFuncs->glGenTextures(1, &tbo); glFuncs->glBindTexture(GL_TEXTURE_2D, tbo); glFuncs->glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 100, 100); glFuncs->glGenFramebuffers(1, &fbo); glFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glFuncs->glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tbo, 0); openGLContext->doneCurrent(); glInitialized = true; }
- 调整析构函数顺序,先销毁GL函数对象,再删除上下文:
OffscreenRender::~OffscreenRender(){ if(glFuncs){ openGLContext->makeCurrent(this); if(glFuncs->glIsProgram(proGen)){ glFuncs->glDeleteProgram(proGen); } if(glFuncs->glIsVertexArray(vaoGen)){ glFuncs->glDeleteVertexArrays(1,&vaoGen); } if(glFuncs->glIsBuffer(vboGen)){ glFuncs->glDeleteBuffers(1,&vboGen); } if(glFuncs->glIsTexture(tbo)){ glFuncs->glDeleteTextures(1,&tbo); } if(glFuncs->glIsFramebuffer(fbo)){ glFuncs->glDeleteFramebuffers(1,&fbo); } delete glFuncs; glFuncs = nullptr; } if(openGLContext){ openGLContext->doneCurrent(); delete openGLContext; openGLContext = nullptr; } }
这样修改后,GL函数对象会先于上下文销毁,解除对上下文的引用,就能安全删除openGLContext而不触发断言。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

