问题描述
我想解决我仍然要解决的问题.那就是使用共享的着色器程序等在不同的顶层窗口中同时渲染2个QOpenGLWidgets.
I would like to solve problem that I still deal with.. thats render 2 QOpenGLWidgets at same time in different top level windows with shared shader programs etc.
为什么我在这里而不是Qt论坛上发布它?我已经做过,但是没有人回应:/
Why do I post it here and not on Qt forums? I already did, but no one response :/
我的第一个尝试是使用一个上下文,没有用.
My first attempt was to use one context, wasnt working.
问题:QOpenGLWidget当前是否有可能?还是我必须去旧的QGLWidget?还是使用其他东西?
The question: Is it even possible currently with QOpenGLWidget? Or I have to go to older QGLWidget? Or use something else?
testAttribute返回true,因此共享没有问题甚至QOpenGLContext :: areSharing也返回true.所以有一些我想念的东西或我不知道的东西.不使用线程.
testAttribute for Qt::AA_ShareOpenGLContexts returns true so there is not problem with sharingeven QOpenGLContext::areSharing returns true. So there is something I missing or I dont know. Not using threads.
如果您还需要其他任何东西来调试此问题,请告诉我.
If you need anything more to debug this problem tell me.
调试输出:
MapView initializeGL:
MapView initializeGL:
void MapView::initializeGL()
{
this->makeCurrent();
initializeOpenGLFunctions();
// Initialize World
world->initialize(this->context(), size(), worldCoords);
// Initialize camera shader
camera->initialize();
// Enable depth testing
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDepthFunc(GL_LEQUAL); // just testing new depth func
glClearColor(0.65f, 0.77f, 1.0f, 1.0f);
}
MapView paintGL:
MapView paintGL:
void MapView::paintGL()
{
this->makeCurrent();
glDrawBuffer(GL_FRONT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
world->draw(...);
}
MapExplorer initializeGL:
MapExplorer initializeGL:
void MapExplorer::initializeGL()
{
this->makeCurrent();
QOpenGLContext* _context = _mapView->context();
_context->setShareContext(this->context());
_context->create();
this->context()->create();
this->makeCurrent();
initializeOpenGLFunctions();
// Enable depth testing
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDepthFunc(GL_LEQUAL); // just testing new depth func
glClearColor(0.65f, 0.77f, 1.0f, 1.0f);
}
MapExplorer paintGL:
MapExplorer paintGL:
void MapExplorer::paintGL()
{
this->makeCurrent();
qDebug() << "MapExplorer" << QOpenGLContext::areSharing(this->context(), _mapView->context()) << (QOpenGLContext::currentContext() == this->context());
QOpenGLShaderProgram* shader = world->getTerrainShader();
qDebug() << shader->create();
shader->bind(); // debug error "QOpenGLShaderProgram::bind: program is not valid in the current context."
// We need the viewport size to calculate tessellation levels and the geometry shader also needs the viewport matrix
shader->setUniformValue("viewportSize", viewportSize);
shader->setUniformValue("viewportMatrix", viewportMatrix);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
qDebug() << "MapExlorer paintGL ends";
//world->drawExplorerView(...);
}
谢谢.
关于,格拉兰.
推荐答案
我已经破解了2天,终于有了一些工作.
Hi I've been hacking this for 2 days, and finally got something to work.
主要参考文献是 qt的threadrenderer示例.
基本上,我有一个 QOpenglWidget 及其自己的上下文,还有一个后台线程从 QOpenglWidget 绘制到共享的上下文强>. QOpenglWidget 可以直接使用由后台线程绘制的帧缓冲区.
Basically I have a QOpenglWidget with its own context, and a background thread drawing to the context shared from QOpenglWidget. The framebuffer drawn by the background thread can be directly used by the QOpenglWidget.
以下是使事情正常进行的步骤:
Here's the steps to make things work:
-
我有 QOpenglWidget
RenderEngine
和后台线程RenderWorker
// the worker is a thread
class RenderWorker : public QThread, protected QOpenGLFunctions
{
// the background thread's context and surface
QOffscreenSurface *surface = nullptr;
QOpenGLContext *context = nullptr;
RenderWorker::RenderWorker()
{
context = new QOpenGLContext();
surface = new QOffscreenSurface();
}
...
}
// the engine is a QOpenglWidget
class RenderEngine : public QOpenGLWidget, protected QOpenGLFunctions
{
protected:
// overwrite
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
private:
// the engine has a background worker
RenderWorker *m_worker = nullptr;
...
}
在 QOpenglWidget 的initializeGL()
void RenderEngine::initializeGL()
{
initializeOpenGLFunctions();
// done with current (QOpenglWidget's) context
QOpenGLContext *current = context();
doneCurrent();
// create the background thread
m_worker = new RenderWorker();
// the background thread's context is shared from current
QOpenGLContext *shared = m_worker->context;
shared->setFormat(current->format());
shared->setShareContext(current);
shared->create();
// must move the shared context to the background thread
shared->moveToThread(m_worker);
// setup the background thread's surface
// must be created here in the main thread
QOffscreenSurface *surface = m_worker->surface;
surface->setFormat(shared->format());
surface->create();
// worker signal
connect(m_worker, SIGNAL(started()), m_worker, SLOT(initializeGL()));
// must move the thread to itself
m_worker->moveToThread(m_worker);
// the worker can finally start
m_worker->start();
}
后台线程必须在其自己的线程中初始化共享上下文
The background thread have to initialize the shared context in its own thread
void RenderWorker::initializeGL()
{
context->makeCurrent(surface);
initializeOpenGLFunctions();
}
现在, QOpenglWidget 可以直接使用(在后台线程中绘制的任何帧缓冲区)(作为纹理等),例如在paintGL()
函数中.
Now any framebuffer drawn in the background thread can be directly used (as texture, etc.) by the QOpenglWidget, e.g. in the paintGL()
function.
据我所知,opengl上下文是绑定到线程的一种.必须先在主线程中创建共享上下文和相应的表面,并将其设置为移动,然后在其中进行初始化.终于用了.
As far as I know, an opengl context is kind of binded to a thread. Shared context and the corresponding surface must be created and setup in the main thread, moved to another thread and initialized in it, before it can be finally used.
这篇关于2 QOpenGLWidget共享上下文导致崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!