Make threaded present optional

This commit is contained in:
Brad Davis 2016-02-08 14:10:51 -08:00
parent 63afa8c1ed
commit 390df78327
2 changed files with 73 additions and 22 deletions

View file

@ -21,11 +21,13 @@
#include <gl/GLWidget.h> #include <gl/GLWidget.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <plugins/PluginContainer.h> #include <plugins/PluginContainer.h>
#include <gl/Config.h> #include <gl/Config.h>
#include <gl/GLEscrow.h> #include <gl/GLEscrow.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#if THREADED_PRESENT
class PresentThread : public QThread, public Dependency { class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex; using Mutex = std::mutex;
using Condition = std::condition_variable; using Condition = std::condition_variable;
@ -33,16 +35,25 @@ class PresentThread : public QThread, public Dependency {
public: public:
PresentThread() { PresentThread() {
connect(qApp, &QCoreApplication::aboutToQuit, [this]{ connect(qApp, &QCoreApplication::aboutToQuit, [this] {
_shutdown = true; shutdown();
}); });
} }
~PresentThread() { ~PresentThread() {
_shutdown = true; shutdown();
wait();
} }
void shutdown() {
if (isRunning()) {
Lock lock(_mutex);
_shutdown = true;
_condition.wait(lock, [&] { return !_shutdown; });
qDebug() << "Present thread shutdown";
}
}
void setNewDisplayPlugin(OpenGLDisplayPlugin* plugin) { void setNewDisplayPlugin(OpenGLDisplayPlugin* plugin) {
Lock lock(_mutex); Lock lock(_mutex);
_newPlugin = plugin; _newPlugin = plugin;
@ -120,6 +131,10 @@ public:
} }
_context->doneCurrent(); _context->doneCurrent();
_context->moveToThread(qApp->thread()); _context->moveToThread(qApp->thread());
Lock lock(_mutex);
_shutdown = false;
_condition.notify_one();
} }
void withMainThreadContext(std::function<void()> f) { void withMainThreadContext(std::function<void()> f) {
@ -159,16 +174,14 @@ private:
QGLContext* _context { nullptr }; QGLContext* _context { nullptr };
}; };
#endif
OpenGLDisplayPlugin::OpenGLDisplayPlugin() { OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
_sceneTextureEscrow.setRecycler([this](GLuint texture){ _sceneTextureEscrow.setRecycler([this](GLuint texture){
cleanupForSceneTexture(texture); cleanupForSceneTexture(texture);
_container->releaseSceneTexture(texture); _container->releaseSceneTexture(texture);
}); });
_overlayTextureEscrow.setRecycler([this](GLuint texture) {
_container->releaseOverlayTexture(texture);
});
connect(&_timer, &QTimer::timeout, this, [&] { connect(&_timer, &QTimer::timeout, this, [&] {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
// On Mac, QT thread timing is such that we can miss one or even two cycles quite often, giving a render rate (including update/simulate) // On Mac, QT thread timing is such that we can miss one or even two cycles quite often, giving a render rate (including update/simulate)
@ -191,9 +204,10 @@ void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) {
void OpenGLDisplayPlugin::activate() { void OpenGLDisplayPlugin::activate() {
_timer.start(1);
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported(); _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
#if THREADED_PRESENT
_timer.start(1);
// Start the present thread if necessary // Start the present thread if necessary
auto presentThread = DependencyManager::get<PresentThread>(); auto presentThread = DependencyManager::get<PresentThread>();
if (!presentThread) { if (!presentThread) {
@ -208,7 +222,15 @@ void OpenGLDisplayPlugin::activate() {
presentThread->start(); presentThread->start();
} }
presentThread->setNewDisplayPlugin(this); presentThread->setNewDisplayPlugin(this);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
customizeContext();
_container->makeRenderingContextCurrent();
#endif
DisplayPlugin::activate(); DisplayPlugin::activate();
} }
void OpenGLDisplayPlugin::stop() { void OpenGLDisplayPlugin::stop() {
@ -216,19 +238,27 @@ void OpenGLDisplayPlugin::stop() {
} }
void OpenGLDisplayPlugin::deactivate() { void OpenGLDisplayPlugin::deactivate() {
#if THREADED_PRESENT
{ {
Lock lock(_mutex); Lock lock(_mutex);
_deactivateWait.wait(lock, [&]{ return _uncustomized; }); _deactivateWait.wait(lock, [&]{ return _uncustomized; });
} }
_timer.stop(); _timer.stop();
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
uncustomizeContext();
_container->makeRenderingContextCurrent();
#endif
DisplayPlugin::deactivate(); DisplayPlugin::deactivate();
} }
void OpenGLDisplayPlugin::customizeContext() { void OpenGLDisplayPlugin::customizeContext() {
#if THREADED_PRESENT
_uncustomized = false; _uncustomized = false;
auto presentThread = DependencyManager::get<PresentThread>(); auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread()); Q_ASSERT(thread() == presentThread->thread());
#endif
enableVsync(); enableVsync();
using namespace oglplus; using namespace oglplus;
@ -297,16 +327,23 @@ void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t scene
// Submit it to the presentation thread via escrow // Submit it to the presentation thread via escrow
_sceneTextureEscrow.submit(sceneTexture); _sceneTextureEscrow.submit(sceneTexture);
#if THREADED_PRESENT
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
present();
_container->makeRenderingContextCurrent();
#endif
} }
void OpenGLDisplayPlugin::submitOverlayTexture(GLuint sceneTexture, const glm::uvec2& sceneSize) { void OpenGLDisplayPlugin::submitOverlayTexture(GLuint overlayTexture, const glm::uvec2& overlaySize) {
// Submit it to the presentation thread via escrow // Submit it to the presentation thread via escrow
_overlayTextureEscrow.submit(sceneTexture); _currentOverlayTexture = overlayTexture;
} }
void OpenGLDisplayPlugin::updateTextures() { void OpenGLDisplayPlugin::updateTextures() {
_currentSceneTexture = _sceneTextureEscrow.fetchAndRelease(_currentSceneTexture); _currentSceneTexture = _sceneTextureEscrow.fetchAndRelease(_currentSceneTexture);
_currentOverlayTexture = _overlayTextureEscrow.fetchAndRelease(_currentOverlayTexture);
} }
void OpenGLDisplayPlugin::updateFramerate() { void OpenGLDisplayPlugin::updateFramerate() {
@ -337,6 +374,11 @@ void OpenGLDisplayPlugin::present() {
internalPresent(); internalPresent();
updateFramerate(); updateFramerate();
} }
#if THREADED_PRESENT
#else
emit requestRender();
#endif
} }
float OpenGLDisplayPlugin::presentRate() { float OpenGLDisplayPlugin::presentRate() {
@ -351,12 +393,8 @@ float OpenGLDisplayPlugin::presentRate() {
} }
void OpenGLDisplayPlugin::drawUnitQuad() { void OpenGLDisplayPlugin::drawUnitQuad() {
try { _program->Bind();
_program->Bind(); _plane->Draw();
_plane->Draw();
} catch (const oglplus::Error& error) {
qWarning() << "The present thread encountered an error writing the scene texture to the output: " << error.what();
}
} }
void OpenGLDisplayPlugin::enableVsync(bool enable) { void OpenGLDisplayPlugin::enableVsync(bool enable) {
@ -385,9 +423,16 @@ void OpenGLDisplayPlugin::swapBuffers() {
} }
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const { void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
#if THREADED_PRESENT
static auto presentThread = DependencyManager::get<PresentThread>(); static auto presentThread = DependencyManager::get<PresentThread>();
presentThread->withMainThreadContext(f); presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent(); _container->makeRenderingContextCurrent();
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
f();
_container->makeRenderingContextCurrent();
#endif
} }
QImage OpenGLDisplayPlugin::getScreenshot() const { QImage OpenGLDisplayPlugin::getScreenshot() const {
@ -399,8 +444,10 @@ QImage OpenGLDisplayPlugin::getScreenshot() const {
return result; return result;
} }
#if THREADED_PRESENT
void OpenGLDisplayPlugin::enableDeactivate() { void OpenGLDisplayPlugin::enableDeactivate() {
Lock lock(_mutex); Lock lock(_mutex);
_uncustomized = true; _uncustomized = true;
_deactivateWait.notify_one(); _deactivateWait.notify_one();
} }
#endif

View file

@ -18,6 +18,8 @@
#include <gl/OglplusHelpers.h> #include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h> #include <gl/GLEscrow.h>
#define THREADED_PRESENT 1
class OpenGLDisplayPlugin : public DisplayPlugin { class OpenGLDisplayPlugin : public DisplayPlugin {
protected: protected:
using Mutex = std::mutex; using Mutex = std::mutex;
@ -45,8 +47,9 @@ public:
virtual QImage getScreenshot() const override; virtual QImage getScreenshot() const override;
protected: protected:
#if THREADED_PRESENT
friend class PresentThread; friend class PresentThread;
#endif
virtual glm::uvec2 getSurfaceSize() const = 0; virtual glm::uvec2 getSurfaceSize() const = 0;
virtual glm::uvec2 getSurfacePixels() const = 0; virtual glm::uvec2 getSurfacePixels() const = 0;
@ -81,15 +84,16 @@ protected:
GLuint _currentSceneTexture { 0 }; GLuint _currentSceneTexture { 0 };
GLuint _currentOverlayTexture { 0 }; GLuint _currentOverlayTexture { 0 };
GLTextureEscrow _overlayTextureEscrow;
GLTextureEscrow _sceneTextureEscrow; GLTextureEscrow _sceneTextureEscrow;
bool _vsyncSupported { false }; bool _vsyncSupported { false };
private: private:
#if THREADED_PRESENT
void enableDeactivate(); void enableDeactivate();
Condition _deactivateWait; Condition _deactivateWait;
bool _uncustomized{ false }; bool _uncustomized{ false };
#endif
}; };