From 9709bedca70f86907c5a0d8e595d3e7d9dc14a2d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 4 Jan 2017 16:55:58 -0800 Subject: [PATCH 01/15] checkpoint --- interface/resources/fonts/hifi-glyphs.ttf | Bin 24436 -> 24580 bytes scripts/system/html/entityList.html | 3 ++- scripts/system/html/js/entityList.js | 4 ++++ scripts/system/libraries/entityList.js | 7 ++++++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 3dc3069ef562a2411b242bc5ad93e83162e6e5ac..09aefffdfef007aa94f3cbdfd02a61612c0ee9e7 100644 GIT binary patch delta 544 zcmX|--%C>g6vsbjyIw_CvN=aL9gHcOM5%K>3d%rizId~k)MnJ>qI1*S98SS#WrR;X z_~5M-k7vx>+xMq;!1e%a$5LiKx4(}a zj4hxpHFI*7)yi$gI({nZjAgK-d>c1TD{S{tMGIMOsX0pzg zf^=)Y21Ei*?v)!{LJ2fe*Vd&Jk^Ti06u}{uOG;mOd0%+=JUq|)mYjc)DgyLVLnjrB zJxi?H`wmzcAozIxQH(uqfE58olIh8Z$!0tS;_C}J*DghNz8`?L1KMc8UMpQ+qk}aC z1PaBsueXjvf1qy{(qm5Eesyy$S_ggs(~gs`+QyWef(!!p9JgG#{bBG0$YRS1EbK?-Q8fQ7OHrLzcg+7OI`O}xn`7nx diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 6ea281e467..197d8f550a 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -25,6 +25,7 @@ +
@@ -94,4 +95,4 @@
- \ No newline at end of file + diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 60aa2ebe25..1af9c1e1d6 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -39,6 +39,7 @@ function loaded() { elInView = document.getElementById("in-view") elRadius = document.getElementById("radius"); elTeleport = document.getElementById("teleport"); + elPal = document.getElementById("pal"); elEntityTable = document.getElementById("entity-table"); elInfoToggle = document.getElementById("info-toggle"); elInfoToggleGlyph = elInfoToggle.firstChild; @@ -274,6 +275,9 @@ function loaded() { elTeleport.onclick = function () { EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' })); } + elPal.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ type: 'pal' })); + } elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); refreshEntities(); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 3e9d3b5648..1dca853668 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -120,6 +120,11 @@ EntityListTool = function(opts) { if (selectionManager.hasSelection()) { MyAvatar.position = selectionManager.worldPosition; } + } else if (data.type == "pal") { + print("fixme got pal"); + if (selectionManager.hasSelection()) { + print('fixme selection', JSON.stringify(selectionManager.selections)); + } } else if (data.type == "delete") { deleteSelectedEntities(); } else if (data.type == "toggleLocked") { @@ -140,4 +145,4 @@ EntityListTool = function(opts) { }); return that; -}; \ No newline at end of file +}; From 12fffe853023a0573b63cfd3c77baa2d079eedf6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 4 Jan 2017 20:37:56 -0800 Subject: [PATCH 02/15] messages --- scripts/system/libraries/entityList.js | 18 ++++++++++++++---- scripts/system/pal.js | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 1dca853668..fa09f27965 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -3,6 +3,7 @@ var ENTITY_LIST_HTML_URL = Script.resolvePath('../html/entityList.html'); EntityListTool = function(opts) { var that = {}; + var url = ENTITY_LIST_HTML_URL; var webView = new OverlayWebWindow({ title: 'Entity List', source: url, toolWindow: true @@ -98,7 +99,6 @@ EntityListTool = function(opts) { webView.emitScriptEvent(JSON.stringify(data)); } - webView.webEventReceived.connect(function(data) { data = JSON.parse(data); if (data.type == "selectionUpdate") { @@ -121,9 +121,19 @@ EntityListTool = function(opts) { MyAvatar.position = selectionManager.worldPosition; } } else if (data.type == "pal") { - print("fixme got pal"); - if (selectionManager.hasSelection()) { - print('fixme selection', JSON.stringify(selectionManager.selections)); + var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. + selectionManager.selections.forEach(function (id) { + var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy; + if (lastEditedBy) { + sessionIds[lastEditedBy] = true; + } + }); + var dedupped = Object.keys(sessionIds); + if (!dedupped.length) { + Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); + } else { + // No need to subscribe if we're just sending. + Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: dedupped}), 'local'); } } else if (data.type == "delete") { deleteSelectedEntities(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index e727f9a1e3..4c4a9c0eaf 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -256,6 +256,27 @@ function removeOverlays() { ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); }); } +// +// Message from other scripts, such as edit.js +// +var CHANNEL = 'com.highfidelity.pal'; +function receiveMessage(channel, messageString, senderID) { + if ((channel !== CHANNEL) || + (senderID !== MyAvatar.sessionUUID)) { + return; + } + var message = JSON.parse(messageString); + switch (message.method) { + case 'select': + print('fixme processing', message.params); + break; + default: + print('Unrecognized PAL message', messageString); + } +} +Messages.subscribe(CHANNEL); +Messages.messageReceived.connect(receiveMessage); + // // Clicks. // @@ -412,6 +433,8 @@ Script.scriptEnding.connect(function () { Users.usernameFromIDReply.disconnect(usernameFromIDReply); Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL); Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL); + Messages.unsubscribe(CHANNEL); + Messages.messageReceived.disconnect(receiveMessage); off(); }); From cb69e707811896852ae119b81c301c55b26995c6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 5 Jan 2017 11:31:50 -0800 Subject: [PATCH 03/15] fix competition between seek mouse and lasers --- .../controllers/handControllerPointer.js | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 0623ddf100..4d478fbdd5 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,6 +20,9 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. +var systemLaserOn = false; + + Script.include("../libraries/controllers.js"); // UTILITIES ------------- @@ -121,6 +124,12 @@ function ignoreMouseActivity() { if (!Reticle.allowMouseCapture) { return true; } + + // if the lasers are on, then reticle/mouse should be hidden and we can ignore it for seeking or depth updating + if (systemLaserOn) { + return true; + } + var pos = Reticle.position; if (!pos || (pos.x == -1 && pos.y == -1)) { return true; @@ -261,6 +270,12 @@ var ONE_MINUS_WEIGHTING = 1 - WEIGHTING; var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20; function isShakingMouse() { // True if the person is waving the mouse around trying to find it. var now = Date.now(), mouse = Reticle.position, isShaking = false; + + // if the lasers are on, then we ignore mouse shaking + if (systemLaserOn) { + return false; + } + if (lastIntegration && (lastIntegration !== now)) { var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration); averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity); @@ -275,9 +290,14 @@ function isShakingMouse() { // True if the person is waving the mouse around try var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { + // if the lasers are on, then we never do seeking + if (systemLaserOn) { + isSeeking = false; + return; + } + if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) { if (!isSeeking) { - print('Start seeking mouse.'); isSeeking = true; } } // e.g., if we're about to turn it on with first movement. @@ -287,7 +307,6 @@ function updateSeeking(doNotStartSeeking) { averageMouseVelocity = lastIntegration = 0; var lookAt2D = HMD.getHUDLookAtPosition2D(); if (!lookAt2D) { // If this happens, something has gone terribly wrong. - print('Cannot seek without lookAt position'); isSeeking = false; return; // E.g., if parallel to location in HUD } @@ -303,7 +322,6 @@ function updateSeeking(doNotStartSeeking) { } var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. if (okX && okY) { - print('Finished seeking mouse'); isSeeking = false; } else { Reticle.setPosition(copy); // Not setReticlePosition @@ -322,7 +340,7 @@ function updateMouseActivity(isClick) { return; } // Bug: mouse clicks should keep going. Just not hand controller clicks handControllerLockOut.update(now); - Reticle.visible = true; + Reticle.visible = !systemLaserOn; } function expireMouseCursor(now) { if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) { @@ -474,7 +492,6 @@ var LASER_ALPHA = 0.5; var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA}; var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA}; var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1}; -var systemLaserOn = false; function clearSystemLaser() { if (!systemLaserOn) { return; From bbf7d165375def2bc113f61376bcba4479b13a7b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 5 Jan 2017 13:45:03 -0800 Subject: [PATCH 04/15] process consistent select message --- interface/resources/qml/hifi/Pal.qml | 18 ++++++---- scripts/system/libraries/entityList.js | 2 +- scripts/system/pal.js | 48 ++++++++++++++------------ 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 833cf4efe2..537e855072 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -427,14 +427,20 @@ Item { sortModel(); break; case 'select': - var sessionId = message.params[0]; + var sessionIds = message.params[0]; var selected = message.params[1]; - var userIndex = findSessionIndex(sessionId); - if (selected) { - table.selection.clear(); // for now, no multi-select - table.selection.select(userIndex); + var userIndex = findSessionIndex(sessionIds[0]); + if (sessionIds.length > 1) { + console.log('FIXME NEEDS MODAL: Only one user can be selected at a time.'); + } else if (userIndex < 0) { + console.log('FIXME NEEEDS MODAL: The last editor has left.'); } else { - table.selection.deselect(userIndex); + if (selected) { + table.selection.clear(); // for now, no multi-select + table.selection.select(userIndex); + } else { + table.selection.deselect(userIndex); + } } break; // Received an "updateUsername()" request from the JS diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index fa09f27965..a57c299128 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -133,7 +133,7 @@ EntityListTool = function(opts) { Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); } else { // No need to subscribe if we're just sending. - Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: dedupped}), 'local'); + Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true]}), 'local'); } } else if (data.type == "delete") { deleteSelectedEntities(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 4c4a9c0eaf..c2c425ae47 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -256,34 +256,13 @@ function removeOverlays() { ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); }); } -// -// Message from other scripts, such as edit.js -// -var CHANNEL = 'com.highfidelity.pal'; -function receiveMessage(channel, messageString, senderID) { - if ((channel !== CHANNEL) || - (senderID !== MyAvatar.sessionUUID)) { - return; - } - var message = JSON.parse(messageString); - switch (message.method) { - case 'select': - print('fixme processing', message.params); - break; - default: - print('Unrecognized PAL message', messageString); - } -} -Messages.subscribe(CHANNEL); -Messages.messageReceived.connect(receiveMessage); - // // Clicks. // function handleClick(pickRay) { ExtendedOverlay.applyPickRay(pickRay, function (overlay) { // Don't select directly. Tell qml, who will give us back a list of ids. - var message = {method: 'select', params: [overlay.key, !overlay.selected]}; + var message = {method: 'select', params: [[overlay.key], !overlay.selected]}; pal.sendToQml(message); return true; }); @@ -354,6 +333,31 @@ function onClicked() { pal.setVisible(!pal.visible); } +// +// Message from other scripts, such as edit.js +// +var CHANNEL = 'com.highfidelity.pal'; +function receiveMessage(channel, messageString, senderID) { + if ((channel !== CHANNEL) || + (senderID !== MyAvatar.sessionUUID)) { + return; + } + var message = JSON.parse(messageString); + switch (message.method) { + case 'select': + if (!pal.visible) { + onClicked(); + } + pal.sendToQml(message); // Accepts objects, not just strings. + break; + default: + print('Unrecognized PAL message', messageString); + } +} +Messages.subscribe(CHANNEL); +Messages.messageReceived.connect(receiveMessage); + + var AVERAGING_RATIO = 0.05; var LOUDNESS_FLOOR = 11.0; var LOUDNESS_SCALE = 2.8 / 5.0; From 0cdeac8736972c9ced47060b38d98ad28253610a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 5 Jan 2017 14:32:31 -0800 Subject: [PATCH 05/15] abstract out letterbox message --- .../resources/qml/hifi/LetterboxMessage.qml | 51 ++++++++++ interface/resources/qml/hifi/Pal.qml | 93 ++----------------- 2 files changed, 58 insertions(+), 86 deletions(-) create mode 100644 interface/resources/qml/hifi/LetterboxMessage.qml diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml new file mode 100644 index 0000000000..30fd09d7bc --- /dev/null +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -0,0 +1,51 @@ +// +// LetterboxMessage.qml +// qml/hifi +// +// Created by Zach Fox and Howard Stearns on 1/5/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../styles-uit" + +Item { + property alias text: popupText.text + visible: false + id: letterbox + anchors.fill: parent + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.5 + radius: hifi.dimensions.borderRadius + } + Rectangle { + width: Math.max(parent.width * 0.75, 400) + height: popupText.contentHeight*1.5 + anchors.centerIn: parent + radius: hifi.dimensions.borderRadius + color: "white" + FiraSansSemiBold { + id: popupText + size: hifi.fontSizes.textFieldInput + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + wrapMode: Text.WordWrap + } + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + letterbox.visible = false + } + } +} diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 5c3535a7ab..2785d83234 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -11,21 +11,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* TODO: - - prototype: - - only show kick/mute when canKick - - margins everywhere - - column head centering - - column head font - - proper button .svg on toolbar - - mvp: - - Show all participants, including ignored, and populate initial ignore/mute status. - - If name is elided, hover should scroll name left so the full name can be read. - - */ - import QtQuick 2.5 import QtQuick.Controls 1.4 import "../styles-uit" @@ -384,81 +369,17 @@ Item { } } // Explanitory popup upon clicking "[?]" next to "NAMES" - Item { - visible: false + LetterboxMessage { id: namesPopup - anchors.fill: pal - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.5 - radius: hifi.dimensions.borderRadius - } - Rectangle { - width: Math.max(parent.width * 0.75, 400) - height: popupText.contentHeight*1.5 - anchors.centerIn: parent - radius: hifi.dimensions.borderRadius - color: "white" - FiraSansSemiBold { - id: popupText - text: "Bold names in the list are Avatar Display Names.\n" + - "If a Display Name isn't set, a unique Session Display Name is assigned." + - "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present." - size: hifi.fontSizes.textFieldInput - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.leftMargin: 15 - anchors.rightMargin: 15 - wrapMode: Text.WordWrap - } - } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - namesPopup.visible = false - } - } + text: "Bold names in the list are Avatar Display Names.\n" + + "If a Display Name isn't set, a unique Session Display Name is assigned." + + "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present." } // Explanitory popup upon clicking "[?]" next to "ADMIN" - Item { - visible: false + LetterboxMessage { id: adminPopup - anchors.fill: pal - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.5 - radius: hifi.dimensions.borderRadius - } - Rectangle { - width: Math.max(parent.width * 0.75, 400) - height: adminPopupText.contentHeight*1.5 - anchors.centerIn: parent - radius: hifi.dimensions.borderRadius - color: "white" - FiraSansSemiBold { - id: adminPopupText - text: 'Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + - "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page." - size: hifi.fontSizes.textFieldInput - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.leftMargin: 15 - anchors.rightMargin: 15 - wrapMode: Text.WordWrap - } - } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - adminPopup.visible = false - } - } + text: "Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n" + + "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page." } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml From ef02c84759e789fe5c51dfdbb2c83c857e670908 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 5 Jan 2017 14:46:32 -0800 Subject: [PATCH 06/15] further simplification of letterbox, and use for selection failures --- interface/resources/qml/hifi/Pal.qml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 2785d83234..7ad322bfa4 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -311,6 +311,11 @@ Item { visible: iAmAdmin color: hifi.colors.lightGrayText } + function letterbox(message) { + letterboxMessage.text = message; + letterboxMessage.visible = true + + } // This Rectangle refers to the [?] popup button next to "NAMES" Rectangle { color: hifi.colors.tableBackgroundLight @@ -334,7 +339,9 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: namesPopup.visible = true + onClicked: letterbox("Bold names in the list are Avatar Display Names.\n" + + "If a Display Name isn't set, a unique Session Display Name is assigned." + + "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.") onEntered: helpText.color = hifi.colors.baseGrayHighlight onExited: helpText.color = hifi.colors.darkGray } @@ -363,23 +370,14 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: adminPopup.visible = true + onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + + "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)") onEntered: adminHelpText.color = "#94132e" onExited: adminHelpText.color = hifi.colors.redHighlight } } - // Explanitory popup upon clicking "[?]" next to "NAMES" LetterboxMessage { - id: namesPopup - text: "Bold names in the list are Avatar Display Names.\n" + - "If a Display Name isn't set, a unique Session Display Name is assigned." + - "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present." - } - // Explanitory popup upon clicking "[?]" next to "ADMIN" - LetterboxMessage { - id: adminPopup - text: "Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n" + - "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page." + id: letterboxMessage } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml @@ -420,9 +418,9 @@ Item { var selected = message.params[1]; var userIndex = findSessionIndex(sessionIds[0]); if (sessionIds.length > 1) { - console.log('FIXME NEEDS MODAL: Only one user can be selected at a time.'); + letterbox('Only one user can be selected at a time.'); } else if (userIndex < 0) { - console.log('FIXME NEEEDS MODAL: The last editor has left.'); + letterbox('The last editor is not among this list of users.'); } else { if (selected) { table.selection.clear(); // for now, no multi-select From 83685740c477a02776037701fa0f2989d0d47f90 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 5 Jan 2017 16:37:40 -0800 Subject: [PATCH 07/15] change wording for common case --- scripts/system/libraries/entityList.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index a57c299128..fd2cd60b98 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -129,7 +129,9 @@ EntityListTool = function(opts) { } }); var dedupped = Object.keys(sessionIds); - if (!dedupped.length) { + if (!selectionManager.selections.length) { + Window.alert('No objects selected.'); + } else if (!dedupped.length) { Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); } else { // No need to subscribe if we're just sending. From 495925a017b0191c6723c8ff6329bf2270909fb0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 5 Jan 2017 16:58:32 -0800 Subject: [PATCH 08/15] This behavior was harder to change than expected... --- assignment-client/src/avatars/AvatarMixer.cpp | 6 +++--- .../src/avatars/AvatarMixerClientData.cpp | 9 +++++++++ .../src/avatars/AvatarMixerClientData.h | 2 +- interface/src/avatar/Avatar.cpp | 4 +++- interface/src/avatar/AvatarManager.cpp | 2 ++ libraries/avatars/src/AvatarHashMap.cpp | 7 +++++++ libraries/avatars/src/AvatarHashMap.h | 1 + libraries/networking/src/NodeList.cpp | 20 +++++++++++++++++++ libraries/networking/src/NodeList.h | 4 ++++ libraries/networking/src/udt/PacketHeaders.h | 3 ++- 10 files changed, 52 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3cacfdedf1..ddecd1b50f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -299,7 +299,7 @@ void AvatarMixer::broadcastAvatarData() { AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); // Check to see if the space bubble is enabled - if ((node->isIgnoreRadiusEnabled() && !getsIgnoredByMe) || (otherNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { + if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) { // Define the minimum bubble size static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f); // Define the scale of the box for the current node @@ -326,11 +326,11 @@ void AvatarMixer::broadcastAvatarData() { // Perform the collision check between the two bounding boxes if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(node, otherNode); - return false; + return getsIgnoredByMe || getsAnyIgnored; } } // Not close enough to ignore - nodeData->removeFromRadiusIgnoringSet(otherNode->getUUID()); + nodeData->removeFromRadiusIgnoringSet(node, otherNode->getUUID()); return true; } }, diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index c65703b8e6..a7a506e1d8 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -57,6 +57,15 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe } } +void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other) { + if (isRadiusIgnoring(other)) { + _radiusIgnoredOthers.erase(other); + auto exitingSpaceBubblePacket = NLPacket::create(PacketType::ExitingSpaceBubble, NUM_BYTES_RFC4122_UUID); + exitingSpaceBubblePacket->write(other.toRfc4122()); + DependencyManager::get()->sendUnreliablePacket(*exitingSpaceBubblePacket, *self); + } +} + void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { _currentViewFrustum.fromByteArray(message); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 861086893a..f18cfdde1b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -89,7 +89,7 @@ public: glm::vec3 getGlobalBoundingBoxCorner() { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); } bool isRadiusIgnoring(const QUuid& other) { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); } void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); } - void removeFromRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.erase(other); } + void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other); void ignoreOther(SharedNodePointer self, SharedNodePointer other); void readViewFrustumPacket(const QByteArray& message); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1ade21930d..659a96afa0 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1339,7 +1339,9 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) { render::ScenePointer scene = qApp->getMain3DScene(); if (scene) { render::PendingChanges pendingChanges; - if (DependencyManager::get()->shouldRenderAvatars() && !DependencyManager::get()->isIgnoringNode(getSessionUUID())) { + if (DependencyManager::get()->shouldRenderAvatars() + && !DependencyManager::get()->isIgnoringNode(getSessionUUID()) + && !DependencyManager::get()->isRadiusIgnoringNode(getSessionUUID())) { addToScene(myHandle, scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e3ccc10a65..e955f94597 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -79,6 +79,7 @@ AvatarManager::AvatarManager(QObject* parent) : packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar"); packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket"); + packetReceiver.registerListener(PacketType::ExitingSpaceBubble, this, "processExitingSpaceBubble"); // when we hear that the user has ignored an avatar by session UUID // immediately remove that avatar instead of waiting for the absence of packets from avatar mixer @@ -256,6 +257,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); + DependencyManager::get()->radiusIgnoreNodeBySessionID(avatar->getSessionUUID(), true); } _avatarFades.push_back(removedAvatar); } diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index c708176da7..f4e94b9a35 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -161,6 +161,13 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S removeAvatar(sessionUUID, reason); } +void AvatarHashMap::processExitingSpaceBubble(QSharedPointer message, SharedNodePointer sendingNode) { + // read the node id + QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + auto nodeList = DependencyManager::get(); + nodeList->radiusIgnoreNodeBySessionID(sessionUUID, false); +} + void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { QWriteLocker locker(&_hashLock); diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 02aef6ac8a..eae4026bfc 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -57,6 +57,7 @@ private slots: void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode); void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); void processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode); + void processExitingSpaceBubble(QSharedPointer message, SharedNodePointer sendingNode); protected: AvatarHashMap(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index bd3203150e..c587037911 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -238,6 +238,10 @@ void NodeList::reset() { _numNoReplyDomainCheckIns = 0; + // lock and clear our set of radius ignored IDs + _radiusIgnoredSetLock.lockForWrite(); + _radiusIgnoredNodeIDs.clear(); + _radiusIgnoredSetLock.unlock(); // lock and clear our set of ignored IDs _ignoredSetLock.lockForWrite(); _ignoredNodeIDs.clear(); @@ -781,6 +785,22 @@ void NodeList::sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationN sendPacket(std::move(ignorePacket), *destinationNode); } +void NodeList::radiusIgnoreNodeBySessionID(const QUuid& nodeID, bool radiusIgnoreEnabled) { + if (radiusIgnoreEnabled) { + QReadLocker radiusIgnoredSetLocker{ &_radiusIgnoredSetLock }; // read lock for insert + // add this nodeID to our set of ignored IDs + _radiusIgnoredNodeIDs.insert(nodeID); + } else { + QWriteLocker radiusIgnoredSetLocker{ &_radiusIgnoredSetLock }; // write lock for unsafe_erase + _radiusIgnoredNodeIDs.unsafe_erase(nodeID); + } +} + +bool NodeList::isRadiusIgnoringNode(const QUuid& nodeID) const { + QReadLocker radiusIgnoredSetLocker{ &_radiusIgnoredSetLock }; // read lock for reading + return _radiusIgnoredNodeIDs.find(nodeID) != _radiusIgnoredNodeIDs.cend(); +} + void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { // enumerate the nodes to send a reliable ignore packet to each that can leverage it if (!nodeID.isNull() && _sessionUUID != nodeID) { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 75958f1847..8e285629dc 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -76,6 +76,8 @@ public: void toggleIgnoreRadius() { ignoreNodesInRadius(!getIgnoreRadiusEnabled()); } void enableIgnoreRadius() { ignoreNodesInRadius(true); } void disableIgnoreRadius() { ignoreNodesInRadius(false); } + void radiusIgnoreNodeBySessionID(const QUuid& nodeID, bool radiusIgnoreEnabled); + bool isRadiusIgnoringNode(const QUuid& other) const; void ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled); bool isIgnoringNode(const QUuid& nodeID) const; void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled); @@ -159,6 +161,8 @@ private: QTimer _keepAlivePingTimer; bool _requestsDomainListData; + mutable QReadWriteLock _radiusIgnoredSetLock; + tbb::concurrent_unordered_set _radiusIgnoredNodeIDs; mutable QReadWriteLock _ignoredSetLock; tbb::concurrent_unordered_set _ignoredNodeIDs; mutable QReadWriteLock _personalMutedSetLock; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3561624f63..37a0e4e804 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -105,7 +105,8 @@ public: UsernameFromIDReply, ViewFrustum, RequestsDomainListData, - LAST_PACKET_TYPE = RequestsDomainListData + ExitingSpaceBubble, + LAST_PACKET_TYPE = ExitingSpaceBubble //FIXME }; }; From 428a7f3bf8c88cb83ddc2142013c26531a320dd7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Jan 2017 17:49:39 -0800 Subject: [PATCH 09/15] Log Dialog doesn't float on top --- interface/src/Application.cpp | 2 +- interface/src/ui/LogDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 832419a75c..681d160821 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6144,7 +6144,7 @@ void Application::loadScriptURLDialog() const { void Application::toggleLogDialog() { if (! _logDialog) { - _logDialog = new LogDialog(_glWidget, getLogger()); + _logDialog = new LogDialog(nullptr, getLogger()); } if (_logDialog->isVisible()) { diff --git a/interface/src/ui/LogDialog.cpp b/interface/src/ui/LogDialog.cpp index 3d88751d54..1e56d0cfd9 100644 --- a/interface/src/ui/LogDialog.cpp +++ b/interface/src/ui/LogDialog.cpp @@ -44,7 +44,7 @@ const QString HIGHLIGHT_COLOR = "#3366CC"; int qTextCursorMeta = qRegisterMetaType("QTextCursor"); int qTextBlockMeta = qRegisterMetaType("QTextBlock"); -LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Dialog) { +LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Window) { _logger = logger; setWindowTitle("Log"); From b7d64804c1397bd158460915618886a285d7de70 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 5 Jan 2017 21:55:09 -0800 Subject: [PATCH 10/15] Remove errant //FIXME --- libraries/networking/src/udt/PacketHeaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 37a0e4e804..e4c4937622 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -106,7 +106,7 @@ public: ViewFrustum, RequestsDomainListData, ExitingSpaceBubble, - LAST_PACKET_TYPE = ExitingSpaceBubble //FIXME + LAST_PACKET_TYPE = ExitingSpaceBubble }; }; From 2f5f53c8e4c2c44b71d0fc3bbc347d74852eab5c Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 6 Jan 2017 10:28:09 -0800 Subject: [PATCH 11/15] cr --- interface/resources/qml/hifi/LetterboxMessage.qml | 5 +++-- scripts/system/libraries/entityList.js | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 30fd09d7bc..290cff6634 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -15,6 +15,7 @@ import "../styles-uit" Item { property alias text: popupText.text + property real radius: hifi.dimensions.borderRadius visible: false id: letterbox anchors.fill: parent @@ -22,13 +23,13 @@ Item { anchors.fill: parent color: "black" opacity: 0.5 - radius: hifi.dimensions.borderRadius + radius: radius } Rectangle { width: Math.max(parent.width * 0.75, 400) height: popupText.contentHeight*1.5 anchors.centerIn: parent - radius: hifi.dimensions.borderRadius + radius: radius color: "white" FiraSansSemiBold { id: popupText diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index fd2cd60b98..085d4f5e27 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -3,7 +3,6 @@ var ENTITY_LIST_HTML_URL = Script.resolvePath('../html/entityList.html'); EntityListTool = function(opts) { var that = {}; - var url = ENTITY_LIST_HTML_URL; var webView = new OverlayWebWindow({ title: 'Entity List', source: url, toolWindow: true From 15b9e750f9b8ce7377cb090344f31a351cc3bd73 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Jan 2017 09:25:38 -0800 Subject: [PATCH 12/15] Adding tracking of Oculus renders, submits, and frames that are over budget --- interface/resources/qml/Stats.qml | 12 +++ interface/src/ui/Stats.cpp | 6 ++ interface/src/ui/Stats.h | 6 ++ plugins/oculus/src/OculusDisplayPlugin.cpp | 85 ++++++++++++++++------ plugins/oculus/src/OculusDisplayPlugin.h | 3 + 5 files changed, 90 insertions(+), 22 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 895055618b..086c1d15d2 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -83,6 +83,18 @@ Item { text: "Missed Frame Count: " + root.appdropped; visible: root.appdropped > 0; } + StatText { + text: "Long Render Count: " + root.longrenders; + visible: root.longrenders > 0; + } + StatText { + text: "Long Submit Count: " + root.longsubmits; + visible: root.longsubmits > 0; + } + StatText { + text: "Long Frame Count: " + root.longframes; + visible: root.longframes > 0; + } StatText { text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 95f31283d0..ff0028322c 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -127,12 +127,18 @@ void Stats::updateStats(bool force) { auto displayPlugin = qApp->getActiveDisplayPlugin(); auto stats = displayPlugin->getHardwareStats(); STAT_UPDATE(appdropped, stats["app_dropped_frame_count"].toInt()); + STAT_UPDATE(longrenders, stats["long_render_count"].toInt()); + STAT_UPDATE(longsubmits, stats["long_submit_count"].toInt()); + STAT_UPDATE(longframes, stats["long_frame_count"].toInt()); STAT_UPDATE(renderrate, displayPlugin->renderRate()); STAT_UPDATE(presentrate, displayPlugin->presentRate()); STAT_UPDATE(presentnewrate, displayPlugin->newFramePresentRate()); STAT_UPDATE(presentdroprate, displayPlugin->droppedFrameRate()); STAT_UPDATE(stutterrate, displayPlugin->stutterRate()); } else { + STAT_UPDATE(appdropped, -1); + STAT_UPDATE(longrenders, -1); + STAT_UPDATE(longsubmits, -1); STAT_UPDATE(presentrate, -1); STAT_UPDATE(presentnewrate, -1); STAT_UPDATE(presentdroprate, -1); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f0bd1f8ab7..3fe851494c 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -40,6 +40,9 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, stutterrate, 0) STATS_PROPERTY(int, appdropped, 0) + STATS_PROPERTY(int, longsubmits, 0) + STATS_PROPERTY(int, longrenders, 0) + STATS_PROPERTY(int, longframes, 0) STATS_PROPERTY(float, presentnewrate, 0) STATS_PROPERTY(float, presentdroprate, 0) @@ -137,6 +140,9 @@ public slots: void forceUpdateStats() { updateStats(true); } signals: + void longsubmitsChanged(); + void longrendersChanged(); + void longframesChanged(); void appdroppedChanged(); void framerateChanged(); void expandedChanged(); diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 060823a748..b076170ae5 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -31,6 +31,10 @@ OculusDisplayPlugin::OculusDisplayPlugin() { bool OculusDisplayPlugin::internalActivate() { bool result = Parent::internalActivate(); + _longSubmits = 0; + _longRenders = 0; + _longFrames = 0; + currentDebugMode = ovrPerfHud_Off; if (result && _session) { ovr_SetInt(_session, OVR_PERF_HUD_MODE, currentDebugMode); @@ -112,35 +116,43 @@ void OculusDisplayPlugin::uncustomizeContext() { Parent::uncustomizeContext(); } +static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC); +static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC); + void OculusDisplayPlugin::hmdPresent() { + static uint64_t lastSubmitEnd = 0; + if (!_customized) { return; } PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) - int curIndex; - ovr_GetTextureSwapChainCurrentIndex(_session, _textureSwapChain, &curIndex); - GLuint curTexId; - ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, curIndex, &curTexId); + { + PROFILE_RANGE_EX(render, "Oculus Blit", 0xff00ff00, (uint64_t)_currentFrame->frameIndex) + int curIndex; + ovr_GetTextureSwapChainCurrentIndex(_session, _textureSwapChain, &curIndex); + GLuint curTexId; + ovr_GetTextureSwapChainBufferGL(_session, _textureSwapChain, curIndex, &curTexId); - // Manually bind the texture to the FBO - // FIXME we should have a way of wrapping raw GL ids in GPU objects without - // taking ownership of the object - auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); - glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); - render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setFramebuffer(_outputFramebuffer); - batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize())); - batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); - batch.resetViewTransform(); - batch.setProjectionTransform(mat4()); - batch.setPipeline(_presentPipeline); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0); + // Manually bind the texture to the FBO + // FIXME we should have a way of wrapping raw GL ids in GPU objects without + // taking ownership of the object + auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); + glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(_outputFramebuffer); + batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize())); + batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); + batch.resetViewTransform(); + batch.setProjectionTransform(mat4()); + batch.setPipeline(_presentPipeline); + batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0); + } { auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain); @@ -148,8 +160,33 @@ void OculusDisplayPlugin::hmdPresent() { _sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime; _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); + + auto submitStart = usecTimestampNow(); + uint64_t nonSubmitInterval = 0; + if (lastSubmitEnd != 0) { + nonSubmitInterval = submitStart - lastSubmitEnd; + if (nonSubmitInterval > FRAME_BUDGET) { + ++_longRenders; + } + } ovrLayerHeader* layers = &_sceneLayer.Header; - result = ovr_SubmitFrame(_session, _currentFrame->frameIndex, &_viewScaleDesc, &layers, 1); + { + PROFILE_RANGE_EX(render, "Oculus Submit", 0xff00ff00, (uint64_t)_currentFrame->frameIndex) + result = ovr_SubmitFrame(_session, _currentFrame->frameIndex, &_viewScaleDesc, &layers, 1); + } + lastSubmitEnd = usecTimestampNow(); + if (nonSubmitInterval != 0) { + auto submitInterval = lastSubmitEnd - submitStart; + if (nonSubmitInterval < FRAME_BUDGET && submitInterval > FRAME_BUDGET) { + ++_longSubmits; + } + if ((nonSubmitInterval + submitInterval) > FRAME_OVER_BUDGET) { + ++_longFrames; + } + } + + + if (!OVR_SUCCESS(result)) { logWarning("Failed to present"); } @@ -168,6 +205,7 @@ void OculusDisplayPlugin::hmdPresent() { _appDroppedFrames.store(appDroppedFrames); _compositorDroppedFrames.store(compositorDroppedFrames); } + _presentRate.increment(); } @@ -176,6 +214,9 @@ QJsonObject OculusDisplayPlugin::getHardwareStats() const { QJsonObject hardwareStats; hardwareStats["app_dropped_frame_count"] = _appDroppedFrames.load(); hardwareStats["compositor_dropped_frame_count"] = _compositorDroppedFrames.load(); + hardwareStats["long_render_count"] = _longRenders.load(); + hardwareStats["long_submit_count"] = _longSubmits.load(); + hardwareStats["long_frame_count"] = _longFrames.load(); return hardwareStats; } diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index ccab31785b..6fc50b829f 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -41,5 +41,8 @@ private: std::atomic_int _compositorDroppedFrames; std::atomic_int _appDroppedFrames; + std::atomic_int _longSubmits; + std::atomic_int _longRenders; + std::atomic_int _longFrames; }; From dec41f67cc53d6c8b2a6dd58220198179d42dbba Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 6 Jan 2017 13:58:23 -0800 Subject: [PATCH 13/15] Fix bugs and generally fix behavior --- assignment-client/src/audio/AudioMixer.cpp | 15 +++++++++++++++ assignment-client/src/audio/AudioMixer.h | 1 + .../src/audio/AudioMixerClientData.h | 3 +++ assignment-client/src/audio/AudioMixerSlave.cpp | 11 ++++++++--- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- interface/src/avatar/Avatar.cpp | 5 +++-- interface/src/avatar/AvatarManager.cpp | 2 ++ libraries/networking/src/NodeList.cpp | 4 ++-- 8 files changed, 35 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 01715497b1..19ebd4ea87 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -68,6 +68,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); + packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled); } @@ -221,6 +222,20 @@ void AudioMixer::handleKillAvatarPacket(QSharedPointer packet, } } +void AudioMixer::handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { + auto nodeList = DependencyManager::get(); + nodeList->getOrCreateLinkedData(senderNode); + + if (senderNode->getLinkedData()) { + AudioMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); + if (nodeData != nullptr) { + bool isRequesting; + message->readPrimitive(&isRequesting); + nodeData->setRequestsDomainListData(isRequesting); + } + } +} + void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRequestMessage(packet); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 59cdec7732..d9759653fb 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -61,6 +61,7 @@ private slots: void handleMuteEnvironmentPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNegotiateAudioFormat(QSharedPointer message, SharedNodePointer sendingNode); void handleNodeKilled(SharedNodePointer killedNode); + void handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode); void handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 07e98c044b..e637fd0409 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -92,6 +92,8 @@ public: glm::vec3 getPosition() { return getAvatarAudioStream() ? getAvatarAudioStream()->getPosition() : glm::vec3(0); } glm::vec3 getAvatarBoundingBoxCorner() { return getAvatarAudioStream() ? getAvatarAudioStream()->getAvatarBoundingBoxCorner() : glm::vec3(0); } glm::vec3 getAvatarBoundingBoxScale() { return getAvatarAudioStream() ? getAvatarAudioStream()->getAvatarBoundingBoxScale() : glm::vec3(0); } + bool getRequestsDomainListData() { return _requestsDomainListData; } + void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } signals: void injectorStreamFinished(const QUuid& streamIdentifier); @@ -122,6 +124,7 @@ private: bool _shouldFlushEncoder { false }; bool _shouldMuteClient { false }; + bool _requestsDomainListData { false }; }; #endif // hifi_AudioMixerClientData_h diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 42e8c5bdef..9958d95aa4 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -209,8 +209,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { // and that it isn't being ignored by our listening node // and that it isn't ignoring our listening node AudioMixerClientData* otherData = static_cast(otherNode->getLinkedData()); + + // When this is true, the AudioMixer will send Audio data to a client about avatars that have ignored them + bool getsAnyIgnored = nodeData->getRequestsDomainListData() && node->getCanKick(); + if (otherData - && !node->isIgnoringNodeWithID(otherNode->getUUID()) && !otherNode->isIgnoringNodeWithID(node->getUUID())) { + && !node->isIgnoringNodeWithID(otherNode->getUUID()) + && (!otherNode->isIgnoringNodeWithID(node->getUUID()) || getsAnyIgnored)) { // check to see if we're ignoring in radius bool insideIgnoreRadius = false; @@ -219,7 +224,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { // We'll always be inside the radius in that case. insideIgnoreRadius = true; // Check to see if the space bubble is enabled - } else if ((node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) && (*otherNode != *node)) { + } else if ((node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled())) { // Define the minimum bubble size static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f); AudioMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); @@ -251,7 +256,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { auto otherNodeStream = streamPair.second; bool isSelfWithEcho = ((*otherNode == *node) && (otherNodeStream->shouldLoopbackForNode())); // Add all audio streams that should be added to the mix - if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius)) { + if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius && getsAnyIgnored)) { addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream); } } diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index ddecd1b50f..11cbd73970 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -326,7 +326,7 @@ void AvatarMixer::broadcastAvatarData() { // Perform the collision check between the two bounding boxes if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(node, otherNode); - return getsIgnoredByMe || getsAnyIgnored; + return getsAnyIgnored; } } // Not close enough to ignore diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 659a96afa0..81eee6b3a7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1339,9 +1339,10 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) { render::ScenePointer scene = qApp->getMain3DScene(); if (scene) { render::PendingChanges pendingChanges; + auto nodelist = DependencyManager::get(); if (DependencyManager::get()->shouldRenderAvatars() - && !DependencyManager::get()->isIgnoringNode(getSessionUUID()) - && !DependencyManager::get()->isRadiusIgnoringNode(getSessionUUID())) { + && !nodelist->isIgnoringNode(getSessionUUID()) + && !nodelist->isRadiusIgnoringNode(getSessionUUID())) { addToScene(myHandle, scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e955f94597..1f5726acba 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -257,6 +257,8 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); + } + if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == YourAvatarEnteredTheirBubble) { DependencyManager::get()->radiusIgnoreNodeBySessionID(avatar->getSessionUUID(), true); } _avatarFades.push_back(removedAvatar); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index c587037911..d890431a45 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -1040,12 +1040,12 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag } void NodeList::setRequestsDomainListData(bool isRequesting) { - // Tell the avatar mixer whether I want to receive any additional data to which I might be entitled + // Tell the avatar mixer and audio mixer whether I want to receive any additional data to which I might be entitled if (_requestsDomainListData == isRequesting) { return; } eachMatchingNode([](const SharedNodePointer& node)->bool { - return node->getType() == NodeType::AvatarMixer; + return (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer); }, [this, isRequesting](const SharedNodePointer& destinationNode) { auto packet = NLPacket::create(PacketType::RequestsDomainListData, sizeof(bool), true); // reliable packet->writePrimitive(isRequesting); From afa6d54ac469a66ff8d8ae52c8b3456c391b47fa Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 6 Jan 2017 14:20:25 -0800 Subject: [PATCH 14/15] Actually fix the behavior... --- assignment-client/src/audio/AudioMixerSlave.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 9958d95aa4..28d3358eb5 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -214,7 +214,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { bool getsAnyIgnored = nodeData->getRequestsDomainListData() && node->getCanKick(); if (otherData - && !node->isIgnoringNodeWithID(otherNode->getUUID()) + && (!node->isIgnoringNodeWithID(otherNode->getUUID()) || (otherData->getRequestsDomainListData() && otherNode->getCanKick())) && (!otherNode->isIgnoringNodeWithID(node->getUUID()) || getsAnyIgnored)) { // check to see if we're ignoring in radius @@ -256,7 +256,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) { auto otherNodeStream = streamPair.second; bool isSelfWithEcho = ((*otherNode == *node) && (otherNodeStream->shouldLoopbackForNode())); // Add all audio streams that should be added to the mix - if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius && getsAnyIgnored)) { + if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius)) { addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream); } } From 215659c6957ab2e7949975860492911f11dd58f7 Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Sat, 7 Jan 2017 08:17:13 -0800 Subject: [PATCH 15/15] Enabled the ability to have other marketplaces. Enabled the ability to have other marketplaces. --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 0e4ed3b0ae..e9868bd38d 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -18,7 +18,7 @@ var DEFAULT_SCRIPTS = [ "system/mute.js", "system/goto.js", "system/hmd.js", - "system/marketplaces/marketplace.js", + "system/marketplaces/marketplaces.js", "system/edit.js", "system/pal.js", //"system/mod.js", // older UX, if you prefer "system/selectAudioDevice.js",