diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 88897a0fed..bd368ef7c2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -656,6 +656,8 @@ void Agent::queryAvatars() { ViewFrustum view; view.setPosition(scriptedAvatar->getWorldPosition()); view.setOrientation(scriptedAvatar->getHeadOrientation()); + view.setProjection(DEFAULT_FIELD_OF_VIEW_DEGREES, DEFAULT_ASPECT_RATIO, + DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP); view.calculate(); ConicalViewFrustum conicalView { view }; @@ -876,18 +878,30 @@ void Agent::aboutToFinish() { DependencyManager::destroy(); // destroy all other created dependencies - DependencyManager::destroy(); - DependencyManager::destroy(); DependencyManager::destroy(); - DependencyManager::destroy(); DependencyManager::destroy(); - DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + // drop our shared pointer to the script engine, then ask ScriptEngines to shutdown scripting // this ensures that the ScriptEngine goes down before ScriptEngines _scriptEngine.clear(); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 76ff5ab2ed..c1943de2cc 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -129,17 +129,12 @@ void AssignmentClient::stopAssignmentClient() { QThread* currentAssignmentThread = _currentAssignment->thread(); // ask the current assignment to stop - BLOCKING_INVOKE_METHOD(_currentAssignment, "stop"); + QMetaObject::invokeMethod(_currentAssignment, "stop"); - // ask the current assignment to delete itself on its thread - _currentAssignment->deleteLater(); - - // when this thread is destroyed we don't need to run our assignment complete method - disconnect(currentAssignmentThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted); - - // wait on the thread from that assignment - it will be gone once the current assignment deletes - currentAssignmentThread->quit(); - currentAssignmentThread->wait(); + auto PROCESS_EVENTS_INTERVAL_MS = 100; + while (!currentAssignmentThread->wait(PROCESS_EVENTS_INTERVAL_MS)) { + QCoreApplication::processEvents(); + } } } diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 51038a782f..bf5d87a6bf 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -21,7 +21,7 @@ #include ScriptableAvatar::ScriptableAvatar() { - _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); + _clientTraitsHandler.reset(new ClientTraitsHandler(this)); } QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 272985093c..ef0c807bc4 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -583,15 +583,29 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer mess void EntityScriptServer::aboutToFinish() { shutdownScriptEngine(); + DependencyManager::get()->setEntityTree(nullptr); + DependencyManager::get()->cleanup(); + + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); - DependencyManager::get()->cleanup(); DependencyManager::destroy(); - DependencyManager::destroy(); - DependencyManager::destroy(); // cleanup the AudioInjectorManager (and any still running injectors) DependencyManager::destroy(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cb41a8c240..8e46b0b426 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -87,7 +87,6 @@ #include #include #include -#include #include #include #include @@ -122,8 +121,6 @@ #include #include #include -#include -#include #include #include #include @@ -264,54 +261,7 @@ extern "C" { #include "AndroidHelper.h" #endif -enum ApplicationEvent { - // Execute a lambda function - Lambda = QEvent::User + 1, - // Trigger the next render - Render, - // Trigger the next idle - Idle, -}; - -class RenderEventHandler : public QObject { - using Parent = QObject; - Q_OBJECT -public: - RenderEventHandler() { - // Transfer to a new thread - moveToNewNamedThread(this, "RenderThread", [](QThread* renderThread) { - hifi::qt::addBlockingForbiddenThread("Render", renderThread); - qApp->_lastTimeRendered.start(); - }, std::bind(&RenderEventHandler::initialize, this), QThread::HighestPriority); - } - -private: - void initialize() { - setObjectName("Render"); - PROFILE_SET_THREAD_NAME("Render"); - setCrashAnnotation("render_thread_id", std::to_string((size_t)QThread::currentThreadId())); - } - - void render() { - if (qApp->shouldPaint()) { - qApp->paintGL(); - } - } - - bool event(QEvent* event) override { - switch ((int)event->type()) { - case ApplicationEvent::Render: - render(); - qApp->_pendingRenderEvent.store(false); - return true; - - default: - break; - } - return Parent::event(event); - } -}; - +#include "graphics/RenderEventHandler.h" Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse") @@ -374,8 +324,6 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI static const int ENTITY_SERVER_ADDED_TIMEOUT = 5000; static const int ENTITY_SERVER_CONNECTION_TIMEOUT = 5000; -static const uint32_t INVALID_FRAME = UINT32_MAX; - static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); @@ -2060,7 +2008,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto displayPlugin = qApp->getActiveDisplayPlugin(); - properties["render_rate"] = _renderLoopCounter.rate(); + properties["render_rate"] = getRenderLoopRate(); properties["target_render_rate"] = getTargetRenderFrameRate(); properties["present_rate"] = displayPlugin->presentRate(); properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate(); @@ -2372,7 +2320,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->createKeyboard(); _pendingIdleEvent = false; - _pendingRenderEvent = false; + _graphicsEngine.startup(); qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); @@ -2642,11 +2590,6 @@ void Application::cleanupBeforeQuit() { // Cleanup all overlays after the scripts, as scripts might add more _overlays.cleanupAllOverlays(); - // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual - // removal of the items. - // See https://highfidelity.fogbugz.com/f/cases/5328 - _main3DScene->enqueueFrame(); // flush all the transactions - _main3DScene->processTransactionQueue(); // process and apply deletions // first stop all timers directly or by invokeMethod // depending on what thread they run in @@ -2661,7 +2604,6 @@ void Application::cleanupBeforeQuit() { } _window->saveGeometry(); - _gpuContext->shutdown(); // Destroy third party processes after scripts have finished using them. #ifdef HAVE_DDE @@ -2719,10 +2661,9 @@ Application::~Application() { _shapeManager.collectGarbage(); assert(_shapeManager.getNumShapes() == 0); - // shutdown render engine - _main3DScene = nullptr; - _renderEngine = nullptr; - + // shutdown graphics engine + _graphicsEngine.shutdown(); + _gameWorkload.shutdown(); DependencyManager::destroy(); @@ -2780,8 +2721,6 @@ Application::~Application() { // Can't log to file past this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); - - _renderEventHandler->deleteLater(); } void Application::initializeGL() { @@ -2871,26 +2810,13 @@ void Application::initializeGL() { #endif - _renderEventHandler = new RenderEventHandler(); - // Build an offscreen GL context for the main thread. _glWidget->makeCurrent(); glClearColor(0.2f, 0.2f, 0.2f, 1); glClear(GL_COLOR_BUFFER_BIT); _glWidget->swapBuffers(); - - // Create the GPU backend - - // Requires the window context, because that's what's used in the actual rendering - // and the GPU backend will make things like the VAO which cannot be shared across - // contexts - _glWidget->makeCurrent(); - gpu::Context::init(); - _glWidget->makeCurrent(); - _gpuContext = std::make_shared(); - - DependencyManager::get()->setGPUContext(_gpuContext); + _graphicsEngine.initializeGPU(_glWidget); } static const QString SPLASH_SKYBOX{ "{\"ProceduralEntity\":{ \"version\":2, \"shaderUrl\":\"qrc:///shaders/splashSkybox.frag\" } }" }; @@ -2904,7 +2830,7 @@ void Application::initializeDisplayPlugins() { // Once time initialization code DisplayPluginPointer targetDisplayPlugin; foreach(auto displayPlugin, displayPlugins) { - displayPlugin->setContext(_gpuContext); + displayPlugin->setContext(_graphicsEngine.getGPUContext()); if (displayPlugin->getName() == lastActiveDisplayPluginName) { targetDisplayPlugin = displayPlugin; } @@ -2974,18 +2900,7 @@ void Application::initializeDisplayPlugins() { void Application::initializeRenderEngine() { // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. DeadlockWatchdogThread::withPause([&] { - // Set up the render engine - render::CullFunctor cullFunctor = LODManager::shouldRender; - _renderEngine->addJob("UpdateScene"); -#ifndef Q_OS_ANDROID - _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); -#endif - _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); - _renderEngine->load(); - _renderEngine->registerScene(_main3DScene); - - // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. - DependencyManager::get()->initializeShapePipelines(); + _graphicsEngine.initializeRender(DISABLE_DEFERRED); DependencyManager::get()->registerKeyboardHighlighting(); }); } @@ -3186,7 +3101,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Recording", DependencyManager::get().data()); surfaceContext->setContextProperty("Preferences", DependencyManager::get().data()); surfaceContext->setContextProperty("AddressManager", DependencyManager::get().data()); - surfaceContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface); + surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine._frameTimingsScriptingInterface); surfaceContext->setContextProperty("Rates", new RatesScriptingInterface(this)); surfaceContext->setContextProperty("TREE_SCALE", TREE_SCALE); @@ -3235,7 +3150,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("LODManager", DependencyManager::get().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get().data()); surfaceContext->setContextProperty("Scene", DependencyManager::get().data()); - surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); + surfaceContext->setContextProperty("Render", _graphicsEngine.getRenderEngine()->getConfiguration().get()); surfaceContext->setContextProperty("Workload", _gameWorkload._engine->getConfiguration().get()); surfaceContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); surfaceContext->setContextProperty("Snapshot", DependencyManager::get().data()); @@ -3523,7 +3438,7 @@ void Application::resizeGL() { auto renderResolutionScale = getRenderResolutionScale(); if (displayPlugin->getRenderResolutionScale() != renderResolutionScale) { - auto renderConfig = _renderEngine->getConfiguration(); + auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); assert(renderConfig); auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask"); assert(mainView); @@ -3754,8 +3669,8 @@ void Application::onPresent(quint32 frameCount) { postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); } expected = false; - if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) { - postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); + if (_graphicsEngine.checkPendingRenderEvent() && !isAboutToQuit()) { + postEvent(_graphicsEngine._renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } } @@ -4543,39 +4458,6 @@ 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() && (_lastTimeRendered.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { - return false; - } - - // Sync up the _renderedFrameIndex - _renderedFrameIndex = displayPlugin->presentCount(); - return true; -} - #ifdef Q_OS_WIN #include #include @@ -4829,26 +4711,13 @@ void Application::idle() { if (displayPlugin) { PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate()); } - PROFILE_COUNTER_IF_CHANGED(app, "renderLoopRate", float, _renderLoopCounter.rate()); - PROFILE_COUNTER_IF_CHANGED(app, "currentDownloads", uint32_t, ResourceCache::getLoadingRequestCount()); + PROFILE_COUNTER_IF_CHANGED(app, "renderLoopRate", float, getRenderLoopRate()); + PROFILE_COUNTER_IF_CHANGED(app, "currentDownloads", uint32_t, ResourceCache::getLoadingRequests().length()); PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", uint32_t, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); - auto renderConfig = _renderEngine->getConfiguration(); - PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_gpuContext->getFrameTimerGPUAverage()); - auto opaqueRangeTimer = renderConfig->getConfig("OpaqueRangeTimer"); - auto linearDepth = renderConfig->getConfig("LinearDepth"); - auto surfaceGeometry = renderConfig->getConfig("SurfaceGeometry"); - auto renderDeferred = renderConfig->getConfig("RenderDeferred"); - auto toneAndPostRangeTimer = renderConfig->getConfig("ToneAndPostRangeTimer"); - - PROFILE_COUNTER(render_detail, "gpuTimes", { - { "OpaqueRangeTimer", opaqueRangeTimer ? opaqueRangeTimer->property("gpuRunTime") : 0 }, - { "LinearDepth", linearDepth ? linearDepth->property("gpuRunTime") : 0 }, - { "SurfaceGeometry", surfaceGeometry ? surfaceGeometry->property("gpuRunTime") : 0 }, - { "RenderDeferred", renderDeferred ? renderDeferred->property("gpuRunTime") : 0 }, - { "ToneAndPostRangeTimer", toneAndPostRangeTimer ? toneAndPostRangeTimer->property("gpuRunTime") : 0 } - }); + auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); + PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine.getGPUContext()->getFrameTimerGPUAverage()); PROFILE_RANGE(app, __FUNCTION__); @@ -5223,9 +5092,6 @@ void Application::init() { #if !defined(DISABLE_QML) DependencyManager::get()->toggleLoginDialog(); #endif - if (!DISABLE_DEFERRED) { - DependencyManager::get()->init(); - } DependencyManager::get()->init(); _timerStart.start(); @@ -5294,7 +5160,7 @@ void Application::init() { } }, Qt::QueuedConnection); - _gameWorkload.startup(getEntities()->getWorkloadSpace(), _main3DScene, _entitySimulation); + _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine.getRenderScene(), _entitySimulation); _entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace()); } @@ -5328,7 +5194,7 @@ void Application::updateLOD(float deltaTime) const { // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!isThrottleRendering()) { float presentTime = getActiveDisplayPlugin()->getAveragePresentTime(); - float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime()); + float engineRunTime = (float)(_graphicsEngine.getRenderEngine()->getConfiguration().get()->getCPURunTime()); float gpuTime = getGPUContext()->getFrameTimerGPUAverage(); float batchTime = getGPUContext()->getFrameTimerBatchAverage(); auto lodManager = DependencyManager::get(); @@ -5729,7 +5595,7 @@ void Application::updateSecondaryCameraViewFrustum() { // camera should be. // Code based on SecondaryCameraJob - auto renderConfig = _renderEngine->getConfiguration(); + auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); assert(renderConfig); auto camera = dynamic_cast(renderConfig->getConfig("SecondaryCamera")); @@ -5798,7 +5664,7 @@ void Application::updateSecondaryCameraViewFrustum() { static bool domainLoadingInProgress = false; void Application::update(float deltaTime) { - PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_renderFrameCount + 1); + PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine._renderFrameCount + 1); if (_aboutToQuit) { return; @@ -6214,7 +6080,7 @@ void Application::update(float deltaTime) { // TODO: Fix this by modeling the way the secondary camera works on how the main camera works // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary // camera should be. - updateSecondaryCameraViewFrustum(); + // updateSecondaryCameraViewFrustum(); } quint64 now = usecTimestampNow(); @@ -6290,13 +6156,6 @@ void Application::update(float deltaTime) { updateRenderArgs(deltaTime); - // HACK - // load the view frustum - // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. - // Then we can move this logic into the Avatar::simulate call. - myAvatar->preDisplaySide(&_appRenderArgs._renderArgs); - - { PerformanceTimer perfTimer("AnimDebugDraw"); AnimDebugDraw::getInstance().update(); @@ -6315,7 +6174,7 @@ void Application::update(float deltaTime) { } void Application::updateRenderArgs(float deltaTime) { - editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { + _graphicsEngine.editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { PerformanceTimer perfTimer("editRenderArgs"); appRenderArgs._headPose = getHMDSensorPose(); @@ -6344,7 +6203,7 @@ void Application::updateRenderArgs(float deltaTime) { _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } - appRenderArgs._renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), + appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), lodManager->getLODAngleHalfTan(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); appRenderArgs._renderArgs._scene = getMain3DScene(); @@ -6429,6 +6288,13 @@ void Application::updateRenderArgs(float deltaTime) { QMutexLocker viewLocker(&_viewMutex); appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum); } + + + // HACK + // load the view frustum + // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. + // Then we can move this logic into the Avatar::simulate call. + myAvatar->preDisplaySide(&appRenderArgs._renderArgs); }); } @@ -7033,7 +6899,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get()); + scriptEngine->registerGlobalObject("Render", _graphicsEngine.getRenderEngine()->getConfiguration().get()); scriptEngine->registerGlobalObject("Workload", _gameWorkload._engine->getConfiguration().get()); GraphicsScriptingInterface::registerMetaTypes(scriptEngine.data()); diff --git a/interface/src/Application.h b/interface/src/Application.h index c22f809070..ead7231ffc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -70,11 +70,11 @@ #include "ui/overlays/Overlays.h" #include "workload/GameWorkload.h" +#include "graphics/GraphicsEngine.h" #include #include #include -#include "FrameTimingsScriptingInterface.h" #include "Sound.h" @@ -153,7 +153,6 @@ public: void updateSecondaryCameraViewFrustum(); void updateCamera(RenderArgs& renderArgs, float deltaTime); - void paintGL(); void resizeGL(); bool event(QEvent* event) override; @@ -203,8 +202,8 @@ public: Overlays& getOverlays() { return _overlays; } - size_t getRenderFrameCount() const { return _renderFrameCount; } - float getRenderLoopRate() const { return _renderLoopCounter.rate(); } + size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); } + float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); } float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second @@ -275,10 +274,10 @@ public: void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond() const; - render::ScenePointer getMain3DScene() override { return _main3DScene; } - const render::ScenePointer& getMain3DScene() const { return _main3DScene; } - render::EnginePointer getRenderEngine() override { return _renderEngine; } - gpu::ContextPointer getGPUContext() const { return _gpuContext; } + render::ScenePointer getMain3DScene() override { return _graphicsEngine.getRenderScene(); } + render::EnginePointer getRenderEngine() override { return _graphicsEngine.getRenderEngine(); } + gpu::ContextPointer getGPUContext() const { return _graphicsEngine.getGPUContext(); } + const GameWorkload& getGameWorkload() const { return _gameWorkload; } @@ -515,7 +514,6 @@ private: bool handleFileOpenEvent(QFileOpenEvent* event); void cleanupBeforeQuit(); - bool shouldPaint() const; void idle(); void update(float deltaTime); @@ -535,8 +533,6 @@ private: void initializeAcceptedFiles(); - void runRenderFrame(RenderArgs* renderArgs/*, Camera& whichCamera, bool selfAvatarOnly = false*/); - bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); bool importFromZIP(const QString& filePath); @@ -586,18 +582,12 @@ private: bool _activatingDisplayPlugin { false }; - uint32_t _renderFrameCount { 0 }; - // Frame Rate Measurement - RateCounter<500> _renderLoopCounter; RateCounter<500> _gameLoopCounter; - FrameTimingsScriptingInterface _frameTimingsScriptingInterface; - QTimer _minimizedWindowTimer; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; - QElapsedTimer _lastTimeRendered; int _minimumGPUTextureMemSizeStabilityCount { 30 }; @@ -683,29 +673,9 @@ private: quint64 _lastFaceTrackerUpdate; - render::ScenePointer _main3DScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; - render::EnginePointer _renderEngine{ new render::RenderEngine() }; - gpu::ContextPointer _gpuContext; // initialized during window creation - GameWorkload _gameWorkload; - mutable QMutex _renderArgsMutex{ QMutex::Recursive }; - struct AppRenderArgs { - render::Args _renderArgs; - glm::mat4 _eyeToWorld; - glm::mat4 _view; - glm::mat4 _eyeOffsets[2]; - glm::mat4 _eyeProjections[2]; - glm::mat4 _headPose; - glm::mat4 _sensorToWorld; - float _sensorToWorldScale { 1.0f }; - bool _isStereo{ false }; - }; - AppRenderArgs _appRenderArgs; - - - using RenderArgsEditor = std::function ; - void editRenderArgs(RenderArgsEditor editor); + GraphicsEngine _graphicsEngine; void updateRenderArgs(float deltaTime); @@ -751,8 +721,6 @@ private: bool _keyboardDeviceHasFocus { true }; - QString _returnFromFullScreenMirrorTo; - ConnectionMonitor _connectionMonitor; QTimer _addAssetToWorldResizeTimer; @@ -786,12 +754,8 @@ private: QUrl _avatarOverrideUrl; bool _saveAvatarOverrideUrl { false }; - QObject* _renderEventHandler{ nullptr }; - - friend class RenderEventHandler; std::atomic _pendingIdleEvent { true }; - std::atomic _pendingRenderEvent { true }; bool quitWhenFinished { false }; diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 646cc53cc0..5063f6118d 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -19,220 +19,207 @@ #include "Util.h" -// Statically provided display and input plugins -extern DisplayPluginList getDisplayPlugins(); - -void Application::editRenderArgs(RenderArgsEditor editor) { - QMutexLocker renderLocker(&_renderArgsMutex); - editor(_appRenderArgs); - -} - -void Application::paintGL() { - // Some plugins process message events, allowing paintGL to be called reentrantly. - - _renderFrameCount++; - _lastTimeRendered.start(); - - auto lastPaintBegin = usecTimestampNow(); - PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); - PerformanceTimer perfTimer("paintGL"); - - if (nullptr == _displayPlugin) { - return; - } - - DisplayPluginPointer displayPlugin; - { - PROFILE_RANGE(render, "/getActiveDisplayPlugin"); - displayPlugin = getActiveDisplayPlugin(); - } - - { - PROFILE_RANGE(render, "/pluginBeginFrameRender"); - // If a display plugin loses it's underlying support, it - // needs to be able to signal us to not use it - if (!displayPlugin->beginFrameRender(_renderFrameCount)) { - QMetaObject::invokeMethod(this, "updateDisplayMode"); - return; - } - } - - RenderArgs renderArgs; - glm::mat4 HMDSensorPose; - glm::mat4 eyeToWorld; - glm::mat4 sensorToWorld; - - bool isStereo; - glm::mat4 stereoEyeOffsets[2]; - glm::mat4 stereoEyeProjections[2]; - - { - QMutexLocker viewLocker(&_renderArgsMutex); - renderArgs = _appRenderArgs._renderArgs; - - // don't render if there is no context. - if (!_appRenderArgs._renderArgs._context) { - return; - } - - HMDSensorPose = _appRenderArgs._headPose; - eyeToWorld = _appRenderArgs._eyeToWorld; - sensorToWorld = _appRenderArgs._sensorToWorld; - isStereo = _appRenderArgs._isStereo; - for_each_eye([&](Eye eye) { - stereoEyeOffsets[eye] = _appRenderArgs._eyeOffsets[eye]; - stereoEyeProjections[eye] = _appRenderArgs._eyeProjections[eye]; - }); - } - - { - PROFILE_RANGE(render, "/gpuContextReset"); - _gpuContext->beginFrame(_appRenderArgs._view, HMDSensorPose); - // Reset the gpu::Context Stages - // Back to the default framebuffer; - gpu::doInBatch("Application_render::gpuContextReset", _gpuContext, [&](gpu::Batch& batch) { - batch.resetStages(); - }); - } - - - { - PROFILE_RANGE(render, "/renderOverlay"); - PerformanceTimer perfTimer("renderOverlay"); - // NOTE: There is no batch associated with this renderArgs - // the ApplicationOverlay class assumes it's viewport is setup to be the device size - renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize()); - _applicationOverlay.renderOverlay(&renderArgs); - } - - { - PROFILE_RANGE(render, "/updateCompositor"); - getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld); - } - - gpu::FramebufferPointer finalFramebuffer; - QSize finalFramebufferSize; - { - PROFILE_RANGE(render, "/getOutputFramebuffer"); - // Primary rendering pass - auto framebufferCache = DependencyManager::get(); - finalFramebufferSize = framebufferCache->getFrameBufferSize(); - // Final framebuffer that will be handed to the display-plugin - finalFramebuffer = framebufferCache->getFramebuffer(); - } - - { - if (isStereo) { - renderArgs._context->enableStereo(true); - renderArgs._context->setStereoProjections(stereoEyeProjections); - renderArgs._context->setStereoViews(stereoEyeOffsets); - } - - renderArgs._hudOperator = displayPlugin->getHUDOperator(); - renderArgs._hudTexture = _applicationOverlay.getOverlayTexture(); - renderArgs._blitFramebuffer = finalFramebuffer; - runRenderFrame(&renderArgs); - } - - auto frame = _gpuContext->endFrame(); - frame->frameIndex = _renderFrameCount; - frame->framebuffer = finalFramebuffer; - frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) { - auto frameBufferCache = DependencyManager::get(); - if (frameBufferCache) { - frameBufferCache->releaseFramebuffer(framebuffer); - } - }; - // deliver final scene rendering commands to the display plugin - { - PROFILE_RANGE(render, "/pluginOutput"); - PerformanceTimer perfTimer("pluginOutput"); - _renderLoopCounter.increment(); - displayPlugin->submitFrame(frame); - } - - // Reset the framebuffer and stereo state - renderArgs._blitFramebuffer.reset(); - renderArgs._context->enableStereo(false); - -#if !defined(DISABLE_QML) - { - auto stats = Stats::getInstance(); - if (stats) { - stats->setRenderDetails(renderArgs._details); - } - } -#endif - - uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; - _frameTimingsScriptingInterface.addValue(lastPaintDuration); -} +//void Application::paintGL() { +// // Some plugins process message events, allowing paintGL to be called reentrantly. +// +// _renderFrameCount++; +// // SG: Moved into the RenderEventHandler +// //_lastTimeRendered.start(); +// +// auto lastPaintBegin = usecTimestampNow(); +// PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); +// PerformanceTimer perfTimer("paintGL"); +// +// if (nullptr == _displayPlugin) { +// return; +// } +// +// DisplayPluginPointer displayPlugin; +// { +// PROFILE_RANGE(render, "/getActiveDisplayPlugin"); +// displayPlugin = getActiveDisplayPlugin(); +// } +// +// { +// PROFILE_RANGE(render, "/pluginBeginFrameRender"); +// // If a display plugin loses it's underlying support, it +// // needs to be able to signal us to not use it +// if (!displayPlugin->beginFrameRender(_renderFrameCount)) { +// QMetaObject::invokeMethod(this, "updateDisplayMode"); +// return; +// } +// } +// +// RenderArgs renderArgs; +// glm::mat4 HMDSensorPose; +// glm::mat4 eyeToWorld; +// glm::mat4 sensorToWorld; +// +// bool isStereo; +// glm::mat4 stereoEyeOffsets[2]; +// glm::mat4 stereoEyeProjections[2]; +// +// { +// QMutexLocker viewLocker(&_renderArgsMutex); +// renderArgs = _appRenderArgs._renderArgs; +// +// // don't render if there is no context. +// if (!_appRenderArgs._renderArgs._context) { +// return; +// } +// +// HMDSensorPose = _appRenderArgs._headPose; +// eyeToWorld = _appRenderArgs._eyeToWorld; +// sensorToWorld = _appRenderArgs._sensorToWorld; +// isStereo = _appRenderArgs._isStereo; +// for_each_eye([&](Eye eye) { +// stereoEyeOffsets[eye] = _appRenderArgs._eyeOffsets[eye]; +// stereoEyeProjections[eye] = _appRenderArgs._eyeProjections[eye]; +// }); +// } +// +// { +// PROFILE_RANGE(render, "/gpuContextReset"); +// _graphicsEngine.getGPUContext()->beginFrame(_appRenderArgs._view, HMDSensorPose); +// // Reset the gpu::Context Stages +// // Back to the default framebuffer; +// gpu::doInBatch("Application_render::gpuContextReset", _graphicsEngine.getGPUContext(), [&](gpu::Batch& batch) { +// batch.resetStages(); +// }); +// } +// +// +// { +// PROFILE_RANGE(render, "/renderOverlay"); +// PerformanceTimer perfTimer("renderOverlay"); +// // NOTE: There is no batch associated with this renderArgs +// // the ApplicationOverlay class assumes it's viewport is setup to be the device size +// renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize() * getRenderResolutionScale()); +// _applicationOverlay.renderOverlay(&renderArgs); +// } +// +// { +// PROFILE_RANGE(render, "/updateCompositor"); +// getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld); +// } +// +// gpu::FramebufferPointer finalFramebuffer; +// QSize finalFramebufferSize; +// { +// PROFILE_RANGE(render, "/getOutputFramebuffer"); +// // Primary rendering pass +// auto framebufferCache = DependencyManager::get(); +// finalFramebufferSize = framebufferCache->getFrameBufferSize(); +// // Final framebuffer that will be handled to the display-plugin +// finalFramebuffer = framebufferCache->getFramebuffer(); +// } +// +// { +// if (isStereo) { +// renderArgs._context->enableStereo(true); +// renderArgs._context->setStereoProjections(stereoEyeProjections); +// renderArgs._context->setStereoViews(stereoEyeOffsets); +// } +// +// renderArgs._hudOperator = displayPlugin->getHUDOperator(); +// renderArgs._hudTexture = _applicationOverlay.getOverlayTexture(); +// renderArgs._blitFramebuffer = finalFramebuffer; +// _graphicsEngine.render_runRenderFrame(&renderArgs); +// } +// +// auto frame = _graphicsEngine.getGPUContext()->endFrame(); +// frame->frameIndex = _renderFrameCount; +// frame->framebuffer = finalFramebuffer; +// frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) { +// auto frameBufferCache = DependencyManager::get(); +// if (frameBufferCache) { +// frameBufferCache->releaseFramebuffer(framebuffer); +// } +// }; +// // deliver final scene rendering commands to the display plugin +// { +// PROFILE_RANGE(render, "/pluginOutput"); +// PerformanceTimer perfTimer("pluginOutput"); +// _renderLoopCounter.increment(); +// displayPlugin->submitFrame(frame); +// } +// +// // Reset the framebuffer and stereo state +// renderArgs._blitFramebuffer.reset(); +// renderArgs._context->enableStereo(false); +// +// { +// Stats::getInstance()->setRenderDetails(renderArgs._details); +// } +// +// uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; +// _frameTimingsScriptingInterface.addValue(lastPaintDuration); +//} // WorldBox Render Data & rendering functions - -class WorldBoxRenderData { -public: - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - - int _val = 0; - static render::ItemID _item; // unique WorldBoxRenderData -}; - -render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; - -namespace render { - template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } - template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } - template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { - if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { - PerformanceTimer perfTimer("worldBox"); - - auto& batch = *args->_batch; - DependencyManager::get()->bindSimpleProgram(batch); - renderWorldBox(args, batch); - } - } -} - -void Application::runRenderFrame(RenderArgs* renderArgs) { - PROFILE_RANGE(render, __FUNCTION__); - PerformanceTimer perfTimer("display"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::runRenderFrame()"); - - // The pending changes collecting the changes here - render::Transaction transaction; - - if (DependencyManager::get()->shouldRenderEntities()) { - // render models... - PerformanceTimer perfTimer("entities"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::runRenderFrame() ... entities..."); - - RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; - - renderArgs->_debugFlags = renderDebugFlags; - } - - // Make sure the WorldBox is in the scene - // For the record, this one RenderItem is the first one we created and added to the scene. - // We could move that code elsewhere but you know... - if (!render::Item::isValidID(WorldBoxRenderData::_item)) { - auto worldBoxRenderData = std::make_shared(); - auto worldBoxRenderPayload = std::make_shared(worldBoxRenderData); - - WorldBoxRenderData::_item = _main3DScene->allocateID(); - - transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); - _main3DScene->enqueueTransaction(transaction); - } - - { - PerformanceTimer perfTimer("EngineRun"); - _renderEngine->getRenderContext()->args = renderArgs; - _renderEngine->run(); - } -} +// +//class WorldBoxRenderData { +//public: +// typedef render::Payload Payload; +// typedef Payload::DataPointer Pointer; +// +// int _val = 0; +// static render::ItemID _item; // unique WorldBoxRenderData +//}; +// +//render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; +// +//namespace render { +// template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } +// template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } +// template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { +// if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { +// PerformanceTimer perfTimer("worldBox"); +// +// auto& batch = *args->_batch; +// DependencyManager::get()->bindSimpleProgram(batch); +// renderWorldBox(args, batch); +// } +// } +//} +// +//void Application::runRenderFrame(RenderArgs* renderArgs) { +// PROFILE_RANGE(render, __FUNCTION__); +// PerformanceTimer perfTimer("display"); +// PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::runRenderFrame()"); +// +// // The pending changes collecting the changes here +// render::Transaction transaction; +// +// if (DependencyManager::get()->shouldRenderEntities()) { +// // render models... +// PerformanceTimer perfTimer("entities"); +// PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), +// "Application::runRenderFrame() ... entities..."); +// +// RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; +// +// renderArgs->_debugFlags = renderDebugFlags; +// } +// +// // Make sure the WorldBox is in the scene +// // For the record, this one RenderItem is the first one we created and added to the scene. +// // We could move that code elsewhere but you know... +// if (!render::Item::isValidID(WorldBoxRenderData::_item)) { +// auto worldBoxRenderData = std::make_shared(); +// auto worldBoxRenderPayload = std::make_shared(worldBoxRenderData); +// +// WorldBoxRenderData::_item = _main3DScene->allocateID(); +// +// transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); +// _main3DScene->enqueueTransaction(transaction); +// } +// +// { +// PerformanceTimer perfTimer("EngineRun"); +// _renderEngine->getRenderContext()->args = renderArgs; +// _renderEngine->run(); +// } +//} diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index f99a373259..e71602b271 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -171,7 +171,7 @@ void SecondaryCameraJobConfig::setOrientation(glm::quat orient) { } void SecondaryCameraJobConfig::enableSecondaryCameraRenderConfigs(bool enabled) { - qApp->getRenderEngine()->getConfiguration()->getConfig()->setEnabled(enabled); + qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob")->setEnabled(enabled); setEnabled(enabled); } @@ -187,11 +187,13 @@ public: void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) { auto args = renderContext->args; + if (cachedArgs) { args->_blitFramebuffer = cachedArgs->_blitFramebuffer; args->_viewport = cachedArgs->_viewport; - args->popViewFrustum(); args->_displayMode = cachedArgs->_displayMode; args->_renderMode = cachedArgs->_renderMode; + } + args->popViewFrustum(); gpu::doInBatch("EndSecondaryCameraFrame::run", args->_context, [&](gpu::Batch& batch) { batch.restoreContextStereo(); diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index bc72c7d7c5..0670252406 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -36,114 +36,6 @@ using namespace std; -void renderWorldBox(RenderArgs* args, gpu::Batch& batch) { - auto geometryCache = DependencyManager::get(); - - // Show center of world - static const glm::vec3 RED(1.0f, 0.0f, 0.0f); - static const glm::vec3 GREEN(0.0f, 1.0f, 0.0f); - static const glm::vec3 BLUE(0.0f, 0.0f, 1.0f); - static const glm::vec3 GREY(0.5f, 0.5f, 0.5f); - static const glm::vec4 GREY4(0.5f, 0.5f, 0.5f, 1.0f); - - static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); - static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); - static const glm::vec4 DASHED_BLUE(0.0f, 0.0f, 1.0f, 1.0f); - static const float DASH_LENGTH = 1.0f; - static const float GAP_LENGTH = 1.0f; - auto transform = Transform{}; - static std::array geometryIds; - static std::once_flag initGeometryIds; - std::call_once(initGeometryIds, [&] { - for (size_t i = 0; i < geometryIds.size(); ++i) { - geometryIds[i] = geometryCache->allocateID(); - } - }); - - batch.setModelTransform(transform); - - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), RED, geometryIds[0]); - geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-HALF_TREE_SCALE, 0.0f, 0.0f), DASHED_RED, - DASH_LENGTH, GAP_LENGTH, geometryIds[1]); - - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), GREEN, geometryIds[2]); - geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -HALF_TREE_SCALE, 0.0f), DASHED_GREEN, - DASH_LENGTH, GAP_LENGTH, geometryIds[3]); - - geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), BLUE, geometryIds[4]); - geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -HALF_TREE_SCALE), DASHED_BLUE, - DASH_LENGTH, GAP_LENGTH, geometryIds[5]); - - // X center boundaries - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), - glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), GREY, - geometryIds[6]); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), - glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY, - geometryIds[7]); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), - glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY, - geometryIds[8]); - geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), - glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY, - geometryIds[9]); - - // Z center boundaries - geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), - glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), GREY, - geometryIds[10]); - geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), - glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), GREY, - geometryIds[11]); - geometryCache->renderLine(batch, glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), - glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY, - geometryIds[12]); - geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), - glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY, - geometryIds[13]); - - // Center boundaries - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), - glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY, - geometryIds[14]); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), - glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), GREY, - geometryIds[15]); - geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), - glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY, - geometryIds[16]); - geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), - glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY, - geometryIds[17]); - - - geometryCache->renderWireCubeInstance(args, batch, GREY4); - - // Draw meter markers along the 3 axis to help with measuring things - const float MARKER_DISTANCE = 1.0f; - const float MARKER_RADIUS = 0.05f; - - transform = Transform().setScale(MARKER_RADIUS); - batch.setModelTransform(transform); - geometryCache->renderSolidSphereInstance(args, batch, RED); - - transform = Transform().setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f)).setScale(MARKER_RADIUS); - batch.setModelTransform(transform); - geometryCache->renderSolidSphereInstance(args, batch, RED); - - transform = Transform().setTranslation(glm::vec3(0.0f, MARKER_DISTANCE, 0.0f)).setScale(MARKER_RADIUS); - batch.setModelTransform(transform); - geometryCache->renderSolidSphereInstance(args, batch, GREEN); - - transform = Transform().setTranslation(glm::vec3(0.0f, 0.0f, MARKER_DISTANCE)).setScale(MARKER_RADIUS); - batch.setModelTransform(transform); - geometryCache->renderSolidSphereInstance(args, batch, BLUE); - - transform = Transform().setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, MARKER_DISTANCE)).setScale(MARKER_RADIUS); - batch.setModelTransform(transform); - geometryCache->renderSolidSphereInstance(args, batch, GREY); -} - // Do some basic timing tests and report the results void runTimingTests() { // How long does it take to make a call to get the time? diff --git a/interface/src/Util.h b/interface/src/Util.h index 976a26ce82..ef289f5416 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -15,14 +15,9 @@ #include #include -#include -#include - class ShapeEntityItem; class ShapeInfo; -void renderWorldBox(RenderArgs* args, gpu::Batch& batch); - void runTimingTests(); void runUnitTests(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index afebd0bb79..7196aa1a2c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -139,7 +139,7 @@ MyAvatar::MyAvatar(QThread* thread) : _flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD), _avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0) { - _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); + _clientTraitsHandler.reset(new ClientTraitsHandler(this)); // give the pointer to our head to inherited _headData variable from AvatarData _headData = new MyHead(this); diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp new file mode 100644 index 0000000000..aecaa74d12 --- /dev/null +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -0,0 +1,301 @@ +// +// GraphicsEngine.cpp +// +// Created by Sam Gateau on 29/6/2018. +// Copyright 2018 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 "GraphicsEngine.h" + +#include + +#include "WorldBox.h" +#include "LODManager.h" + +#include +#include +#include +#include +#include +#include + +#include "RenderEventHandler.h" + +#include +#include +#include +#include + +#include +#include +#include "ui/Stats.h" +#include "Application.h" + +GraphicsEngine::GraphicsEngine() { +} + +GraphicsEngine::~GraphicsEngine() { +} + +void GraphicsEngine::initializeGPU(GLWidget* glwidget) { + + _renderEventHandler = new RenderEventHandler( + [this]() { return this->shouldPaint(); }, + [this]() { this->render_performFrame(); } + ); + + // Requires the window context, because that's what's used in the actual rendering + // and the GPU backend will make things like the VAO which cannot be shared across + // contexts + glwidget->makeCurrent(); + gpu::Context::init(); + glwidget->makeCurrent(); + _gpuContext = std::make_shared(); + + DependencyManager::get()->setGPUContext(_gpuContext); +} + +void GraphicsEngine::initializeRender(bool disableDeferred) { + + // Set up the render engine + render::CullFunctor cullFunctor = LODManager::shouldRender; + _renderEngine->addJob("UpdateScene"); +#ifndef Q_OS_ANDROID + _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !disableDeferred); +#endif + _renderEngine->addJob("RenderMainView", cullFunctor, !disableDeferred, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); + _renderEngine->load(); + _renderEngine->registerScene(_renderScene); + + // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. + DependencyManager::get()->initializeShapePipelines(); +} + +void GraphicsEngine::startup() { + static_cast(_renderEventHandler)->resumeThread(); +} + +void GraphicsEngine::shutdown() { + // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual + // removal of the items. + // See https://highfidelity.fogbugz.com/f/cases/5328 + _renderScene->enqueueFrame(); // flush all the transactions + _renderScene->processTransactionQueue(); // process and apply deletions + + _gpuContext->shutdown(); + + + // shutdown render engine + _renderScene = nullptr; + _renderEngine = nullptr; + + _renderEventHandler->deleteLater(); +} + + +void GraphicsEngine::render_runRenderFrame(RenderArgs* renderArgs) { + PROFILE_RANGE(render, __FUNCTION__); + PerformanceTimer perfTimer("render"); + + // Make sure the WorldBox is in the scene + // For the record, this one RenderItem is the first one we created and added to the scene. + // We could move that code elsewhere but you know... + if (!render::Item::isValidID(WorldBoxRenderData::_item)) { + render::Transaction transaction; + auto worldBoxRenderData = std::make_shared(); + auto worldBoxRenderPayload = std::make_shared(worldBoxRenderData); + + WorldBoxRenderData::_item = _renderScene->allocateID(); + + transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); + _renderScene->enqueueTransaction(transaction); + } + + { + _renderEngine->getRenderContext()->args = renderArgs; + _renderEngine->run(); + } +} + +static const unsigned int THROTTLED_SIM_FRAMERATE = 15; +static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; + + + + +bool GraphicsEngine::shouldPaint() const { + + 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() { + bool expected = false; + return (_renderEventHandler && static_cast(_renderEventHandler)->_pendingRenderEvent.compare_exchange_strong(expected, true)); +} + + + +void GraphicsEngine::render_performFrame() { + // Some plugins process message events, allowing paintGL to be called reentrantly. + + _renderFrameCount++; + + auto lastPaintBegin = usecTimestampNow(); + PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); + PerformanceTimer perfTimer("paintGL"); + + DisplayPluginPointer displayPlugin; + { + PROFILE_RANGE(render, "/getActiveDisplayPlugin"); + displayPlugin = qApp->getActiveDisplayPlugin(); + } + + { + PROFILE_RANGE(render, "/pluginBeginFrameRender"); + // If a display plugin loses it's underlying support, it + // needs to be able to signal us to not use it + if (!displayPlugin->beginFrameRender(_renderFrameCount)) { + QMetaObject::invokeMethod(qApp, "updateDisplayMode"); + return; + } + } + + RenderArgs renderArgs; + glm::mat4 HMDSensorPose; + glm::mat4 eyeToWorld; + glm::mat4 sensorToWorld; + + bool isStereo; + glm::mat4 stereoEyeOffsets[2]; + glm::mat4 stereoEyeProjections[2]; + + { + QMutexLocker viewLocker(&_renderArgsMutex); + renderArgs = _appRenderArgs._renderArgs; + + // don't render if there is no context. + if (!_appRenderArgs._renderArgs._context) { + return; + } + + HMDSensorPose = _appRenderArgs._headPose; + eyeToWorld = _appRenderArgs._eyeToWorld; + sensorToWorld = _appRenderArgs._sensorToWorld; + isStereo = _appRenderArgs._isStereo; + for_each_eye([&](Eye eye) { + stereoEyeOffsets[eye] = _appRenderArgs._eyeOffsets[eye]; + stereoEyeProjections[eye] = _appRenderArgs._eyeProjections[eye]; + }); + } + + { + PROFILE_RANGE(render, "/gpuContextReset"); + getGPUContext()->beginFrame(_appRenderArgs._view, HMDSensorPose); + // Reset the gpu::Context Stages + // Back to the default framebuffer; + gpu::doInBatch("Application_render::gpuContextReset", getGPUContext(), [&](gpu::Batch& batch) { + batch.resetStages(); + }); + } + + + { + PROFILE_RANGE(render, "/renderOverlay"); + PerformanceTimer perfTimer("renderOverlay"); + // NOTE: There is no batch associated with this renderArgs + // the ApplicationOverlay class assumes it's viewport is setup to be the device size + renderArgs._viewport = glm::ivec4(0, 0, qApp->getDeviceSize()); + qApp->getApplicationOverlay().renderOverlay(&renderArgs); + } + + { + PROFILE_RANGE(render, "/updateCompositor"); + qApp->getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld); + } + + gpu::FramebufferPointer finalFramebuffer; + QSize finalFramebufferSize; + { + PROFILE_RANGE(render, "/getOutputFramebuffer"); + // Primary rendering pass + auto framebufferCache = DependencyManager::get(); + finalFramebufferSize = framebufferCache->getFrameBufferSize(); + // Final framebuffer that will be handled to the display-plugin + finalFramebuffer = framebufferCache->getFramebuffer(); + } + + { + if (isStereo) { + renderArgs._context->enableStereo(true); + renderArgs._context->setStereoProjections(stereoEyeProjections); + renderArgs._context->setStereoViews(stereoEyeOffsets); + } + + renderArgs._hudOperator = displayPlugin->getHUDOperator(); + renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); + renderArgs._blitFramebuffer = finalFramebuffer; + render_runRenderFrame(&renderArgs); + } + + auto frame = getGPUContext()->endFrame(); + frame->frameIndex = _renderFrameCount; + frame->framebuffer = finalFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) { + auto frameBufferCache = DependencyManager::get(); + if (frameBufferCache) { + frameBufferCache->releaseFramebuffer(framebuffer); + } + }; + // deliver final scene rendering commands to the display plugin + { + PROFILE_RANGE(render, "/pluginOutput"); + PerformanceTimer perfTimer("pluginOutput"); + _renderLoopCounter.increment(); + displayPlugin->submitFrame(frame); + } + + // Reset the framebuffer and stereo state + renderArgs._blitFramebuffer.reset(); + renderArgs._context->enableStereo(false); + + { + auto stats = Stats::getInstance(); + if (stats) { + stats->setRenderDetails(renderArgs._details); + } + } + + uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; + _frameTimingsScriptingInterface.addValue(lastPaintDuration); +} + + +void GraphicsEngine::editRenderArgs(RenderArgsEditor editor) { + QMutexLocker renderLocker(&_renderArgsMutex); + editor(_appRenderArgs); +} diff --git a/interface/src/graphics/GraphicsEngine.h b/interface/src/graphics/GraphicsEngine.h new file mode 100644 index 0000000000..490f5312b5 --- /dev/null +++ b/interface/src/graphics/GraphicsEngine.h @@ -0,0 +1,90 @@ +// +// GraphicsEngine.h +// +// Created by Sam Gateau on 29/6/2018. +// Copyright 2018 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 +// +#ifndef hifi_GraphicsEngine_h +#define hifi_GraphicsEngine_h + +#include +#include +#include + +#include + +#include +#include + +#include "FrameTimingsScriptingInterface.h" + + +struct AppRenderArgs { + render::Args _renderArgs; + glm::mat4 _eyeToWorld; + glm::mat4 _view; + glm::mat4 _eyeOffsets[2]; + glm::mat4 _eyeProjections[2]; + glm::mat4 _headPose; + glm::mat4 _sensorToWorld; + float _sensorToWorldScale{ 1.0f }; + bool _isStereo{ false }; +}; + +using RenderArgsEditor = std::function ; + + +class GraphicsEngine { +public: + GraphicsEngine(); + ~GraphicsEngine(); + + void initializeGPU(GLWidget*); + void initializeRender(bool disableDeferred); + void startup(); + void shutdown(); + + render::ScenePointer getRenderScene() const { return _renderScene; } + render::EnginePointer getRenderEngine() const { return _renderEngine; } + gpu::ContextPointer getGPUContext() const { return _gpuContext; } + + // Same as the one in application + bool shouldPaint() const; + bool checkPendingRenderEvent(); + + size_t getRenderFrameCount() const { return _renderFrameCount; } + float getRenderLoopRate() const { return _renderLoopCounter.rate(); } + + // Feed GRaphics Engine with new frame configuration + void editRenderArgs(RenderArgsEditor editor); + +private: + // Thread specific calls + void render_performFrame(); + void render_runRenderFrame(RenderArgs* renderArgs); + +protected: + + mutable QMutex _renderArgsMutex{ QMutex::Recursive }; + AppRenderArgs _appRenderArgs; + + RateCounter<500> _renderLoopCounter; + + uint32_t _renderFrameCount{ 0 }; + render::ScenePointer _renderScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; + render::EnginePointer _renderEngine{ new render::RenderEngine() }; + + gpu::ContextPointer _gpuContext; // initialized during window creation + + QObject* _renderEventHandler{ nullptr }; + friend class RenderEventHandler; + + FrameTimingsScriptingInterface _frameTimingsScriptingInterface; + + friend class Application; +}; + +#endif // hifi_GraphicsEngine_h diff --git a/interface/src/graphics/RenderEventHandler.cpp b/interface/src/graphics/RenderEventHandler.cpp new file mode 100644 index 0000000000..bdb2cae060 --- /dev/null +++ b/interface/src/graphics/RenderEventHandler.cpp @@ -0,0 +1,58 @@ +// +// RenderEventHandler.cpp +// +// Created by Bradley Austin Davis on 29/6/2018. +// Copyright 2018 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 "RenderEventHandler.h" + +#include "Application.h" +#include +#include + +#include "CrashHandler.h" + +RenderEventHandler::RenderEventHandler(CheckCall checkCall, RenderCall renderCall) : + _checkCall(checkCall), + _renderCall(renderCall) +{ + // Transfer to a new thread + moveToNewNamedThread(this, "RenderThread", [this](QThread* renderThread) { + hifi::qt::addBlockingForbiddenThread("Render", renderThread); + _lastTimeRendered.start(); + }, std::bind(&RenderEventHandler::initialize, this), QThread::HighestPriority); +} + +void RenderEventHandler::initialize() { + setObjectName("Render"); + PROFILE_SET_THREAD_NAME("Render"); + setCrashAnnotation("render_thread_id", std::to_string((size_t)QThread::currentThreadId())); +} + +void RenderEventHandler::resumeThread() { + _pendingRenderEvent = false; +} + +void RenderEventHandler::render() { + if (_checkCall()) { + _lastTimeRendered.start(); + _renderCall(); + } +} + +bool RenderEventHandler::event(QEvent* event) { + switch ((int)event->type()) { + case ApplicationEvent::Render: + render(); + _pendingRenderEvent.store(false); + return true; + + default: + break; + } + return Parent::event(event); +} + diff --git a/interface/src/graphics/RenderEventHandler.h b/interface/src/graphics/RenderEventHandler.h new file mode 100644 index 0000000000..93f8b548d0 --- /dev/null +++ b/interface/src/graphics/RenderEventHandler.h @@ -0,0 +1,52 @@ +// +// RenderEventHandler.h +// +// Created by Bradley Austin Davis on 29/6/2018. +// Copyright 2018 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 +// +#ifndef hifi_RenderEventHandler_h +#define hifi_RenderEventHandler_h + +#include +#include +#include "gl/OffscreenGLCanvas.h" + +enum ApplicationEvent { + // Execute a lambda function + Lambda = QEvent::User + 1, + // Trigger the next render + Render, + // Trigger the next idle + Idle, +}; + +class RenderEventHandler : public QObject { + using Parent = QObject; + Q_OBJECT +public: + + using CheckCall = std::function ; + using RenderCall = std::function ; + + CheckCall _checkCall; + RenderCall _renderCall; + + RenderEventHandler(CheckCall checkCall, RenderCall renderCall); + + QElapsedTimer _lastTimeRendered; + std::atomic _pendingRenderEvent{ true }; + + void resumeThread(); + +private: + void initialize(); + + void render(); + + bool event(QEvent* event) override; +}; + +#endif // #include hifi_RenderEventHandler_h \ No newline at end of file diff --git a/interface/src/graphics/WorldBox.cpp b/interface/src/graphics/WorldBox.cpp new file mode 100644 index 0000000000..908055e9c6 --- /dev/null +++ b/interface/src/graphics/WorldBox.cpp @@ -0,0 +1,138 @@ +// +// WorldBox.cpp +// +// Created by Sam Gateau on 01/07/2018. +// Copyright 2018 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 "WorldBox.h" + +#include "OctreeConstants.h" + +render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; + + +namespace render { + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); } + template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } + template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { + if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { + PerformanceTimer perfTimer("worldBox"); + + auto& batch = *args->_batch; + DependencyManager::get()->bindSimpleProgram(batch); + WorldBoxRenderData::renderWorldBox(args, batch); + } + } +} + +void WorldBoxRenderData::renderWorldBox(RenderArgs* args, gpu::Batch& batch) { + auto geometryCache = DependencyManager::get(); + + // Show center of world + static const glm::vec3 RED(1.0f, 0.0f, 0.0f); + static const glm::vec3 GREEN(0.0f, 1.0f, 0.0f); + static const glm::vec3 BLUE(0.0f, 0.0f, 1.0f); + static const glm::vec3 GREY(0.5f, 0.5f, 0.5f); + static const glm::vec4 GREY4(0.5f, 0.5f, 0.5f, 1.0f); + + static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f); + static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f); + static const glm::vec4 DASHED_BLUE(0.0f, 0.0f, 1.0f, 1.0f); + static const float DASH_LENGTH = 1.0f; + static const float GAP_LENGTH = 1.0f; + auto transform = Transform{}; + static std::array geometryIds; + static std::once_flag initGeometryIds; + std::call_once(initGeometryIds, [&] { + for (size_t i = 0; i < geometryIds.size(); ++i) { + geometryIds[i] = geometryCache->allocateID(); + } + }); + + batch.setModelTransform(transform); + + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), RED, geometryIds[0]); + geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-HALF_TREE_SCALE, 0.0f, 0.0f), DASHED_RED, + DASH_LENGTH, GAP_LENGTH, geometryIds[1]); + + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), GREEN, geometryIds[2]); + geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -HALF_TREE_SCALE, 0.0f), DASHED_GREEN, + DASH_LENGTH, GAP_LENGTH, geometryIds[3]); + + geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), BLUE, geometryIds[4]); + geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -HALF_TREE_SCALE), DASHED_BLUE, + DASH_LENGTH, GAP_LENGTH, geometryIds[5]); + + // X center boundaries + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), + glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), GREY, + geometryIds[6]); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), + glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY, + geometryIds[7]); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), + glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY, + geometryIds[8]); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), + glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY, + geometryIds[9]); + + // Z center boundaries + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), + glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), GREY, + geometryIds[10]); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE), + glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), GREY, + geometryIds[11]); + geometryCache->renderLine(batch, glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), + glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY, + geometryIds[12]); + geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), + glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY, + geometryIds[13]); + + // Center boundaries + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), + glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY, + geometryIds[14]); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), + glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), GREY, + geometryIds[15]); + geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), + glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY, + geometryIds[16]); + geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), + glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY, + geometryIds[17]); + + + geometryCache->renderWireCubeInstance(args, batch, GREY4); + + // Draw meter markers along the 3 axis to help with measuring things + const float MARKER_DISTANCE = 1.0f; + const float MARKER_RADIUS = 0.05f; + + transform = Transform().setScale(MARKER_RADIUS); + batch.setModelTransform(transform); + geometryCache->renderSolidSphereInstance(args, batch, RED); + + transform = Transform().setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f)).setScale(MARKER_RADIUS); + batch.setModelTransform(transform); + geometryCache->renderSolidSphereInstance(args, batch, RED); + + transform = Transform().setTranslation(glm::vec3(0.0f, MARKER_DISTANCE, 0.0f)).setScale(MARKER_RADIUS); + batch.setModelTransform(transform); + geometryCache->renderSolidSphereInstance(args, batch, GREEN); + + transform = Transform().setTranslation(glm::vec3(0.0f, 0.0f, MARKER_DISTANCE)).setScale(MARKER_RADIUS); + batch.setModelTransform(transform); + geometryCache->renderSolidSphereInstance(args, batch, BLUE); + + transform = Transform().setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, MARKER_DISTANCE)).setScale(MARKER_RADIUS); + batch.setModelTransform(transform); + geometryCache->renderSolidSphereInstance(args, batch, GREY); +} + diff --git a/interface/src/graphics/WorldBox.h b/interface/src/graphics/WorldBox.h new file mode 100644 index 0000000000..114777ba0f --- /dev/null +++ b/interface/src/graphics/WorldBox.h @@ -0,0 +1,43 @@ +// +// WorldBox.h +// +// Created by Sam Gateau on 01/07/2018. +// Copyright 2018 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 +// +#ifndef hifi_WorldBox_h +#define hifi_WorldBox_h + +#include + +#include +#include + +#include +#include +#include "Menu.h" + + + +class WorldBoxRenderData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + int _val = 0; + static render::ItemID _item; // unique WorldBoxRenderData + + + + static void renderWorldBox(RenderArgs* args, gpu::Batch& batch); +}; + +namespace render { + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff); + template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff); + template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args); +} + +#endif // hifi_WorldBox_h \ No newline at end of file diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 26781631db..3579776213 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -55,8 +55,6 @@ ApplicationOverlay::~ApplicationOverlay() { // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { PROFILE_RANGE(render, __FUNCTION__); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); - buildFramebufferObject(); if (!_overlayFramebuffer) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 36c6ed6c50..7d88cab209 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1490,7 +1490,7 @@ protected: bool _isClientAvatar { false }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer - std::unique_ptr _clientTraitsHandler; + std::unique_ptr _clientTraitsHandler; template T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index f8247d9e52..a301341a8e 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -22,7 +22,7 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) : _owningAvatar(owningAvatar) { auto nodeList = DependencyManager::get(); - QObject::connect(nodeList.data(), &NodeList::nodeAdded, [this](SharedNodePointer addedNode){ + QObject::connect(nodeList.data(), &NodeList::nodeAdded, this, [this](SharedNodePointer addedNode) { if (addedNode->getType() == NodeType::AvatarMixer) { resetForNewMixer(); } diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 69a8587581..4d65f0eb1b 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -180,9 +180,7 @@ void Deck::processFrames() { #ifdef WANT_RECORDING_DEBUG qCDebug(recordingLog) << "Setting timer for next processing " << nextInterval; #endif - _timer.singleShot(nextInterval, [this] { - processFrames(); - }); + _timer.singleShot(nextInterval, this, &Deck::processFrames); } void Deck::removeClip(const ClipConstPointer& clip) { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 0e380b6c02..bbb370010e 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -303,7 +303,7 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() { } void AmbientOcclusionEffect::configure(const Config& config) { - DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); + DependencyManager::get()->setAmbientOcclusionEnabled(config.isEnabled()); bool shouldUpdateBlurs = false; bool shouldUpdateTechnique = false; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 864aef09e4..af6f6b21a3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -78,7 +78,6 @@ using AmbientOcclusionFramebufferPointer = std::shared_ptr(blurName); - blurConfig->setProperty("filterScale", 1.0f); + blurConfig->filterScale = 1.0f; } } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 166366e65b..8afccfca13 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -25,7 +25,6 @@ class DebugDeferredBufferConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled) Q_PROPERTY(int mode MEMBER mode WRITE setMode) Q_PROPERTY(glm::vec4 size MEMBER size NOTIFY dirty) public: diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8cf56ec7ad..e0fd9e970d 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -606,11 +606,16 @@ void RenderDeferredCleanup::run(const render::RenderContextPointer& renderContex } } +RenderDeferred::RenderDeferred(bool renderShadows): + _renderShadows(renderShadows) +{ + DependencyManager::get()->init(); +} + void RenderDeferred::configure(const Config& config) { } void RenderDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { - PROFILE_RANGE(render, "DeferredLighting"); auto deferredTransform = inputs.get0(); auto deferredFramebuffer = inputs.get1(); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index a838d0c4d6..9fd9554d31 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -189,8 +189,7 @@ public: using Config = RenderDeferredConfig; using JobModel = render::Job::ModelI; - RenderDeferred() {} - RenderDeferred(bool renderShadows) : _renderShadows(renderShadows) {} + RenderDeferred(bool renderShadows = false); void configure(const Config& config); diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 8fe3b0fef5..d5e1ca4644 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -444,10 +444,12 @@ LightStageSetup::LightStageSetup() { } void LightStageSetup::run(const render::RenderContextPointer& renderContext) { - auto stage = renderContext->_scene->getStage(LightStage::getName()); - if (!stage) { - stage = std::make_shared(); - renderContext->_scene->resetStage(LightStage::getName(), stage); + if (renderContext->_scene) { + auto stage = renderContext->_scene->getStage(LightStage::getName()); + if (!stage) { + stage = std::make_shared(); + renderContext->_scene->resetStage(LightStage::getName(), stage); + } } } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 0b6aebadd7..b5b3e9c5b9 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -38,7 +38,7 @@ using namespace render; extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void RenderShadowTask::configure(const Config& configuration) { - DependencyManager::get()->setShadowMapEnabled(configuration.enabled); + DependencyManager::get()->setShadowMapEnabled(configuration.isEnabled()); // This is a task, so must still propogate configure() to its Jobs // Task::configure(configuration); } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 8aaf554514..e90725d66d 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -38,7 +38,6 @@ protected: class RenderShadowTaskConfig : public render::Task::Config::Persistent { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) public: RenderShadowTaskConfig() : render::Task::Config::Persistent(QStringList() << "Render" << "Engine" << "Shadows", true) {} diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMappingEffect.h index 046e7606b3..69694b13f5 100644 --- a/libraries/render-utils/src/ToneMappingEffect.h +++ b/libraries/render-utils/src/ToneMappingEffect.h @@ -64,7 +64,6 @@ private: class ToneMappingConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled) Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); Q_PROPERTY(int curve MEMBER curve WRITE setCurve); public: diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 3f7c07ded3..0c7441404a 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -20,7 +20,6 @@ namespace render { class DrawSceneOctreeConfig : public Job::Config { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty()) Q_PROPERTY(bool showVisibleCells READ getShowVisibleCells WRITE setShowVisibleCells NOTIFY dirty()) Q_PROPERTY(bool showEmptyCells READ getShowEmptyCells WRITE setShowEmptyCells NOTIFY dirty()) Q_PROPERTY(int numAllocatedCells READ getNumAllocatedCells) @@ -77,7 +76,6 @@ namespace render { class DrawItemSelectionConfig : public Job::Config { Q_OBJECT - Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty()) Q_PROPERTY(bool showInsideItems READ getShowInsideItems WRITE setShowInsideItems NOTIFY dirty()) Q_PROPERTY(bool showInsideSubcellItems READ getShowInsideSubcellItems WRITE setShowInsideSubcellItems NOTIFY dirty()) Q_PROPERTY(bool showPartialItems READ getShowPartialItems WRITE setShowPartialItems NOTIFY dirty()) diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index a1b61a4e77..76ed5aa663 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -26,7 +26,7 @@ using namespace render; void DrawStatusConfig::dirtyHelper() { - enabled = showNetwork || showDisplay; + _isEnabled = showNetwork || showDisplay; emit dirty(); } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index f36574bed6..709eeca9b2 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -113,6 +113,13 @@ void doEvery(quint64& lastReportUsecs, quint64 secs, F lamdba) { // Maximum accuracy in msecs float secTimestampNow(); +// Custom deleter for QObjects that calls deleteLater +struct LaterDeleter { + void operator()(QObject* ptr) { + ptr->deleteLater(); + } +}; + float randFloat(); int randIntInRange (int min, int max); float randFloatInRange (float min,float max); diff --git a/libraries/task/src/task/Config.cpp b/libraries/task/src/task/Config.cpp index b378237c9c..5e8e4b246d 100644 --- a/libraries/task/src/task/Config.cpp +++ b/libraries/task/src/task/Config.cpp @@ -18,6 +18,17 @@ using namespace task; +JobConfig::~JobConfig() { + +} + +void JobConfig::setEnabled(bool enable) { + if (_isEnabled != enable) { + _isEnabled = enable; + emit dirtyEnabled(); + } +} + void JobConfig::setPresetList(const QJsonObject& object) { for (auto it = object.begin(); it != object.end(); it++) { JobConfig* child = findChild(it.key(), Qt::FindDirectChildrenOnly); @@ -68,3 +79,57 @@ void TaskConfig::refresh() { _task->applyConfiguration(); } +TaskConfig* TaskConfig::getRootConfig(const std::string& jobPath, std::string& jobName) const { + TaskConfig* root = const_cast (this); + + std::list tokens; + std::size_t pos = 0, sepPos; + while ((sepPos = jobPath.find_first_of('.', pos)) != std::string::npos) { + std::string token = jobPath.substr(pos, sepPos - pos); + if (!token.empty()) { + tokens.push_back(token); + } + pos = sepPos + 1; + } + { + std::string token = jobPath.substr(pos, sepPos - pos); + if (!token.empty()) { + tokens.push_back(token); + } + } + + if (tokens.empty()) { + return root; + } + else { + while (tokens.size() > 1) { + auto taskName = tokens.front(); + tokens.pop_front(); + root = root->findChild((taskName.empty() ? QString() : QString(taskName.c_str()))); + if (!root) { + return nullptr; + } + } + jobName = tokens.front(); + } + return root; +} + +JobConfig* TaskConfig::getJobConfig(const std::string& jobPath) const { + std::string jobName; + auto root = getRootConfig(jobPath, jobName); + + if (!root) { + return nullptr; + } + if (jobName.empty()) { + return root; + } else { + + auto found = root->findChild((jobName.empty() ? QString() : QString(jobName.c_str()))); + if (!found) { + return nullptr; + } + return found; + } +} diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index 4379dbbaa6..da9b95a274 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -50,12 +50,10 @@ public: _default = toJsonValue(*this).toObject().toVariantMap(); _presets.unite(list.toVariantMap()); - if (C::alwaysEnabled || C::enabled) { + if (C::isEnabled()) { _presets.insert(DEFAULT, _default); } - if (!C::alwaysEnabled) { - _presets.insert(NONE, QVariantMap{{ "enabled", false }}); - } + _presets.insert(NONE, QVariantMap{{ "enabled", false }}); auto preset = _preset.get(); if (preset != _preset.getDefault() && _presets.contains(preset)) { @@ -92,17 +90,21 @@ class JobConfig : public QObject { Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY dirtyEnabled()) double _msCPURunTime{ 0.0 }; + +protected: + friend class TaskConfig; + + bool _isEnabled{ true }; + public: using Persistent = PersistentConfig; JobConfig() = default; - JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} + JobConfig(bool enabled): _isEnabled{ enabled } {} + ~JobConfig(); - bool isEnabled() { return alwaysEnabled || enabled; } - void setEnabled(bool enable) { enabled = alwaysEnabled || enable; emit dirtyEnabled(); } - - bool alwaysEnabled{ true }; - bool enabled{ true }; + bool isEnabled() const { return _isEnabled; } + void setEnabled(bool enable); virtual void setPresetList(const QJsonObject& object); @@ -200,6 +202,7 @@ public: */ class TaskConfig : public JobConfig { Q_OBJECT + public: using Persistent = PersistentConfig; @@ -221,27 +224,13 @@ public: // // getter for qml integration, prefer the templated getter Q_INVOKABLE QObject* getConfig(const QString& name) { return getConfig(name.toStdString()); } + // getter for cpp (strictly typed), prefer this getter - template typename T::Config* getConfig(std::string job = "") const { - const TaskConfig* root = this; - QString path = (job.empty() ? QString() : QString(job.c_str())); // an empty string is not a null string - auto tokens = path.split('.', QString::SkipEmptyParts); + TaskConfig* getRootConfig(const std::string& jobPath, std::string& jobName) const; + JobConfig* getJobConfig(const std::string& jobPath) const; - if (tokens.empty()) { - tokens.push_back(QString()); - } - else { - while (tokens.size() > 1) { - auto name = tokens.front(); - tokens.pop_front(); - root = QObject::findChild(name); - if (!root) { - return nullptr; - } - } - } - - return root->findChild(tokens.front()); + template typename T::Config* getConfig(std::string jobPath = "") const { + return dynamic_cast(getJobConfig(jobPath)); } Q_INVOKABLE bool isTask() const override { return true; } diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index 87e7090d64..b00e5a8de7 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -164,7 +164,7 @@ public: void run(const ContextPointer& jobContext) override { jobContext->jobConfig = std::static_pointer_cast(Concept::_config); - if (jobContext->jobConfig->alwaysEnabled || jobContext->jobConfig->isEnabled()) { + if (jobContext->jobConfig->isEnabled()) { jobRun(_data, jobContext, _input.get(), _output.edit()); } jobContext->jobConfig.reset(); @@ -340,7 +340,7 @@ public: void run(const ContextPointer& jobContext) override { auto config = std::static_pointer_cast(Concept::_config); - if (config->alwaysEnabled || config->enabled) { + if (config->isEnabled()) { for (auto job : TaskConcept::_jobs) { job.run(jobContext); if (jobContext->taskFlow.doAbortTask()) { diff --git a/scripts/developer/utilities/lib/jet/jet.js b/scripts/developer/utilities/lib/jet/jet.js index d78b433a68..40563e4b2c 100644 --- a/scripts/developer/utilities/lib/jet/jet.js +++ b/scripts/developer/utilities/lib/jet/jet.js @@ -99,6 +99,121 @@ function job_print_functor(printout, showProps, showInOuts, maxDepth) { } } +// Use this function to create a functor that will build a tree datastructure of the Job visited + +function job_tree_model_array_functor(jobTreeArray, newNodeFunctor) { + var jobsRoot; + var numJobs = 0; + var jobTreePath = [] + if (newNodeFunctor === undefined) newNodeFunctor = function (node) {} + + return function (job, depth, index) { + var id = numJobs + var newItem = {"name": job.objectName, "level": depth, "index": index, "id": id, "subNode": [], "path": ""} + if (depth == 0) { + newNodeFunctor(newItem) + jobTreeArray.push(newItem) + numJobs++ + jobsRoot = jobTreeArray[0].subNode; + } else { + if (jobTreePath.length < depth) { + var node = jobsRoot; + var path; + for (var n = 0; n < jobTreePath.length; n++) { + newItem.path += (n > 0 ? "." : "") + node[jobTreePath[n]].name + node = node[jobTreePath[n]].subNode + } + + newNodeFunctor(newItem) + node.push((newItem)) + numJobs++ + jobTreePath.push(0); + } else if (jobTreePath.length >= depth) { + var node = jobsRoot; + for (var n = 0; n < (depth - 1); n++) { + newItem.path += (n > 0 ? "." : "") + node[jobTreePath[n]].name + node = node[jobTreePath[n]].subNode + } + + newNodeFunctor(newItem) + node.push((newItem)) + numJobs++ + jobTreePath[depth-1] = index; + while (jobTreePath.length > depth) { + jobTreePath.pop(); + } + } + } + return true; + } +} + +function job_tree_model_functor(jobTreeModel, maxLevel, newNodeFunctor) { + var jobsRoot; + var numJobs = 0; + var jobTreePath = [] + if (newNodeFunctor === undefined) newNodeFunctor = function (node) {} + + return function (job, depth, index) { + var id = numJobs + var newItem = {"name": job.objectName, "level": depth, "index": index, "id": id, "subNode": [], "path": "", "init": (depth < maxLevel), "ud": {}} + if (depth == 0) { + newNodeFunctor(newItem) + jobTreeModel.append(newItem) + numJobs++ + jobsRoot = jobTreeModel.get(0).subNode; + } else { + if (jobTreePath.length < depth) { + var node = jobsRoot; + var path; + for (var n = 0; n < jobTreePath.length; n++) { + newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name + node = node.get(jobTreePath[n]).subNode + } + + newNodeFunctor(newItem) + node.append((newItem)) + numJobs++ + jobTreePath.push(0); + } else if (jobTreePath.length >= depth) { + var node = jobsRoot; + for (var n = 0; n < (depth - 1); n++) { + newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name + node = node.get(jobTreePath[n]).subNode + } + + newNodeFunctor(newItem) + node.append((newItem)) + numJobs++ + jobTreePath[depth-1] = index; + while (jobTreePath.length > depth) { + jobTreePath.pop(); + } + } + } + return true; + } +} + + +// Traverse the jobTreenode data structure created above +function job_traverseTreeNode(root, functor, depth) { + if (root.subNode.length) { + depth++; + for (var i = 0; i 0 ? "." : "") + node.get(jobTreePath[n]).name - node = node.get(jobTreePath[n]).subNode - } - node.append(newItem) - jobTreePath.push(0); - } else if (jobTreePath.length >= depth) { - var node = jobsRoot; - for (var n = 0; n < (depth - 1); n++) { - newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name - node = node.get(jobTreePath[n]).subNode - } - node.append(newItem) - jobTreePath[depth-1] = index; - while (jobTreePath.length > depth) { - jobTreePath.pop(); - } - } - } - return true; - } - + var functor = Jet.job_tree_model_functor(jobsModel, 3, function(node) { + node["cpuT"] = 0.0 + }) Jet.task_traverseTree(rootConfig, functor); } - + ListModel { id: jobsModel + property var engineJobItemModel : [] } Component { @@ -77,30 +44,46 @@ Rectangle { id: objRecursiveColumn clip: true visible: model.init - - MouseArea { - width: objRow.implicitWidth - height: objRow.implicitHeight - onDoubleClicked: { - for(var i = 1; i < parent.children.length - 1; ++i) { - parent.children[i].visible = !parent.children[i].visible - } + + function switchFold() { + for(var i = 1; i < children.length - 1; ++i) { + children[i].visible = !children[i].visible } - Row { - id: objRow - Item { - height: 1 - width: model.level * 15 + } + + Row { + id: objRow + Item { + height: 1 + width: model.level * 15 + } + + HifiControls.CheckBox { + id: objCheck + property var config: root.rootConfig.getConfig(model.path + "." + model.name); + text: " " + checked: root.rootConfig.getConfig(model.path + "." + model.name).enabled + onCheckedChanged: { root.rootConfig.getConfig(model.path + "." + model.name).enabled = checked } + } + + MouseArea { + width: objLabel.implicitWidth + height: objLabel.implicitHeight + onDoubleClicked: { + parent.parent.switchFold() } - HifiControls.CheckBox { - property var config: root.rootConfig.getConfig(model.path + "." + model.name); + + HifiControls.Label { + id: objLabel + colorScheme: (root.rootConfig.getConfig(model.path + "." + model.name) ? hifi.colorSchemes.dark : hifi.colorSchemes.light) text: (objRecursiveColumn.children.length > 2 ? objRecursiveColumn.children[1].visible ? - qsTr("- ") : qsTr("+ ") : qsTr(" ")) + model.name + " ms=" + config.cpuRunTime.toFixed(2) - checked: config.enabled + qsTr("- ") : qsTr("+ ") : qsTr(" ")) + model.name + + " id=" + model.id } } } + Repeater { model: subNode delegate: objRecursiveDelegate diff --git a/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml b/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml new file mode 100644 index 0000000000..f3ca550e44 --- /dev/null +++ b/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml @@ -0,0 +1,265 @@ +// +// jet/TaskTimeFrameView.qml +// +// Created by Sam Gateau, 2018/06/15 +// Copyright 2018 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 1.4 as Original +import QtQuick.Controls.Styles 1.4 + +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls + +import "../jet.js" as Jet + +Rectangle { + HifiConstants { id: hifi;} + color: Qt.rgba(hifi.colors.baseGray.r, hifi.colors.baseGray.g, hifi.colors.baseGray.b, 0.8); + id: root; + + property var rootConfig : Workload + + property var jobsTree + property var jobsArray + + + Component.onCompleted: { + if (!jobsTree) { jobsTree = new Array(); } + if (!jobsArray) { jobsArray = new Array(); } + + var tfunctor = Jet.job_tree_model_array_functor(jobsTree, function(node) { + var job = { "fullpath": (node.path + "." + node.name), "cpuT": 0.0, "depth": node.level, "name": node.name } + jobsArray.push(job) + }) + Jet.task_traverseTree(rootConfig, tfunctor); + + for (var j = 0; j ") + height: 24 + width: 24 + onClicked: { + print("list of highlight styles") + myCanvasTimer.running = !myCanvasTimer.running + } + } + } + Canvas { + id: mycanvas + anchors.top:myHeaderRow.bottom + anchors.bottom:parent.bottom + anchors.left:parent.left + anchors.right:parent.right + + property var frameDuration: 10 + property var frameViewBegin: 0 + property var frameViewRange: width + + function reset() { + frameViewBegin = 0 + frameViewRange = width + } + + function checkView() { + if (frameViewBegin > width * 0.9) { + frameViewBegin = width * 0.9 + } else if (frameViewBegin + frameViewRange < width * 0.1) { + frameViewBegin = width * 0.1 -frameViewRange + } + } + + function drag(deltaX) { + frameViewBegin -= deltaX + checkView() + } + + function pivotScale(pivotX, deltaX) { + var newRange = frameViewRange + 2 * deltaX + if (newRange <= 1) { + newRange = 2; + } + frameViewBegin = pivotX - frameViewRange * (pivotX - frameViewBegin) / newRange + frameViewRange = newRange + print( "pivot= " + pivotX + " deltaX= " + (deltaX)) + checkView() + } + + + onPaint: { + // print("mycanvasOnPaint " + jobsArray.length) + var lineHeight = 12; + + function getXFromTime(t) { + return (t / mycanvas.frameDuration) * mycanvas.frameViewRange - (mycanvas.frameViewBegin) + } + function getWFromDuration(d) { + return (d / mycanvas.frameDuration) * mycanvas.frameViewRange + } + function displayBackground(ctx) { + ctx.fillStyle = Qt.rgba(0, 0, 0, root.backgroundOpacity); + ctx.fillRect(0, 0, width, height); + + ctx.strokeStyle= "grey"; + ctx.lineWidth="2"; + + ctx.beginPath(); + ctx.moveTo(0, lineHeight + 1); + ctx.lineTo(width, lineHeight + 1); + ctx.moveTo(0, height); + ctx.lineTo(width, height); + + var x0 = getXFromTime(0) + ctx.moveTo(x0, 0); + ctx.lineTo(x0, height); + + x0 = getXFromTime(5) + ctx.moveTo(x0, 0); + ctx.lineTo(x0, height); + + x0 = getXFromTime(10) + ctx.moveTo(x0, 0); + ctx.lineTo(x0, height); + + ctx.stroke(); + } + + function drawJob(ctx, depth, index, duration, timeOffset) { + //print(root.jobsArray[index].cpuT) + ctx.fillStyle = ( depth % 2 ? ( index % 2 ? "blue" : "yellow") : ( index % 2 ? "green" : "red")) + ctx.fillRect(getXFromTime(timeOffset), lineHeight * 2 * depth,getWFromDuration(duration), lineHeight); + + if (depth,getWFromDuration(duration) >= width * 0.1) { + ctx.fillStyle = "grey"; + ctx.textAlign = "center"; + ctx.fillText( root.jobsArray[index].name, getXFromTime(timeOffset + duration * 0.5), lineHeight * 2 * depth); + + } + } + + var ctx = getContext("2d"); + ctx.clearRect(0, 0, width, height); + ctx.font="12px Verdana"; + + displayBackground(ctx); + if (jobsArray.length > 0) { + mycanvas.frameDuration = Math.max(jobsArray[0].cpuT, 1) + var rangeStack =new Array() + rangeStack.push( { "b": 0.0, "e": mycanvas.frameDuration } ) + + drawJob(ctx, 0, 0, jobsArray[0].cpuT, 0) + + for (var i = 1; i lastDepth) { + timeOffset = rangeStack[lastDepth].b + while(rangeStack.length <= depth) { + rangeStack.push( { "b": timeOffset, "e": timeOffset + duration } ) + } + + } else { + if (depth < lastDepth) { + while(rangeStack.length != (depth + 1)) { + rangeStack.pop() + } + } + + timeOffset = rangeStack[depth].e + rangeStack[depth].b = timeOffset + rangeStack[depth].e = timeOffset + duration + } + if (duration > 0.0) { + drawJob(ctx, depth, i, duration, timeOffset) + } + } + } + } + } + + MouseArea { + id: hitbox + anchors.fill: mycanvas + acceptedButtons: Qt.LeftButton | Qt.RightButton + + property var pivotX + property var dragPos + onPressed: { + dragPos = { "x":mouse.x, "y":mouse.y } + pivotX = mouse.x + } + onPositionChanged: { + if (dragPos !== undefined) { + var delta = mouse.x - dragPos.x + + if (mouse.buttons & Qt.LeftButton) { + mycanvas.drag(delta) + } + + if (mouse.buttons & Qt.RightButton) { + mycanvas.pivotScale(pivotX, delta) + } + + dragPos.x = mouse.x + dragPos.y = mouse.y + mycanvas.requestPaint() + } + } + onReleased: { + dragPos = undefined + } + + onWheel: { + mycanvas.pivotScale(wheel.x, mycanvas.frameViewRange * 0.02 * (wheel.angleDelta.y / 120.0)) + mycanvas.requestPaint() + } + + onDoubleClicked: { + mycanvas.reset() + mycanvas.requestPaint() + } + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/jet/qml/qmldir b/scripts/developer/utilities/lib/jet/qml/qmldir index 2df22caa6a..e16820914b 100644 --- a/scripts/developer/utilities/lib/jet/qml/qmldir +++ b/scripts/developer/utilities/lib/jet/qml/qmldir @@ -1,2 +1,3 @@ TaskList 1.0 TaskList.qml TaskViewList 1.0 TaskViewList.qml +TaskTimeFrameView 1.0 TaskTimeFrameView.qml diff --git a/scripts/developer/utilities/render/engineInspector.js b/scripts/developer/utilities/render/engineInspector.js index dcf13157b5..cd2b74f907 100644 --- a/scripts/developer/utilities/render/engineInspector.js +++ b/scripts/developer/utilities/render/engineInspector.js @@ -1,13 +1,57 @@ - function openEngineTaskView() { - // Set up the qml ui - var qml = Script.resolvePath('engineInspector.qml'); - var window = new OverlayWindow({ - title: 'Render Engine', - source: qml, - width: 300, - height: 400 + (function() { + var TABLET_BUTTON_NAME = "Inspector"; + var QMLAPP_URL = Script.resolvePath("./engineInspector.qml"); + var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg"); + var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg"); + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: ICON_URL, + activeIcon: ACTIVE_ICON_URL }); - window.setPosition(200, 50); - //window.closed.connect(function() { Script.stop(); }); - } - openEngineTaskView(); \ No newline at end of file + + Script.scriptEnding.connect(function () { + killWindow() + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + }); + + button.clicked.connect(onClicked); + + var onScreen = false; + var window; + + function onClicked() { + if (onScreen) { + killWindow() + } else { + createWindow() + } + } + + function createWindow() { + var qml = Script.resolvePath(QMLAPP_URL); + window = new OverlayWindow({ + title: 'Render Engine Inspector', + source: qml, + width: 250, + height: 500 + }); + window.setPosition(200, 50); + window.closed.connect(killWindow); + onScreen = true + button.editProperties({isActive: true}); + } + + function killWindow() { + if (window !== undefined) { + window.closed.disconnect(killWindow); + window.close() + window = undefined + } + onScreen = false + button.editProperties({isActive: false}) + } + }()); + \ No newline at end of file diff --git a/scripts/developer/utilities/render/engineInspector.qml b/scripts/developer/utilities/render/engineInspector.qml index 16dd8eb985..1e05605ac7 100644 --- a/scripts/developer/utilities/render/engineInspector.qml +++ b/scripts/developer/utilities/render/engineInspector.qml @@ -1,7 +1,7 @@ // -// deferredLighting.qml +// EngineInspector.qml // -// Created by Sam Gateau on 6/6/2016 +// Created by Sam Gateau on 06/07/2018 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -18,13 +18,13 @@ import "../lib/jet/qml" as Jet Item { HifiConstants { id: hifi;} - id: render; + id: root; anchors.fill: parent - property var mainViewTask: Render.getConfig("RenderMainView") + property var rootConfig: Render.getConfig("") Jet.TaskListView { - rootConfig: Render - anchors.fill: render + rootConfig: root.rootConfig + anchors.fill: root } } \ No newline at end of file diff --git a/scripts/developer/utilities/render/engineProfiler.js b/scripts/developer/utilities/render/engineProfiler.js new file mode 100644 index 0000000000..418cab8622 --- /dev/null +++ b/scripts/developer/utilities/render/engineProfiler.js @@ -0,0 +1,59 @@ + + + (function() { + var TABLET_BUTTON_NAME = "Profiler"; + var QMLAPP_URL = Script.resolvePath("./engineProfiler.qml"); + var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg"); + var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg"); + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: ICON_URL, + activeIcon: ACTIVE_ICON_URL + }); + + Script.scriptEnding.connect(function () { + killWindow() + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + }); + + button.clicked.connect(onClicked); + + var onScreen = false; + var window; + + function onClicked() { + if (onScreen) { + killWindow() + } else { + createWindow() + } + } + + function createWindow() { + var qml = Script.resolvePath(QMLAPP_URL); + window = Desktop.createWindow(Script.resolvePath(QMLAPP_URL), { + title: 'Render Engine Profiler', + flags: Desktop.ALWAYS_ON_TOP, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 500, y: 100} + }); + window.setPosition(200, 50); + window.closed.connect(killWindow); + onScreen = true + button.editProperties({isActive: true}); + } + + function killWindow() { + if (window !== undefined) { + window.closed.disconnect(killWindow); + window.close() + window = undefined + } + onScreen = false + button.editProperties({isActive: false}) + } + }()); + \ No newline at end of file diff --git a/scripts/developer/utilities/render/engineProfiler.qml b/scripts/developer/utilities/render/engineProfiler.qml new file mode 100644 index 0000000000..bfa049d089 --- /dev/null +++ b/scripts/developer/utilities/render/engineProfiler.qml @@ -0,0 +1,31 @@ +// +// EngineProfiler.qml +// +// Created by Sam Gateau on 06/07/2018 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls + +import "../lib/jet/qml" as Jet + +Item { + HifiConstants { id: hifi;} + id: root; + anchors.fill: parent + + property var rootConfig: Render.getConfig("") + + + Jet.TaskTimeFrameView { + rootConfig: root.rootConfig + anchors.fill: root + } +} \ No newline at end of file diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index d5365133b6..3a8462c5cb 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -615,7 +615,9 @@ function notificationPollCallbackHistory(historyArray) { ui.notificationDisplayBanner(message); } else { for (var i = 0; i < notificationCount; i++) { - message = '"' + (historyArray[i].message) + '" ' + + var historyMessage = historyArray[i].message; + var sanitizedHistoryMessage = historyMessage.replace(/<\/?[^>]+(>|$)/g, ""); + message = '"' + sanitizedHistoryMessage + '" ' + "Open INVENTORY to see all activity."; ui.notificationDisplayBanner(message); }