diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 779307c19d..cc6c4930ff 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -369,14 +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; } } 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/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/examples/html/entityProperties.html b/examples/html/entityProperties.html index e3eb19dc4a..225a1d7957 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 @@ <div class="label">Ambient URL</div> <div class="value"> <input type="text" id="property-zone-key-ambient-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1262,6 +1294,7 @@ <div class="label">Skybox URL</div> <div class="value"> <input type="text" id="property-zone-skybox-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1273,6 +1306,7 @@ <div class="label">Source URL</div> <div class="value"> <input type="text" id="property-web-source-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1286,12 +1320,14 @@ <div class="label">Href - Hifi://address</div> <div class="value"> <input id="property-hyperlink-href" class="url"> + <div class="update-url-version"></div> </div> </div> <div class="hyperlink-section property"> <div class="label">Description</div> <div class="value"> <input id="property-hyperlink-description" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1375,16 +1411,19 @@ <div class="label">X-axis Texture URL</div> <div class="value"> <input type="text" id="property-x-texture-url" class="url"> + <div class="update-url-version"></div> </div> <div class="label">Y-axis Texture URL</div> <div class="value"> <input type="text" id="property-y-texture-url" class="url"> + <div class="update-url-version"></div> </div> <div class="label">Z-axis Texture URL</div> <div class="value"> <input type="text" id="property-z-texture-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1566,6 +1605,7 @@ <div class="label">Collision Sound URL</div> <div class="value"> <input id="property-collision-sound-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1583,6 +1623,7 @@ </div> <div class="value"> <input id="property-script-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1595,6 +1636,7 @@ <div class="label">Model URL</div> <div class="value"> <input type="text" id="property-model-url" class="url"> + <div class="update-url-version"></div> </div> </div> @@ -1613,12 +1655,14 @@ <div class="label">Compound Shape URL</div> <div class="value"> <input type="text" id="property-compound-shape-url" class="url"> + <div class="update-url-version"></div> </div> </div> <div class="model-section property"> <div class="label">Animation URL</div> <div class="value"> <input type="text" id="property-model-animation-url" class="url"> + <div class="update-url-version"></div> </div> </div> <div class="model-section property"> 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 @@ <script type="text/javascript" src="jquery-2.1.4.min.js"></script> <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> <script type="text/javascript" src="eventBridgeLoader.js"></script> - <script> - var myBridge; - window.onload = function() { - openEventBridge(function(eventBridge) { - myBridge = eventBridge; - myBridge.scriptEventReceived.connect(function(message) { + openEventBridge(function() { + EventBridge.scriptEventReceived.connect(function(message) { console.log("HTML side received message: " + message); }); }); } testClick = function() { - myBridge.emitWebEvent("HTML side sending message - button click"); + EventBridge.emitWebEvent(["Foo", "Bar", { "baz": 1} ]); } </script> </head> 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 { 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/interface/src/Application.cpp b/interface/src/Application.cpp index e7bccae892..b9effa444b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1415,7 +1415,7 @@ void Application::paintGL() { _lastFramesPerSecondUpdate = now; } - PROFILE_RANGE(__FUNCTION__); + PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); PerformanceTimer perfTimer("paintGL"); if (nullptr == _displayPlugin) { @@ -2554,11 +2554,12 @@ 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(); { - PROFILE_RANGE(__FUNCTION__); static uint64_t lastIdleStart{ now }; uint64_t idleStartToStartDuration = now - lastIdleStart; if (idleStartToStartDuration != 0) { @@ -3146,6 +3147,9 @@ void Application::updateDialogs(float deltaTime) { } void Application::update(float deltaTime) { + + PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); @@ -3246,9 +3250,13 @@ void Application::update(float deltaTime) { QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); if (_physicsEnabled) { + PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PerformanceTimer perfTimer("physics"); { + PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PerformanceTimer perfTimer("updateStates)"); static VectorOfMotionStates motionStates; _entitySimulation.getObjectsToRemoveFromPhysics(motionStates); @@ -3281,12 +3289,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([&] { @@ -3321,17 +3331,24 @@ void Application::update(float deltaTime) { qApp->setAvatarSimrateSample(1.0f / deltaTime); - avatarManager->updateOtherAvatars(deltaTime); + { + PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); + avatarManager->updateOtherAvatars(deltaTime); + } qApp->updateMyAvatarLookAtPosition(); // update sensorToWorldMatrix for camera and hand controllers myAvatar->updateSensorToWorldMatrix(); - avatarManager->updateMyAvatar(deltaTime); + { + PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); + avatarManager->updateMyAvatar(deltaTime); + } } { + PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("overlays"); _overlays.update(deltaTime); } @@ -3351,6 +3368,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; @@ -4677,13 +4695,18 @@ qreal Application::getDevicePixelRatio() { } DisplayPlugin* Application::getActiveDisplayPlugin() { - std::unique_lock<std::recursive_mutex> 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<std::mutex> lock(_displayPluginLock); + result = _displayPlugin.get(); } - - return _displayPlugin.get(); + return result; } const DisplayPlugin* Application::getActiveDisplayPlugin() const { @@ -4801,20 +4824,26 @@ void Application::updateDisplayMode() { return; } - if (_displayPlugin) { - _displayPlugin->deactivate(); - } - auto offscreenUi = DependencyManager::get<OffscreenUi>(); - // 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<std::mutex> 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 }; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 35bc2aa696..63c67f6b9f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -107,7 +107,7 @@ Menu::Menu() { auto scriptEngines = DependencyManager::get<ScriptEngines>(); // Edit > Stop All Scripts... [advanced] - addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, + addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, scriptEngines.data(), SLOT(stopAllScripts()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); @@ -140,7 +140,7 @@ Menu::Menu() { // Edit > Reload All Content [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - + // Edit > Package Model... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0, @@ -153,7 +153,7 @@ Menu::Menu() { auto audioIO = DependencyManager::get<AudioClient>(); // Audio > Mute - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, + addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, audioIO.data(), SLOT(toggleMute())); // Audio > Show Level Meter @@ -458,7 +458,7 @@ Menu::Menu() { avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false, + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true, avatar, SLOT(setUseAnimPreAndPostRotations(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true, avatar, SLOT(setEnableInverseKinematics(bool))); @@ -534,7 +534,7 @@ Menu::Menu() { // Developer > Audio >>> MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio"); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true, + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true, audioIO.data(), SLOT(toggleAudioNoiseReduction())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, audioIO.data(), SLOT(toggleServerEcho())); @@ -617,7 +617,7 @@ Menu::Menu() { QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true, + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true, NULL, NULL, UNSPECIFIED_POSITION, "Advanced"); #endif } @@ -651,7 +651,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { } else if (properties.isCheckable) { menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, properties.isChecked, - MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition, properties.grouping); } else { menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 778f5af4c8..056b5b76c4 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) { { diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index e8f429f22c..a5747e4f96 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,7 +13,7 @@ #include "AnimationLogging.h" #include "AnimUtil.h" -bool AnimClip::usePreAndPostPoseFromAnim = false; +bool AnimClip::usePreAndPostPoseFromAnim = true; AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) : AnimNode(AnimNode::Type::Clip, id), 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 <GeometryUtil.h> #include <NumericalConstants.h> #include <DebugDraw.h> +#include <shared/NsightHelpers.h> #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) { 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; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index ca36ab35f0..d842dc553b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -21,7 +21,7 @@ #include <gl/GLWidget.h> #include <NumericalConstants.h> #include <DependencyManager.h> - +#include <shared/NsightHelpers.h> #include <plugins/PluginContainer.h> #include <gl/Config.h> #include <gl/GLEscrow.h> @@ -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(); } @@ -527,6 +531,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/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 <GLMHelpers.h> #include <SimpleMovingAverage.h> #include <gl/OglplusHelpers.h> -#include <gl/GLEscrow.h> #define THREADED_PRESENT 1 +#include <gl/GLEscrow.h> class OpenGLDisplayPlugin : public DisplayPlugin { protected: diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index c3782b907f..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 <gpu/GLBackend.h> #include <CursorManager.h> #include <gl/GLWidget.h> +#include <shared/NsightHelpers.h> #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(); @@ -149,6 +153,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/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index d6fbdd5229..d6835502d0 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -113,16 +113,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) { return _originalTextures; } - // TODO: Remove this line and enforce passing a texturemap as stringified JSON - 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 _originalTextures; } - QJsonObject texturesAsJsonObject = texturesAsJson.object(); - return texturesAsJsonObject.toVariantMap(); + return texturesJson.object().toVariantMap(); } void RenderableModelEntityItem::remapTextures() { 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: diff --git a/libraries/networking/src/udt/CongestionControl.cpp b/libraries/networking/src/udt/CongestionControl.cpp index 1d1a6628fe..d30be2c139 100644 --- a/libraries/networking/src/udt/CongestionControl.cpp +++ b/libraries/networking/src/udt/CongestionControl.cpp @@ -201,7 +201,7 @@ void DefaultCC::onTimeout() { void DefaultCC::stopSlowStart() { _slowStart = false; - + if (_receiveRate > 0) { // Set the sending rate to the receiving rate. setPacketSendPeriod(USECS_PER_SECOND / _receiveRate); diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index e5f3508b81..af70295840 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -103,6 +103,7 @@ SendQueue& Connection::getSendQueue() { QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission); QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive); QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout); + QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss); // set defaults on the send queue from our congestion control object and estimatedTimeout() _sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod); @@ -140,6 +141,12 @@ void Connection::queueTimeout() { }); } +void Connection::queueShortCircuitLoss(quint32 sequenceNumber) { + updateCongestionControlAndSendQueue([this, sequenceNumber]{ + _congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber }); + }); +} + void Connection::sendReliablePacket(std::unique_ptr<Packet> packet) { Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably."); getSendQueue().queuePacket(std::move(packet)); diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 4f5a8793e7..08a2df9b97 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -87,6 +87,7 @@ private slots: void recordRetransmission(); void queueInactive(); void queueTimeout(); + void queueShortCircuitLoss(quint32 sequenceNumber); private: void sendACK(bool wasCausedBySyncTimeout = true); diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 9701561ec7..2ffa42cb82 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -128,13 +128,13 @@ void SendQueue::stop() { _emptyCondition.notify_one(); } -void SendQueue::sendPacket(const Packet& packet) { - _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination); +int SendQueue::sendPacket(const Packet& packet) { + return _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination); } void SendQueue::ack(SequenceNumber ack) { // this is a response from the client, re-set our timeout expiry and our last response time - _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); if (_lastACKSequenceNumber == (uint32_t) ack) { return; @@ -164,7 +164,7 @@ void SendQueue::ack(SequenceNumber ack) { void SendQueue::nak(SequenceNumber start, SequenceNumber end) { // this is a response from the client, re-set our timeout expiry - _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); { std::lock_guard<std::mutex> nakLocker(_naksLock); @@ -177,8 +177,8 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) { void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) { // this is a response from the client, re-set our timeout expiry - _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); - + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); + { std::lock_guard<std::mutex> nakLocker(_naksLock); _naks.clear(); @@ -232,15 +232,16 @@ SequenceNumber SendQueue::getNextSequenceNumber() { return _currentSequenceNumber; } -void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber) { +bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber) { // write the sequence number and send the packet newPacket->writeSequenceNumber(sequenceNumber); - sendPacket(*newPacket); - + // Save packet/payload size before we move it auto packetSize = newPacket->getDataSize(); auto payloadSize = newPacket->getPayloadSize(); + auto bytesWritten = sendPacket(*newPacket); + { // Insert the packet we have just sent in the sent list QWriteLocker locker(&_sentLock); @@ -249,8 +250,24 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, entry.second.swap(newPacket); } Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list"); - + emit packetSent(packetSize, payloadSize); + + if (bytesWritten < 0) { + // this is a short-circuit loss - we failed to put this packet on the wire + // so immediately add it to the loss list + + { + std::lock_guard<std::mutex> nakLocker(_naksLock); + _naks.append(sequenceNumber); + } + + emit shortCircuitLoss(quint32(sequenceNumber)); + + return false; + } else { + return true; + } } void SendQueue::run() { @@ -285,12 +302,14 @@ void SendQueue::run() { auto nextPacketTimestamp = p_high_resolution_clock::now(); while (_state == State::Running) { - bool sentAPacket = maybeResendPacket(); + bool attemptedToSendPacket = maybeResendPacket(); // if we didn't find a packet to re-send AND we think we can fit a new packet on the wire // (this is according to the current flow window size) then we send out a new packet - if (!sentAPacket) { - sentAPacket = maybeSendNewPacket(); + auto newPacketCount = 0; + if (!attemptedToSendPacket) { + newPacketCount = maybeSendNewPacket(); + attemptedToSendPacket = (newPacketCount > 0); } // since we're a while loop, give the thread a chance to process events @@ -300,12 +319,13 @@ void SendQueue::run() { // If the send queue has been innactive, skip the sleep for // Either _isRunning will have been set to false and we'll break // Or something happened and we'll keep going - if (_state != State::Running || isInactive(sentAPacket)) { + if (_state != State::Running || isInactive(attemptedToSendPacket)) { return; } // push the next packet timestamp forwards by the current packet send period - nextPacketTimestamp += std::chrono::microseconds(_packetSendPeriod); + auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod; + nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta); // sleep as long as we need until next packet send, if we can const auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - p_high_resolution_clock::now()); @@ -314,7 +334,7 @@ void SendQueue::run() { } } -bool SendQueue::maybeSendNewPacket() { +int SendQueue::maybeSendNewPacket() { if (!isFlowWindowFull()) { // we didn't re-send a packet, so time to send a new one @@ -324,38 +344,43 @@ bool SendQueue::maybeSendNewPacket() { // grab the first packet we will send std::unique_ptr<Packet> firstPacket = _packets.takePacket(); Q_ASSERT(firstPacket); - - std::unique_ptr<Packet> secondPacket; - bool shouldSendPairTail = false; - - if (((uint32_t) nextNumber & 0xF) == 0) { - // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets - // pull off a second packet if we can before we unlock - shouldSendPairTail = true; - - secondPacket = _packets.takePacket(); + + + // attempt to send the first packet + if (sendNewPacketAndAddToSentList(move(firstPacket), nextNumber)) { + std::unique_ptr<Packet> secondPacket; + bool shouldSendPairTail = false; + + if (((uint32_t) nextNumber & 0xF) == 0) { + // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets + // pull off a second packet if we can before we unlock + shouldSendPairTail = true; + + secondPacket = _packets.takePacket(); + } + + // do we have a second in a pair to send as well? + if (secondPacket) { + sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber()); + } else if (shouldSendPairTail) { + // we didn't get a second packet to send in the probe pair + // send a control packet of type ProbePairTail so the receiver can still do + // proper bandwidth estimation + static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); + _socket->writeBasePacket(*pairTailPacket, _destination); + } + + // we attempted to send two packets, return 2 + return 2; + } else { + // we attempted to send a single packet, return 1 + return 1; } - - // definitely send the first packet - sendNewPacketAndAddToSentList(move(firstPacket), nextNumber); - - // do we have a second in a pair to send as well? - if (secondPacket) { - sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber()); - } else if (shouldSendPairTail) { - // we didn't get a second packet to send in the probe pair - // send a control packet of type ProbePairTail so the receiver can still do - // proper bandwidth estimation - static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); - _socket->writeBasePacket(*pairTailPacket, _destination); - } - - // We sent our packet(s), return here - return true; } } + // No packets were sent - return false; + return 0; } bool SendQueue::maybeResendPacket() { @@ -375,8 +400,9 @@ bool SendQueue::maybeResendPacket() { // see if we can find the packet to re-send auto it = _sentPackets.find(resendNumber); - + if (it != _sentPackets.end()) { + auto& entry = it->second; // we found the packet - grab it auto& resendPacket = *(entry.second); @@ -437,7 +463,7 @@ bool SendQueue::maybeResendPacket() { return false; } -bool SendQueue::isInactive(bool sentAPacket) { +bool SendQueue::isInactive(bool attemptedToSendPacket) { // check for connection timeout first // that will be the case if we have had 16 timeouts since hearing back from the client, and it has been @@ -447,7 +473,8 @@ bool SendQueue::isInactive(bool sentAPacket) { auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse); - if (sinceLastResponse >= quint64(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) && + if (sinceLastResponse > 0 && + sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) && _lastReceiverResponse > 0 && sinceLastResponse > MIN_MS_BEFORE_INACTIVE) { // If the flow window has been full for over CONSIDER_INACTIVE_AFTER, @@ -462,7 +489,7 @@ bool SendQueue::isInactive(bool sentAPacket) { return true; } - if (!sentAPacket) { + if (!attemptedToSendPacket) { // During our processing above we didn't send any packets // If that is still the case we should use a condition_variable_any to sleep until we have data to handle. diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 9400ae8352..21f6141c3c 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -79,6 +79,7 @@ signals: void queueInactive(); + void shortCircuitLoss(quint32 sequenceNumber); void timeout(); private slots: @@ -91,13 +92,13 @@ private: void sendHandshake(); - void sendPacket(const Packet& packet); - void sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber); + int sendPacket(const Packet& packet); + bool sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber); - bool maybeSendNewPacket(); // Figures out what packet to send next + int maybeSendNewPacket(); // Figures out what packet to send next bool maybeResendPacket(); // Determines whether to resend a packet and which one - bool isInactive(bool sentAPacket); + bool isInactive(bool attemptedToSendPacket); void deactivate(); // makes the queue inactive and cleans it up bool isFlowWindowFull() const; @@ -122,7 +123,7 @@ private: std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC std::atomic<int> _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC - std::atomic<uint64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK) + std::atomic<int64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK) std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 484f049944..56805e8f83 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: filter only, culling is disabled + { + 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 only, culling is disabled + { + 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 only, culling is disabled + { + 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 only, culling is disabled + { + 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; diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp index e48e228588..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" @@ -15,8 +16,28 @@ 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 +#endif // _WIN32 diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h index 3acdf14411..9853171b34 100644 --- a/libraries/shared/src/shared/NsightHelpers.h +++ b/libraries/shared/src/shared/NsightHelpers.h @@ -9,16 +9,21 @@ #ifndef hifi_gl_NsightHelpers_h #define hifi_gl_NsightHelpers_h -#if defined(NSIGHT_FOUND) - class ProfileRange { - public: - ProfileRange(const char *name); - ~ProfileRange(); - }; +#ifdef _WIN32 +#include <stdint.h> + +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); +#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 \ No newline at end of file +#endif diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index f12fb51b19..43fd5a64df 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -14,6 +14,8 @@ #include <QtQml/QQmlContext> +#include <QtWebChannel/QWebChannel> + #include <QtScript/QScriptContext> #include <QtScript/QScriptEngine> @@ -31,19 +33,39 @@ 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) { + _uid = QUuid::createUuid().toString(); + asQuickItem()->setProperty("uid", _uid); + auto webchannelVar = qmlWindow->property("webChannel"); + _webchannel = qvariant_cast<QWebChannel*>(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); + } +} -// FIXME remove. -void QmlWebWindowClass::handleNavigation(const QString& url) { +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<OffscreenUi>()->returnFromUiThread([&]()->QVariant { + if (_qmlWindow.isNull()) { + return QVariant(); + } return _qmlWindow->property(URL_PROPERTY); }); return result.toString(); @@ -54,6 +76,8 @@ extern QString fixupHifiUrl(const QString& urlString); void QmlWebWindowClass::setURL(const QString& urlString) { DependencyManager::get<OffscreenUi>()->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..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,12 +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 slots: - void handleNavigation(const QString& url); +private: + QString _uid; + QWebChannel* _webchannel { nullptr }; }; #endif diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 679e86f4ae..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<int> 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<QmlWindowClass*(QObject*)> 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<void(QQmlContext*, QObject*)>, [&](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,12 +153,9 @@ 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<const QQuickItem*>(_qmlWindow)); + Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data())); // Forward messages received from QML on to the script connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection); } @@ -228,19 +169,11 @@ 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<OffscreenUi>()->getToolWindow(); } - return dynamic_cast<QQuickItem*>(_qmlWindow); + return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data()); } void QmlWindowClass::setVisible(bool visible) { @@ -260,32 +193,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<QQuickItem*>(_qmlWindow); - return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { - return QVariant::fromValue(targetTab->isEnabled()); - }).toBool(); - } else { - QQuickItem* targetWindow = asQuickItem(); - return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { - return QVariant::fromValue(targetWindow->isVisible()); - }).toBool(); - } + return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { + if (_qmlWindow.isNull()) { + return QVariant::fromValue(false); + } + if (_toolWindow) { + return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled()); + } else { + return QVariant::fromValue(asQuickItem()->isVisible()); + } + }).toBool(); } glm::vec2 QmlWindowClass::getPosition() const { - QQuickItem* targetWindow = asQuickItem(); QVariant result = DependencyManager::get<OffscreenUi>()->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<OffscreenUi>()->executeOnUiThread([=] { - targetWindow->setPosition(QPointF(position.x, position.y)); + if (!_qmlWindow.isNull()) { + asQuickItem()->setPosition(QPointF(position.x, position.y)); + } }); } @@ -299,17 +234,21 @@ glm::vec2 toGlm(const QSizeF& size) { } glm::vec2 QmlWindowClass::getSize() const { - QQuickItem* targetWindow = asQuickItem(); QVariant result = DependencyManager::get<OffscreenUi>()->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<OffscreenUi>()->executeOnUiThread([=] { - targetWindow->setSize(QSizeF(size.x, size.y)); + if (!_qmlWindow.isNull()) { + asQuickItem()->setSize(QSizeF(size.x, size.y)); + } }); } @@ -318,9 +257,10 @@ void QmlWindowClass::setSize(int width, int height) { } void QmlWindowClass::setTitle(const QString& title) { - QQuickItem* targetWindow = asQuickItem(); DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { - targetWindow->setProperty(TITLE_PROPERTY, title); + if (!_qmlWindow.isNull()) { + asQuickItem()->setProperty(TITLE_PROPERTY, title); + } }); } @@ -345,7 +285,12 @@ void QmlWindowClass::hasClosed() { } void QmlWindowClass::raise() { - QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection); + auto offscreenUi = DependencyManager::get<OffscreenUi>(); + 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..eda6ce674c 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -10,42 +10,21 @@ #define hifi_ui_QmlWindowClass_h #include <QtCore/QObject> -#include <GLMHelpers.h> +#include <QtCore/QPointer> #include <QtScript/QScriptValue> #include <QtQuick/QQuickItem> -#include <QtWebChannel/QWebChannelAbstractTransport> + +#include <GLMHelpers.h> 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) - 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,21 +43,19 @@ 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; }; - 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); signals: void visibilityChanged(bool visible); // Tool window + void positionChanged(); + void sizeChanged(); void moved(glm::vec2 position); void resized(QSizeF size); void closed(); @@ -92,19 +69,13 @@ protected: static QScriptValue internalConstructor(const QString& qmlSource, QScriptContext* context, QScriptEngine* engine, std::function<QmlWindowClass*(QObject*)> 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; - QObject* _qmlWindow; + QPointer<QObject> _qmlWindow; QString _source; }; 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 <shared/NsightHelpers.h> #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 0cd9bac15f..0e7541066e 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -21,7 +21,7 @@ #include <PerfStat.h> #include <plugins/PluginContainer.h> #include <ViewFrustum.h> - +#include <shared/NsightHelpers.h> #include "OpenVrHelpers.h" Q_DECLARE_LOGGING_CATEGORY(displayplugins) @@ -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) { @@ -119,14 +122,11 @@ 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. + // 3 frames of prediction + vsyncToPhotons = 44ms total const float NUM_PREDICTION_FRAMES = 3.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 = frameDuration + vsyncToPhotons; #endif vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; @@ -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 }; @@ -152,6 +155,10 @@ void OpenVrDisplayPlugin::hmdPresent() { _compositor->Submit(vr::Eye_Left, &texture, &leftBounds); _compositor->Submit(vr::Eye_Right, &texture, &rightBounds); +} + +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); 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 }; 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) { 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 = $('<div class="log-line">').text(msg); - if (!shouldDisplayLogMessage(msg)) { - $logLine.hide(); + for (line in array) { + var $logLine = $('<div class="log-line">').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 diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index 533e6371e9..2b5e306b09 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -77,7 +77,7 @@ UDTTest::UDTTest(int& argc, char** argv) : // randomize the seed for packet size randomization srand(time(NULL)); - + _socket.bind(QHostAddress::AnyIPv4, _argumentParser.value(PORT_OPTION).toUInt()); qDebug() << "Test socket is listening on" << _socket.localPort();