diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0eb36173c2..5a340f471e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2338,6 +2338,7 @@ void Application::initializeGL() { qFatal("Unable to make offscreen context current"); } _offscreenContext->doneCurrent(); + _offscreenContext->setThreadContext(); _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); // The UI can't be created until the primary OpenGL diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 40dcf1b8c7..9bd7d89634 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -130,14 +131,14 @@ public: CHECK_GL_ERROR(); _context->doneCurrent(); while (!_shutdown) { - if (_pendingMainThreadOperation) { + if (_pendingOtherThreadOperation) { PROFILE_RANGE(render, "MainThreadOp") { Lock lock(_mutex); _context->doneCurrent(); // Move the context to the main thread - _context->moveToThread(qApp->thread()); - _pendingMainThreadOperation = false; + _context->moveToThread(_targetOperationThread); + _pendingOtherThreadOperation = false; // Release the main thread to do it's action _condition.notify_one(); } @@ -146,7 +147,7 @@ public: { // Main thread does it's thing while we wait on the lock to release Lock lock(_mutex); - _condition.wait(lock, [&] { return _finishedMainThreadOperation; }); + _condition.wait(lock, [&] { return _finishedOtherThreadOperation; }); } } @@ -214,23 +215,25 @@ public: _condition.notify_one(); } - void withMainThreadContext(std::function f) { + void withOtherThreadContext(std::function f) { // Signal to the thread that there is work to be done on the main thread Lock lock(_mutex); - _pendingMainThreadOperation = true; - _finishedMainThreadOperation = false; - _condition.wait(lock, [&] { return !_pendingMainThreadOperation; }); + _targetOperationThread = QThread::currentThread(); + _pendingOtherThreadOperation = true; + _finishedOtherThreadOperation = false; + _condition.wait(lock, [&] { return !_pendingOtherThreadOperation; }); _context->makeCurrent(); f(); _context->doneCurrent(); + _targetOperationThread = nullptr; // Move the context back to the presentation thread _context->moveToThread(this); // restore control of the context to the presentation thread and signal // the end of the operation - _finishedMainThreadOperation = true; + _finishedOtherThreadOperation = true; lock.unlock(); _condition.notify_one(); } @@ -244,9 +247,11 @@ private: Mutex _mutex; // Used to allow the main thread to perform context operations Condition _condition; - bool _pendingMainThreadOperation { false }; - bool _finishedMainThreadOperation { false }; - QThread* _mainThread { nullptr }; + + + QThread* _targetOperationThread { nullptr }; + bool _pendingOtherThreadOperation { false }; + bool _finishedOtherThreadOperation { false }; std::queue _newPluginQueue; gl::Context* _context { nullptr }; }; @@ -744,10 +749,12 @@ void OpenGLDisplayPlugin::swapBuffers() { context->swapBuffers(); } -void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { +void OpenGLDisplayPlugin::withOtherThreadContext(std::function f) const { static auto presentThread = DependencyManager::get(); - presentThread->withMainThreadContext(f); - _container->makeRenderingContextCurrent(); + presentThread->withOtherThreadContext(f); + if (!OffscreenGLCanvas::restoreThreadContext()) { + qWarning("Unable to restore original OpenGL context"); + } } bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { @@ -784,7 +791,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { } auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); - withMainThreadContext([&] { + withOtherThreadContext([&] { glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); }); return screenshot.mirrored(false, true); @@ -797,7 +804,7 @@ QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() const { auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(region.z, region.w, QImage::Format_ARGB32); - withMainThreadContext([&] { + withOtherThreadContext([&] { glBackend->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot); }); return screenshot.mirrored(false, true); @@ -886,7 +893,7 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { #if !defined(USE_GLES) auto glBackend = const_cast(*this).getGLBackend(); - withMainThreadContext([&] { + withOtherThreadContext([&] { GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture()); GLuint targetTexture = target->texture(); GLuint fbo[2] {0, 0}; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 1176471b40..bf06486095 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -119,7 +119,7 @@ protected: void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor); virtual void updateFrameData(); - void withMainThreadContext(std::function f) const; + void withOtherThreadContext(std::function f) const; void present(); virtual void swapBuffers(); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 380ba085cc..1bde9e289e 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -119,3 +120,29 @@ void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) { moveToThread(thread); _context->moveToThread(thread); } + +static const char* THREAD_CONTEXT_PROPERTY = "offscreenGlCanvas"; + +void OffscreenGLCanvas::setThreadContext() { + QThread::currentThread()->setProperty(THREAD_CONTEXT_PROPERTY, QVariant::fromValue(this)); +} + +bool OffscreenGLCanvas::restoreThreadContext() { + // Restore the rendering context for this thread + auto threadCanvasVariant = QThread::currentThread()->property(THREAD_CONTEXT_PROPERTY); + if (!threadCanvasVariant.isValid()) { + return false; + } + + auto threadCanvasObject = qvariant_cast(threadCanvasVariant); + auto threadCanvas = static_cast(threadCanvasObject); + if (!threadCanvas) { + return false; + } + + if (!threadCanvas->makeCurrent()) { + qFatal("Unable to restore Offscreen rendering context"); + } + + return true; +} diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index be0e0d9678..ed644b98fb 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -32,6 +32,9 @@ public: } QObject* getContextObject(); + void setThreadContext(); + static bool restoreThreadContext(); + private slots: void onMessageLogged(const QOpenGLDebugMessage &debugMessage); diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp index cce1b68da9..6b66ce9314 100644 --- a/libraries/qml/src/qml/impl/RenderEventHandler.cpp +++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp @@ -58,6 +58,7 @@ void RenderEventHandler::onInitalize() { return; } + _canvas.setThreadContext(); if (!_canvas.makeCurrent()) { qFatal("Unable to make QML rendering context current on render thread"); } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 4b5f0e6517..2949e72c74 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -485,7 +485,7 @@ bool OpenVrDisplayPlugin::internalActivate() { if (_threadedSubmit) { _submitThread = std::make_shared(*this); if (!_submitCanvas) { - withMainThreadContext([&] { + withOtherThreadContext([&] { _submitCanvas = std::make_shared(); _submitCanvas->create(); _submitCanvas->doneCurrent();