diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0ad27e0dd3..24dcf4ce72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2425,6 +2425,7 @@ void Application::cleanupBeforeQuit() { } _window->saveGeometry(); + // _gpuContext->shutdown(); // Destroy third party processes after scripts have finished using them. @@ -4178,41 +4179,37 @@ bool Application::acceptSnapshot(const QString& urlString) { } return true; } - -static uint32_t _renderedFrameIndex { INVALID_FRAME }; - -bool Application::shouldPaint() const { - if (_aboutToQuit || _window->isMinimized()) { - return false; - } - - - auto displayPlugin = getActiveDisplayPlugin(); - -#ifdef DEBUG_PAINT_DELAY - static uint64_t paintDelaySamples{ 0 }; - static uint64_t paintDelayUsecs{ 0 }; - - paintDelayUsecs += displayPlugin->getPaintDelayUsecs(); - - static const int PAINT_DELAY_THROTTLE = 1000; - if (++paintDelaySamples % PAINT_DELAY_THROTTLE == 0) { - qCDebug(interfaceapp).nospace() << - "Paint delay (" << paintDelaySamples << " samples): " << - (float)paintDelaySamples / paintDelayUsecs << "us"; - } -#endif - - // Throttle if requested - //if (displayPlugin->isThrottled() && (_graphicsEngine._renderEventHandler->_lastTimeRendered.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { - if (displayPlugin->isThrottled() && !_graphicsEngine.shouldPaint()) { - return false; - } - - // Sync up the _renderedFrameIndex - _renderedFrameIndex = displayPlugin->presentCount(); - return true; -} +// +//bool Application::shouldPaint() const { +// if (_aboutToQuit || _window->isMinimized()) { +// return false; +// } +// +// +// auto displayPlugin = getActiveDisplayPlugin(); +// +//#ifdef DEBUG_PAINT_DELAY +// static uint64_t paintDelaySamples{ 0 }; +// static uint64_t paintDelayUsecs{ 0 }; +// +// paintDelayUsecs += displayPlugin->getPaintDelayUsecs(); +// +// static const int PAINT_DELAY_THROTTLE = 1000; +// if (++paintDelaySamples % PAINT_DELAY_THROTTLE == 0) { +// qCDebug(interfaceapp).nospace() << +// "Paint delay (" << paintDelaySamples << " samples): " << +// (float)paintDelaySamples / paintDelayUsecs << "us"; +// } +//#endif +// +// // Throttle if requested +// //if (displayPlugin->isThrottled() && (_graphicsEngine._renderEventHandler->_lastTimeRendered.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { +// if (displayPlugin->isThrottled() && !_graphicsEngine.shouldPaint()) { +// return false; +// } +// +// return true; +//} #ifdef Q_OS_WIN #include diff --git a/interface/src/Application.h b/interface/src/Application.h index fb8edf3b86..6d57ca9d8d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -157,7 +157,7 @@ public: void updateSecondaryCameraViewFrustum(); void updateCamera(RenderArgs& renderArgs, float deltaTime); - bool shouldPaint() const; + // bool shouldPaint() const; // void paintGL(); void resizeGL(); diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index 21c9a9b22d..d115d8914c 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -53,6 +53,7 @@ void GraphicsEngine::initializeGPU(GLWidget* glwidget) { _renderEventHandler = new RenderEventHandler( glwidget->qglContext(), + [this]() { return this->shouldPaint(); }, [this]() { this->render_performFrame(); } ); @@ -147,12 +148,37 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI bool GraphicsEngine::shouldPaint() const { - // Throttle if requested - if ((static_cast(_renderEventHandler)->_lastTimeRendered.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { - return false; - } - - return true; + +// if (_aboutToQuit || _window->isMinimized()) { +// return false; + // } + + + auto displayPlugin = qApp->getActiveDisplayPlugin(); + +#ifdef DEBUG_PAINT_DELAY + static uint64_t paintDelaySamples{ 0 }; + static uint64_t paintDelayUsecs{ 0 }; + + paintDelayUsecs += displayPlugin->getPaintDelayUsecs(); + + static const int PAINT_DELAY_THROTTLE = 1000; + if (++paintDelaySamples % PAINT_DELAY_THROTTLE == 0) { + qCDebug(interfaceapp).nospace() << + "Paint delay (" << paintDelaySamples << " samples): " << + (float)paintDelaySamples / paintDelayUsecs << "us"; + } +#endif + + // Throttle if requested + //if (displayPlugin->isThrottled() && (_graphicsEngine._renderEventHandler->_lastTimeRendered.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { + if ( displayPlugin->isThrottled() && + (static_cast(_renderEventHandler)->_lastTimeRendered.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { + return false; + } + + return true; + // } } bool GraphicsEngine::checkPendingRenderEvent() { diff --git a/interface/src/graphics/GraphicsEngine.h b/interface/src/graphics/GraphicsEngine.h index 780b4020d2..bc2121429a 100644 --- a/interface/src/graphics/GraphicsEngine.h +++ b/interface/src/graphics/GraphicsEngine.h @@ -16,6 +16,8 @@ #include +#include "RenderThread.h" + #include #include @@ -51,6 +53,8 @@ public: render::EnginePointer getRenderEngine() const { return _renderEngine; } gpu::ContextPointer getGPUContext() const { return _gpuContext; } + FrameQueuePointer getFrameQueue() const { return _frameQueue; } + // Same as the one in application bool shouldPaint() const; bool checkPendingRenderEvent(); @@ -79,6 +83,8 @@ protected: gpu::ContextPointer _gpuContext; // initialized during window creation + FrameQueuePointer _frameQueue{ new FrameQueue() }; + QObject* _renderEventHandler{ nullptr }; friend class RenderEventHandler; diff --git a/interface/src/graphics/RenderEventHandler.cpp b/interface/src/graphics/RenderEventHandler.cpp index 95e931ebd8..5b587ab171 100644 --- a/interface/src/graphics/RenderEventHandler.cpp +++ b/interface/src/graphics/RenderEventHandler.cpp @@ -15,7 +15,8 @@ #include "CrashHandler.h" -RenderEventHandler::RenderEventHandler(QOpenGLContext* context, RenderCall renderCall) : +RenderEventHandler::RenderEventHandler(QOpenGLContext* context, CheckCall checkCall, RenderCall renderCall) : + _checkCall(checkCall), _renderCall(renderCall) { _renderContext = new OffscreenGLCanvas(); @@ -52,7 +53,7 @@ void RenderEventHandler::resumeThread() { } void RenderEventHandler::render() { - if (qApp->shouldPaint()) { + if (_checkCall()) { _lastTimeRendered.start(); _renderCall(); } diff --git a/interface/src/graphics/RenderEventHandler.h b/interface/src/graphics/RenderEventHandler.h index 913898eb7f..43b16a76bd 100644 --- a/interface/src/graphics/RenderEventHandler.h +++ b/interface/src/graphics/RenderEventHandler.h @@ -28,10 +28,13 @@ class RenderEventHandler : public QObject { Q_OBJECT public: + using CheckCall = std::function ; using RenderCall = std::function ; + + CheckCall _checkCall; RenderCall _renderCall; - RenderEventHandler(QOpenGLContext* context, RenderCall renderCall); + RenderEventHandler(QOpenGLContext* context, CheckCall checkCall, RenderCall renderCall); QElapsedTimer _lastTimeRendered; std::atomic _pendingRenderEvent{ true }; diff --git a/interface/src/graphics/RenderThread.cpp b/interface/src/graphics/RenderThread.cpp new file mode 100644 index 0000000000..63df2033e8 --- /dev/null +++ b/interface/src/graphics/RenderThread.cpp @@ -0,0 +1 @@ +#include "RenderThread.h" \ No newline at end of file diff --git a/interface/src/graphics/RenderThread.h b/interface/src/graphics/RenderThread.h new file mode 100644 index 0000000000..abddf746e6 --- /dev/null +++ b/interface/src/graphics/RenderThread.h @@ -0,0 +1,108 @@ +#ifndef PRODUCERCONSUMERPIPE_H +#define PRODUCERCONSUMERPIPE_H + +#include +#include +#include +#include +#include + +// Producer is blocked if the consumer doesn't consume enough +// Consumer reads same value if producer doesn't produce enough +template +class ProducerConsumerPipe { +public: + + ProducerConsumerPipe(); + ProducerConsumerPipe(const T& initValue); + + const T& read(); + void read(T& value, const T& resetValue); + void write(const T& value); + bool isWritePossible(); + +private: + + short _readIndex; + short _writeIndex; + std::array _values; + std::array _used; + + void initialize(); + void updateReadIndex(); +}; + +template +ProducerConsumerPipe::ProducerConsumerPipe() { + initialize(); +} + +template +ProducerConsumerPipe::ProducerConsumerPipe(const T& initValue) { + _values.fill(initValue); + initialize(); +} + +template +void ProducerConsumerPipe::initialize() { + _readIndex = 0; + _writeIndex = 2; + _used[_readIndex].test_and_set(std::memory_order_acquire); + _used[_writeIndex].test_and_set(std::memory_order_acquire); + _used[1].clear(); +} + +template +void ProducerConsumerPipe::updateReadIndex() { + int nextReadIndex = (_readIndex + 1) % _values.size(); + + if (!_used[nextReadIndex].test_and_set(std::memory_order_acquire)) { + int readIndex = _readIndex; + _used[readIndex].clear(std::memory_order_release); + _readIndex = nextReadIndex; + } +} + +template +const T& ProducerConsumerPipe::read() { + updateReadIndex(); + return _values[_readIndex]; +} + +template +void ProducerConsumerPipe::read(T& value, const T& resetValue) { + updateReadIndex(); + value = _values[_readIndex]; + _values[_readIndex] = resetValue; +} + +template +bool ProducerConsumerPipe::isWritePossible() { + int nextWriteIndex = (_writeIndex + 1) % _values.size(); + return (_used[nextWriteIndex].test_and_set(std::memory_order_acquire)); +} + +template +void ProducerConsumerPipe::write(const T& value) { + int nextWriteIndex = (_writeIndex + 1) % _values.size(); + int writeIndex = _writeIndex; + + _values[writeIndex] = value; + + while (_used[nextWriteIndex].test_and_set(std::memory_order_acquire)) { + // spin + std::this_thread::yield(); + } + _used[writeIndex].clear(std::memory_order_release); + _writeIndex = nextWriteIndex; +} + + +#include + +using FrameQueue = ProducerConsumerPipe; + +using FrameQueuePointer = std::shared_ptr; + + +#endif