A worker thread with OpenGL support, am I doing it right?
Ik work on an application where it's possible to load very large CAD files, loading can take quite some time, so to keep the UI responsive loading is done in a workerthread.
The workerthread requires an active OpenGL context as well, so it can upload vertexbuffers to the GPU. I came up with the following solution.
- in main thread first make current context not current
- store pointer to surface
- create Worker Object and pass pointers to surface and context
- change thread affinity of context (moveToThread)
- start workerthread
- make context current in workerthread
- do work
- when work is finished call doneCurrent (make not current)
- restore thread affinity
- make context active again in main thread
It requires some hassle but it works, I am wondering how other applications deal with this problem?
Code:
/// <summary>Starts a workerthread and handles its progress events</summary>
int ProgressHandler::handleWork(boost::function<int(void)>& functor, bool requiresRenderContext/* = false*/)
{
// careful only call this method from the main thread
//ASSERT(GetCurrentThreadId() == theApp.GetThreadId());
QOpenGLContext* context = nullptr;
QSurface* surface = nullptr;
// make current OpenGL context not current if required, this is needed to be able to make it current in the workerthread
if (requiresRenderContext)
{
context = QOpenGLContext::currentContext();
surface = context->surface();
context->doneCurrent();
}
// create a Worker object
int result = 0;
deskproto::Worker work(functor, result, context, surface);
context->moveToThread(&work);
connect(&work, &QThread::finished, this, &ProgressHandler::onWorkerFinished);
// start workerthread
work.start();
// enter modal loop
m_progressDialog.exec();
// wait until thread finishes
work.wait();
// make RenderContext current again for main thread
if (requiresRenderContext)
{
bool result = context->makeCurrent(surface);
assert(result);
}
// re-throw any uncaught exception in main thread
work.rethrow();
return result;
}
Code:
/// <summary>
/// SwapContext allows to make an OpenGL context active for a different thread without changing the function
/// e.g. used for loading 3D geometry, plotdata and bitmapdata in a workerthread (functions which require an active OpenGL context)
/// </summary>
void Worker::run()
{
#ifdef _DEBUG
util::SetThreadName("Worker");
#endif
// Make context active for current thread, only works if previous context is made not current
if (m_context)
{
m_context->makeCurrent(m_surface);
}
try
{
// Call the functor object
m_result = m_functor();
}
catch(...)
{
// catch any uncaught exceptions and re-trow them on main thread
Worker::m_exception = std::current_exception();
}
// Make context not current(restore)
if (m_context)
{
m_context->doneCurrent();
m_context
->moveToThread
(QApplication::instance()->thread
());
}
}
Re: A worker thread with OpenGL support, am I doing it right?
Why not load all the data in a worker thread and then push it to the GPU in the main thread?
Re: A worker thread with OpenGL support, am I doing it right?
That is to keep the GUI thread responsive, uploading the vertex buffers to the GPU might take 5 - 6 seconds as wel (depending on your machine).
Re: A worker thread with OpenGL support, am I doing it right?
Then I don't see how you can improve what you already have :)