From 63d6f888e61f8cd05a70baaf3b1ec299ea410196 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 21 Mar 2016 17:31:13 -0700 Subject: [PATCH 01/32] 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 02/32] 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 03/32] 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 04/32] 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 05/32] 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 06/32] 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 07/32] 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 08/32] 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 09/32] 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 10/32] 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 11/32] 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 12/32] 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 88c4e54cff936f7c4aa96bb1196f7163c1db2a12 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 23 Mar 2016 17:38:40 -0700 Subject: [PATCH 13/32] 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 8bbabf186f859a8169110a033de125773facef7a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 5 Mar 2016 02:49:55 -0800 Subject: [PATCH 14/32] 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 15/32] 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 16/32] 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 17/32] 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 18/32] 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 19/32] 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 20/32] 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 21/32] 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 22/32] 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 23/32] 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 24/32] 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 6a5a443eff7dc2edf4c8a55e134f7afce331a199 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 24 Mar 2016 18:03:51 -0700 Subject: [PATCH 25/32] Avatar: Eye-tracking works again This replaces the calculation of the Head left and right eye positions used for eye tracking. Which was inadvertently removed in this commit 7483b8546b8e5003f87ea814a90fce144edafad9 --- interface/src/avatar/Head.cpp | 7 +++++++ interface/src/avatar/MyAvatar.cpp | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 620c35c832..5587096ce3 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -231,6 +231,13 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _leftEyePosition = _rightEyePosition = getPosition(); _eyePosition = calculateAverageEyePosition(); + + if (!billboard && _owningAvatar) { + auto skeletonModel = static_cast(_owningAvatar)->getSkeletonModel(); + if (skeletonModel) { + skeletonModel->getEyePositions(_leftEyePosition, _rightEyePosition); + } + } } void Head::calculateMouthShapes() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8f11c635e9..210e6feb5b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -897,7 +897,9 @@ void MyAvatar::updateLookAtTargetAvatar() { // Scale by proportional differences between avatar and human. float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye) * ipdScale; float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); - gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; + if (avatarEyeSeparation > 0.0f) { + gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; + } } // And now we can finally add that offset to the camera. From 413c3b88cf3012c64d7faa472c4d56ca5db56be7 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 19:40:02 -0700 Subject: [PATCH 26/32] 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 27/32] 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 d3d724530a97018f16699da3d8854652390202eb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 24 Mar 2016 21:09:45 -0700 Subject: [PATCH 28/32] minor optimiztion --- libraries/entities/src/EntitySimulation.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 014ab33094..14bfc5ac7a 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -101,12 +101,15 @@ void EntitySimulation::expireMortalEntities(const quint64& now) { prepareEntityForDelete(entity); } else { if (expiry < _nextExpiry) { - // remeber the smallest _nextExpiry so we know when to start the next search + // remember the smallest _nextExpiry so we know when to start the next search _nextExpiry = expiry; } ++itemItr; } } + if (_mortalEntities.size() < 1) { + _nextExpiry = -1; + } } } From ac5c055a1ea1fe07ed2aa3664a8ce1ce366335f5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 24 Mar 2016 21:11:05 -0700 Subject: [PATCH 29/32] don't verify simulated bit when adding to delete list --- .../physics/src/PhysicalEntitySimulation.cpp | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 497ed2031f..2d219915c8 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -57,16 +57,18 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { } void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) { - EntitySimulation::removeEntityInternal(entity); - QMutexLocker lock(&_mutex); - _entitiesToAddToPhysics.remove(entity); + if (entity->isSimulated()) { + EntitySimulation::removeEntityInternal(entity); + QMutexLocker lock(&_mutex); + _entitiesToAddToPhysics.remove(entity); - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState) { - _outgoingChanges.remove(motionState); - _entitiesToRemoveFromPhysics.insert(entity); - } else { - _entitiesToDelete.insert(entity); + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + _outgoingChanges.remove(motionState); + _entitiesToRemoveFromPhysics.insert(entity); + } else { + _entitiesToDelete.insert(entity); + } } } @@ -175,7 +177,7 @@ void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionState _entitiesToRelease.insert(entity); } - if (entity->isSimulated() && entity->isDead()) { + if (entity->isDead()) { _entitiesToDelete.insert(entity); } } @@ -190,7 +192,7 @@ void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() { entity->setPhysicsInfo(nullptr); delete motionState; - if (entity->isSimulated() && entity->isDead()) { + if (entity->isDead()) { _entitiesToDelete.insert(entity); } } From c00a412d3bd0d68f215bd3dfb7dbcf77c8d7cbbe Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 25 Mar 2016 09:10:46 -0700 Subject: [PATCH 30/32] 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) { From a47614c9b6970e32065b45f9f5961f28b6b3fd08 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 25 Mar 2016 09:48:25 -0700 Subject: [PATCH 31/32] Revert "Build a better event bridge" This reverts commit a43fde0803141657c45f82371136079455bed263. --- 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, 192 insertions(+), 108 deletions(-) diff --git a/examples/html/eventBridgeLoader.js b/examples/html/eventBridgeLoader.js index de6553ee1c..ebfb6dc740 100644 --- a/examples/html/eventBridgeLoader.js +++ b/examples/html/eventBridgeLoader.js @@ -10,11 +10,50 @@ var EventBridge; -openEventBridge = function(callback) { - new QWebChannel(qt.webChannelTransport, function(channel) { - console.log("uid " + EventBridgeUid); - EventBridge = channel.objects[EventBridgeUid]; - callback(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); } }); } +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 553ce83417..e59535701d 100644 --- a/examples/html/qmlWebTest.html +++ b/examples/html/qmlWebTest.html @@ -4,17 +4,21 @@ + diff --git a/examples/tests/qmlWebTest.js b/examples/tests/qmlWebTest.js index d29f2ba002..5faa68668d 100644 --- a/examples/tests/qmlWebTest.js +++ b/examples/tests/qmlWebTest.js @@ -8,14 +8,26 @@ webWindow.eventBridge.webEventReceived.connect(function(data) { print("JS Side event received: " + data); }); +var titles = ["A", "B", "C"]; +var titleIndex = 0; + Script.setInterval(function() { - var message = [ Math.random(), Math.random() ]; - print("JS Side sending: " + message); - webWindow.emitScriptEvent(message); -}, 5 * 1000); + 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); -Script.scriptEnding.connect(function(){ +Script.setTimeout(function() { + print("Closing script"); webWindow.close(); - webWindow.deleteLater(); -}); - + Script.stop(); +}, 15 * 1000) diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 0058770462..fd4e629568 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -1,7 +1,6 @@ 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 @@ -16,22 +15,11 @@ 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 5313ca23e9..75aa50aa34 100644 --- a/interface/resources/qml/ToolWindow.qml +++ b/interface/resources/qml/ToolWindow.qml @@ -37,33 +37,14 @@ 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 + enabled: false; + // we need to store the original url here for future identification 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 + "\";"); - } - } } } } @@ -132,23 +113,20 @@ Windows.Window { var tab = tabView.getTab(index); tab.title = ""; - tab.enabled = false; tab.originalUrl = ""; - tab.item.url = "about:blank"; - tab.item.enabled = false; + tab.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); - var tab = tabView.getTab(existingTabIndex); - return tab.item; + console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source) + return tabView.getTab(existingTabIndex); } var freeTabIndex = findFreeTab(); @@ -157,22 +135,25 @@ 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); } - 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; + console.log("Updating visibility based on child tab added"); + newTab.enabledChanged.connect(updateVisiblity) + updateVisiblity(); + return newTab } } diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 1361e6e322..18080cd448 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -59,7 +59,6 @@ WebEngineView { request.openIn(newWindow.webView) } - // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 - // See https://bugreports.qt.io/browse/QTBUG-49521 - //profile: desktop.browserProfile + + profile: desktop.browserProfile } diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 43fd5a64df..0228f77f4f 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -14,8 +14,6 @@ #include -#include - #include #include @@ -37,29 +35,8 @@ 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 35322ef0ea..27c0e6996d 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -11,13 +11,10 @@ #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); @@ -26,18 +23,9 @@ 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 37d461acd0..b7fe330a4e 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -26,6 +26,10 @@ #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"; @@ -33,6 +37,54 @@ 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) @@ -116,8 +168,10 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, } offscreenUi->returnFromUiThread([&] { + setupServer(); retVal = builder(newTab); retVal->_toolWindow = true; + registerObject(url.toLower(), retVal); return QVariant(); }); } else { @@ -125,8 +179,10 @@ 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); } @@ -153,7 +209,10 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* }); } -QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) { +QmlWindowClass::QmlWindowClass(QObject* qmlWindow) + : _windowId(++nextWindowId), _qmlWindow(qmlWindow) +{ + qDebug() << "Created window with ID " << _windowId; Q_ASSERT(_qmlWindow); Q_ASSERT(dynamic_cast(_qmlWindow.data())); // Forward messages received from QML on to the script @@ -169,6 +228,14 @@ 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 eda6ce674c..fb7dbf1253 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -13,16 +13,38 @@ #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) @@ -47,7 +69,8 @@ public slots: Q_INVOKABLE void raise(); Q_INVOKABLE void close(); - Q_INVOKABLE QObject* getEventBridge() { return this; }; + Q_INVOKABLE int getWindowId() const { return _windowId; }; + Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; }; // Scripts can use this to send a message to the QML object void sendToQml(const QVariant& message); @@ -69,12 +92,18 @@ 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 aad40f5de0d1e152c20ba060f242610e0713cf33 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 09:58:34 -0700 Subject: [PATCH 32/32] Drop network data after processing --- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/animation/src/AnimNodeLoader.h | 2 +- .../model-networking/src/model-networking/ModelCache.cpp | 4 ++-- .../model-networking/src/model-networking/ModelCache.h | 4 ++-- libraries/networking/src/ResourceCache.cpp | 6 +++--- libraries/networking/src/ResourceCache.h | 4 +--- 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index e222de54f9..70cf7e248f 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -611,7 +611,7 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j return loadNode(rootVal.toObject(), jsonUrl); } -void AnimNodeLoader::onRequestDone(const QByteArray& data) { +void AnimNodeLoader::onRequestDone(const QByteArray data) { auto node = load(data, _url); if (node) { emit success(node); diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h index 27b94f81bb..d6fdfa7e2c 100644 --- a/libraries/animation/src/AnimNodeLoader.h +++ b/libraries/animation/src/AnimNodeLoader.h @@ -36,7 +36,7 @@ protected: static AnimNode::Pointer load(const QByteArray& contents, const QUrl& jsonUrl); protected slots: - void onRequestDone(const QByteArray& data); + void onRequestDone(const QByteArray data); void onRequestError(QNetworkReply::NetworkError error); protected: diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index b0b769d5e9..e5094a5224 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -293,7 +293,7 @@ void NetworkGeometry::requestModel(const QUrl& url) { connect(_resource, &Resource::failed, this, &NetworkGeometry::modelRequestError); } -void NetworkGeometry::mappingRequestDone(const QByteArray& data) { +void NetworkGeometry::mappingRequestDone(const QByteArray data) { assert(_state == RequestMappingState); // parse the mapping file @@ -325,7 +325,7 @@ void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { emit onFailure(*this, MappingRequestError); } -void NetworkGeometry::modelRequestDone(const QByteArray& data) { +void NetworkGeometry::modelRequestDone(const QByteArray data) { assert(_state == RequestModelState); _state = ParsingModelState; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 1c76a0b878..550b16d2ba 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -113,10 +113,10 @@ public slots: void textureLoaded(const QWeakPointer& networkTexture); protected slots: - void mappingRequestDone(const QByteArray& data); + void mappingRequestDone(const QByteArray data); void mappingRequestError(QNetworkReply::NetworkError error); - void modelRequestDone(const QByteArray& data); + void modelRequestDone(const QByteArray data); void modelRequestError(QNetworkReply::NetworkError error); void modelParseSuccess(FBXGeometry* geometry); 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..7f4d86393b 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -194,12 +194,11 @@ 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. /// This can be used instead of downloadFinished to access data before it is processed. - void loaded(const QByteArray& request); + void loaded(const QByteArray request); /// Fired when the resource has finished loading. void finished(bool success); @@ -235,7 +234,6 @@ protected: QHash, float> _loadPriorities; QWeakPointer _self; QPointer _cache; - QByteArray _data; private slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal);