From 265bd8ee670db8f7aa74428c58a38b296daf34eb Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 9 Dec 2015 17:59:38 -0800 Subject: [PATCH 1/2] Moving QML rendering off the main thread --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 110 +++++++------------- 1 file changed, 39 insertions(+), 71 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 482eb1b36f..d42ae5889c 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -8,15 +8,16 @@ #include "OffscreenQmlSurface.h" #include "OglplusHelpers.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -25,9 +26,6 @@ #include "GLEscrow.h" #include "OffscreenGLCanvas.h" -// FIXME move to threaded rendering with Qt 5.5 -//#define QML_THREADED - // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. @@ -56,13 +54,11 @@ private: Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") -#ifdef QML_THREADED static const QEvent::Type INIT = QEvent::Type(QEvent::User + 1); static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); static const QEvent::Type UPDATE = QEvent::Type(QEvent::User + 5); -#endif class OffscreenQmlRenderer : public OffscreenGLCanvas { friend class OffscreenQmlSurface; @@ -70,22 +66,30 @@ public: OffscreenQmlRenderer(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { OffscreenGLCanvas::create(shareContext); -#ifdef QML_THREADED + + _renderControl = new QMyQuickRenderControl(); + + // Create a QQuickWindow that is associated with out render control. Note that this + // window never gets created or shown, meaning that it will never get an underlying + // native (platform) window. + QQuickWindow::setDefaultAlphaBuffer(true); + // Weirdness... QQuickWindow NEEDS to be created on the rendering thread, or it will refuse to render + // because it retains an internal 'context' object that retains the thread it was created on, + // regardless of whether you later move it to another thread. + _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setColor(QColor(255, 255, 255, 0)); + _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); + // Qt 5.5 - _renderControl->prepareThread(_renderThread); + _renderControl->prepareThread(&_thread); _context->moveToThread(&_thread); moveToThread(&_thread); _thread.setObjectName("QML Thread"); _thread.start(); post(INIT); -#else - init(); -#endif } -#ifdef QML_THREADED - bool event(QEvent *e) - { + bool event(QEvent *e) { switch (int(e->type())) { case INIT: { @@ -120,7 +124,6 @@ public: QCoreApplication::postEvent(this, new QEvent(type)); } -#endif private: @@ -143,27 +146,9 @@ private: void init() { - _renderControl = new QMyQuickRenderControl(); connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender); connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate); - // Create a QQuickWindow that is associated with out render control. Note that this - // window never gets created or shown, meaning that it will never get an underlying - // native (platform) window. - QQuickWindow::setDefaultAlphaBuffer(true); - // Weirdness... QQuickWindow NEEDS to be created on the rendering thread, or it will refuse to render - // because it retains an internal 'context' object that retains the thread it was created on, - // regardless of whether you later move it to another thread. - _quickWindow = new QQuickWindow(_renderControl); - _quickWindow->setColor(QColor(255, 255, 255, 0)); - _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); - -#ifdef QML_THREADED - // However, because we want to use synchronous events with the quickwindow, we need to move it back to the main - // thread after it's created. - _quickWindow->moveToThread(qApp->thread()); -#endif - if (!makeCurrent()) { qWarning("Failed to make context current on render thread"); return; @@ -189,17 +174,15 @@ private: doneCurrent(); -#ifdef QML_THREADED _context->moveToThread(QCoreApplication::instance()->thread()); _cond.wakeOne(); -#endif } - void resize(const QSize& newSize) { + void resize() { // Update our members if (_quickWindow) { - _quickWindow->setGeometry(QRect(QPoint(), newSize)); - _quickWindow->contentItem()->setSize(newSize); + _quickWindow->setGeometry(QRect(QPoint(), _newSize)); + _quickWindow->contentItem()->setSize(_newSize); } // Qt bug in 5.4 forces this check of pixel ratio, @@ -209,7 +192,7 @@ private: pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); } - uvec2 newOffscreenSize = toGlm(newSize * pixelRatio); + uvec2 newOffscreenSize = toGlm(_newSize * pixelRatio); _textures.setSize(newOffscreenSize); if (newOffscreenSize == _size) { return; @@ -222,7 +205,7 @@ private: return; } - qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio; + qDebug() << "Offscreen UI resizing to " << _newSize.width() << "x" << _newSize.height() << " with pixel ratio " << pixelRatio; setupFbo(); doneCurrent(); } @@ -241,11 +224,8 @@ private: //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _textures._size); _renderControl->sync(); -#ifdef QML_THREADED _cond.wakeOne(); lock->unlock(); -#endif - using namespace oglplus; @@ -271,20 +251,14 @@ private: } void aboutToQuit() { -#ifdef QML_THREADED QMutexLocker lock(&_quitMutex); _quit = true; -#endif } void stop() { -#ifdef QML_THREADED - QMutexLocker lock(&_quitMutex); + QMutexLocker lock(&_mutex); post(STOP); _cond.wait(&_mutex); -#else - cleanup(); -#endif } bool allowNewFrame(uint8_t fps) { @@ -297,13 +271,12 @@ private: QQuickWindow* _quickWindow{ nullptr }; QMyQuickRenderControl* _renderControl{ nullptr }; -#ifdef QML_THREADED QThread _thread; QMutex _mutex; QWaitCondition _cond; QMutex _quitMutex; -#endif + QSize _newSize; bool _quit; FramebufferPtr _fbo; RenderbufferPtr _depthStencil; @@ -346,9 +319,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { } void OffscreenQmlSurface::resize(const QSize& newSize) { -#ifdef QML_THREADED - QMutexLocker _locker(&(_renderer->_mutex)); -#endif + if (!_renderer || !_renderer->_quickWindow) { QSize currentSize = _renderer->_quickWindow->geometry().size(); if (newSize == currentSize) { @@ -362,11 +333,12 @@ void OffscreenQmlSurface::resize(const QSize& newSize) { _rootItem->setSize(newSize); } -#ifdef QML_THREADED + { + QMutexLocker _locker(&(_renderer->_mutex)); + _renderer->_newSize = newSize; + } + _renderer->post(RESIZE); -#else - _renderer->resize(newSize); -#endif } QQuickItem* OffscreenQmlSurface::getRootItem() { @@ -466,11 +438,7 @@ void OffscreenQmlSurface::updateQuick() { } if (_render) { -#ifdef QML_THREADED _renderer->post(RENDER); -#else - _renderer->render(nullptr); -#endif _render = false; } From ae1345bb13275e4e7cb2f0bd84ff08c09b0c5696 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 10 Dec 2015 14:04:34 -0800 Subject: [PATCH 2/2] Adding nsight instrumenting to the QML render thread --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 35 ++++++++++--------- libraries/gpu/src/gpu/Batch.cpp | 7 ---- libraries/gpu/src/gpu/Batch.h | 14 ++------ libraries/shared/CMakeLists.txt | 1 + libraries/shared/src/shared/NsightHelpers.cpp | 22 ++++++++++++ libraries/shared/src/shared/NsightHelpers.h | 24 +++++++++++++ 6 files changed, 67 insertions(+), 36 deletions(-) create mode 100644 libraries/shared/src/shared/NsightHelpers.cpp create mode 100644 libraries/shared/src/shared/NsightHelpers.h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index d42ae5889c..d30294ae7c 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include "GLEscrow.h" #include "OffscreenGLCanvas.h" + // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. @@ -220,9 +222,6 @@ private: return; } - //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _size); - //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _textures._size); - _renderControl->sync(); _cond.wakeOne(); lock->unlock(); @@ -231,22 +230,24 @@ private: _quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y)); - TexturePtr texture = _textures.getNextTexture(); - _fbo->Bind(Framebuffer::Target::Draw); - _fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0); - _fbo->Complete(Framebuffer::Target::Draw); - //Context::Clear().ColorBuffer(); { - _renderControl->render(); - // FIXME The web browsers seem to be leaving GL in an error state. - // Need a debug context with sync logging to figure out why. - // for now just clear the errors - glGetError(); + PROFILE_RANGE("qml_render") + TexturePtr texture = _textures.getNextTexture(); + _fbo->Bind(Framebuffer::Target::Draw); + _fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0); + _fbo->Complete(Framebuffer::Target::Draw); + { + _renderControl->render(); + // FIXME The web browsers seem to be leaving GL in an error state. + // Need a debug context with sync logging to figure out why. + // for now just clear the errors + glGetError(); + } + // FIXME probably unecessary + DefaultFramebuffer().Bind(Framebuffer::Target::Draw); + _quickWindow->resetOpenGLState(); + _escrow.submit(GetName(*texture)); } - // FIXME probably unecessary - DefaultFramebuffer().Bind(Framebuffer::Target::Draw); - _quickWindow->resetOpenGLState(); - _escrow.submit(GetName(*texture)); _lastRenderTime = usecTimestampNow(); } diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 80b3a4f158..14871aafd1 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -16,13 +16,6 @@ #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" -ProfileRange::ProfileRange(const char *name) { - nvtxRangePush(name); -} -ProfileRange::~ProfileRange() { - nvtxRangePop(); -} - ProfileRangeBatch::ProfileRangeBatch(gpu::Batch& batch, const char *name) : _batch(batch) { _batch.pushProfileRange(name); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index da1f13151e..2afcc7caa9 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -15,6 +15,8 @@ #include #include +#include + #include "Framebuffer.h" #include "Pipeline.h" #include "Query.h" @@ -22,18 +24,6 @@ #include "Texture.h" #include "Transform.h" - -#if defined(NSIGHT_FOUND) - class ProfileRange { - public: - ProfileRange(const char *name); - ~ProfileRange(); - }; -#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); -#else -#define PROFILE_RANGE(name) -#endif - class QDebug; namespace gpu { diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 2691c51128..489819747f 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -4,3 +4,4 @@ set(TARGET_NAME shared) setup_hifi_library(Gui Network Script Widgets) target_zlib() +target_nsight() diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp new file mode 100644 index 0000000000..e48e228588 --- /dev/null +++ b/libraries/shared/src/shared/NsightHelpers.cpp @@ -0,0 +1,22 @@ +// +// Created by Bradley Austin Davis on 2015/12/10 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "NsightHelpers.h" + +#if defined(NSIGHT_FOUND) +#include "nvToolsExt.h" + +ProfileRange::ProfileRange(const char *name) { + nvtxRangePush(name); +} + +ProfileRange::~ProfileRange() { + nvtxRangePop(); +} + +#endif diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h new file mode 100644 index 0000000000..3acdf14411 --- /dev/null +++ b/libraries/shared/src/shared/NsightHelpers.h @@ -0,0 +1,24 @@ +// +// Created by Bradley Austin Davis on 2015/12/10 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_gl_NsightHelpers_h +#define hifi_gl_NsightHelpers_h + +#if defined(NSIGHT_FOUND) + class ProfileRange { + public: + ProfileRange(const char *name); + ~ProfileRange(); + }; +#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); +#else +#define PROFILE_RANGE(name) +#endif + + +#endif \ No newline at end of file