From cdbb7b2ad4294796ae007c0d74f0c07df1d5a4dd Mon Sep 17 00:00:00 2001 From: Menithal Date: Sat, 19 Mar 2016 12:32:32 +0200 Subject: [PATCH 01/31] Set UserAnimPreAndPostRotations true by Default If Blender avatars are animated by Blender animations, they have missing Pre and Post rotations. This step is no longer necessary to have false, and also makes sure all animations from Blender Work on all avatars (from other platforms). This commit make it default to be enabled: --- interface/src/Menu.cpp | 14 +++++++------- libraries/animation/src/AnimClip.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 35bc2aa696..63c67f6b9f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -107,7 +107,7 @@ Menu::Menu() { auto scriptEngines = DependencyManager::get(); // Edit > Stop All Scripts... [advanced] - addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, + addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, scriptEngines.data(), SLOT(stopAllScripts()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); @@ -140,7 +140,7 @@ Menu::Menu() { // Edit > Reload All Content [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - + // Edit > Package Model... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0, @@ -153,7 +153,7 @@ Menu::Menu() { auto audioIO = DependencyManager::get(); // Audio > Mute - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, + addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, audioIO.data(), SLOT(toggleMute())); // Audio > Show Level Meter @@ -458,7 +458,7 @@ Menu::Menu() { avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false, + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true, avatar, SLOT(setUseAnimPreAndPostRotations(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true, avatar, SLOT(setEnableInverseKinematics(bool))); @@ -534,7 +534,7 @@ Menu::Menu() { // Developer > Audio >>> MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio"); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true, + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true, audioIO.data(), SLOT(toggleAudioNoiseReduction())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, audioIO.data(), SLOT(toggleServerEcho())); @@ -617,7 +617,7 @@ Menu::Menu() { QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true, + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true, NULL, NULL, UNSPECIFIED_POSITION, "Advanced"); #endif } @@ -651,7 +651,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { } else if (properties.isCheckable) { menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, properties.isChecked, - MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition, properties.grouping); } else { menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index e8f429f22c..a5747e4f96 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,7 +13,7 @@ #include "AnimationLogging.h" #include "AnimUtil.h" -bool AnimClip::usePreAndPostPoseFromAnim = false; +bool AnimClip::usePreAndPostPoseFromAnim = true; AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) : AnimNode(AnimNode::Type::Clip, id), From 63d6f888e61f8cd05a70baaf3b1ec299ea410196 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 21 Mar 2016 17:31:13 -0700 Subject: [PATCH 02/31] Update openvr sdk to 0.9.19 --- cmake/externals/openvr/CMakeLists.txt | 4 ++-- plugins/openvr/src/OpenVrHelpers.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index 3fe7df44d0..930a339d12 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://github.com/ValveSoftware/openvr/archive/v0.9.15.zip - URL_MD5 0ff8560b49b6da1150fcc47360e8ceca + URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip + URL_MD5 843f9dde488584d8af1f3ecf2252b4e0 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index d2c3649933..8ddf028dd2 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -51,7 +51,7 @@ vr::IVRSystem* acquireOpenVrSystem() { if (!activeHmd) { qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building"; vr::EVRInitError eError = vr::VRInitError_None; - activeHmd = vr::VR_Init(&eError); + activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene); qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; } if (activeHmd) { From 0d046b12de6ce57bc006921cfd5df6059443eca0 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 21 Mar 2016 17:39:10 -0700 Subject: [PATCH 03/31] OpenVR: Moved WaitGetPoses after main window preview. --- .../src/display-plugins/hmd/HmdDisplayPlugin.cpp | 2 ++ .../display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h | 1 + plugins/openvr/src/OpenVrDisplayPlugin.cpp | 2 ++ plugins/openvr/src/OpenVrDisplayPlugin.h | 1 + 4 files changed, 6 insertions(+) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index c3782b907f..340804de04 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -149,6 +149,8 @@ void HmdDisplayPlugin::internalPresent() { }); swapBuffers(); } + + postPreview(); } void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 659a3a16fa..fede16c3a5 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -31,6 +31,7 @@ public: protected: virtual void hmdPresent() = 0; virtual bool isHmdMounted() const = 0; + virtual void postPreview() {}; void internalActivate() override; void compositeOverlay() override; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 843b70807d..adad0648e8 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -152,7 +152,9 @@ void OpenVrDisplayPlugin::hmdPresent() { _compositor->Submit(vr::Eye_Left, &texture, &leftBounds); _compositor->Submit(vr::Eye_Right, &texture, &rightBounds); +} +void OpenVrDisplayPlugin::postPreview() { vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; _compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0); _hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index caaf75a4d0..78b76cb78d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -35,6 +35,7 @@ protected: void hmdPresent() override; bool isHmdMounted() const override; + void postPreview() override; private: vr::IVRSystem* _system { nullptr }; From 225c462685724c43aef558f2661d13d79949047a Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 21 Mar 2016 21:47:59 -0700 Subject: [PATCH 04/31] Improved NSIGHT instrumentation * Application::idle, physics and avatar processing are tracked * Application::paintGL frameNumber payload is tracked * OpenGLDisplayPlugin::present is profiled with corresponding frameNumber payload * Added color and payload support to Nsight ProfileRange class. --- interface/src/Application.cpp | 17 +++++++++++--- .../display-plugins/OpenGLDisplayPlugin.cpp | 5 ++++- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 4 ++++ libraries/shared/src/shared/NsightHelpers.cpp | 19 ++++++++++++++++ libraries/shared/src/shared/NsightHelpers.h | 22 +++++++++---------- plugins/oculus/src/OculusDisplayPlugin.cpp | 4 ++++ plugins/openvr/src/OpenVrDisplayPlugin.cpp | 7 +++++- 7 files changed, 62 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d8cbafbd2..af41da4b5d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1401,7 +1401,7 @@ void Application::paintGL() { _lastFramesPerSecondUpdate = now; } - PROFILE_RANGE(__FUNCTION__); + PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); PerformanceTimer perfTimer("paintGL"); if (nullptr == _displayPlugin) { @@ -2499,6 +2499,8 @@ static uint32_t _renderedFrameIndex { INVALID_FRAME }; void Application::idle(uint64_t now) { + PROFILE_RANGE(__FUNCTION__); + if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -2556,7 +2558,6 @@ void Application::idle(uint64_t now) { _lastTimeUpdated.start(); { - PROFILE_RANGE(__FUNCTION__); static uint64_t lastIdleStart{ now }; uint64_t idleStartToStartDuration = now - lastIdleStart; if (idleStartToStartDuration != 0) { @@ -3173,6 +3174,9 @@ void Application::updateDialogs(float deltaTime) { } void Application::update(float deltaTime) { + + PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); @@ -3269,6 +3273,8 @@ void Application::update(float deltaTime) { updateDialogs(deltaTime); // update various stats dialogs if present if (_physicsEnabled) { + PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PerformanceTimer perfTimer("physics"); AvatarManager* avatarManager = DependencyManager::get().data(); @@ -3339,9 +3345,13 @@ void Application::update(float deltaTime) { } } - _avatarUpdate->synchronousProcess(); + { + PROFILE_RANGE_EX("Avatars", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + _avatarUpdate->synchronousProcess(); + } { + PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("overlays"); _overlays.update(deltaTime); } @@ -3361,6 +3371,7 @@ void Application::update(float deltaTime) { // Update my voxel servers with my current voxel query... { + PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("queryOctree"); quint64 sinceLastQuery = now - _lastQueriedTime; const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index ca36ab35f0..4678694d4a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -21,7 +21,7 @@ #include #include #include - +#include #include #include #include @@ -527,6 +527,9 @@ void OpenGLDisplayPlugin::internalPresent() { void OpenGLDisplayPlugin::present() { incrementPresentCount(); + + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) + updateTextures(); if (_currentSceneTexture) { // Write all layers to a local framebuffer diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 340804de04..b022b10887 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "../Logging.h" #include "../CompositorHelper.h" @@ -106,6 +107,9 @@ void HmdDisplayPlugin::compositePointer() { } void HmdDisplayPlugin::internalPresent() { + + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) + // Composite together the scene, overlay and mouse cursor hmdPresent(); diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp index e48e228588..5b952acea8 100644 --- a/libraries/shared/src/shared/NsightHelpers.cpp +++ b/libraries/shared/src/shared/NsightHelpers.cpp @@ -15,8 +15,27 @@ ProfileRange::ProfileRange(const char *name) { nvtxRangePush(name); } +ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) { + + nvtxEventAttributes_t eventAttrib = {0}; + eventAttrib.version = NVTX_VERSION; + eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; + eventAttrib.colorType = NVTX_COLOR_ARGB; + eventAttrib.color = argbColor; + eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; + eventAttrib.message.ascii = name; + eventAttrib.payload.llValue = payload; + eventAttrib.payloadType = NVTX_PAYLOAD_TYPE_UNSIGNED_INT64; + + nvtxRangePushEx(&eventAttrib); +} + ProfileRange::~ProfileRange() { nvtxRangePop(); } +#else +ProfileRange::ProfileRange(const char *name) {} +ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {} +ProfileRange::~ProfileRange() {} #endif diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h index 3acdf14411..7a4f434ce9 100644 --- a/libraries/shared/src/shared/NsightHelpers.h +++ b/libraries/shared/src/shared/NsightHelpers.h @@ -9,16 +9,16 @@ #ifndef hifi_gl_NsightHelpers_h #define hifi_gl_NsightHelpers_h -#if defined(NSIGHT_FOUND) - class ProfileRange { - public: - ProfileRange(const char *name); - ~ProfileRange(); - }; +#include + +class ProfileRange { +public: + ProfileRange(const char *name); + ProfileRange(const char *name, uint32_t argbColor, uint64_t payload); + ~ProfileRange(); +}; + #define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); -#else -#define PROFILE_RANGE(name) +#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload); + #endif - - -#endif \ No newline at end of file diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 5ab56e1659..71a858e1e8 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -6,6 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OculusDisplayPlugin.h" +#include #include "OculusHelpers.h" const QString OculusDisplayPlugin::NAME("Oculus Rift"); @@ -54,6 +55,9 @@ void OculusDisplayPlugin::updateFrameData() { } void OculusDisplayPlugin::hmdPresent() { + + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex) + if (!_currentSceneTexture) { return; } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index adad0648e8..e123512cf9 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -21,7 +21,7 @@ #include #include #include - +#include #include "OpenVrHelpers.h" Q_DECLARE_LOGGING_CATEGORY(displayplugins) @@ -144,6 +144,9 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { } void OpenVrDisplayPlugin::hmdPresent() { + + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex) + // Flip y-axis since GL UV coords are backwards. static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; @@ -155,6 +158,8 @@ void OpenVrDisplayPlugin::hmdPresent() { } void OpenVrDisplayPlugin::postPreview() { + PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex) + vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; _compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0); _hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); From b1286517c6430072b43d654f3cdbe78a8ea65c3f Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 21 Mar 2016 21:51:16 -0700 Subject: [PATCH 05/31] Removed frame latency in GLEscrow This could occur if the GLEscrow queue has extra frames in it. When new textures were fetched, before this fix, they were simply popped from the queue. This commit introduces a loop, that will fetch from the queue until it is empty. --- libraries/gl/src/gl/GLEscrow.h | 8 +++++--- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index 357398c79b..d4772c85e9 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -203,13 +203,15 @@ public: // Also releases any previous texture held by the caller bool fetchSignaledAndRelease(T& value) { T originalValue = value; - if (fetchSignaled(value)) { + bool fetched = false; + while (fetchSignaled(value)) { + fetched = true; if (originalValue != invalid()) { release(originalValue); } - return true; + originalValue = value; } - return false; + return fetched; } bool fetchAndReleaseWithFence(T& value, GLsync& sync) { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index e123512cf9..1b87225388 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -119,14 +119,14 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); #if THREADED_PRESENT - // TODO: this seems awfuly long, 44ms total, but it produced the best results. - const float NUM_PREDICTION_FRAMES = 3.0f; + // 2 frames of prediction + vsyncToPhotons = 33ms total + const float NUM_PREDICTION_FRAMES = 2.0f; float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons; #else uint64_t frameCounter; float timeSinceLastVsync; _system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter); - float predictedSecondsFromNow = 3.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons; + float predictedSecondsFromNow = 2.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons; #endif vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; From 50ac101d568d6cfdbb635bf0c3b6860dd769c381 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Tue, 22 Mar 2016 10:36:00 -0700 Subject: [PATCH 06/31] Avatar and Rig NSIGHT instrumentation --- interface/src/Application.cpp | 16 ++++++++++++---- libraries/animation/src/Rig.cpp | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e61ef7f7e0..06755ae70a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3245,6 +3245,8 @@ void Application::update(float deltaTime) { PerformanceTimer perfTimer("physics"); { + PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PerformanceTimer perfTimer("updateStates)"); static VectorOfMotionStates motionStates; _entitySimulation.getObjectsToRemoveFromPhysics(motionStates); @@ -3277,12 +3279,14 @@ void Application::update(float deltaTime) { }); } { + PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("stepSimulation"); getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); } { + PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("havestChanges"); if (_physicsEngine->hasOutgoingChanges()) { getEntities()->getTree()->withWriteLock([&] { @@ -3313,17 +3317,21 @@ void Application::update(float deltaTime) { // AvatarManager update { - PROFILE_RANGE_EX("Avatars", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); - PerformanceTimer perfTimer("AvatarManger"); qApp->setAvatarSimrateSample(1.0f / deltaTime); - avatarManager->updateOtherAvatars(deltaTime); + { + PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); + avatarManager->updateOtherAvatars(deltaTime); + } qApp->updateMyAvatarLookAtPosition(); - avatarManager->updateMyAvatar(deltaTime); + { + PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); + avatarManager->updateMyAvatar(deltaTime); + } } { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ae9adc71c2..a2b664d064 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "AnimationLogging.h" #include "AnimClip.h" @@ -852,6 +853,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { + PROFILE_RANGE_EX(__FUNCTION__, 0xffff00ff, 0); + setModelOffset(rootTransform); if (_animNode) { From 209524a5c08441d9df6e10e232ca2b3f8c098548 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Tue, 22 Mar 2016 15:38:10 -0700 Subject: [PATCH 07/31] openvr: Non-threaded present work In the unlikely event you hit 90htz in non-threaded present mode, you will have perfect tracking and latency. This verifies that there are no other sources of tracking or rendering latency, other then the ones we already know about. --- .../src/display-plugins/OpenGLDisplayPlugin.h | 2 +- libraries/gl/src/gl/GLEscrow.h | 5 +++++ plugins/openvr/src/OpenVrDisplayPlugin.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 501232f7e7..7295b07ad3 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -17,9 +17,9 @@ #include #include #include -#include #define THREADED_PRESENT 1 +#include class OpenGLDisplayPlugin : public DisplayPlugin { protected: diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index d4772c85e9..cf96e43e1f 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -71,11 +71,16 @@ public: } bool signaled() const { +#if THREADED_PRESENT auto result = glClientWaitSync(_sync, 0, 0); if (GL_TIMEOUT_EXPIRED != result && GL_WAIT_FAILED != result) { return true; } return false; +#else + glWaitSync(_sync, 0, GL_TIMEOUT_IGNORED); + return true; +#endif } }; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 1b87225388..07629a442f 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -69,6 +69,9 @@ void OpenVrDisplayPlugin::internalActivate() { _compositor = vr::VRCompositor(); Q_ASSERT(_compositor); + // enable async time warp + // _compositor->ForceInterleavedReprojectionOn(true); + // set up default sensor space such that the UI overlay will align with the front of the room. auto chaperone = vr::VRChaperone(); if (chaperone) { @@ -123,10 +126,7 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { const float NUM_PREDICTION_FRAMES = 2.0f; float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons; #else - uint64_t frameCounter; - float timeSinceLastVsync; - _system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter); - float predictedSecondsFromNow = 2.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons; + float predictedSecondsFromNow = frameDuration + vsyncToPhotons; #endif vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; From c40b70688cbee4345ae720df7844fa7fc5694391 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 22 Mar 2016 17:43:36 -0700 Subject: [PATCH 08/31] Made Nsight wrapper win32 only. --- libraries/shared/src/shared/NsightHelpers.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h index 7a4f434ce9..9853171b34 100644 --- a/libraries/shared/src/shared/NsightHelpers.h +++ b/libraries/shared/src/shared/NsightHelpers.h @@ -9,6 +9,7 @@ #ifndef hifi_gl_NsightHelpers_h #define hifi_gl_NsightHelpers_h +#ifdef _WIN32 #include class ProfileRange { @@ -20,5 +21,9 @@ public: #define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); #define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload); +#else +#define PROFILE_RANGE(name) +#define PROFILE_RANGE_EX(name, argbColor, payload) +#endif #endif From 9dd24f81d4ea8a7f213c1b6786b92ed97470105c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 22 Mar 2016 18:24:48 -0700 Subject: [PATCH 09/31] Fix for osx and linux --- libraries/shared/src/shared/NsightHelpers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp index 5b952acea8..2539ff8864 100644 --- a/libraries/shared/src/shared/NsightHelpers.cpp +++ b/libraries/shared/src/shared/NsightHelpers.cpp @@ -8,6 +8,7 @@ #include "NsightHelpers.h" +#ifdef _WIN32 #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" @@ -39,3 +40,4 @@ ProfileRange::ProfileRange(const char *name) {} ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {} ProfileRange::~ProfileRange() {} #endif +#endif // _WIN32 From 12ecd592ab136506c7a1514570b87284d0aa9148 Mon Sep 17 00:00:00 2001 From: Menithal Date: Thu, 24 Mar 2016 01:14:48 +0200 Subject: [PATCH 10/31] Added Refresh Buttons to entityProperties Pressing buttons next to the url bar will now allow for content to have an automatic unix timestamp to be applied / updated to the end of the url. Applies for all url entity Properties --- examples/html/entityProperties.html | 44 +++++++++++++++++++++++++++++ examples/html/style.css | 12 +++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index e3eb19dc4a..3380623c66 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -451,6 +451,37 @@ var elPreviewCameraButton = document.getElementById("preview-camera-button"); + var urlUpdaters = document.getElementsByClassName("update-url-version"); + var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters. + var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/; + + var refreshEvent = function(event){ + var urlElement = event.target.parentElement.getElementsByClassName("url")[0]; + var content = urlElement.value; + var date = new Date(); + var timeStamp = date.getTime(); + + if(content.length > 0){ + if(PARAM_REGEXP.test(content)){ + // Has params, so lets remove existing definition and append again. + content = content.replace(TIMESTAMP_REGEXP,"") + "&"; + }else{ + content += "?"; + } + content = content.replace("?&","?"); + urlElement.value = content + "HFTime=" + timeStamp; + } + var evt = document.createEvent("HTMLEvents"); + evt.initEvent("change", true, true ); + urlElement.dispatchEvent(evt); + + }; + + for(var index = 0; index < urlUpdaters.length; index++){ + var urlUpdater = urlUpdaters[index]; + urlUpdater.addEventListener("click", refreshEvent); + } + if (window.EventBridge !== undefined) { var properties; EventBridge.scriptEventReceived.connect(function(data) { @@ -1185,6 +1216,7 @@
Ambient URL
+
@@ -1262,6 +1294,7 @@
Skybox URL
+
@@ -1273,6 +1306,7 @@
Source URL
+
@@ -1286,12 +1320,14 @@
Href - Hifi://address
+
@@ -1375,16 +1411,19 @@
X-axis Texture URL
+
Y-axis Texture URL
+
Z-axis Texture URL
+
@@ -1566,6 +1605,7 @@
Collision Sound URL
+
@@ -1583,6 +1623,7 @@
+
@@ -1595,6 +1636,7 @@
Model URL
+
@@ -1613,12 +1655,14 @@
Compound Shape URL
+
Animation URL
+
diff --git a/examples/html/style.css b/examples/html/style.css index 83982dab15..3ae1c1739b 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -134,8 +134,18 @@ textarea { resize: vertical; } +.update-url-version{ + width:17px; + height:17px; + float:right; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAQAAACRZI9xAAABAUlEQVQoz33RvyvEARjH8SdlUX6UbhFJfgwmE3aTxWEiitxqN/gH7IxCKTcymLiJFIOJsihiUKcrCYnuXoZv+F45n2d4lvfz6fPpCSFs6BO1JllvLmXk5F37dC0vJ5NGmlBxoFp7Rn6RDpRRtG5KtynrijhLoBBGQcllKkFWAXsyCbKEFxeGqmJmFZFLkE639vX+12hCSVft0nUR8RbLcRN/aSHGIqL2tVYPconL32qLtaiL88Rl0qtXc+pTDv1OPGMlidtgS8Wp2RR05FEFM98/GlRQ9mTHvB7jVt2rKGPAT9xh2z6qfnToHV1SjZpN23Tl051di1oco9W/pdttaBRfEhFXOZV7vEsAAAAASUVORK5CYII=); + padding:0 !important; + margin:0 2px 0 0 !important; +} + input.url { - width: 100%; + width:85%; + padding-right: 20px; } input.coord { From 8bb387dc740734b426573f4d77dda8615d20bcf9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 23 Mar 2016 17:11:16 -0700 Subject: [PATCH 11/31] Revert GLEscrow changes --- libraries/gl/src/gl/GLEscrow.h | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index cf96e43e1f..357398c79b 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -71,16 +71,11 @@ public: } bool signaled() const { -#if THREADED_PRESENT auto result = glClientWaitSync(_sync, 0, 0); if (GL_TIMEOUT_EXPIRED != result && GL_WAIT_FAILED != result) { return true; } return false; -#else - glWaitSync(_sync, 0, GL_TIMEOUT_IGNORED); - return true; -#endif } }; @@ -208,15 +203,13 @@ public: // Also releases any previous texture held by the caller bool fetchSignaledAndRelease(T& value) { T originalValue = value; - bool fetched = false; - while (fetchSignaled(value)) { - fetched = true; + if (fetchSignaled(value)) { if (originalValue != invalid()) { release(originalValue); } - originalValue = value; + return true; } - return fetched; + return false; } bool fetchAndReleaseWithFence(T& value, GLsync& sync) { From cec6e7dd8dedab70aea2f36a5bb3d13f872147ba Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 23 Mar 2016 17:12:48 -0700 Subject: [PATCH 12/31] Application: reduced idle PROFILE_RANGE spam --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ad25160dd7..c7e67c4d24 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2496,8 +2496,6 @@ static uint32_t _renderedFrameIndex { INVALID_FRAME }; void Application::idle(uint64_t now) { - PROFILE_RANGE(__FUNCTION__); - if (_aboutToQuit || _inPaint) { return; // bail early, nothing to do here. } @@ -2550,6 +2548,8 @@ void Application::idle(uint64_t now) { return; } + PROFILE_RANGE(__FUNCTION__); + // We're going to execute idle processing, so restart the last idle timer _lastTimeUpdated.start(); @@ -3140,7 +3140,7 @@ void Application::updateDialogs(float deltaTime) { void Application::update(float deltaTime) { - PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); From 73725106f0f6824d09ea3bb29704348fc54b21c2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 23 Mar 2016 17:13:29 -0700 Subject: [PATCH 13/31] OpenGLDisplayPlugin: For non-threaded present use fetchAndReleasewithGpuWait --- .../src/display-plugins/OpenGLDisplayPlugin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 4678694d4a..d842dc553b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -404,7 +404,11 @@ void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overla void OpenGLDisplayPlugin::updateTextures() { // FIXME intrduce a GPU wait instead of a CPU/GPU sync point? +#if THREADED_PRESENT if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) { +#else + if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) { +#endif updateFrameData(); } From 57da7c2ba71e848dba7b785e970ff3c005c45625 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 23 Mar 2016 17:29:46 -0700 Subject: [PATCH 14/31] add handling for short circuit loss in SendQueue --- .../networking/src/udt/CongestionControl.cpp | 2 +- libraries/networking/src/udt/Connection.cpp | 7 ++ libraries/networking/src/udt/Connection.h | 1 + libraries/networking/src/udt/SendQueue.cpp | 99 +++++++++++-------- libraries/networking/src/udt/SendQueue.h | 7 +- tools/udt-test/src/UDTTest.cpp | 2 +- 6 files changed, 73 insertions(+), 45 deletions(-) diff --git a/libraries/networking/src/udt/CongestionControl.cpp b/libraries/networking/src/udt/CongestionControl.cpp index 1d1a6628fe..d30be2c139 100644 --- a/libraries/networking/src/udt/CongestionControl.cpp +++ b/libraries/networking/src/udt/CongestionControl.cpp @@ -201,7 +201,7 @@ void DefaultCC::onTimeout() { void DefaultCC::stopSlowStart() { _slowStart = false; - + if (_receiveRate > 0) { // Set the sending rate to the receiving rate. setPacketSendPeriod(USECS_PER_SECOND / _receiveRate); diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index e5f3508b81..af70295840 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -103,6 +103,7 @@ SendQueue& Connection::getSendQueue() { QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission); QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive); QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout); + QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss); // set defaults on the send queue from our congestion control object and estimatedTimeout() _sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod); @@ -140,6 +141,12 @@ void Connection::queueTimeout() { }); } +void Connection::queueShortCircuitLoss(quint32 sequenceNumber) { + updateCongestionControlAndSendQueue([this, sequenceNumber]{ + _congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber }); + }); +} + void Connection::sendReliablePacket(std::unique_ptr packet) { Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably."); getSendQueue().queuePacket(std::move(packet)); diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 4f5a8793e7..08a2df9b97 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -87,6 +87,7 @@ private slots: void recordRetransmission(); void queueInactive(); void queueTimeout(); + void queueShortCircuitLoss(quint32 sequenceNumber); private: void sendACK(bool wasCausedBySyncTimeout = true); diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 9701561ec7..2c3303537c 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -128,8 +128,8 @@ void SendQueue::stop() { _emptyCondition.notify_one(); } -void SendQueue::sendPacket(const Packet& packet) { - _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination); +int SendQueue::sendPacket(const Packet& packet) { + return _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination); } void SendQueue::ack(SequenceNumber ack) { @@ -178,7 +178,7 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) { void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) { // this is a response from the client, re-set our timeout expiry _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); - + { std::lock_guard nakLocker(_naksLock); _naks.clear(); @@ -232,15 +232,16 @@ SequenceNumber SendQueue::getNextSequenceNumber() { return _currentSequenceNumber; } -void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber) { +bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber) { // write the sequence number and send the packet newPacket->writeSequenceNumber(sequenceNumber); - sendPacket(*newPacket); - + // Save packet/payload size before we move it auto packetSize = newPacket->getDataSize(); auto payloadSize = newPacket->getPayloadSize(); + auto bytesWritten = sendPacket(*newPacket); + { // Insert the packet we have just sent in the sent list QWriteLocker locker(&_sentLock); @@ -249,8 +250,24 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, entry.second.swap(newPacket); } Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list"); - + emit packetSent(packetSize, payloadSize); + + if (bytesWritten < 0) { + // this is a short-circuit loss - we failed to put this packet on the wire + // so immediately add it to the loss list + + { + std::lock_guard nakLocker(_naksLock); + _naks.append(sequenceNumber); + } + + emit shortCircuitLoss(quint32(sequenceNumber)); + + return false; + } else { + return true; + } } void SendQueue::run() { @@ -285,12 +302,12 @@ void SendQueue::run() { auto nextPacketTimestamp = p_high_resolution_clock::now(); while (_state == State::Running) { - bool sentAPacket = maybeResendPacket(); + bool attemptedToSendPacket = maybeResendPacket(); // if we didn't find a packet to re-send AND we think we can fit a new packet on the wire // (this is according to the current flow window size) then we send out a new packet - if (!sentAPacket) { - sentAPacket = maybeSendNewPacket(); + if (!attemptedToSendPacket) { + attemptedToSendPacket = maybeSendNewPacket(); } // since we're a while loop, give the thread a chance to process events @@ -300,7 +317,7 @@ void SendQueue::run() { // If the send queue has been innactive, skip the sleep for // Either _isRunning will have been set to false and we'll break // Or something happened and we'll keep going - if (_state != State::Running || isInactive(sentAPacket)) { + if (_state != State::Running || isInactive(attemptedToSendPacket)) { return; } @@ -324,33 +341,34 @@ bool SendQueue::maybeSendNewPacket() { // grab the first packet we will send std::unique_ptr firstPacket = _packets.takePacket(); Q_ASSERT(firstPacket); - - std::unique_ptr secondPacket; - bool shouldSendPairTail = false; - - if (((uint32_t) nextNumber & 0xF) == 0) { - // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets - // pull off a second packet if we can before we unlock - shouldSendPairTail = true; - - secondPacket = _packets.takePacket(); + + + // attempt to send the first packet + if (sendNewPacketAndAddToSentList(move(firstPacket), nextNumber)) { + std::unique_ptr secondPacket; + bool shouldSendPairTail = false; + + if (((uint32_t) nextNumber & 0xF) == 0) { + // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets + // pull off a second packet if we can before we unlock + shouldSendPairTail = true; + + secondPacket = _packets.takePacket(); + } + + // do we have a second in a pair to send as well? + if (secondPacket) { + sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber()); + } else if (shouldSendPairTail) { + // we didn't get a second packet to send in the probe pair + // send a control packet of type ProbePairTail so the receiver can still do + // proper bandwidth estimation + static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); + _socket->writeBasePacket(*pairTailPacket, _destination); + } } - - // definitely send the first packet - sendNewPacketAndAddToSentList(move(firstPacket), nextNumber); - - // do we have a second in a pair to send as well? - if (secondPacket) { - sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber()); - } else if (shouldSendPairTail) { - // we didn't get a second packet to send in the probe pair - // send a control packet of type ProbePairTail so the receiver can still do - // proper bandwidth estimation - static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); - _socket->writeBasePacket(*pairTailPacket, _destination); - } - - // We sent our packet(s), return here + + // We attempted to send packet(s), return here return true; } } @@ -375,8 +393,9 @@ bool SendQueue::maybeResendPacket() { // see if we can find the packet to re-send auto it = _sentPackets.find(resendNumber); - + if (it != _sentPackets.end()) { + auto& entry = it->second; // we found the packet - grab it auto& resendPacket = *(entry.second); @@ -437,7 +456,7 @@ bool SendQueue::maybeResendPacket() { return false; } -bool SendQueue::isInactive(bool sentAPacket) { +bool SendQueue::isInactive(bool attemptedToSendPacket) { // check for connection timeout first // that will be the case if we have had 16 timeouts since hearing back from the client, and it has been @@ -462,7 +481,7 @@ bool SendQueue::isInactive(bool sentAPacket) { return true; } - if (!sentAPacket) { + if (!attemptedToSendPacket) { // During our processing above we didn't send any packets // If that is still the case we should use a condition_variable_any to sleep until we have data to handle. diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 9400ae8352..dc151e9f4d 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -79,6 +79,7 @@ signals: void queueInactive(); + void shortCircuitLoss(quint32 sequenceNumber); void timeout(); private slots: @@ -91,13 +92,13 @@ private: void sendHandshake(); - void sendPacket(const Packet& packet); - void sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber); + int sendPacket(const Packet& packet); + bool sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber); bool maybeSendNewPacket(); // Figures out what packet to send next bool maybeResendPacket(); // Determines whether to resend a packet and which one - bool isInactive(bool sentAPacket); + bool isInactive(bool attemptedToSendPacket); void deactivate(); // makes the queue inactive and cleans it up bool isFlowWindowFull() const; diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index 533e6371e9..2b5e306b09 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -77,7 +77,7 @@ UDTTest::UDTTest(int& argc, char** argv) : // randomize the seed for packet size randomization srand(time(NULL)); - + _socket.bind(QHostAddress::AnyIPv4, _argumentParser.value(PORT_OPTION).toUInt()); qDebug() << "Test socket is listening on" << _socket.localPort(); From 88c4e54cff936f7c4aa96bb1196f7163c1db2a12 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 23 Mar 2016 17:38:40 -0700 Subject: [PATCH 15/31] openvr: predict 3 frames into the future Becasue I revered the latency fix in the GLEscrow queue. *sigh* --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index b479c19fb4..0e7541066e 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -122,8 +122,8 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); #if THREADED_PRESENT - // 2 frames of prediction + vsyncToPhotons = 33ms total - const float NUM_PREDICTION_FRAMES = 2.0f; + // 3 frames of prediction + vsyncToPhotons = 44ms total + const float NUM_PREDICTION_FRAMES = 3.0f; float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons; #else float predictedSecondsFromNow = frameDuration + vsyncToPhotons; From 1b322c8d015fd59a657e71c4aa88b4949c5df0f1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 23 Mar 2016 18:07:28 -0700 Subject: [PATCH 16/31] don't oversend because of packet pairs --- libraries/networking/src/udt/SendQueue.cpp | 21 ++++++++++++++------- libraries/networking/src/udt/SendQueue.h | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 2c3303537c..30c9f2f491 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -306,8 +306,10 @@ void SendQueue::run() { // if we didn't find a packet to re-send AND we think we can fit a new packet on the wire // (this is according to the current flow window size) then we send out a new packet + auto newPacketCount = 0; if (!attemptedToSendPacket) { - attemptedToSendPacket = maybeSendNewPacket(); + newPacketCount = maybeSendNewPacket(); + attemptedToSendPacket = (newPacketCount > 0); } // since we're a while loop, give the thread a chance to process events @@ -322,7 +324,8 @@ void SendQueue::run() { } // push the next packet timestamp forwards by the current packet send period - nextPacketTimestamp += std::chrono::microseconds(_packetSendPeriod); + auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod; + nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta); // sleep as long as we need until next packet send, if we can const auto timeToSleep = duration_cast(nextPacketTimestamp - p_high_resolution_clock::now()); @@ -331,7 +334,7 @@ void SendQueue::run() { } } -bool SendQueue::maybeSendNewPacket() { +int SendQueue::maybeSendNewPacket() { if (!isFlowWindowFull()) { // we didn't re-send a packet, so time to send a new one @@ -366,14 +369,18 @@ bool SendQueue::maybeSendNewPacket() { static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); _socket->writeBasePacket(*pairTailPacket, _destination); } - } - // We attempted to send packet(s), return here - return true; + // we attempted to send two packets, return 2 + return 2; + } else { + // we attempted to send a single packet, return 1 + return 1; + } } } + // No packets were sent - return false; + return 0; } bool SendQueue::maybeResendPacket() { diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index dc151e9f4d..77c7f3e85e 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -95,7 +95,7 @@ private: int sendPacket(const Packet& packet); bool sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber); - bool maybeSendNewPacket(); // Figures out what packet to send next + int maybeSendNewPacket(); // Figures out what packet to send next bool maybeResendPacket(); // Determines whether to resend a packet and which one bool isInactive(bool attemptedToSendPacket); From ef3f5e022177a15687b937b478fb6653c73aef87 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 23 Mar 2016 18:23:30 -0700 Subject: [PATCH 17/31] fix for race in last receiver response --- libraries/networking/src/udt/SendQueue.cpp | 9 +++++---- libraries/networking/src/udt/SendQueue.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 30c9f2f491..2ffa42cb82 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -134,7 +134,7 @@ int SendQueue::sendPacket(const Packet& packet) { void SendQueue::ack(SequenceNumber ack) { // this is a response from the client, re-set our timeout expiry and our last response time - _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); if (_lastACKSequenceNumber == (uint32_t) ack) { return; @@ -164,7 +164,7 @@ void SendQueue::ack(SequenceNumber ack) { void SendQueue::nak(SequenceNumber start, SequenceNumber end) { // this is a response from the client, re-set our timeout expiry - _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); { std::lock_guard nakLocker(_naksLock); @@ -177,7 +177,7 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) { void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) { // this is a response from the client, re-set our timeout expiry - _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); { std::lock_guard nakLocker(_naksLock); @@ -473,7 +473,8 @@ bool SendQueue::isInactive(bool attemptedToSendPacket) { auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse); - if (sinceLastResponse >= quint64(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) && + if (sinceLastResponse > 0 && + sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) && _lastReceiverResponse > 0 && sinceLastResponse > MIN_MS_BEFORE_INACTIVE) { // If the flow window has been full for over CONSIDER_INACTIVE_AFTER, diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 77c7f3e85e..21f6141c3c 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -123,7 +123,7 @@ private: std::atomic _estimatedTimeout { 0 }; // Estimated timeout, set from CC std::atomic _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC - std::atomic _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK) + std::atomic _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK) std::atomic _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC From 8bbabf186f859a8169110a033de125773facef7a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 5 Mar 2016 02:49:55 -0800 Subject: [PATCH 18/31] Fix async issues related to JS / QML object lifetimes --- libraries/ui/src/QmlWebWindowClass.cpp | 13 +++--- libraries/ui/src/QmlWebWindowClass.h | 3 -- libraries/ui/src/QmlWindowClass.cpp | 58 ++++++++++++++++---------- libraries/ui/src/QmlWindowClass.h | 16 +++---- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index f12fb51b19..0228f77f4f 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -31,19 +31,18 @@ static const char* const URL_PROPERTY = "source"; // Method called by Qt scripts to create a new web window in the overlay QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine, - [&](QObject* object) { return new QmlWebWindowClass(object); }); + [&](QObject* object) { return new QmlWebWindowClass(object); }); } QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) { } -// FIXME remove. -void QmlWebWindowClass::handleNavigation(const QString& url) { -} - QString QmlWebWindowClass::getURL() const { QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant { + if (_qmlWindow.isNull()) { + return QVariant(); + } return _qmlWindow->property(URL_PROPERTY); }); return result.toString(); @@ -54,6 +53,8 @@ extern QString fixupHifiUrl(const QString& urlString); void QmlWebWindowClass::setURL(const QString& urlString) { DependencyManager::get()->executeOnUiThread([=] { - _qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString)); + if (!_qmlWindow.isNull()) { + _qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString)); + } }); } diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index 14e533c7b4..27c0e6996d 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -26,9 +26,6 @@ public slots: signals: void urlChanged(); - -private slots: - void handleNavigation(const QString& url); }; #endif diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 679e86f4ae..b7fe330a4e 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -214,7 +214,7 @@ QmlWindowClass::QmlWindowClass(QObject* qmlWindow) { qDebug() << "Created window with ID " << _windowId; Q_ASSERT(_qmlWindow); - Q_ASSERT(dynamic_cast(_qmlWindow)); + Q_ASSERT(dynamic_cast(_qmlWindow.data())); // Forward messages received from QML on to the script connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection); } @@ -240,7 +240,7 @@ QQuickItem* QmlWindowClass::asQuickItem() const { if (_toolWindow) { return DependencyManager::get()->getToolWindow(); } - return dynamic_cast(_qmlWindow); + return _qmlWindow.isNull() ? nullptr : dynamic_cast(_qmlWindow.data()); } void QmlWindowClass::setVisible(bool visible) { @@ -260,32 +260,34 @@ void QmlWindowClass::setVisible(bool visible) { bool QmlWindowClass::isVisible() const { // The tool window itself has special logic based on whether any tabs are enabled - if (_toolWindow) { - auto targetTab = dynamic_cast(_qmlWindow); - return DependencyManager::get()->returnFromUiThread([&] { - return QVariant::fromValue(targetTab->isEnabled()); - }).toBool(); - } else { - QQuickItem* targetWindow = asQuickItem(); - return DependencyManager::get()->returnFromUiThread([&] { - return QVariant::fromValue(targetWindow->isVisible()); - }).toBool(); - } + return DependencyManager::get()->returnFromUiThread([&] { + if (_qmlWindow.isNull()) { + return QVariant::fromValue(false); + } + if (_toolWindow) { + return QVariant::fromValue(dynamic_cast(_qmlWindow.data())->isEnabled()); + } else { + return QVariant::fromValue(asQuickItem()->isVisible()); + } + }).toBool(); } glm::vec2 QmlWindowClass::getPosition() const { - QQuickItem* targetWindow = asQuickItem(); QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant { - return targetWindow->position(); + if (_qmlWindow.isNull()) { + return QVariant(QPointF(0, 0)); + } + return asQuickItem()->position(); }); return toGlm(result.toPointF()); } void QmlWindowClass::setPosition(const glm::vec2& position) { - QQuickItem* targetWindow = asQuickItem(); DependencyManager::get()->executeOnUiThread([=] { - targetWindow->setPosition(QPointF(position.x, position.y)); + if (!_qmlWindow.isNull()) { + asQuickItem()->setPosition(QPointF(position.x, position.y)); + } }); } @@ -299,17 +301,21 @@ glm::vec2 toGlm(const QSizeF& size) { } glm::vec2 QmlWindowClass::getSize() const { - QQuickItem* targetWindow = asQuickItem(); QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant { + if (_qmlWindow.isNull()) { + return QVariant(QSizeF(0, 0)); + } + QQuickItem* targetWindow = asQuickItem(); return QSizeF(targetWindow->width(), targetWindow->height()); }); return toGlm(result.toSizeF()); } void QmlWindowClass::setSize(const glm::vec2& size) { - QQuickItem* targetWindow = asQuickItem(); DependencyManager::get()->executeOnUiThread([=] { - targetWindow->setSize(QSizeF(size.x, size.y)); + if (!_qmlWindow.isNull()) { + asQuickItem()->setSize(QSizeF(size.x, size.y)); + } }); } @@ -318,9 +324,10 @@ void QmlWindowClass::setSize(int width, int height) { } void QmlWindowClass::setTitle(const QString& title) { - QQuickItem* targetWindow = asQuickItem(); DependencyManager::get()->executeOnUiThread([=] { - targetWindow->setProperty(TITLE_PROPERTY, title); + if (!_qmlWindow.isNull()) { + asQuickItem()->setProperty(TITLE_PROPERTY, title); + } }); } @@ -345,7 +352,12 @@ void QmlWindowClass::hasClosed() { } void QmlWindowClass::raise() { - QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection); + auto offscreenUi = DependencyManager::get(); + offscreenUi->executeOnUiThread([=] { + if (!_qmlWindow.isNull()) { + QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection); + } + }); } #include "QmlWindowClass.moc" diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 26152b1f24..fb7dbf1253 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -10,11 +10,13 @@ #define hifi_ui_QmlWindowClass_h #include -#include +#include #include #include #include +#include + class QScriptEngine; class QScriptContext; class QmlWindowClass; @@ -38,14 +40,13 @@ private: const QmlWindowClass* _webWindow { nullptr }; QWebSocket *_socket { nullptr }; }; - // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping class QmlWindowClass : public QObject { Q_OBJECT Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT) Q_PROPERTY(int windowId READ getWindowId CONSTANT) - Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) - Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) + Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) public: @@ -64,11 +65,8 @@ public slots: glm::vec2 getSize() const; void setSize(const glm::vec2& size); void setSize(int width, int height); - void setTitle(const QString& title); - - // Ugh.... do not want to do Q_INVOKABLE void raise(); Q_INVOKABLE void close(); Q_INVOKABLE int getWindowId() const { return _windowId; }; @@ -79,6 +77,8 @@ public slots: signals: void visibilityChanged(bool visible); // Tool window + void positionChanged(); + void sizeChanged(); void moved(glm::vec2 position); void resized(QSizeF size); void closed(); @@ -104,7 +104,7 @@ protected: // for tool window panes in QML bool _toolWindow { false }; const int _windowId; - QObject* _qmlWindow; + QPointer _qmlWindow; QString _source; }; From a43fde0803141657c45f82371136079455bed263 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 5 Mar 2016 02:49:55 -0800 Subject: [PATCH 19/31] Build a better event bridge --- examples/html/eventBridgeLoader.js | 49 ++------------ examples/html/qmlWebTest.html | 10 +-- examples/tests/qmlWebTest.js | 28 +++----- interface/resources/qml/QmlWebWindow.qml | 12 ++++ interface/resources/qml/ToolWindow.qml | 61 +++++++++++------ interface/resources/qml/controls/WebView.qml | 5 +- libraries/ui/src/QmlWebWindowClass.cpp | 23 +++++++ libraries/ui/src/QmlWebWindowClass.h | 12 ++++ libraries/ui/src/QmlWindowClass.cpp | 69 +------------------- libraries/ui/src/QmlWindowClass.h | 31 +-------- 10 files changed, 108 insertions(+), 192 deletions(-) diff --git a/examples/html/eventBridgeLoader.js b/examples/html/eventBridgeLoader.js index ebfb6dc740..de6553ee1c 100644 --- a/examples/html/eventBridgeLoader.js +++ b/examples/html/eventBridgeLoader.js @@ -10,50 +10,11 @@ var EventBridge; -EventBridgeConnectionProxy = function(parent) { - this.parent = parent; - this.realSignal = this.parent.realBridge.scriptEventReceived - this.webWindowId = this.parent.webWindow.windowId; -} - -EventBridgeConnectionProxy.prototype.connect = function(callback) { - var that = this; - this.realSignal.connect(function(id, message) { - if (id === that.webWindowId) { callback(message); } +openEventBridge = function(callback) { + new QWebChannel(qt.webChannelTransport, function(channel) { + console.log("uid " + EventBridgeUid); + EventBridge = channel.objects[EventBridgeUid]; + callback(EventBridge); }); } -EventBridgeProxy = function(webWindow) { - this.webWindow = webWindow; - this.realBridge = this.webWindow.eventBridge; - this.scriptEventReceived = new EventBridgeConnectionProxy(this); -} - -EventBridgeProxy.prototype.emitWebEvent = function(data) { - this.realBridge.emitWebEvent(data); -} - -openEventBridge = function(callback) { - EVENT_BRIDGE_URI = "ws://localhost:51016"; - socket = new WebSocket(this.EVENT_BRIDGE_URI); - - socket.onclose = function() { - console.error("web channel closed"); - }; - - socket.onerror = function(error) { - console.error("web channel error: " + error); - }; - - socket.onopen = function() { - channel = new QWebChannel(socket, function(channel) { - console.log("Document url is " + document.URL); - var webWindow = channel.objects[document.URL.toLowerCase()]; - console.log("WebWindow is " + webWindow) - eventBridgeProxy = new EventBridgeProxy(webWindow); - EventBridge = eventBridgeProxy; - if (callback) { callback(eventBridgeProxy); } - }); - } -} - diff --git a/examples/html/qmlWebTest.html b/examples/html/qmlWebTest.html index e59535701d..553ce83417 100644 --- a/examples/html/qmlWebTest.html +++ b/examples/html/qmlWebTest.html @@ -4,21 +4,17 @@ - diff --git a/examples/tests/qmlWebTest.js b/examples/tests/qmlWebTest.js index 5faa68668d..d29f2ba002 100644 --- a/examples/tests/qmlWebTest.js +++ b/examples/tests/qmlWebTest.js @@ -8,26 +8,14 @@ webWindow.eventBridge.webEventReceived.connect(function(data) { print("JS Side event received: " + data); }); -var titles = ["A", "B", "C"]; -var titleIndex = 0; - Script.setInterval(function() { - webWindow.eventBridge.emitScriptEvent("JS Event sent"); - var size = webWindow.size; - var position = webWindow.position; - print("Window url: " + webWindow.url) - print("Window visible: " + webWindow.visible) - print("Window size: " + size.x + "x" + size.y) - print("Window pos: " + position.x + "x" + position.y) - webWindow.setVisible(!webWindow.visible); - webWindow.setTitle(titles[titleIndex]); - webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100); - titleIndex += 1; - titleIndex %= titles.length; -}, 2 * 1000); + var message = [ Math.random(), Math.random() ]; + print("JS Side sending: " + message); + webWindow.emitScriptEvent(message); +}, 5 * 1000); -Script.setTimeout(function() { - print("Closing script"); +Script.scriptEnding.connect(function(){ webWindow.close(); - Script.stop(); -}, 15 * 1000) + webWindow.deleteLater(); +}); + diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index fd4e629568..0058770462 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -1,6 +1,7 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtWebEngine 1.1 +import QtWebChannel 1.0 import "windows" as Windows import "controls" as Controls @@ -15,11 +16,22 @@ Windows.Window { // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer destroyOnCloseButton: false property alias source: webview.url + property alias webChannel: webview.webChannel + // A unique identifier to let the HTML JS find the event bridge + // object (our C++ wrapper) + property string uid; + + // This is for JS/QML communication, which is unused in a WebWindow, + // but not having this here results in spurious warnings about a + // missing signal + signal sendToScript(var message); Controls.WebView { id: webview url: "about:blank" anchors.fill: parent focus: true + onUrlChanged: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";"); + Component.onCompleted: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";"); } } // dialog diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml index 75aa50aa34..5313ca23e9 100644 --- a/interface/resources/qml/ToolWindow.qml +++ b/interface/resources/qml/ToolWindow.qml @@ -37,14 +37,33 @@ Windows.Window { Repeater { model: 4 Tab { + // Force loading of the content even if the tab is not visible + // (required for letting the C++ code access the webview) active: true - enabled: false; - // we need to store the original url here for future identification + enabled: false property string originalUrl: ""; - onEnabledChanged: toolWindow.updateVisiblity(); + Controls.WebView { id: webView; + // we need to store the original url here for future identification + // A unique identifier to let the HTML JS find the event bridge + // object (our C++ wrapper) + property string uid; anchors.fill: parent + enabled: false + + // This is for JS/QML communication, which is unused in a WebWindow, + // but not having this here results in spurious warnings about a + // missing signal + signal sendToScript(var message); + + onUrlChanged: webView.runJavaScript("EventBridgeUid = \"" + uid + "\";"); + onEnabledChanged: toolWindow.updateVisiblity(); + onLoadingChanged: { + if (loadRequest.status == WebEngineView.LoadSucceededStatus) { + webView.runJavaScript("EventBridgeUid = \"" + uid + "\";"); + } + } } } } @@ -113,20 +132,23 @@ Windows.Window { var tab = tabView.getTab(index); tab.title = ""; - tab.originalUrl = ""; tab.enabled = false; + tab.originalUrl = ""; + tab.item.url = "about:blank"; + tab.item.enabled = false; } function addWebTab(properties) { if (!properties.source) { - console.warn("Attempted to open Web Tool Pane without URL") + console.warn("Attempted to open Web Tool Pane without URL"); return; } var existingTabIndex = findIndexForUrl(properties.source); if (existingTabIndex >= 0) { - console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source) - return tabView.getTab(existingTabIndex); + console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source); + var tab = tabView.getTab(existingTabIndex); + return tab.item; } var freeTabIndex = findFreeTab(); @@ -135,25 +157,22 @@ Windows.Window { return; } - var newTab = tabView.getTab(freeTabIndex); - newTab.title = properties.title || "Unknown"; - newTab.originalUrl = properties.source; - newTab.item.url = properties.source; - newTab.active = true; - if (properties.width) { - tabView.width = Math.min(Math.max(tabView.width, properties.width), - toolWindow.maxSize.x); + tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x); } if (properties.height) { - tabView.height = Math.min(Math.max(tabView.height, properties.height), - toolWindow.maxSize.y); + tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y); } - console.log("Updating visibility based on child tab added"); - newTab.enabledChanged.connect(updateVisiblity) - updateVisiblity(); - return newTab + var tab = tabView.getTab(freeTabIndex); + tab.title = properties.title || "Unknown"; + tab.enabled = true; + tab.originalUrl = properties.source; + + var result = tab.item; + result.enabled = true; + result.url = properties.source; + return result; } } diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 18080cd448..1361e6e322 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -59,6 +59,7 @@ WebEngineView { request.openIn(newWindow.webView) } - - profile: desktop.browserProfile + // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 + // See https://bugreports.qt.io/browse/QTBUG-49521 + //profile: desktop.browserProfile } diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 0228f77f4f..43fd5a64df 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -14,6 +14,8 @@ #include +#include + #include #include @@ -35,8 +37,29 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi } QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) { + _uid = QUuid::createUuid().toString(); + asQuickItem()->setProperty("uid", _uid); + auto webchannelVar = qmlWindow->property("webChannel"); + _webchannel = qvariant_cast(webchannelVar); + Q_ASSERT(_webchannel); + _webchannel->registerObject(_uid, this); } +void QmlWebWindowClass::emitScriptEvent(const QVariant& scriptMessage) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage)); + } else { + emit scriptEventReceived(scriptMessage); + } +} + +void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage)); + } else { + emit webEventReceived(webMessage); + } +} QString QmlWebWindowClass::getURL() const { QVariant result = DependencyManager::get()->returnFromUiThread([&]()->QVariant { diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index 27c0e6996d..35322ef0ea 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -11,10 +11,13 @@ #include "QmlWindowClass.h" +class QWebChannel; + // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping class QmlWebWindowClass : public QmlWindowClass { Q_OBJECT Q_PROPERTY(QString url READ getURL CONSTANT) + Q_PROPERTY(QString uid READ getUid CONSTANT) public: static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); @@ -23,9 +26,18 @@ public: public slots: QString getURL() const; void setURL(const QString& url); + const QString& getUid() const { return _uid; } + void emitScriptEvent(const QVariant& scriptMessage); + void emitWebEvent(const QVariant& webMessage); signals: void urlChanged(); + void scriptEventReceived(const QVariant& message); + void webEventReceived(const QVariant& message); + +private: + QString _uid; + QWebChannel* _webchannel { nullptr }; }; #endif diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index b7fe330a4e..37d461acd0 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -26,10 +26,6 @@ #include "OffscreenUi.h" -QWebSocketServer* QmlWindowClass::_webChannelServer { nullptr }; -static QWebChannel webChannel; -static const uint16_t WEB_CHANNEL_PORT = 51016; -static std::atomic nextWindowId; static const char* const SOURCE_PROPERTY = "source"; static const char* const TITLE_PROPERTY = "title"; static const char* const WIDTH_PROPERTY = "width"; @@ -37,54 +33,6 @@ static const char* const HEIGHT_PROPERTY = "height"; static const char* const VISIBILE_PROPERTY = "visible"; static const char* const TOOLWINDOW_PROPERTY = "toolWindow"; -void QmlScriptEventBridge::emitWebEvent(const QString& data) { - QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data)); -} - -void QmlScriptEventBridge::emitScriptEvent(const QString& data) { - QMetaObject::invokeMethod(this, "scriptEventReceived", Qt::QueuedConnection, - Q_ARG(int, _webWindow->getWindowId()), Q_ARG(QString, data)); -} - -class QmlWebTransport : public QWebChannelAbstractTransport { - Q_OBJECT -public: - QmlWebTransport(QWebSocket* webSocket) : _webSocket(webSocket) { - // Translate from the websocket layer to the webchannel layer - connect(webSocket, &QWebSocket::textMessageReceived, [this](const QString& message) { - QJsonParseError error; - QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error); - if (error.error || !document.isObject()) { - qWarning() << "Unable to parse incoming JSON message" << message; - return; - } - emit messageReceived(document.object(), this); - }); - } - - virtual void sendMessage(const QJsonObject &message) override { - // Translate from the webchannel layer to the websocket layer - _webSocket->sendTextMessage(QJsonDocument(message).toJson(QJsonDocument::Compact)); - } - -private: - QWebSocket* const _webSocket; -}; - - -void QmlWindowClass::setupServer() { - if (!_webChannelServer) { - _webChannelServer = new QWebSocketServer("EventBridge Server", QWebSocketServer::NonSecureMode); - if (!_webChannelServer->listen(QHostAddress::LocalHost, WEB_CHANNEL_PORT)) { - qFatal("Failed to open web socket server."); - } - - QObject::connect(_webChannelServer, &QWebSocketServer::newConnection, [] { - webChannel.connectTo(new QmlWebTransport(_webChannelServer->nextPendingConnection())); - }); - } -} - QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, QScriptContext* context, QScriptEngine* engine, std::function builder) @@ -168,10 +116,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, } offscreenUi->returnFromUiThread([&] { - setupServer(); retVal = builder(newTab); retVal->_toolWindow = true; - registerObject(url.toLower(), retVal); return QVariant(); }); } else { @@ -179,10 +125,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection, Q_ARG(const QString&, qmlSource), Q_ARG(std::function, [&](QQmlContext* context, QObject* object) { - setupServer(); retVal = builder(object); context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership); - registerObject(url.toLower(), retVal); if (!title.isEmpty()) { retVal->setTitle(title); } @@ -209,10 +153,7 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* }); } -QmlWindowClass::QmlWindowClass(QObject* qmlWindow) - : _windowId(++nextWindowId), _qmlWindow(qmlWindow) -{ - qDebug() << "Created window with ID " << _windowId; +QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) { Q_ASSERT(_qmlWindow); Q_ASSERT(dynamic_cast(_qmlWindow.data())); // Forward messages received from QML on to the script @@ -228,14 +169,6 @@ QmlWindowClass::~QmlWindowClass() { close(); } -void QmlWindowClass::registerObject(const QString& name, QObject* object) { - webChannel.registerObject(name, object); -} - -void QmlWindowClass::deregisterObject(QObject* object) { - webChannel.deregisterObject(object); -} - QQuickItem* QmlWindowClass::asQuickItem() const { if (_toolWindow) { return DependencyManager::get()->getToolWindow(); diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index fb7dbf1253..eda6ce674c 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -13,38 +13,16 @@ #include #include #include -#include #include class QScriptEngine; class QScriptContext; -class QmlWindowClass; -class QWebSocketServer; -class QWebSocket; -class QmlScriptEventBridge : public QObject { - Q_OBJECT -public: - QmlScriptEventBridge(const QmlWindowClass* webWindow) : _webWindow(webWindow) {} - -public slots : - void emitWebEvent(const QString& data); - void emitScriptEvent(const QString& data); - -signals: - void webEventReceived(const QString& data); - void scriptEventReceived(int windowId, const QString& data); - -private: - const QmlWindowClass* _webWindow { nullptr }; - QWebSocket *_socket { nullptr }; -}; // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping class QmlWindowClass : public QObject { Q_OBJECT Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT) - Q_PROPERTY(int windowId READ getWindowId CONSTANT) Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged) Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) @@ -69,8 +47,7 @@ public slots: Q_INVOKABLE void raise(); Q_INVOKABLE void close(); - Q_INVOKABLE int getWindowId() const { return _windowId; }; - Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; }; + Q_INVOKABLE QObject* getEventBridge() { return this; }; // Scripts can use this to send a message to the QML object void sendToQml(const QVariant& message); @@ -92,18 +69,12 @@ protected: static QScriptValue internalConstructor(const QString& qmlSource, QScriptContext* context, QScriptEngine* engine, std::function function); - static void setupServer(); - static void registerObject(const QString& name, QObject* object); - static void deregisterObject(QObject* object); - static QWebSocketServer* _webChannelServer; QQuickItem* asQuickItem() const; - QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) }; // FIXME needs to be initialized in the ctor once we have support // for tool window panes in QML bool _toolWindow { false }; - const int _windowId; QPointer _qmlWindow; QString _source; }; From 24f2bdac7795ac5c3cb3fc3235f7821f031ba1be Mon Sep 17 00:00:00 2001 From: Menithal Date: Thu, 24 Mar 2016 20:58:32 +0200 Subject: [PATCH 20/31] Fixed Styling --- examples/html/entityProperties.html | 80 ++++++++++++++--------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 3380623c66..225a1d7957 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -451,36 +451,36 @@ var elPreviewCameraButton = document.getElementById("preview-camera-button"); - var urlUpdaters = document.getElementsByClassName("update-url-version"); - var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters. - var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/; - - var refreshEvent = function(event){ - var urlElement = event.target.parentElement.getElementsByClassName("url")[0]; - var content = urlElement.value; - var date = new Date(); - var timeStamp = date.getTime(); + var urlUpdaters = document.getElementsByClassName("update-url-version"); + var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters. + var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/; - if(content.length > 0){ - if(PARAM_REGEXP.test(content)){ - // Has params, so lets remove existing definition and append again. - content = content.replace(TIMESTAMP_REGEXP,"") + "&"; - }else{ - content += "?"; - } - content = content.replace("?&","?"); - urlElement.value = content + "HFTime=" + timeStamp; - } - var evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", true, true ); - urlElement.dispatchEvent(evt); + var refreshEvent = function(event){ + var urlElement = event.target.parentElement.getElementsByClassName("url")[0]; + var content = urlElement.value; + var date = new Date(); + var timeStamp = date.getTime(); - }; + if(content.length > 0){ + if(PARAM_REGEXP.test(content)){ + // Has params, so lets remove existing definition and append again. + content = content.replace(TIMESTAMP_REGEXP,"") + "&"; + }else{ + content += "?"; + } + content = content.replace("?&","?"); + urlElement.value = content + "HFTime=" + timeStamp; + } - for(var index = 0; index < urlUpdaters.length; index++){ - var urlUpdater = urlUpdaters[index]; - urlUpdater.addEventListener("click", refreshEvent); - } + var evt = document.createEvent("HTMLEvents"); + evt.initEvent("change", true, true ); + urlElement.dispatchEvent(evt); + }; + + for(var index = 0; index < urlUpdaters.length; index++){ + var urlUpdater = urlUpdaters[index]; + urlUpdater.addEventListener("click", refreshEvent); + } if (window.EventBridge !== undefined) { var properties; @@ -1216,7 +1216,7 @@
Ambient URL
-
+
@@ -1294,7 +1294,7 @@
Skybox URL
-
+
@@ -1306,7 +1306,7 @@
Source URL
-
+
@@ -1320,14 +1320,14 @@
Href - Hifi://address
-
+
@@ -1411,19 +1411,19 @@
X-axis Texture URL
-
+
Y-axis Texture URL
-
+
Z-axis Texture URL
-
+
@@ -1605,7 +1605,7 @@
Collision Sound URL
-
+
@@ -1623,7 +1623,7 @@
-
+
@@ -1636,7 +1636,7 @@
Model URL
-
+
@@ -1655,14 +1655,14 @@
Compound Shape URL
-
+
Animation URL
-
+
From c6ca984782348f4c5a5f9cacbe3cf2d53cf4ca69 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 24 Mar 2016 12:59:52 -0700 Subject: [PATCH 21/31] Fix Reverb Zone settings to work correctly with new reverb. --- assignment-client/src/audio/AudioMixer.cpp | 15 ++++++++------- domain-server/resources/describe-settings.json | 6 +++--- libraries/audio-client/src/AudioClient.cpp | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 779307c19d..fda528473f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -370,13 +370,14 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { wetLevel = _zoneReverbSettings[i].wetLevel; // Modulate wet level with distance to wall - float MIN_ATTENUATION_DISTANCE = 2.0f; - float MAX_ATTENUATION = -12; // dB - glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter()); - float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z); - if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) { - wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE); - } + //float MIN_ATTENUATION_DISTANCE = 2.0f; + //float MAX_ATTENUATION = -12; // dB + //glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter()); + //float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z); + //if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) { + // wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE); + //} + break; } } diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 80ee32efa1..44a1796a8d 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -308,7 +308,7 @@ "name": "reverb", "type": "table", "label": "Reverb Settings", - "help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet level of -10 db. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet level of -5 db.", + "help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.", "numbered": true, "columns": [ { @@ -325,9 +325,9 @@ }, { "name": "wet_level", - "label": "Wet Level", + "label": "Wet/Dry Mix", "can_set": true, - "placeholder": "(in db)" + "placeholder": "(in percent)" } ] } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4d44a771f7..7e01196dc7 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -565,10 +565,10 @@ void AudioClient::updateReverbOptions() { _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); reverbChanged = true; } - //if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) { - // _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); - // reverbChanged = true; - //} + if (_zoneReverbOptions.getWetDryMix() != _receivedAudioStream.getWetLevel()) { + _zoneReverbOptions.setWetDryMix(_receivedAudioStream.getWetLevel()); + reverbChanged = true; + } if (_reverbOptions != &_zoneReverbOptions) { _reverbOptions = &_zoneReverbOptions; From c302c803b0a80902e0e3cbea853d321b81687d23 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 24 Mar 2016 13:20:29 -0700 Subject: [PATCH 22/31] CullTask: added ability to disable culling during rendering. You can use the following JavaScript to disable culling: Render.RenderDeferredTask.CullSceneSelection.skipCulling = true --- libraries/render/src/render/CullTask.cpp | 129 ++++++++++++++++------- libraries/render/src/render/CullTask.h | 5 +- 2 files changed, 95 insertions(+), 39 deletions(-) diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 484f049944..00780d0176 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -114,6 +114,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render void CullSpatialSelection::configure(const Config& config) { _justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum); _freezeFrustum = config.freezeFrustum; + _skipCulling = config.skipCulling; } void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, @@ -191,60 +192,112 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re // visibility cull if partially selected ( octree cell contianing it was partial) // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) - // inside & fit items: easy, just filter - { - PerformanceTimer perfTimer("insideFitItems"); - for (auto id : inSelection.insideItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // inside & subcell items: filter & distance cull - { - PerformanceTimer perfTimer("insideSmallItems"); - for (auto id : inSelection.insideSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.solidAngleTest(itemBound.bound)) { + if (_skipCulling) { + // inside & fit items: easy, just filter + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); } } } - } - // partial & fit items: filter & frustum cull - { - PerformanceTimer perfTimer("partialFitItems"); - for (auto id : inSelection.partialItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { + // inside & subcell items: filter & distance cull + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); } } } - } - // partial & subcell items:: filter & frutum cull & solidangle cull - { - PerformanceTimer perfTimer("partialSmallItems"); - for (auto id : inSelection.partialSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { + // partial & fit items: filter & frustum cull + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & subcell items:: filter & frutum cull & solidangle cull + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + } else { + + // inside & fit items: easy, just filter + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // inside & subcell items: filter & distance cull + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); } } } } + + // partial & fit items: filter & frustum cull + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + + // partial & subcell items:: filter & frutum cull & solidangle cull + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (_filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + } } details._rendered += (int)outItems.size(); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index a6a32e4561..e84f018e91 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -70,14 +70,16 @@ namespace render { Q_OBJECT Q_PROPERTY(int numItems READ getNumItems) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + Q_PROPERTY(bool skipCulling MEMBER skipCulling WRITE setSkipCulling) public: int numItems{ 0 }; int getNumItems() { return numItems; } bool freezeFrustum{ false }; + bool skipCulling{ false }; public slots: void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } - + void setSkipCulling(bool enabled) { skipCulling = enabled; emit dirty(); } signals: void dirty(); }; @@ -85,6 +87,7 @@ namespace render { class CullSpatialSelection { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; + bool _skipCulling{ false }; ViewFrustum _frozenFrutstum; public: using Config = CullSpatialSelectionConfig; From ba4520fe56ad879dc2b330208cdce4f1706cc460 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 24 Mar 2016 13:23:30 -0700 Subject: [PATCH 23/31] Avatar: fix for Avatar animation updates while wearing HMD The boundingSphere vs frustum check now uses the displayViewFrustum, which more accurately reflects the actual projection matrix used for rendering. --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7b63b7fc5a..f722210a8e 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -187,7 +187,7 @@ void Avatar::simulate(float deltaTime) { // simple frustum check float boundingRadius = getBoundingRadius(); - bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius); + bool inView = qApp->getDisplayViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius); if (_shouldAnimate && !_shouldSkipRender && inView) { { From 063891d65eff3615d923b2fe31dd814589e0b90e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 24 Mar 2016 13:29:57 -0700 Subject: [PATCH 24/31] remove dead code --- assignment-client/src/audio/AudioMixer.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index fda528473f..cc6c4930ff 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -369,15 +369,6 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { reverbTime = _zoneReverbSettings[i].reverbTime; wetLevel = _zoneReverbSettings[i].wetLevel; - // Modulate wet level with distance to wall - //float MIN_ATTENUATION_DISTANCE = 2.0f; - //float MAX_ATTENUATION = -12; // dB - //glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter()); - //float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z); - //if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) { - // wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE); - //} - break; } } From 520cccb2190b34edca8e5301119765be433827ba Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 24 Mar 2016 15:19:46 -0700 Subject: [PATCH 25/31] Make display plugin switching atomic from other threads --- interface/src/Application.cpp | 47 +++++++++++++++++++++-------------- interface/src/Application.h | 2 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e7bccae892..c8860ffdaa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4677,13 +4677,18 @@ qreal Application::getDevicePixelRatio() { } DisplayPlugin* Application::getActiveDisplayPlugin() { - std::unique_lock lock(_displayPluginLock); - if (nullptr == _displayPlugin && QThread::currentThread() == thread()) { - updateDisplayMode(); - Q_ASSERT(_displayPlugin); + DisplayPlugin* result = nullptr; + if (QThread::currentThread() == thread()) { + if (nullptr == _displayPlugin) { + updateDisplayMode(); + Q_ASSERT(_displayPlugin); + } + result = _displayPlugin.get(); + } else { + std::unique_lock lock(_displayPluginLock); + result = _displayPlugin.get(); } - - return _displayPlugin.get(); + return result; } const DisplayPlugin* Application::getActiveDisplayPlugin() const { @@ -4801,20 +4806,26 @@ void Application::updateDisplayMode() { return; } - if (_displayPlugin) { - _displayPlugin->deactivate(); - } - auto offscreenUi = DependencyManager::get(); - // FIXME probably excessive and useless context switching - _offscreenContext->makeCurrent(); - newDisplayPlugin->activate(); - _offscreenContext->makeCurrent(); - offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); - _offscreenContext->makeCurrent(); - getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); - _displayPlugin = newDisplayPlugin; + // Make the switch atomic from the perspective of other threads + { + std::unique_lock lock(_displayPluginLock); + + if (_displayPlugin) { + _displayPlugin->deactivate(); + } + + // FIXME probably excessive and useless context switching + _offscreenContext->makeCurrent(); + newDisplayPlugin->activate(); + _offscreenContext->makeCurrent(); + offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); + _offscreenContext->makeCurrent(); + getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); + _displayPlugin = newDisplayPlugin; + } + emit activeDisplayPluginChanged(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 695d30998a..d21e647bc7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -383,7 +383,7 @@ private: OffscreenGLCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; - std::recursive_mutex _displayPluginLock; + std::mutex _displayPluginLock; InputPluginList _activeInputPlugins; bool _activatingDisplayPlugin { false }; From 8d0e419a5c1cf5f65e94ebb88b0b883ce58f2890 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 15:50:48 -0700 Subject: [PATCH 26/31] Clean image-reader to drop image sooner --- .../model-networking/src/model-networking/TextureCache.cpp | 3 +-- libraries/model-networking/src/model-networking/TextureCache.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 58a82d5f11..28f4882b86 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -320,13 +320,12 @@ void ImageReader::run() { } QMetaObject::invokeMethod(texture.data(), "setImage", - Q_ARG(const QImage&, image), Q_ARG(void*, theTexture), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); QThread::currentThread()->setPriority(originalPriority); } -void NetworkTexture::setImage(const QImage& image, void* voidTexture, int originalWidth, +void NetworkTexture::setImage(void* voidTexture, int originalWidth, int originalHeight) { _originalWidth = originalWidth; _originalHeight = originalHeight; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 6fb0cc3177..858a40de36 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -136,7 +136,7 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); // FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on... - Q_INVOKABLE void setImage(const QImage& image, void* texture, int originalWidth, int originalHeight); + Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight); private: From afc853572a547fb0932df1de29262aa4a09063ea Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 15:50:51 -0700 Subject: [PATCH 27/31] Drop resource data after load --- libraries/networking/src/ResourceCache.cpp | 6 +++--- libraries/networking/src/ResourceCache.h | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 3de94ed839..9738233c85 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -423,12 +423,12 @@ void Resource::handleReplyFinished() { auto result = _request->getResult(); if (result == ResourceRequest::Success) { - _data = _request->getData(); auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - emit loaded(_data); - downloadFinished(_data); + auto data = _request->getData(); + emit loaded(data); + downloadFinished(data); } else { switch (result) { case ResourceRequest::Result::Timeout: { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 77878794b5..fd3dd75d15 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -194,7 +194,6 @@ public: Q_INVOKABLE void allReferencesCleared(); const QUrl& getURL() const { return _url; } - const QByteArray& getData() const { return _data; } signals: /// Fired when the resource has been downloaded. @@ -235,7 +234,6 @@ protected: QHash, float> _loadPriorities; QWeakPointer _self; QPointer _cache; - QByteArray _data; private slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); From 76c30d901421fcc1e6e57c53e11ed9ca005b4859 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 24 Mar 2016 17:56:11 -0700 Subject: [PATCH 28/31] Fix sometimes slow server-console logs. --- server-console/src/log.js | 46 ++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/server-console/src/log.js b/server-console/src/log.js index e45848e5a5..3634eaeaa7 100644 --- a/server-console/src/log.js +++ b/server-console/src/log.js @@ -44,6 +44,14 @@ ready = function() { var domainServer = remote.getGlobal('domainServer'); var acMonitor = remote.getGlobal('acMonitor'); + var pendingLines = { + 'ds': new Array(), + 'ac': new Array() + }; + + var UPDATE_INTERVAL = 16; // Update log at ~60 fps + var interval = setInterval(flushPendingLines, UPDATE_INTERVAL); + var logWatchers = { 'ds': { }, @@ -83,7 +91,7 @@ ready = function() { var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 }); logTail.on('line', function(msg) { - appendLogMessage(msg, stream); + pendingLines[stream].push(msg); }); logTail.on('error', function(error) { @@ -107,6 +115,7 @@ ready = function() { } window.onbeforeunload = function(e) { + clearInterval(interval); domainServer.removeListener('logs-updated', updateLogFiles); acMonitor.removeListener('logs-updated', updateLogFiles); }; @@ -164,14 +173,23 @@ ready = function() { return !filter || message.toLowerCase().indexOf(filter) >= 0; } - function appendLogMessage(msg, name) { + function appendLogMessages(name) { + var array = pendingLines[name]; + if (array.length === 0) { + return; + } + if (array.length > maxLogLines) { + array = array.slice(-maxLogLines); + } + + console.log(name, array.length); + var id = name == "ds" ? "domain-server" : "assignment-client"; var $pidLog = $('#' + id); - var size = ++tabStates[id].size; + var size = tabStates[id].size + array.length; if (size > maxLogLines) { - $pidLog.find('div.log-line:first').remove(); - removed = true; + $pidLog.find('div.log-line:lt(' + (size - maxLogLines) + ')').remove(); } var wasAtBottom = false; @@ -179,17 +197,25 @@ ready = function() { wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height()); } - var $logLine = $('
').text(msg); - if (!shouldDisplayLogMessage(msg)) { - $logLine.hide(); + for (line in array) { + var $logLine = $('
').text(array[line]); + if (!shouldDisplayLogMessage(array[line])) { + $logLine.hide(); + } + + $pidLog.append($logLine); } - $pidLog.append($logLine); + delete pendingLines[name]; + pendingLines[name] = new Array(); if (wasAtBottom) { $pidLog.scrollTop($pidLog[0].scrollHeight); } - + } + function flushPendingLines() { + appendLogMessages('ds'); + appendLogMessages('ac'); } // handle filtering of table rows on input change From 413c3b88cf3012c64d7faa472c4d56ca5db56be7 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 19:40:02 -0700 Subject: [PATCH 29/31] Accept textures as list or json --- .../src/RenderableModelEntityItem.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ff4ed28150..98559a56a4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -113,14 +113,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) { return _originalTexturesMap; } - QString jsonTextures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; + // Legacy: a ,\n-delimited list of filename:"texturepath" + if (*textures.cbegin() != '{') { + textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; + } + QJsonParseError error; - QJsonDocument texturesAsJson = QJsonDocument::fromJson(jsonTextures.toUtf8(), &error); + QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures; + return _originalTexturesMap; } - QJsonObject texturesAsJsonObject = texturesAsJson.object(); - return texturesAsJsonObject.toVariantMap(); + return texturesJson.object().toVariantMap(); } void RenderableModelEntityItem::remapTextures() { From cd5beabf10cc8479637c0779f0587329b697b8cf Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 24 Mar 2016 20:37:16 -0700 Subject: [PATCH 30/31] Revert "Drop resource data after load" --- libraries/networking/src/ResourceCache.cpp | 6 +++--- libraries/networking/src/ResourceCache.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 9738233c85..3de94ed839 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -423,12 +423,12 @@ void Resource::handleReplyFinished() { auto result = _request->getResult(); if (result == ResourceRequest::Success) { + _data = _request->getData(); auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); - auto data = _request->getData(); - emit loaded(data); - downloadFinished(data); + emit loaded(_data); + downloadFinished(_data); } else { switch (result) { case ResourceRequest::Result::Timeout: { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index fd3dd75d15..77878794b5 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -194,6 +194,7 @@ public: Q_INVOKABLE void allReferencesCleared(); const QUrl& getURL() const { return _url; } + const QByteArray& getData() const { return _data; } signals: /// Fired when the resource has been downloaded. @@ -234,6 +235,7 @@ protected: QHash, float> _loadPriorities; QWeakPointer _self; QPointer _cache; + QByteArray _data; private slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); From c00a412d3bd0d68f215bd3dfb7dbcf77c8d7cbbe Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 25 Mar 2016 09:10:46 -0700 Subject: [PATCH 31/31] Fix misleading comments for _skipCulling case. --- libraries/render/src/render/CullTask.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 00780d0176..56805e8f83 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -193,7 +193,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) if (_skipCulling) { - // inside & fit items: easy, just filter + // inside & fit items: filter only, culling is disabled { PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { @@ -205,7 +205,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re } } - // inside & subcell items: filter & distance cull + // inside & subcell items: filter only, culling is disabled { PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { @@ -217,7 +217,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re } } - // partial & fit items: filter & frustum cull + // partial & fit items: filter only, culling is disabled { PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { @@ -229,7 +229,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re } } - // partial & subcell items:: filter & frutum cull & solidangle cull + // partial & subcell items: filter only, culling is disabled { PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) {