From 8a5fb07ca77aea2c6f6bd5562cd7a501509e027d Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Thu, 8 Dec 2016 21:12:47 -0800 Subject: [PATCH 01/44] Merge pull request #9169 from samcake/red Fix the highlight issue introduced RC 29 - 5774 --- libraries/render-utils/src/local_lights_shading.slf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index d3542fcdfa..0b884d8bc0 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -85,7 +85,7 @@ void main(void) { vec3 fragEyeDir = normalize(fragEyeVector.xyz); // COmpute the rougness into gloss2 once: - float fragGloss2 = pow(frag.roughness + 0.001, 2.0); + float fragGloss2 = pow(frag.roughness + 0.001, 4.0); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); int numLightTouching = 0; From 9b65acc54e558ccf3bec8d98914e14c9933729a1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Dec 2016 09:28:31 -0800 Subject: [PATCH 02/44] Merge pull request #9177 from samcake/blue Fix the missing lighting on AMD --- .../render-utils/src/LightClusterGrid.slh | 14 +++++++++----- libraries/render-utils/src/LightClusters.cpp | 18 +++++++++--------- .../render-utils/src/local_lights_shading.slf | 5 ++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/LightClusterGrid.slh b/libraries/render-utils/src/LightClusterGrid.slh index f4ec35a75c..722d37814d 100644 --- a/libraries/render-utils/src/LightClusterGrid.slh +++ b/libraries/render-utils/src/LightClusterGrid.slh @@ -24,7 +24,7 @@ struct FrustumGrid { mat4 eyeToWorldMat; }; -uniform frustumGridBuffer { +layout(std140) uniform frustumGridBuffer { FrustumGrid frustumGrid; }; @@ -51,16 +51,20 @@ float projection_getFar(mat4 projection) { #define GRID_INDEX_TYPE ivec4 #define GRID_FETCH_BUFFER(i) i / 4][i % 4 <@else@> -#define GRID_NUM_ELEMENTS 16384 +#define GRID_NUM_ELEMENTS 4096 +#define GRID_INDEX_TYPE ivec4 +#define GRID_FETCH_BUFFER(i) i / 4][i % 4 + + <@endif@> -uniform clusterGridBuffer { +layout(std140) uniform clusterGridBuffer { GRID_INDEX_TYPE _clusterGridTable[GRID_NUM_ELEMENTS]; }; -uniform clusterContentBuffer { +layout(std140) uniform clusterContentBuffer { GRID_INDEX_TYPE _clusterGridContent[GRID_NUM_ELEMENTS]; }; diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index d3a384f1df..8dc95da65b 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -27,21 +27,21 @@ enum LightClusterGridShader_MapSlot { DEFERRED_BUFFER_LINEAR_DEPTH_UNIT = 0, - DEFERRED_BUFFER_COLOR_UNIT, - DEFERRED_BUFFER_NORMAL_UNIT, - DEFERRED_BUFFER_EMISSIVE_UNIT, - DEFERRED_BUFFER_DEPTH_UNIT, + DEFERRED_BUFFER_COLOR_UNIT = 1, + DEFERRED_BUFFER_NORMAL_UNIT = 2, + DEFERRED_BUFFER_EMISSIVE_UNIT = 3, + DEFERRED_BUFFER_DEPTH_UNIT = 4, }; enum LightClusterGridShader_BufferSlot { LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0, - DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, - CAMERA_CORRECTION_BUFFER_SLOT, + DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT =1, + CAMERA_CORRECTION_BUFFER_SLOT = 2, LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT, - LIGHT_INDEX_GPU_SLOT, + LIGHT_INDEX_GPU_SLOT = 5, - LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, - LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT = 6, + LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT = 7, }; FrustumGrid::FrustumGrid(const FrustumGrid& source) : diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index 0b884d8bc0..a4e28a9757 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -34,7 +34,6 @@ in vec2 _texCoord0; out vec4 _fragColor; void main(void) { - // Grab the fragment data from the uv vec2 texCoord = _texCoord0.st; @@ -49,7 +48,7 @@ void main(void) { // Frag pos in world mat4 invViewMat = getViewInverse(); vec4 fragPos = invViewMat * fragPosition; - + // From frag world pos find the cluster vec4 clusterEyePos = frustumGrid_worldToEye(fragPos); ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz); @@ -84,7 +83,7 @@ void main(void) { vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - // COmpute the rougness into gloss2 once: + // Compute the rougness into gloss2 once: float fragGloss2 = pow(frag.roughness + 0.001, 4.0); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); From a4126b3ee000054c76fd069b8a15e008632bb7f3 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 14:23:18 -0800 Subject: [PATCH 03/44] revert "fix competition between seek mouse and lasers" --- .../controllers/handControllerPointer.js | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 4d478fbdd5..0623ddf100 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,9 +20,6 @@ // 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 ------------- @@ -124,12 +121,6 @@ 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; @@ -270,12 +261,6 @@ 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); @@ -290,14 +275,9 @@ 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. @@ -307,6 +287,7 @@ 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 } @@ -322,6 +303,7 @@ 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 @@ -340,7 +322,7 @@ function updateMouseActivity(isClick) { return; } // Bug: mouse clicks should keep going. Just not hand controller clicks handControllerLockOut.update(now); - Reticle.visible = !systemLaserOn; + Reticle.visible = true; } function expireMouseCursor(now) { if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) { @@ -492,6 +474,7 @@ 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 dc2a42df4f277c76a9adbb9d73471f34e36b1bee Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 14:24:24 -0800 Subject: [PATCH 04/44] fix mouse vs lasers, again --- scripts/system/controllers/handControllerPointer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 0623ddf100..2a64b856bc 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,6 +20,10 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. +var activeTrigger; +function isLaserOn() { + return activeTrigger.partial(); +} Script.include("../libraries/controllers.js"); // UTILITIES ------------- @@ -275,7 +279,7 @@ 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 (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) { + if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { if (!isSeeking) { print('Start seeking mouse.'); isSeeking = true; @@ -374,7 +378,7 @@ setupHandler(Controller.mouseDoublePressEvent, onMouseClick); var leftTrigger = new Trigger('left'); var rightTrigger = new Trigger('right'); -var activeTrigger = rightTrigger; +activeTrigger = rightTrigger; var activeHand = Controller.Standard.RightHand; var LEFT_HUD_LASER = 1; var RIGHT_HUD_LASER = 2; From 4813324b76ea03e0017d4bd2db04761c29672c84 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 16:24:05 -0800 Subject: [PATCH 05/44] use update signal instead of timer --- scripts/system/controllers/handControllerPointer.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 2a64b856bc..1f8f0f53e6 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -571,12 +571,6 @@ function update() { Reticle.visible = false; } -var BASIC_TIMER_INTERVAL = 20; // 20ms = 50hz good enough -var updateIntervalTimer = Script.setInterval(function(){ - update(); -}, BASIC_TIMER_INTERVAL); - - // Check periodically for changes to setup. var SETTINGS_CHANGE_RECHECK_INTERVAL = 10 * 1000; // 10 seconds function checkSettings() { @@ -586,9 +580,10 @@ function checkSettings() { checkSettings(); var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL); +Script.update.connect(update); Script.scriptEnding.connect(function () { Script.clearInterval(settingsChecker); - Script.clearInterval(updateIntervalTimer); + Script.update.disconnect(update); OffscreenFlags.navigationFocusDisabled = false; }); From b3c64d4693fc662df773b9199b6a12d9da4e1e8a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 16:32:15 -0800 Subject: [PATCH 06/44] remove log spam --- scripts/system/controllers/handControllerPointer.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 1f8f0f53e6..b7dfcaeb56 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -280,10 +280,7 @@ var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { - if (!isSeeking) { - print('Start seeking mouse.'); - isSeeking = true; - } + isSeeking = true; } // e.g., if we're about to turn it on with first movement. if (!isSeeking) { return; @@ -291,7 +288,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 } @@ -307,7 +303,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 From aba5be4dd21c53c99eaaf870cde6856ba9f3df49 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 17:01:46 -0800 Subject: [PATCH 07/44] don't set bogus values --- scripts/system/controllers/handControllerPointer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index b7dfcaeb56..2c732b4cdc 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -482,7 +482,6 @@ function clearSystemLaser() { HMD.disableExtraLaser(); systemLaserOn = false; weMovedReticle = true; - Reticle.position = { x: -1, y: -1 }; } function setColoredLaser() { // answer trigger state if lasers supported, else falsey. var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW; From 698d63bef9bd80a470879c2fe97a14021cdc14c6 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 14 Jan 2017 10:26:13 -0800 Subject: [PATCH 08/44] Fix audio failures on systems that return incorrect/unsupported mixFormat --- libraries/audio-client/src/AudioClient.cpp | 42 +++++++++------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1aa4439de9..1e3dc11338 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -424,35 +424,25 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat = desiredAudioFormat; // - // Attempt the device sample rate in decreasing order of preference. + // Attempt the device sample rate and channel count in decreasing order of preference. // - if (audioDevice.supportedSampleRates().contains(48000)) { - adjustedAudioFormat.setSampleRate(48000); - } else if (audioDevice.supportedSampleRates().contains(44100)) { - adjustedAudioFormat.setSampleRate(44100); - } else if (audioDevice.supportedSampleRates().contains(32000)) { - adjustedAudioFormat.setSampleRate(32000); - } else if (audioDevice.supportedSampleRates().contains(24000)) { - adjustedAudioFormat.setSampleRate(24000); - } else if (audioDevice.supportedSampleRates().contains(16000)) { - adjustedAudioFormat.setSampleRate(16000); - } else if (audioDevice.supportedSampleRates().contains(96000)) { - adjustedAudioFormat.setSampleRate(96000); - } else if (audioDevice.supportedSampleRates().contains(192000)) { - adjustedAudioFormat.setSampleRate(192000); - } else if (audioDevice.supportedSampleRates().contains(88200)) { - adjustedAudioFormat.setSampleRate(88200); - } else if (audioDevice.supportedSampleRates().contains(176400)) { - adjustedAudioFormat.setSampleRate(176400); + const int sampleRates[] = { 48000, 44100, 32000, 24000, 16000, 96000, 192000, 88200, 176400 }; + const int inputChannels[] = { 1, 2, 4, 6, 8 }; // prefer mono + const int outputChannels[] = { 2, 4, 6, 8, 1 }; // prefer stereo, downmix as last resort + + for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) { + for (int sampleRate : sampleRates) { + + adjustedAudioFormat.setChannelCount(channelCount); + adjustedAudioFormat.setSampleRate(sampleRate); + + if (audioDevice.isFormatSupported(adjustedAudioFormat)) { + return true; + } + } } - if (adjustedAudioFormat != desiredAudioFormat) { - // return the nearest in case it needs 2 channels - adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat); - return true; - } else { - return false; - } + return false; // a supported format could not be found } bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples, From bb715bf7adb0c8b167709c85b970e47ae7ce41e0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 16 Jan 2017 10:09:47 -0800 Subject: [PATCH 09/44] Fix bug in FBX parse changes --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 5272969e6b..42922ce226 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -972,7 +972,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS static const QVariant EMISSIVE = QByteArray("Emissive"); static const QVariant AMBIENT_FACTOR = QByteArray("AmbientFactor"); static const QVariant SHININESS = QByteArray("Shininess"); - static const QVariant OPACITY = QByteArray("Shininess"); + static const QVariant OPACITY = QByteArray("Opacity"); static const QVariant MAYA_USE_NORMAL_MAP = QByteArray("Maya|use_normal_map"); static const QVariant MAYA_BASE_COLOR = QByteArray("Maya|base_color"); static const QVariant MAYA_USE_COLOR_MAP = QByteArray("Maya|use_color_map"); From 167aa7620e3f28d527e3454f3b5ec1df321b3624 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jan 2017 15:13:28 -0800 Subject: [PATCH 10/44] fix cauterization hack --- .../render-utils/src/MeshPartPayload.cpp | 19 ++++++++++++++++--- libraries/render-utils/src/MeshPartPayload.h | 5 ++++- libraries/render-utils/src/Model.cpp | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 5c4c0890a7..e3b2527e67 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -359,8 +359,11 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices) { _transform = transform; + _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = AABox(); @@ -373,6 +376,11 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound.transform(transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + if (cauterizedClusterMatrices.size() != 0) { + _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); + } else { + _cauterizedTransform = _transform; + } } } } @@ -527,9 +535,14 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: } else { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } + batch.setModelTransform(_transform); + } else { + if (canCauterize && _model->getCauterizeBones()) { + batch.setModelTransform(_cauterizedTransform); + } else { + batch.setModelTransform(_transform); + } } - - batch.setModelTransform(_transform); } void ModelMeshPartPayload::startFade() { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b048dc903f..b7a8cf63f0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -85,7 +85,9 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); + void updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices); // Entity fade in void startFade(); @@ -106,6 +108,7 @@ public: Model* _model; + Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7c1a6e14d0..3a3095458c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -238,7 +238,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); } } }); From ef6e83788eb3f3c7451e39d49095a79e8e97974f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 16 Jan 2017 14:54:32 -0800 Subject: [PATCH 11/44] handControllerGrab update loop change --- scripts/system/controllers/handControllerGrab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2ed09232e6..57698bd0dc 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3082,7 +3082,7 @@ var handleHandMessages = function(channel, message, sender) { Messages.messageReceived.connect(handleHandMessages); -var TARGET_UPDATE_HZ = 50; // 50hz good enough (no change in logic) +var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var lastInterval = Date.now(); @@ -3095,7 +3095,7 @@ var updateTotalWork = 0; var UPDATE_PERFORMANCE_DEBUGGING = false; -var updateIntervalTimer = Script.setInterval(function(){ +function updateWrapper(){ intervalCount++; var thisInterval = Date.now(); @@ -3141,11 +3141,12 @@ var updateIntervalTimer = Script.setInterval(function(){ updateTotalWork = 0; } -}, BASIC_TIMER_INTERVAL_MS); +} +Script.update.connect(updateWrapper); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - Script.clearInterval(updateIntervalTimer); + Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From ec93a663e7c523902d20d3e04e0ae744eabbfbed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 14:05:23 -0800 Subject: [PATCH 12/44] don't send AVATAR_SELF_ID over wire --- libraries/entities/src/EntityEditPacketSender.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index e05db07d0d..97648c7079 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -100,7 +100,18 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); - if (EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut)) { + bool success; + if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) { + EntityItemProperties propertiesCopy = properties; + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + propertiesCopy.setParentID(myNodeID); + success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut); + } else { + success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut); + } + + if (success) { #ifdef WANT_DEBUG qCDebug(entities) << "calling queueOctreeEditMessage()..."; qCDebug(entities) << " id:" << entityItemID; From 6b4c800c2ee98e59ba1ba7aa6ea6b1e33e058d86 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 15:50:59 -0800 Subject: [PATCH 13/44] don't call stepKinematicMotion on kinematic entities --- libraries/physics/src/EntityMotionState.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e9891020b3..c90ca5a1a9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,15 +199,12 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC) { + if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - _entity->setAcceleration(glm::vec3(0.0f)); - } else { - _entity->setAcceleration(_entity->getGravity()); - } + _entity->setAcceleration(_entity->getGravity()); + uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); From c0e313830c829543e341eb0ae45b8fa1c2982605 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:14:15 -0800 Subject: [PATCH 14/44] make hasAncestorOfType understand AVATAR_SELF_ID --- libraries/physics/src/EntityMotionState.cpp | 9 ++++++--- libraries/shared/src/SpatiallyNestable.cpp | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c90ca5a1a9..e9891020b3 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,12 +199,15 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { + if (_motionType == MOTION_TYPE_KINEMATIC) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - _entity->setAcceleration(_entity->getGravity()); - + if (_entity->hasAncestorOfType(NestableType::Avatar)) { + _entity->setAcceleration(glm::vec3(0.0f)); + } else { + _entity->setAcceleration(_entity->getGravity()); + } uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 35e574bf06..f071bda71f 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1034,6 +1034,13 @@ AACube SpatiallyNestable::getQueryAACube() const { bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { bool success; + if (nestableType == NestableType::Avatar) { + QUuid parentID = getParentID(); + if (parentID == AVATAR_SELF_ID) { + return true; + } + } + SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { return false; From 933aed47194f5a9491561939c65f419709ae69d3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:36:06 -0800 Subject: [PATCH 15/44] don't call stepKinematicMotion on kinematic entities when they are children of avatars --- libraries/physics/src/EntityMotionState.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e9891020b3..c90ca5a1a9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,15 +199,12 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC) { + if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - _entity->setAcceleration(glm::vec3(0.0f)); - } else { - _entity->setAcceleration(_entity->getGravity()); - } + _entity->setAcceleration(_entity->getGravity()); + uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); From f13fdf2a4c62b8cd4b3b70504e5d7403710ff030 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:43:36 -0800 Subject: [PATCH 16/44] make findAncestorOfType understand AVATAR_SELF_ID --- libraries/shared/src/SpatiallyNestable.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index f071bda71f..ddc3f416e0 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1055,6 +1055,14 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) const { bool success; + + if (nestableType == NestableType::Avatar) { + QUuid parentID = getParentID(); + if (parentID == AVATAR_SELF_ID) { + return AVATAR_SELF_ID; // TODO -- can we put nodeID here? + } + } + SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { return QUuid(); From b90d7e26899067b6c4ba56cde235fddced1b3372 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:47:03 -0800 Subject: [PATCH 17/44] another AVATAR_SELF_ID adjustment --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 233ce7d88e..2503687dce 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1828,7 +1828,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask // "bootstrapping" problem where you can shoot yourself across the room by grabbing something // and holding it against your own avatar. QUuid ancestorID = findAncestorOfType(NestableType::Avatar); - if (!ancestorID.isNull() && ancestorID == Physics::getSessionUUID()) { + if (!ancestorID.isNull() && + (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } } From 0dc7152dcca44bbba6237e4b82bf60e09ec03055 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 09:50:48 -0800 Subject: [PATCH 18/44] if an entity has an avatar parent but ends up in EntityMotionState::setWorldTransform, ignore the update from bullet --- libraries/physics/src/EntityMotionState.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c90ca5a1a9..d652e78f6a 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -230,6 +230,9 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(entityTreeIsLocked()); measureBodyAcceleration(); bool positionSuccess; + if (_entity->hasAncestorOfType(NestableType::Avatar)) { + return; + } _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); if (!positionSuccess) { static QString repeatedMessage = From f68fcbb9cd89a37da03f305044d1df8fa87a0421 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 12:10:26 -0800 Subject: [PATCH 19/44] when the parentID is changed by a network update, set physics flags --- libraries/entities/src/EntityItem.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 2503687dce..a656827d5e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -828,7 +828,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef { // parentID and parentJointIndex are also protected by simulation ownership bool oldOverwrite = overwriteLocalData; overwriteLocalData = overwriteLocalData && !weOwnSimulation; - READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID); + READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, updateParentID); READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); overwriteLocalData = oldOverwrite; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d652e78f6a..c90ca5a1a9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -230,9 +230,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(entityTreeIsLocked()); measureBodyAcceleration(); bool positionSuccess; - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - return; - } _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); if (!positionSuccess) { static QString repeatedMessage = From 40730c1d19376b455e8350d671696529372fd970 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 14:16:47 -0800 Subject: [PATCH 20/44] avoid bootstrapping self with something that has someone else as simulation owner --- libraries/entities/src/EntityItem.cpp | 50 +++++++++++++++------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a656827d5e..6543af5355 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1823,29 +1823,6 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the - // "bootstrapping" problem where you can shoot yourself across the room by grabbing something - // and holding it against your own avatar. - QUuid ancestorID = findAncestorOfType(NestableType::Avatar); - if (!ancestorID.isNull() && - (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - } - } - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - // also, don't bootstrap our own avatar with a hold action - QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); - QList::const_iterator i = holdActions.begin(); - while (i != holdActions.end()) { - EntityActionPointer action = *i; - if (action->isMine()) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - break; - } - i++; - } - } if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != (bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) { @@ -1855,6 +1832,33 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask; } } + + if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { + bool iAmHoldingThis = false; + // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the + // "bootstrapping" problem where you can shoot yourself across the room by grabbing something + // and holding it against your own avatar. + QUuid ancestorID = findAncestorOfType(NestableType::Avatar); + if (!ancestorID.isNull() && + (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { + iAmHoldingThis = true; + } + // also, don't bootstrap our own avatar with a hold action + QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); + QList::const_iterator i = holdActions.begin(); + while (i != holdActions.end()) { + EntityActionPointer action = *i; + if (action->isMine()) { + iAmHoldingThis = true; + break; + } + i++; + } + + if (iAmHoldingThis) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; + } + } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); } } From 725917af0d29615b0fdfdbd321f99d3ee9d5dfff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 16:49:26 -0800 Subject: [PATCH 21/44] send UserActivityLogger events when tablet is opened or closed --- .../src/UserActivityLoggerScriptingInterface.cpp | 8 ++++++++ .../networking/src/UserActivityLoggerScriptingInterface.h | 2 ++ scripts/system/tablet-ui/tabletUI.js | 5 +++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 02d1711230..f38d24c31f 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -16,6 +16,14 @@ void UserActivityLoggerScriptingInterface::enabledEdit() { logAction("enabled_edit"); } +void UserActivityLoggerScriptingInterface::openedTablet() { + logAction("opened_tablet"); +} + +void UserActivityLoggerScriptingInterface::closedTablet() { + logAction("closed_tablet"); +} + void UserActivityLoggerScriptingInterface::openedMarketplace() { logAction("opened_marketplace"); } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index a202858a1c..b827b2262a 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -21,6 +21,8 @@ class UserActivityLoggerScriptingInterface : public QObject, public Dependency { Q_OBJECT public: Q_INVOKABLE void enabledEdit(); + Q_INVOKABLE void openedTablet(); + Q_INVOKABLE void closedTablet(); Q_INVOKABLE void openedMarketplace(); Q_INVOKABLE void toggledAway(bool isAway); Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index eab3d85adc..dc1d71f402 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, HMD, WebTablet, UIWebTablet */ +/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, MyAvatar */ (function() { // BEGIN LOCAL_SCOPE var tabletShown = false; @@ -65,8 +65,10 @@ hideTabletUI(); HMD.closeTablet(); } else if (HMD.showTablet && !tabletShown) { + UserActivityLogger.openedTablet(); showTabletUI(); } else if (!HMD.showTablet && tabletShown) { + UserActivityLogger.closedTablet(); hideTabletUI(); } } @@ -86,7 +88,6 @@ var accumulatedLevel = 0.0; // Note: Might have to tweak the following two based on the rate we're getting the data var AVERAGING_RATIO = 0.05; - var MIC_LEVEL_UPDATE_INTERVAL_MS = 100; // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js function getMicLevel() { From 6d1d36e0f0a01881b2033052367b690aa8d061ad Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 Feb 2017 10:22:43 -0800 Subject: [PATCH 22/44] fix for bad merge around cauterizedClusterMatrices --- libraries/render-utils/src/MeshPartPayload.cpp | 10 +--------- libraries/render-utils/src/MeshPartPayload.h | 4 +--- libraries/render-utils/src/Model.cpp | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 17a8c07c39..333371ed76 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -370,22 +370,14 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { _transform = transform; - _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = _adjustedLocalBound; _worldBound.transform(_transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); - if (cauterizedClusterMatrices.size() != 0) { - _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); - } else { - _cauterizedTransform = _transform; - } } } else { _worldBound = _localBound; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index bdd9d737e9..c585c95025 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -90,8 +90,7 @@ public: void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices); + const QVector& clusterMatrices); float computeFadeAlpha() const; @@ -111,7 +110,6 @@ public: Model* _model; - Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 53e57541c1..e1627f2fd6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -248,7 +248,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); } } }); From 13b2b6086f224fbcef336ddb6c153d2bb565f054 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 21 Feb 2017 19:47:26 +0100 Subject: [PATCH 23/44] Fix recorder.js playback in interface --- interface/src/avatar/MyAvatar.cpp | 24 +++++-- interface/src/avatar/MyAvatar.h | 1 + libraries/animation/src/Rig.cpp | 6 +- libraries/animation/src/Rig.h | 2 + libraries/audio-client/src/AudioClient.cpp | 62 +++++++++---------- libraries/audio-client/src/AudioClient.h | 13 ++-- libraries/audio/src/AudioInjectorOptions.cpp | 2 +- .../developer/utilities/record/recorder.js | 10 ++- 8 files changed, 71 insertions(+), 49 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 29f41c89fd..b9f673287b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -148,13 +148,22 @@ MyAvatar::MyAvatar(RigPointer rig) : auto player = DependencyManager::get(); auto recorder = DependencyManager::get(); connect(player.data(), &Deck::playbackStateChanged, [=] { - if (player->isPlaying()) { + bool isPlaying = player->isPlaying(); + if (isPlaying) { auto recordingInterface = DependencyManager::get(); if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } } else { clearRecordingBasis(); + useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); + } + + auto audioIO = DependencyManager::get(); + audioIO->setPlayingBackRecording(isPlaying); + + if (_rig) { + _rig->setEnableAnimations(!isPlaying); } }); @@ -180,8 +189,8 @@ MyAvatar::MyAvatar(RigPointer rig) : if (recordingInterface->getPlayerUseSkeletonModel() && dummyAvatar.getSkeletonModelURL().isValid() && (dummyAvatar.getSkeletonModelURL() != getSkeletonModelURL())) { - // FIXME - //myAvatar->useFullAvatarURL() + + setSkeletonModelURL(dummyAvatar.getSkeletonModelURL()); } if (recordingInterface->getPlayerUseDisplayName() && dummyAvatar.getDisplayName() != getDisplayName()) { @@ -204,6 +213,11 @@ MyAvatar::MyAvatar(RigPointer rig) : // head orientation _headData->setLookAtPosition(headData->getLookAtPosition()); } + + auto jointData = dummyAvatar.getRawJointData(); + if (jointData.length() > 0 && _rig) { + _rig->copyJointsFromJointData(jointData); + } }); connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); @@ -472,7 +486,9 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model - _rig->copyJointsIntoJointData(_jointData); + if (_rigEnabled) { + _rig->copyJointsIntoJointData(_jointData); + } } { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c4fe86356d..5c2c0f2765 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -486,6 +486,7 @@ private: std::unordered_set _headBoneSet; RigPointer _rig; bool _prevShouldDrawHead; + bool _rigEnabled { true }; bool _enableDebugDrawDefaultPose { false }; bool _enableDebugDrawAnimPose { false }; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ac16b16c1d..07462e9878 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -483,6 +483,10 @@ void Rig::setEnableInverseKinematics(bool enable) { _enableInverseKinematics = enable; } +void Rig::setEnableAnimations(bool enable) { + _enabledAnimations = enable; +} + AnimPose Rig::getAbsoluteDefaultPose(int index) const { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { return _absoluteDefaultPoses[index]; @@ -907,7 +911,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setModelOffset(rootTransform); - if (_animNode) { + if (_animNode && _enabledAnimations) { PerformanceTimer perfTimer("handleTriggers"); updateAnimationStateHandlers(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index aa091fe10c..78a669b249 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -210,6 +210,7 @@ public: void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; void setEnableInverseKinematics(bool enable); + void setEnableAnimations(bool enable); const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; } @@ -314,6 +315,7 @@ protected: int32_t _numOverrides { 0 }; bool _lastEnableInverseKinematics { true }; bool _enableInverseKinematics { true }; + bool _enabledAnimations { true }; mutable uint32_t _jointNameWarningCount { 0 }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2e532d67bf..0e2991ae30 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -39,13 +39,10 @@ #include #include #include -#include #include #include -#include #include -#include "PositionalAudioStream.h" #include "AudioClientLogging.h" #include "AudioLogging.h" @@ -151,17 +148,17 @@ static inline float convertToFloat(int16_t sample) { AudioClient::AudioClient() : AbstractAudioInterface(), _gate(this), - _audioInput(NULL), + _audioInput(nullptr), _desiredInputFormat(), _inputFormat(), _numInputCallbackBytes(0), - _audioOutput(NULL), + _audioOutput(nullptr), _desiredOutputFormat(), _outputFormat(), _outputFrameSize(0), _numOutputCallbackBytes(0), - _loopbackAudioOutput(NULL), - _loopbackOutputDevice(NULL), + _loopbackAudioOutput(nullptr), + _loopbackOutputDevice(nullptr), _inputRingBuffer(0), _localInjectorsStream(0), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), @@ -179,9 +176,9 @@ AudioClient::AudioClient() : _isNoiseGateEnabled(true), _reverb(false), _reverbOptions(&_scriptReverbOptions), - _inputToNetworkResampler(NULL), - _networkToOutputResampler(NULL), - _localToOutputResampler(NULL), + _inputToNetworkResampler(nullptr), + _networkToOutputResampler(nullptr), + _localToOutputResampler(nullptr), _localAudioThread(this), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), @@ -294,12 +291,12 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { IPropertyStore* pPropertyStore; pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore); pEndpoint->Release(); - pEndpoint = NULL; + pEndpoint = nullptr; PROPVARIANT pv; PropVariantInit(&pv); HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); pPropertyStore->Release(); - pPropertyStore = NULL; + pPropertyStore = nullptr; deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); if (!IsWindows8OrGreater()) { // Windows 7 provides only the 31 first characters of the device name. @@ -313,9 +310,9 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { QString deviceName; HRESULT hr = S_OK; - CoInitialize(NULL); - IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; - CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoInitialize(nullptr); + IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; + CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint); if (hr == E_NOTFOUND) { @@ -325,7 +322,7 @@ QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { deviceName = ::friendlyNameForAudioDevice(pEndpoint); } pMMDeviceEnumerator->Release(); - pMMDeviceEnumerator = NULL; + pMMDeviceEnumerator = nullptr; CoUninitialize(); return deviceName; } @@ -396,9 +393,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } } else { HRESULT hr = S_OK; - CoInitialize(NULL); - IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; - CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoInitialize(nullptr); + IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; + CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint); if (hr == E_NOTFOUND) { @@ -408,7 +405,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { deviceName = friendlyNameForAudioDevice(pEndpoint); } pMMDeviceEnumerator->Release(); - pMMDeviceEnumerator = NULL; + pMMDeviceEnumerator = nullptr; CoUninitialize(); } @@ -968,8 +965,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - - if (!_inputDevice) { + if (!_inputDevice || _playingBackRecording) { return; } @@ -1358,10 +1354,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn // That in turn causes it to be disconnected (see for example // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). _audioInput->stop(); - _inputDevice = NULL; + _inputDevice = nullptr; delete _audioInput; - _audioInput = NULL; + _audioInput = nullptr; _numInputCallbackBytes = 0; _inputAudioDeviceName = ""; @@ -1370,7 +1366,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (_inputToNetworkResampler) { // if we were using an input to network resampler, delete it here delete _inputToNetworkResampler; - _inputToNetworkResampler = NULL; + _inputToNetworkResampler = nullptr; } if (!inputDeviceInfo.isNull()) { @@ -1465,29 +1461,29 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutput->stop(); delete _audioOutput; - _audioOutput = NULL; + _audioOutput = nullptr; - _loopbackOutputDevice = NULL; + _loopbackOutputDevice = nullptr; delete _loopbackAudioOutput; - _loopbackAudioOutput = NULL; + _loopbackAudioOutput = nullptr; delete[] _outputMixBuffer; - _outputMixBuffer = NULL; + _outputMixBuffer = nullptr; delete[] _outputScratchBuffer; - _outputScratchBuffer = NULL; + _outputScratchBuffer = nullptr; delete[] _localOutputMixBuffer; - _localOutputMixBuffer = NULL; + _localOutputMixBuffer = nullptr; } if (_networkToOutputResampler) { // if we were using an input to network resampler, delete it here delete _networkToOutputResampler; - _networkToOutputResampler = NULL; + _networkToOutputResampler = nullptr; delete _localToOutputResampler; - _localToOutputResampler = NULL; + _localToOutputResampler = nullptr; } if (!outputDeviceInfo.isNull()) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 699ba71ef7..9dc86c1ecf 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -145,6 +145,8 @@ public: void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } + void setPlayingBackRecording(bool playingBackRecording) { _playingBackRecording = playingBackRecording; } + Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); void checkDevices(); @@ -326,14 +328,14 @@ private: // for local audio (used by audio injectors thread) float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; - float* _localOutputMixBuffer { NULL }; + float* _localOutputMixBuffer { nullptr }; AudioInjectorsThread _localAudioThread; Mutex _localAudioMutex; // for output audio (used by this thread) int _outputPeriod { 0 }; - float* _outputMixBuffer { NULL }; - int16_t* _outputScratchBuffer { NULL }; + float* _outputMixBuffer { nullptr }; + int16_t* _outputScratchBuffer { nullptr }; AudioLimiter _audioLimiter; @@ -367,13 +369,16 @@ private: QVector _inputDevices; QVector _outputDevices; - bool _hasReceivedFirstPacket = false; + bool _hasReceivedFirstPacket { false }; QVector _activeLocalAudioInjectors; + bool _playingBackRecording { false }; + CodecPluginPointer _codec; QString _selectedCodecName; Encoder* _encoder { nullptr }; // for outbound mic stream + Encoder* _playbackEncoder { nullptr }; QThread* _checkDevicesThread { nullptr }; }; diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 1a92697828..0af74a796c 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -91,4 +91,4 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt qCWarning(audio) << "Unknown audio injector option:" << it.name(); } } - } \ No newline at end of file +} diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index 083037461d..ea0a4505cc 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -1,3 +1,4 @@ +debugger; // // Recorder.js // examples @@ -12,14 +13,14 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Script.include("/~/system/libraries/toolBars.js"); -var recordingFile = "recording.rec"; +var recordingFile = "recording.hfr"; function setPlayerOptions() { Recording.setPlayFromCurrentLocation(true); Recording.setPlayerUseDisplayName(false); Recording.setPlayerUseAttachments(false); Recording.setPlayerUseHeadModel(false); - Recording.setPlayerUseSkeletonModel(false); + Recording.setPlayerUseSkeletonModel(true); } var windowDimensions = Controller.getViewportDimensions(); @@ -142,7 +143,6 @@ function setupTimer() { backgroundAlpha: 1.0, visible: true }); - } function updateTimer() { @@ -272,7 +272,7 @@ function mousePressEvent(event) { } } else if (loadIcon === toolBar.clicked(clickedOverlay)) { if (!Recording.isRecording() && !Recording.isPlaying()) { - recordingFile = Window.browse("Load recorcding from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); + recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { Recording.loadRecording(recordingFile); } @@ -345,5 +345,3 @@ Script.scriptEnding.connect(scriptEnding); // Should be called last to put everything into position moveUI(); - - From 15518e9aa17c970187ba368dc851d7ac2ecc6798 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 21 Feb 2017 20:20:23 +0100 Subject: [PATCH 24/44] remove debugger line --- scripts/developer/utilities/record/recorder.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index ea0a4505cc..0e335116d5 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -1,4 +1,3 @@ -debugger; // // Recorder.js // examples From 72eb797910733125b6e844de7350260f17258791 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 21 Feb 2017 20:22:29 +0100 Subject: [PATCH 25/44] remove unused encoder (was for testing) --- libraries/audio-client/src/AudioClient.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 9dc86c1ecf..739c4b459c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -378,7 +378,6 @@ private: CodecPluginPointer _codec; QString _selectedCodecName; Encoder* _encoder { nullptr }; // for outbound mic stream - Encoder* _playbackEncoder { nullptr }; QThread* _checkDevicesThread { nullptr }; }; From 2017ea4491a91027cc6d3c498b346bc9d36e6c8e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 21 Feb 2017 14:18:00 -0800 Subject: [PATCH 26/44] hacking on using sorted avatars for bandwidth budget --- .../src/avatars/AvatarMixerClientData.cpp | 12 ++- .../src/avatars/AvatarMixerClientData.h | 11 +++ .../src/avatars/AvatarMixerSlave.cpp | 76 ++++++++++++++++++- interface/src/avatar/Avatar.cpp | 5 -- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/AvatarManager.cpp | 66 ++++------------ libraries/avatars/src/AvatarData.cpp | 63 +++++++++++++++ libraries/avatars/src/AvatarData.h | 27 ++++++- libraries/avatars/src/AvatarHashMap.cpp | 1 + libraries/avatars/src/AvatarHashMap.h | 1 - 10 files changed, 199 insertions(+), 64 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index df6a64e874..1df6c24a60 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -74,14 +74,22 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& return true; } +uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { + // return the matching PacketSequenceNumber, or the default if we don't have it + auto nodeMatch = _lastBroadcastTimes.find(nodeUUID); + if (nodeMatch != _lastBroadcastTimes.end()) { + return nodeMatch->second; + } + return 0; +} + uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { return nodeMatch->second; - } else { - return 0; } + return 0; } void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) { diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 87f7bb9cc9..7eea31c9ab 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -43,6 +43,7 @@ public: int parseData(ReceivedMessage& message) override; AvatarData& getAvatar() { return *_avatar; } const AvatarData* getConstAvatarData() const { return _avatar.get(); } + AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; } bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid); @@ -51,6 +52,12 @@ public: { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } + uint64_t getLastBroadcastTime(const QUuid& nodeUUID) const; + void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { + _lastBroadcastTimes[nodeUUID] = broadcastTime; + } + Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } + uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } @@ -106,6 +113,9 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } + ViewFrustum getViewFrustom() const { return _currentViewFrustum; } + + quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 result = 0; if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) { @@ -134,6 +144,7 @@ private: uint16_t _lastReceivedSequenceNumber { 0 }; std::unordered_map _lastBroadcastSequenceNumbers; std::unordered_set _hasReceivedFirstPacketsFrom; + std::unordered_map _lastBroadcastTimes; // this is a map of the last time we encoded an "other" avatar for // sending to "this" node diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 46133a221f..4c95c96483 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -187,9 +187,75 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + + QList avatarList; + std::unordered_map avatarDataToNodes; + + //qDebug() << "------------------------------"; + int listItem = 0; + std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + if (otherNodeData) { + listItem++; + AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); + avatarList << otherAvatar; + avatarDataToNodes[otherAvatar] = otherNode; + /* + qDebug() << "listItem [" << listItem << "] " + << "otherNode:" << otherNode.data() + << "otherNode->getUUID():" << otherNode->getUUID() + << "otherNodeData:" << otherNodeData + << "otherAvatar:" << otherAvatar.get(); + + qDebug() << "avatarDataToNodes[" << otherAvatar.get() << "]=" << otherNode.data(); + */ + } + + }); + /* + qDebug() << "------------------------------"; + qDebug() << "avatarList.size:" << avatarList.size(); + qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); + */ + + ViewFrustum cameraView = nodeData->getViewFrustom(); + std::priority_queue sortedAvatars = AvatarData::sortAvatars( + avatarList, cameraView, + + [&](AvatarSharedPointer avatar)->uint64_t{ + auto avatarNode = avatarDataToNodes[avatar]; + if (avatarNode) { + return nodeData->getLastBroadcastTime(avatarNode->getUUID()); + } + return 0; // ??? + }, + + [this](AvatarSharedPointer avatar)->bool{ + // FIXME -- when to ignore this node + return false; + }); + + // this is an AGENT we have received head data from // send back a packet with other active node data to this node - std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + //std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + + int avatarRank = 0; + while (!sortedAvatars.empty()) { + AvatarPriority sortData = sortedAvatars.top(); + sortedAvatars.pop(); + const auto& avatarData = sortData.avatar; + avatarRank++; + + auto otherNode = avatarDataToNodes[avatarData]; + + //qDebug() << "otherNode (" << otherNode.data() << ")= avatarDataToNodes[" << avatarData.get() << "]"; + + if (!otherNode) { + //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << ":" << avatarData.get() << "... otherNode unknown!!"; + continue; + } + //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << "] avatarData: " << avatarData.get() << " otherNode:" << otherNode->getUUID() << " ... "; bool shouldConsider = false; quint64 startIgnoreCalculation = usecTimestampNow(); @@ -286,7 +352,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // are out of view, this also appears to disable this random distribution. if (distanceToAvatar != 0.0f && !getsOutOfView - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { + // -- && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... no longer doing random + ) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); @@ -395,6 +462,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), otherNodeData->getLastReceivedSequenceNumber()); + // remember the last time we sent details about this other node to the receiver + nodeData->setLastBroadcastTime(otherNode->getUUID(), start); + } } @@ -406,7 +476,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { } } } - }); + }; quint64 startPacketSending = usecTimestampNow(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ed8f083a41..64ecb1dc29 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -334,11 +334,6 @@ void Avatar::updateAvatarEntities() { setAvatarEntityDataChanged(false); } -bool Avatar::shouldDie() const { - const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; - return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; -} - void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 80d387fd33..53f0562bdd 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -178,7 +178,6 @@ public: uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; } void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; } - bool shouldDie() const; void animateScaleChanges(float deltaTime); void setTargetScale(float targetScale) override; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c3fc974365..1a9866aae4 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -148,16 +148,6 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri } - -class AvatarPriority { -public: - AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {} - AvatarSharedPointer avatar; - float priority; - // NOTE: we invert the less-than operator to sort high priorities to front - bool operator<(const AvatarPriority& other) const { return priority > other.priority; } -}; - void AvatarManager::updateOtherAvatars(float deltaTime) { // lock the hash for read to check the size QReadLocker lock(&_hashLock); @@ -173,57 +163,31 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { QList avatarList = avatarMap.values(); ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - glm::vec3 frustumCenter = cameraView.getPosition(); - const float OUT_OF_VIEW_PENALTY = -10.0; + std::priority_queue sortedAvatars = AvatarData::sortAvatars( + avatarList, cameraView, - std::priority_queue sortedAvatars; - { - PROFILE_RANGE(simulation, "sort"); - for (int32_t i = 0; i < avatarList.size(); ++i) { - const auto& avatar = std::static_pointer_cast(avatarList.at(i)); - if (avatar == _myAvatar || !avatar->isInitialized()) { + [](AvatarSharedPointer avatar)->uint64_t{ + return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); + }, + + [this](AvatarSharedPointer avatar)->bool{ + const auto& castedAvatar = std::static_pointer_cast(avatar); + if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars - continue; + return true; // ignore it } if (avatar->shouldDie()) { removeAvatar(avatar->getID()); - continue; + return true; // ignore it } if (avatar->isDead()) { - continue; + return true; // ignore it } - // priority = weighted linear combination of: - // (a) apparentSize - // (b) proximity to center of view - // (c) time since last update - // (d) TIME_PENALTY to help recently updated entries sort toward back - glm::vec3 avatarPosition = avatar->getPosition(); - glm::vec3 offset = avatarPosition - frustumCenter; - float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - float radius = avatar->getBoundingRadius(); - const glm::vec3& forward = cameraView.getDirection(); - float apparentSize = radius / distance; - float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance; - const float TIME_PENALTY = 0.080f; // seconds - float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY; - // NOTE: we are adding values of different units to get a single measure of "priority". - // Thus we multiply each component by a conversion "weight" that scales its units - // relative to the others. These weights are pure magic tuning and are hard coded in the - // relation below: (hint: unitary weights are not explicityly shown) - float priority = apparentSize + 0.25f * cosineAngle + age; - - // decrement priority of avatars outside keyhole - if (distance > cameraView.getCenterRadius()) { - if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { - priority += OUT_OF_VIEW_PENALTY; - } - } - sortedAvatars.push(AvatarPriority(avatar, priority)); - } - } + return false; + }); render::PendingChanges pendingChanges; const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec @@ -256,7 +220,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t now = usecTimestampNow(); if (now < renderExpiry) { // we're within budget - const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY; + const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; avatar->simulate(deltaTime, inView); avatar->updateRenderItem(pendingChanges); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2263964e8f..dffdc8c1ac 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -2309,3 +2310,65 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra vec3FromScriptValue(intersection, value.intersection); } } + +const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; + +std::priority_queue AvatarData::sortAvatars( + QList avatarList, + const ViewFrustum& cameraView, + std::function lastUpdated, + std::function shouldIgnore) { + + uint64_t startTime = usecTimestampNow(); + + glm::vec3 frustumCenter = cameraView.getPosition(); + + std::priority_queue sortedAvatars; + { + PROFILE_RANGE(simulation, "sort"); + for (int32_t i = 0; i < avatarList.size(); ++i) { + const auto& avatar = avatarList.at(i); + + // FIXME - probably some lambda that allows the caller to reject some avatars + + if (shouldIgnore(avatar)) { + continue; + } + + // priority = weighted linear combination of: + // (a) apparentSize + // (b) proximity to center of view + // (c) time since last update + // (d) TIME_PENALTY to help recently updated entries sort toward back + glm::vec3 avatarPosition = avatar->getPosition(); + glm::vec3 offset = avatarPosition - frustumCenter; + float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero + + // FIXME - AvatarData has something equivolent to this + float radius = 1.0f; // avatar->getBoundingRadius(); + + const glm::vec3& forward = cameraView.getDirection(); + float apparentSize = radius / distance; + float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance; + + // FIXME - probably some lambda that allows the caller to specify the "updated since" + uint64_t lastUpdatedTime = lastUpdated(avatar); // avatar->getLastRenderUpdateTime() + float age = (float)(startTime - lastUpdatedTime) / (float)(USECS_PER_SECOND); + + // NOTE: we are adding values of different units to get a single measure of "priority". + // Thus we multiply each component by a conversion "weight" that scales its units + // relative to the others. These weights are pure magic tuning and are hard coded in the + // relation below: (hint: unitary weights are not explicityly shown) + float priority = apparentSize + 0.25f * cosineAngle + age; + + // decrement priority of avatars outside keyhole + if (distance > cameraView.getCenterRadius()) { + if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { + priority += OUT_OF_VIEW_PENALTY; + } + } + sortedAvatars.push(AvatarPriority(avatar, priority)); + } + } + return sortedAvatars; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 264da75de2..71ade436ff 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -14,6 +14,8 @@ #include #include +#include + /* VS2010 defines stdint.h, but not inttypes.h */ #if defined(_MSC_VER) typedef signed char int8_t; @@ -57,6 +59,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include "AABox.h" #include "HeadData.h" @@ -304,6 +307,14 @@ public: RateCounter<> jointDataRate; }; +class AvatarPriority { +public: + AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {} + AvatarSharedPointer avatar; + float priority; + // NOTE: we invert the less-than operator to sort high priorities to front + bool operator<(const AvatarPriority& other) const { return priority < other.priority; } +}; class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT @@ -539,7 +550,7 @@ public: void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } - const AABox& getLocalAABox() const { return _localAABox; } + //const AABox& getLocalAABox() const { return _localAABox; } int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); } int getAverageBytesReceivedPerSecond() const; @@ -578,6 +589,20 @@ public: } + bool shouldDie() const { + const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; + return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; + } + + static const float OUT_OF_VIEW_PENALTY; + + static std::priority_queue sortAvatars( + QList avatarList, + const ViewFrustum& cameraView, + std::function lastUpdated, + std::function shouldIgnore); + + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 00c515a635..cdbf5f2a85 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -190,3 +190,4 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol _lastOwnerSessionUUID = oldUUID; emit avatarSessionChangedEvent(sessionUUID, oldUUID); } + diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index dd097aef22..104ac83261 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -27,7 +27,6 @@ #include "AvatarData.h" - class AvatarHashMap : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY From 8cc0b383c4c28d27de675a50465f17d8328ab307 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 21 Feb 2017 16:22:22 -0800 Subject: [PATCH 27/44] wire up bandwidth buget to sorting --- .../src/avatars/AvatarMixerSlave.cpp | 23 ++++++++++++++----- libraries/avatars/src/AvatarData.cpp | 2 ++ .../developer/debugging/debugAvatarMixer.js | 10 +++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 4c95c96483..5c71b88af9 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -213,11 +213,15 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { }); /* - qDebug() << "------------------------------"; qDebug() << "avatarList.size:" << avatarList.size(); qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); */ + //qDebug() << "------------------------------"; + AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); + + //qDebug() << "thisAvatar:" << thisAvatar.get(); + ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, @@ -230,10 +234,10 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { return 0; // ??? }, - [this](AvatarSharedPointer avatar)->bool{ - // FIXME -- when to ignore this node - return false; + [thisAvatar](AvatarSharedPointer avatar)->bool{ + return (avatar == thisAvatar); // ignore ourselves... }); + //qDebug() << "------------------------------"; // this is an AGENT we have received head data from @@ -269,6 +273,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldConsider = false; + //qDebug() << "shouldConsider = false ... line:" << __LINE__; } else { const AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); @@ -304,6 +309,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(node, otherNode); shouldConsider = getsAnyIgnored; + //qDebug() << "shouldConsider = getsAnyIgnored " << getsAnyIgnored << " ... line:" << __LINE__; + } } // Not close enough to ignore @@ -352,12 +359,13 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // are out of view, this also appears to disable this random distribution. if (distanceToAvatar != 0.0f && !getsOutOfView - // -- && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... no longer doing random + && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... we don't want to do this random stuff ) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; + //qDebug() << "shouldConsider = false ... line:" << __LINE__; } if (shouldConsider) { @@ -380,7 +388,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + //qDebug() << "shouldConsider = false ... line:" << __LINE__; + } + else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; } @@ -399,6 +409,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; + //qDebug() << "shouldConsider = false ... line:" << __LINE__; } if (shouldConsider) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index dffdc8c1ac..9416216c5a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2367,6 +2367,8 @@ std::priority_queue AvatarData::sortAvatars( priority += OUT_OF_VIEW_PENALTY; } } + + //qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age; sortedAvatars.push(AvatarPriority(avatar, priority)); } } diff --git a/scripts/developer/debugging/debugAvatarMixer.js b/scripts/developer/debugging/debugAvatarMixer.js index ebd43fc2f0..90f2de13a9 100644 --- a/scripts/developer/debugging/debugAvatarMixer.js +++ b/scripts/developer/debugging/debugAvatarMixer.js @@ -58,11 +58,14 @@ function updateOverlays() { // setup a position for the overlay that is just above this avatar's head var overlayPosition = avatar.getJointPosition("Head"); - overlayPosition.y += 1.05; + overlayPosition.y += 1.15; + + var rows = 8; var text = avatarID + "\n" +"--- Data from Mixer ---\n" +"All: " + AvatarManager.getAvatarDataRate(avatarID).toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID).toFixed(2) + "hz)" + "\n" + /* +" GP: " + AvatarManager.getAvatarDataRate(avatarID,"globalPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"globalPosition").toFixed(2) + "hz)" + "\n" +" LP: " + AvatarManager.getAvatarDataRate(avatarID,"localPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"localPosition").toFixed(2) + "hz)" + "\n" +" BB: " + AvatarManager.getAvatarDataRate(avatarID,"avatarBoundingBox").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"avatarBoundingBox").toFixed(2) + "hz)" + "\n" @@ -74,11 +77,12 @@ function updateOverlays() { +" AF: " + AvatarManager.getAvatarDataRate(avatarID,"additionalFlags").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"additionalFlags").toFixed(2) + "hz)" + "\n" +" PI: " + AvatarManager.getAvatarDataRate(avatarID,"parentInfo").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"parentInfo").toFixed(2) + "hz)" + "\n" +" FT: " + AvatarManager.getAvatarDataRate(avatarID,"faceTracker").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"faceTracker").toFixed(2) + "hz)" + "\n" + */ +" JD: " + AvatarManager.getAvatarDataRate(avatarID,"jointData").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"jointData").toFixed(2) + "hz)" + "\n" +"--- Simulation ---\n" +"All: " + AvatarManager.getAvatarSimulationRate(avatarID,"avatar").toFixed(2) + "hz \n" +" inView: " + AvatarManager.getAvatarSimulationRate(avatarID,"avatarInView").toFixed(2) + "hz \n" - +" SM: " + AvatarManager.getAvatarSimulationRate(avatarID,"skeletonModel").toFixed(2) + "hz \n" + //+" SM: " + AvatarManager.getAvatarSimulationRate(avatarID,"skeletonModel").toFixed(2) + "hz \n" +" JD: " + AvatarManager.getAvatarSimulationRate(avatarID,"jointData").toFixed(2) + "hz \n" if (avatarID in debugOverlays) { @@ -93,7 +97,7 @@ function updateOverlays() { position: overlayPosition, dimensions: { x: 1.25, - y: 19 * 0.13 + y: rows * 0.13 }, lineHeight: 0.1, font:{size:0.1}, From 4c42e95607852cec20d23ffe716290aba8157c16 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 08:13:37 -0800 Subject: [PATCH 28/44] add some debugging --- .../src/avatars/AvatarMixerSlave.cpp | 23 +++++++++++++------ libraries/avatars/src/AvatarData.cpp | 11 ++++++--- libraries/avatars/src/AvatarData.h | 3 ++- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 5c71b88af9..360c6e26fd 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -217,11 +217,19 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); */ - //qDebug() << "------------------------------"; AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); //qDebug() << "thisAvatar:" << thisAvatar.get(); +#ifdef WANT_DEBUG + bool printDebug = nodeData->getAvatarSharedPointer()->getDisplayName() == "ZappoMan"; + + if (printDebug) { + qDebug() << "------------------------------"; + } +#else + bool printDebug = false; +#endif ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, @@ -236,14 +244,15 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { [thisAvatar](AvatarSharedPointer avatar)->bool{ return (avatar == thisAvatar); // ignore ourselves... - }); - //qDebug() << "------------------------------"; + }, printDebug); +#ifdef WANT_DEBUG + if (printDebug) { + qDebug() << "------------------------------"; + } +#endif - // this is an AGENT we have received head data from - // send back a packet with other active node data to this node - //std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { - + // loop through our sorted avatars and allocate our bandwidth to them accordingly int avatarRank = 0; while (!sortedAvatars.empty()) { AvatarPriority sortData = sortedAvatars.top(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9416216c5a..fd21670853 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2317,7 +2317,8 @@ std::priority_queue AvatarData::sortAvatars( QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore) { + std::function shouldIgnore, + bool printDebug) { uint64_t startTime = usecTimestampNow(); @@ -2359,16 +2360,20 @@ std::priority_queue AvatarData::sortAvatars( // Thus we multiply each component by a conversion "weight" that scales its units // relative to the others. These weights are pure magic tuning and are hard coded in the // relation below: (hint: unitary weights are not explicityly shown) - float priority = apparentSize + 0.25f * cosineAngle + age; + float priority = apparentSize + 10.0f * (0.25f * cosineAngle) + age; // decrement priority of avatars outside keyhole + bool outOfView = false; if (distance > cameraView.getCenterRadius()) { if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { priority += OUT_OF_VIEW_PENALTY; + outOfView = true; } } - //qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age; + if (printDebug) { + qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age << "outOfView:" << outOfView; + } sortedAvatars.push(AvatarPriority(avatar, priority)); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 71ade436ff..a75e81ed3d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -600,7 +600,8 @@ public: QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore); + std::function shouldIgnore, + bool printDebug = false); public slots: From 05995163cf610405f0519511d8009b220697a613 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 09:49:47 -0800 Subject: [PATCH 29/44] add support for tuning sorting --- assignment-client/src/avatars/AvatarMixer.cpp | 20 ++++++++++++++++++- assignment-client/src/avatars/AvatarMixer.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 15 ++++++++++++++ libraries/avatars/src/AvatarData.h | 6 +++--- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3a8aa0182c..59896432e7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -47,7 +47,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket"); - + packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting"); packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket"); packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); @@ -317,6 +317,24 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { } } + +void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode) { + auto start = usecTimestampNow(); + + message->readPrimitive(&AvatarData::_avatarSortCoefficientSize); + message->readPrimitive(&AvatarData::_avatarSortCoefficientCenter); + message->readPrimitive(&AvatarData::_avatarSortCoefficientAge); + + qCDebug(avatars) << "New avatar sorting... " + << "size:" << AvatarData::_avatarSortCoefficientSize + << "center:" << AvatarData::_avatarSortCoefficientCenter + << "age:" << AvatarData::_avatarSortCoefficientAge; + + auto end = usecTimestampNow(); + _handleAdjustAvatarSortingElapsedTime += (end - start); +} + + void AvatarMixer::handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode) { auto start = usecTimestampNow(); getOrCreateClientData(senderNode); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 32b0ffed69..1925ec1ebd 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -39,6 +39,7 @@ public slots: private slots: void queueIncomingPacket(QSharedPointer message, SharedNodePointer node); + void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); void handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message); @@ -92,6 +93,7 @@ private: quint64 _broadcastAvatarDataNodeTransform { 0 }; quint64 _broadcastAvatarDataNodeFunctor { 0 }; + quint64 _handleAdjustAvatarSortingElapsedTime { 0 }; quint64 _handleViewFrustumPacketElapsedTime { 0 }; quint64 _handleAvatarIdentityPacketElapsedTime { 0 }; quint64 _handleKillAvatarPacketElapsedTime { 0 }; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 9dc73d9239..4cdf735e96 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -572,14 +572,29 @@ float AvatarManager::getAvatarSortCoefficient(const QString& name) { // HACK void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptValue& value) { + bool somethingChanged = false; if (value.isNumber()) { float numericalValue = (float)value.toNumber(); if (name == "size") { AvatarData::_avatarSortCoefficientSize = numericalValue; + somethingChanged = true; } else if (name == "center") { AvatarData::_avatarSortCoefficientCenter = numericalValue; + somethingChanged = true; } else if (name == "age") { AvatarData::_avatarSortCoefficientAge = numericalValue; + somethingChanged = true; } } + if (somethingChanged) { + size_t packetSize = sizeof(AvatarData::_avatarSortCoefficientSize) + + sizeof(AvatarData::_avatarSortCoefficientCenter) + + sizeof(AvatarData::_avatarSortCoefficientAge); + + auto packet = NLPacket::create(PacketType::AdjustAvatarSorting, packetSize); + packet->writePrimitive(AvatarData::_avatarSortCoefficientSize); + packet->writePrimitive(AvatarData::_avatarSortCoefficientCenter); + packet->writePrimitive(AvatarData::_avatarSortCoefficientAge); + DependencyManager::get()->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer); + } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 9d5bc507e0..daa496f6e1 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -605,9 +605,9 @@ public: // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. - static float _avatarSortCoefficientSize { 0.5f }; - static float _avatarSortCoefficientCenter { 0.25 }; - static float _avatarSortCoefficientAge { 1.0f }; + static float _avatarSortCoefficientSize; + static float _avatarSortCoefficientCenter; + static float _avatarSortCoefficientAge; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 050e3088f8..01973a6786 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -112,7 +112,8 @@ public: ReloadEntityServerScript, EntityPhysics, EntityServerScriptLog, - LAST_PACKET_TYPE = EntityServerScriptLog + AdjustAvatarSorting, + LAST_PACKET_TYPE = AdjustAvatarSorting }; }; From 618f37eb0622abfdbc5e540956535ef20121a9f7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 11:14:22 -0800 Subject: [PATCH 30/44] fix crash in recording playback when clip loader fails --- libraries/script-engine/src/RecordingScriptingInterface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index 710f342322..41bb780b47 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -56,6 +56,11 @@ bool RecordingScriptingInterface::loadRecording(const QString& url) { using namespace recording; auto loader = ClipCache::instance().getClipLoader(url); + if (!loader) { + qWarning() << "Clip failed to load from " << url; + return false; + } + if (!loader->isLoaded()) { QEventLoop loop; QObject::connect(loader.data(), &Resource::loaded, &loop, &QEventLoop::quit); From 6ac33dbf61382605d77b5a76b2d086a3e4636632 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 22 Feb 2017 17:23:36 -0800 Subject: [PATCH 31/44] count avatar joint updates and non-updates --- interface/resources/qml/Stats.qml | 4 +-- interface/src/avatar/Avatar.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 42 ++++++++++++++++++++------ interface/src/avatar/AvatarManager.h | 8 ++--- interface/src/ui/Stats.cpp | 4 +-- interface/src/ui/Stats.h | 8 ++--- 6 files changed, 47 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index faf37d5366..58d589b667 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -107,11 +107,11 @@ Item { } StatText { visible: root.expanded - text: "Fully Simulated Avatars: " + root.fullySimulatedAvatarCount + text: "Avatars Updated: " + root.updatedAvatarCount } StatText { visible: root.expanded - text: "Partially Simulated Avatars: " + root.partiallySimulatedAvatarCount + text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount } } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 53f0562bdd..ca4dbd2af8 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -183,6 +183,8 @@ public: Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const; + bool hasNewJointData() const { return _hasNewJointData; } + public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4cdf735e96..af9f915fec 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -195,8 +195,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET; uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET; - int fullySimulatedAvatars = 0; - int partiallySimulatedAvatars = 0; + int numAvatarsUpdated = 0; + int numAVatarsNotUpdated = 0; while (!sortedAvatars.empty()) { const AvatarPriority& sortData = sortedAvatars.top(); const auto& avatar = std::static_pointer_cast(sortData.avatar); @@ -217,33 +217,57 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } avatar->animateScaleChanges(deltaTime); + const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); if (now < renderExpiry) { // we're within budget - const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && avatar->hasNewJointData()) { + numAvatarsUpdated++; + } avatar->simulate(deltaTime, inView); avatar->updateRenderItem(pendingChanges); avatar->setLastRenderUpdateTime(startTime); - fullySimulatedAvatars++; } else if (now < maxExpiry) { // we've spent most of our time budget, but we still simulate() the avatar as it if were out of view // --> some avatars may freeze until their priority trickles up - const bool inView = false; - avatar->simulate(deltaTime, inView); - partiallySimulatedAvatars++; + bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && avatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + avatar->simulate(deltaTime, false); } else { // we've spent ALL of our time budget --> bail on the rest of the avatar updates + // --> more avatars may freeze until their priority trickles up // --> some scale or fade animations may glitch // --> some avatar velocity measurements may be a little off + + // HACK: no time simulate, but we will take the time to count how many were tragically missed + bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (!inView) { + break; + } + if (inView && avatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + sortedAvatars.pop(); + while (inView && !sortedAvatars.empty()) { + const AvatarPriority& newSortData = sortedAvatars.top(); + const auto& newAvatar = std::static_pointer_cast(newSortData.avatar); + inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && newAvatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + sortedAvatars.pop(); + } break; } sortedAvatars.pop(); } _avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC; - _fullySimulatedAvatars = fullySimulatedAvatars; - _partiallySimulatedAvatars = partiallySimulatedAvatars; + _numAvatarsUpdated = numAvatarsUpdated; + _numAvatarsNotUpdated = numAVatarsNotUpdated; qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges); simulateAvatarFades(deltaTime); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 8302bf4ed6..e1f5a3b411 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -43,8 +43,8 @@ public: std::shared_ptr getMyAvatar() { return _myAvatar; } AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const override; - int getFullySimulatedAvatars() const { return _fullySimulatedAvatars; } - int getPartiallySimulatedAvatars() const { return _partiallySimulatedAvatars; } + int getNumAvatarsUpdated() const { return _numAvatarsUpdated; } + int getNumAvatarsNotUpdated() const { return _numAvatarsNotUpdated; } float getAvatarSimulationTime() const { return _avatarSimulationTime; } void updateMyAvatar(float deltaTime); @@ -120,8 +120,8 @@ private: VectorOfMotionStates _motionStatesToRemoveFromPhysics; RateCounter<> _myAvatarSendRate; - int _fullySimulatedAvatars { 0 }; - int _partiallySimulatedAvatars { 0 }; + int _numAvatarsUpdated { 0 }; + int _numAvatarsNotUpdated { 0 }; float _avatarSimulationTime { 0.0f }; }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e82f99bed2..1075bbdaa4 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -121,8 +121,8 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); - STAT_UPDATE(fullySimulatedAvatarCount, avatarManager->getFullySimulatedAvatars()); - STAT_UPDATE(partiallySimulatedAvatarCount, avatarManager->getPartiallySimulatedAvatars()); + STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated()); + STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated()); STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE(framerate, qApp->getFps()); if (qApp->getActiveDisplayPlugin()) { diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f501f4b09a..6be084100c 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -49,8 +49,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarCount, 0) - STATS_PROPERTY(int, fullySimulatedAvatarCount, 0) - STATS_PROPERTY(int, partiallySimulatedAvatarCount, 0) + STATS_PROPERTY(int, updatedAvatarCount, 0) + STATS_PROPERTY(int, notUpdatedAvatarCount, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) STATS_PROPERTY(float, mbpsIn, 0) @@ -159,8 +159,8 @@ signals: void simrateChanged(); void avatarSimrateChanged(); void avatarCountChanged(); - void fullySimulatedAvatarCountChanged(); - void partiallySimulatedAvatarCountChanged(); + void updatedAvatarCountChanged(); + void notUpdatedAvatarCountChanged(); void packetInCountChanged(); void packetOutCountChanged(); void mbpsInChanged(); From 4c4506b1f7c12f23a3c3401c9b15cf1641806220 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 21:47:40 -0800 Subject: [PATCH 32/44] some cleanup --- assignment-client/src/avatars/AvatarMixer.cpp | 20 ++++---- .../src/avatars/AvatarMixerClientData.h | 10 ++-- .../src/avatars/AvatarMixerSlave.cpp | 46 ++----------------- 3 files changed, 21 insertions(+), 55 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 59896432e7..50363f62e3 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -309,7 +309,8 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { }, [&](const SharedNodePointer& node) { QMetaObject::invokeMethod(node->getLinkedData(), - "removeLastBroadcastSequenceNumber", + //"removeLastBroadcastSequenceNumber", + "cleanupKilledNode", Qt::AutoConnection, Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); } @@ -321,14 +322,17 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode) { auto start = usecTimestampNow(); - message->readPrimitive(&AvatarData::_avatarSortCoefficientSize); - message->readPrimitive(&AvatarData::_avatarSortCoefficientCenter); - message->readPrimitive(&AvatarData::_avatarSortCoefficientAge); + // only allow admins with kick rights to change this value... + if (senderNode->getCanKick()) { + message->readPrimitive(&AvatarData::_avatarSortCoefficientSize); + message->readPrimitive(&AvatarData::_avatarSortCoefficientCenter); + message->readPrimitive(&AvatarData::_avatarSortCoefficientAge); - qCDebug(avatars) << "New avatar sorting... " - << "size:" << AvatarData::_avatarSortCoefficientSize - << "center:" << AvatarData::_avatarSortCoefficientCenter - << "age:" << AvatarData::_avatarSortCoefficientAge; + qCDebug(avatars) << "New avatar sorting... " + << "size:" << AvatarData::_avatarSortCoefficientSize + << "center:" << AvatarData::_avatarSortCoefficientCenter + << "age:" << AvatarData::_avatarSortCoefficientAge; + } auto end = usecTimestampNow(); _handleAdjustAvatarSortingElapsedTime += (end - start); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 7eea31c9ab..9cf73a94f4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -53,11 +53,14 @@ public: Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } uint64_t getLastBroadcastTime(const QUuid& nodeUUID) const; - void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { - _lastBroadcastTimes[nodeUUID] = broadcastTime; - } + void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { _lastBroadcastTimes[nodeUUID] = broadcastTime; } Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } + Q_INVOKABLE void cleanupKilledNode(const QUuid& nodeUUID) { + removeLastBroadcastSequenceNumber(nodeUUID); + removeLastBroadcastTime(nodeUUID); + } + uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } @@ -115,7 +118,6 @@ public: ViewFrustum getViewFrustom() const { return _currentViewFrustum; } - quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 result = 0; if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) { diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 360c6e26fd..5ad4561aa9 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -191,7 +191,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { QList avatarList; std::unordered_map avatarDataToNodes; - //qDebug() << "------------------------------"; int listItem = 0; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); @@ -200,36 +199,14 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); avatarList << otherAvatar; avatarDataToNodes[otherAvatar] = otherNode; - /* - qDebug() << "listItem [" << listItem << "] " - << "otherNode:" << otherNode.data() - << "otherNode->getUUID():" << otherNode->getUUID() - << "otherNodeData:" << otherNodeData - << "otherAvatar:" << otherAvatar.get(); - - qDebug() << "avatarDataToNodes[" << otherAvatar.get() << "]=" << otherNode.data(); - */ } }); - /* - qDebug() << "avatarList.size:" << avatarList.size(); - qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); - */ AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); //qDebug() << "thisAvatar:" << thisAvatar.get(); -#ifdef WANT_DEBUG - bool printDebug = nodeData->getAvatarSharedPointer()->getDisplayName() == "ZappoMan"; - - if (printDebug) { - qDebug() << "------------------------------"; - } -#else - bool printDebug = false; -#endif ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, @@ -239,18 +216,12 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { if (avatarNode) { return nodeData->getLastBroadcastTime(avatarNode->getUUID()); } - return 0; // ??? + return 0; }, [thisAvatar](AvatarSharedPointer avatar)->bool{ return (avatar == thisAvatar); // ignore ourselves... - }, printDebug); - -#ifdef WANT_DEBUG - if (printDebug) { - qDebug() << "------------------------------"; - } -#endif + }); // loop through our sorted avatars and allocate our bandwidth to them accordingly int avatarRank = 0; @@ -262,13 +233,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { auto otherNode = avatarDataToNodes[avatarData]; - //qDebug() << "otherNode (" << otherNode.data() << ")= avatarDataToNodes[" << avatarData.get() << "]"; - if (!otherNode) { - //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << ":" << avatarData.get() << "... otherNode unknown!!"; continue; } - //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << "] avatarData: " << avatarData.get() << " otherNode:" << otherNode->getUUID() << " ... "; bool shouldConsider = false; quint64 startIgnoreCalculation = usecTimestampNow(); @@ -282,8 +249,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; - } else { const AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); @@ -318,8 +283,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(node, otherNode); shouldConsider = getsAnyIgnored; - //qDebug() << "shouldConsider = getsAnyIgnored " << getsAnyIgnored << " ... line:" << __LINE__; - } } // Not close enough to ignore @@ -397,9 +360,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; - } - else if (lastSeqFromSender - lastSeqToReceiver > 1) { + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; } @@ -418,7 +379,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; } if (shouldConsider) { From edf7c016a12cd51ac5892d495a518142c44233e7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 22:06:28 -0800 Subject: [PATCH 33/44] more cleanup --- assignment-client/src/avatars/AvatarMixer.cpp | 1 - .../src/avatars/AvatarMixerSlave.cpp | 6 ++---- libraries/avatars/src/AvatarData.cpp | 17 +++-------------- libraries/avatars/src/AvatarData.h | 7 +------ 4 files changed, 6 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 50363f62e3..a347b5ef47 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -309,7 +309,6 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { }, [&](const SharedNodePointer& node) { QMetaObject::invokeMethod(node->getLinkedData(), - //"removeLastBroadcastSequenceNumber", "cleanupKilledNode", Qt::AutoConnection, Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 5ad4561aa9..9b501a5e31 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -187,7 +187,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - + // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes + // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes QList avatarList; std::unordered_map avatarDataToNodes; @@ -204,9 +205,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { }); AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); - - //qDebug() << "thisAvatar:" << thisAvatar.get(); - ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 7c1b1baf26..e2574683a2 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -69,8 +69,7 @@ AvatarData::AvatarData() : _displayNameAlpha(1.0f), _errorLogExpiry(0), _owningAvatarMixer(), - _targetVelocity(0.0f), - _localAABox(DEFAULT_LOCAL_AABOX_CORNER, DEFAULT_LOCAL_AABOX_SCALE) + _targetVelocity(0.0f) { setBodyPitch(0.0f); setBodyYaw(-90.0f); @@ -2321,8 +2320,7 @@ std::priority_queue AvatarData::sortAvatars( QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore, - bool printDebug) { + std::function shouldIgnore) { uint64_t startTime = usecTimestampNow(); @@ -2335,8 +2333,6 @@ std::priority_queue AvatarData::sortAvatars( const auto& avatar = avatarList.at(i); bool outOfView = false; - // FIXME - probably some lambda that allows the caller to reject some avatars - if (shouldIgnore(avatar)) { continue; } @@ -2352,13 +2348,10 @@ std::priority_queue AvatarData::sortAvatars( // FIXME - AvatarData has something equivolent to this float radius = 1.0f; // avatar->getBoundingRadius(); - const glm::vec3& forward = cameraView.getDirection(); float apparentSize = 2.0f * radius / distance; float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance; - - uint64_t lastUpdatedTime = lastUpdated(avatar); // avatar->getLastRenderUpdateTime() - float age = (float)(startTime - lastUpdatedTime) / (float)(USECS_PER_SECOND); + float age = (float)(startTime - lastUpdated(avatar)) / (float)(USECS_PER_SECOND); // NOTE: we are adding values of different units to get a single measure of "priority". // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. @@ -2375,10 +2368,6 @@ std::priority_queue AvatarData::sortAvatars( outOfView = true; } } - - if (printDebug) { - qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age << "outOfView:" << outOfView; - } sortedAvatars.push(AvatarPriority(avatar, priority)); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index daa496f6e1..8608137b20 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -550,8 +550,6 @@ public: void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } - //const AABox& getLocalAABox() const { return _localAABox; } - int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); } int getAverageBytesReceivedPerSecond() const; int getReceiveRate() const; @@ -600,8 +598,7 @@ public: QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore, - bool printDebug = false); + std::function shouldIgnore); // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. @@ -692,8 +689,6 @@ protected: glm::vec3 _targetVelocity; - AABox _localAABox; - SimpleMovingAverage _averageBytesReceived; // During recording, this holds the starting position, orientation & scale of the recorded avatar From 06f0087459a56431f40cd6ec72f7501303b9c36d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 22:36:37 -0800 Subject: [PATCH 34/44] wire up radius properly --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 5 +++++ interface/src/avatar/AvatarManager.cpp | 4 ++++ libraries/avatars/src/AvatarData.cpp | 7 ++++--- libraries/avatars/src/AvatarData.h | 3 ++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 9b501a5e31..0004afa97c 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -217,6 +217,11 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { return 0; }, + [&](AvatarSharedPointer avatar)->float{ + glm::vec3 nodeBoxHalfScale = (avatar->getPosition() - avatar->getGlobalBoundingBoxCorner()); + return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); + }, + [thisAvatar](AvatarSharedPointer avatar)->bool{ return (avatar == thisAvatar); // ignore ourselves... }); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index af9f915fec..b1a09b46ce 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -171,6 +171,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); }, + [](AvatarSharedPointer avatar)->float{ + return std::static_pointer_cast(avatar)->getBoundingRadius(); + }, + [this](AvatarSharedPointer avatar)->bool{ const auto& castedAvatar = std::static_pointer_cast(avatar); if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e2574683a2..0c145a6a30 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2319,7 +2319,8 @@ float AvatarData::_avatarSortCoefficientAge { 1.0f }; std::priority_queue AvatarData::sortAvatars( QList avatarList, const ViewFrustum& cameraView, - std::function lastUpdated, + std::function getLastUpdated, + std::function getBoundingRadius, std::function shouldIgnore) { uint64_t startTime = usecTimestampNow(); @@ -2346,12 +2347,12 @@ std::priority_queue AvatarData::sortAvatars( float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero // FIXME - AvatarData has something equivolent to this - float radius = 1.0f; // avatar->getBoundingRadius(); + float radius = getBoundingRadius(avatar); const glm::vec3& forward = cameraView.getDirection(); float apparentSize = 2.0f * radius / distance; float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance; - float age = (float)(startTime - lastUpdated(avatar)) / (float)(USECS_PER_SECOND); + float age = (float)(startTime - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); // NOTE: we are adding values of different units to get a single measure of "priority". // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8608137b20..0eddc29cda 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -597,7 +597,8 @@ public: static std::priority_queue sortAvatars( QList avatarList, const ViewFrustum& cameraView, - std::function lastUpdated, + std::function getLastUpdated, + std::function getBoundingRadius, std::function shouldIgnore); // TODO: remove this HACK once we settle on optimal sort coefficients From 40037bee55d063ae1f81c3811e9c7623370f37c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 23:28:40 -0800 Subject: [PATCH 35/44] added some stats for the random drops --- assignment-client/src/avatars/AvatarMixer.cpp | 8 +++++++- assignment-client/src/avatars/AvatarMixerSlave.cpp | 10 +++++++--- assignment-client/src/avatars/AvatarMixerSlave.h | 6 ++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a347b5ef47..a90fdb100c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -506,6 +506,9 @@ void AvatarMixer::sendStatsPacket() { float averageOthersIncluded = averageNodes ? stats.numOthersIncluded / averageNodes : 0.0f; slaveObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); + float averageRandomDrops = averageNodes ? stats.randomDrops / averageNodes : 0.0f; + slaveObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); + slaveObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(stats.processIncomingPacketsElapsedTime); slaveObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(stats.ignoreCalculationElapsedTime); slaveObject["timing_3_toByteArray"] = TIGHT_LOOP_STAT_UINT64(stats.toByteArrayElapsedTime); @@ -533,9 +536,12 @@ void AvatarMixer::sendStatsPacket() { float averageOutboundAvatarKbps = averageNodes ? ((aggregateStats.numBytesSent / secondsSinceLastStats) / BYTES_PER_KILOBIT) / averageNodes : 0.0f; slavesAggregatObject["sent_5_averageOutboundAvatarKbps"] = averageOutboundAvatarKbps; + float averageRandomDrops = averageNodes ? aggregateStats.randomDrops / averageNodes : 0.0f; + slavesAggregatObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); + float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); - + slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime); slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime); slavesAggregatObject["timing_3_toByteArray"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.toByteArrayElapsedTime); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 0004afa97c..5073aab934 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -332,15 +332,19 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // // NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that // are out of view, this also appears to disable this random distribution. + // + // FIXME - This approach for managing the outbound bandwidth is less than ideal, + // it would be better to more directly budget the number of bytes to send + // per frame and simply exit the sorted avatar list once that budget is + // surpassed. We will work on that next. [BHG 2/22/17] if (distanceToAvatar != 0.0f && !getsOutOfView - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... we don't want to do this random stuff + && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) ) { - quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; + _stats.randomDrops++; } if (shouldConsider) { diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index b1a5b56c4f..50ae4570b5 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -25,6 +25,8 @@ public: int numBytesSent { 0 }; int numIdentityPackets { 0 }; int numOthersIncluded { 0 }; + int randomDrops { 0 }; + quint64 ignoreCalculationElapsedTime { 0 }; quint64 avatarDataPackingElapsedTime { 0 }; quint64 packetSendingElapsedTime { 0 }; @@ -43,6 +45,8 @@ public: numBytesSent = 0; numIdentityPackets = 0; numOthersIncluded = 0; + randomDrops = 0; + ignoreCalculationElapsedTime = 0; avatarDataPackingElapsedTime = 0; packetSendingElapsedTime = 0; @@ -60,6 +64,8 @@ public: numBytesSent += rhs.numBytesSent; numIdentityPackets += rhs.numIdentityPackets; numOthersIncluded += rhs.numOthersIncluded; + randomDrops += rhs.randomDrops; + ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime; avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime; packetSendingElapsedTime += rhs.packetSendingElapsedTime; From d4adee8b388d6c2cb482f8e0792316892594a68c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 00:15:07 -0800 Subject: [PATCH 36/44] add over bandwidth exit to sorted avatar loop --- assignment-client/src/avatars/AvatarMixer.cpp | 10 ++++++-- .../src/avatars/AvatarMixerSlave.cpp | 23 +++++++++++++++---- .../src/avatars/AvatarMixerSlave.h | 3 +++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a90fdb100c..6db9d05234 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -508,6 +508,9 @@ void AvatarMixer::sendStatsPacket() { float averageRandomDrops = averageNodes ? stats.randomDrops / averageNodes : 0.0f; slaveObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); + + float averageOverBudgetAvatars = averageNodes ? stats.overBudgetAvatars / averageNodes : 0.0f; + slaveObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slaveObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(stats.processIncomingPacketsElapsedTime); slaveObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(stats.ignoreCalculationElapsedTime); @@ -536,11 +539,14 @@ void AvatarMixer::sendStatsPacket() { float averageOutboundAvatarKbps = averageNodes ? ((aggregateStats.numBytesSent / secondsSinceLastStats) / BYTES_PER_KILOBIT) / averageNodes : 0.0f; slavesAggregatObject["sent_5_averageOutboundAvatarKbps"] = averageOutboundAvatarKbps; + float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; + slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); + float averageRandomDrops = averageNodes ? aggregateStats.randomDrops / averageNodes : 0.0f; slavesAggregatObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); - float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; - slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); + float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f; + slavesAggregatObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime); slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 5073aab934..21eaebc176 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -129,6 +129,11 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; + // max number of avatarBytes per frame + auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; + + int overBudgetAvatars = 0; + // keep track of the number of other avatars held back in this frame int numAvatarsHeldBack = 0; @@ -333,10 +338,10 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that // are out of view, this also appears to disable this random distribution. // - // FIXME - This approach for managing the outbound bandwidth is less than ideal, - // it would be better to more directly budget the number of bytes to send - // per frame and simply exit the sorted avatar list once that budget is - // surpassed. We will work on that next. [BHG 2/22/17] + // FIXME - This is the old approach for managing the outbound bandwidth. I've left it + // in for now, even though we're also directly managing budget by calculating the + // number of bytes to send per frame and simply exiting the sorted avatar list + // once that budget is surpassed. I need to remove this old logic next. [BHG 2/22/17] if (distanceToAvatar != 0.0f && !getsOutOfView && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) @@ -372,6 +377,16 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { ++numAvatarsWithSkippedFrames; } + // NOTE: Here's where we determine if we are over budget and stop considering avatars after this. + // + // FIXME - revisit this code, remove the random drop logic, and move this code higher up into + // the loop so we don't bother with all these other calculations once over budget. [BHG 2/22/17] + if (numAvatarDataBytes > maxAvatarBytesPerFrame) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + shouldConsider = false; + } + // we're going to send this avatar if (shouldConsider) { diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 50ae4570b5..1bbd367b2b 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -26,6 +26,7 @@ public: int numIdentityPackets { 0 }; int numOthersIncluded { 0 }; int randomDrops { 0 }; + int overBudgetAvatars { 0 }; quint64 ignoreCalculationElapsedTime { 0 }; quint64 avatarDataPackingElapsedTime { 0 }; @@ -46,6 +47,7 @@ public: numIdentityPackets = 0; numOthersIncluded = 0; randomDrops = 0; + overBudgetAvatars = 0; ignoreCalculationElapsedTime = 0; avatarDataPackingElapsedTime = 0; @@ -65,6 +67,7 @@ public: numIdentityPackets += rhs.numIdentityPackets; numOthersIncluded += rhs.numOthersIncluded; randomDrops += rhs.randomDrops; + overBudgetAvatars += rhs.overBudgetAvatars; ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime; avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime; From 4f17498daf5c22d1e67e302d6b77ae248a5d8e49 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 00:19:06 -0800 Subject: [PATCH 37/44] fix warning --- libraries/avatars/src/AvatarData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 0c145a6a30..4993c0ac5d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2332,7 +2332,6 @@ std::priority_queue AvatarData::sortAvatars( PROFILE_RANGE(simulation, "sort"); for (int32_t i = 0; i < avatarList.size(); ++i) { const auto& avatar = avatarList.at(i); - bool outOfView = false; if (shouldIgnore(avatar)) { continue; @@ -2366,7 +2365,6 @@ std::priority_queue AvatarData::sortAvatars( if (distance > cameraView.getCenterRadius()) { if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { priority += OUT_OF_VIEW_PENALTY; - outOfView = true; } } sortedAvatars.push(AvatarPriority(avatar, priority)); From 5033e7be11bfd59654416b92662bd0774a56af8b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 23 Feb 2017 18:22:36 +0100 Subject: [PATCH 38/44] CR fixes --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/audio-client/src/AudioClient.cpp | 46 +++++++++++----------- libraries/audio-client/src/AudioClient.h | 10 ++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b9f673287b..19d403a5c5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -160,7 +160,7 @@ MyAvatar::MyAvatar(RigPointer rig) : } auto audioIO = DependencyManager::get(); - audioIO->setPlayingBackRecording(isPlaying); + audioIO->setIsPlayingBackRecording(isPlaying); if (_rig) { _rig->setEnableAnimations(!isPlaying); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 0e2991ae30..0e06cf8661 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -148,17 +148,17 @@ static inline float convertToFloat(int16_t sample) { AudioClient::AudioClient() : AbstractAudioInterface(), _gate(this), - _audioInput(nullptr), + _audioInput(NULL), _desiredInputFormat(), _inputFormat(), _numInputCallbackBytes(0), - _audioOutput(nullptr), + _audioOutput(NULL), _desiredOutputFormat(), _outputFormat(), _outputFrameSize(0), _numOutputCallbackBytes(0), - _loopbackAudioOutput(nullptr), - _loopbackOutputDevice(nullptr), + _loopbackAudioOutput(NULL), + _loopbackOutputDevice(NULL), _inputRingBuffer(0), _localInjectorsStream(0), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), @@ -176,9 +176,9 @@ AudioClient::AudioClient() : _isNoiseGateEnabled(true), _reverb(false), _reverbOptions(&_scriptReverbOptions), - _inputToNetworkResampler(nullptr), - _networkToOutputResampler(nullptr), - _localToOutputResampler(nullptr), + _inputToNetworkResampler(NULL), + _networkToOutputResampler(NULL), + _localToOutputResampler(NULL), _localAudioThread(this), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), @@ -393,9 +393,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } } else { HRESULT hr = S_OK; - CoInitialize(nullptr); - IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; - CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoInitialize(NULL); + IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint); if (hr == E_NOTFOUND) { @@ -405,7 +405,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { deviceName = friendlyNameForAudioDevice(pEndpoint); } pMMDeviceEnumerator->Release(); - pMMDeviceEnumerator = nullptr; + pMMDeviceEnumerator = NULL; CoUninitialize(); } @@ -965,7 +965,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - if (!_inputDevice || _playingBackRecording) { + if (!_inputDevice || _isPlayingBackRecording) { return; } @@ -1354,10 +1354,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn // That in turn causes it to be disconnected (see for example // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). _audioInput->stop(); - _inputDevice = nullptr; + _inputDevice = NULL; delete _audioInput; - _audioInput = nullptr; + _audioInput = NULL; _numInputCallbackBytes = 0; _inputAudioDeviceName = ""; @@ -1366,7 +1366,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (_inputToNetworkResampler) { // if we were using an input to network resampler, delete it here delete _inputToNetworkResampler; - _inputToNetworkResampler = nullptr; + _inputToNetworkResampler = NULL; } if (!inputDeviceInfo.isNull()) { @@ -1461,29 +1461,29 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutput->stop(); delete _audioOutput; - _audioOutput = nullptr; + _audioOutput = NULL; - _loopbackOutputDevice = nullptr; + _loopbackOutputDevice = NULL; delete _loopbackAudioOutput; - _loopbackAudioOutput = nullptr; + _loopbackAudioOutput = NULL; delete[] _outputMixBuffer; - _outputMixBuffer = nullptr; + _outputMixBuffer = NULL; delete[] _outputScratchBuffer; - _outputScratchBuffer = nullptr; + _outputScratchBuffer = NULL; delete[] _localOutputMixBuffer; - _localOutputMixBuffer = nullptr; + _localOutputMixBuffer = NULL; } if (_networkToOutputResampler) { // if we were using an input to network resampler, delete it here delete _networkToOutputResampler; - _networkToOutputResampler = nullptr; + _networkToOutputResampler = NULL; delete _localToOutputResampler; - _localToOutputResampler = nullptr; + _localToOutputResampler = NULL; } if (!outputDeviceInfo.isNull()) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 739c4b459c..072990e0c5 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -145,7 +145,7 @@ public: void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } - void setPlayingBackRecording(bool playingBackRecording) { _playingBackRecording = playingBackRecording; } + void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; } Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); @@ -328,14 +328,14 @@ private: // for local audio (used by audio injectors thread) float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; - float* _localOutputMixBuffer { nullptr }; + float* _localOutputMixBuffer { NULL }; AudioInjectorsThread _localAudioThread; Mutex _localAudioMutex; // for output audio (used by this thread) int _outputPeriod { 0 }; - float* _outputMixBuffer { nullptr }; - int16_t* _outputScratchBuffer { nullptr }; + float* _outputMixBuffer { NULL }; + int16_t* _outputScratchBuffer { NULL }; AudioLimiter _audioLimiter; @@ -373,7 +373,7 @@ private: QVector _activeLocalAudioInjectors; - bool _playingBackRecording { false }; + bool _isPlayingBackRecording { false }; CodecPluginPointer _codec; QString _selectedCodecName; From dde9640c66a9be73e8c65c8d87a3b12d22f6a787 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 10:29:17 -0800 Subject: [PATCH 39/44] remove old full rate distance code --- assignment-client/src/avatars/AvatarMixer.cpp | 10 +- .../src/avatars/AvatarMixerClientData.cpp | 2 - .../src/avatars/AvatarMixerClientData.h | 9 -- .../src/avatars/AvatarMixerSlave.cpp | 131 ++++++------------ .../src/avatars/AvatarMixerSlave.h | 5 +- libraries/avatars/src/AvatarData.cpp | 7 + libraries/avatars/src/AvatarData.h | 2 + 7 files changed, 53 insertions(+), 113 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 6db9d05234..0f6863f9ae 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -506,11 +506,8 @@ void AvatarMixer::sendStatsPacket() { float averageOthersIncluded = averageNodes ? stats.numOthersIncluded / averageNodes : 0.0f; slaveObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); - float averageRandomDrops = averageNodes ? stats.randomDrops / averageNodes : 0.0f; - slaveObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); - float averageOverBudgetAvatars = averageNodes ? stats.overBudgetAvatars / averageNodes : 0.0f; - slaveObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); + slaveObject["sent_7_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slaveObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(stats.processIncomingPacketsElapsedTime); slaveObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(stats.ignoreCalculationElapsedTime); @@ -542,11 +539,8 @@ void AvatarMixer::sendStatsPacket() { float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); - float averageRandomDrops = averageNodes ? aggregateStats.randomDrops / averageNodes : 0.0f; - slavesAggregatObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); - float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f; - slavesAggregatObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); + slavesAggregatObject["sent_7_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime); slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 1df6c24a60..717e14535f 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -126,8 +126,6 @@ bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar->getDisplayName(); - jsonObject["full_rate_distance"] = _fullRateDistance; - jsonObject["max_av_distance"] = _maxAvatarDistance; jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame; jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 9cf73a94f4..51b8d692e2 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -68,12 +68,6 @@ public: bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } - void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } - float getFullRateDistance() const { return _fullRateDistance; } - - void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; } - float getMaxAvatarDistance() const { return _maxAvatarDistance; } - void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; } @@ -156,9 +150,6 @@ private: HRCTime _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ false }; - float _fullRateDistance = FLT_MAX; - float _maxAvatarDistance = FLT_MAX; - int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 21eaebc176..1f9fbde89a 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -66,13 +66,16 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { } -void AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { +int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { + int bytesSent = 0; QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); - individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); + individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious + bytesSent += individualData.size(); identityPacket->write(individualData); DependencyManager::get()->sendPacket(std::move(identityPacket), *destinationNode); _stats.numIdentityPackets++; + return bytesSent; } static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; @@ -117,9 +120,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // reset the internal state for correct random number distribution distribution.reset(); - // reset the max distance for this frame - float maxAvatarDistanceThisFrame = 0.0f; - // reset the number of sent avatars nodeData->resetNumAvatarsSentLastFrame(); @@ -128,10 +128,14 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; + int identityBytesSent = 0; // max number of avatarBytes per frame auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; + // FIXME - find a way to not send the sessionID for every avatar + int minimumBytesPerAvatar = AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID; + int overBudgetAvatars = 0; // keep track of the number of other avatars held back in this frame @@ -140,9 +144,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // keep track of the number of other avatar frames skipped int numAvatarsWithSkippedFrames = 0; - // use the data rate specifically for avatar data for FRD adjustment checks - float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); - // When this is true, the AvatarMixer will send Avatar data to a client about avatars that are not in the view frustrum bool getsOutOfView = nodeData->getRequestsDomainListData(); @@ -152,43 +153,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them bool getsAnyIgnored = getsIgnoredByMe && node->getCanKick(); - // Check if it is time to adjust what we send this client based on the observed - // bandwidth to this node. We do this once a second, which is also the window for - // the bandwidth reported by node->getOutboundBandwidth(); - if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) { - - const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f; - const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO); - const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f)); - - // get the current full rate distance so we can work with it - float currentFullRateDistance = nodeData->getFullRateDistance(); - - if (avatarDataRateLastSecond > _maxKbpsPerNode) { - - // is the FRD greater than the farthest avatar? - // if so, before we calculate anything, set it to that distance - currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance()); - - // we're adjusting the full rate distance to target a bandwidth in the middle - // of the hysterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - - nodeData->setFullRateDistance(currentFullRateDistance); - nodeData->resetNumFramesSinceFRDAdjustment(); - } else if (currentFullRateDistance < nodeData->getMaxAvatarDistance() - && avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) { - // we are constrained AND we've recovered to below the acceptable ratio - // lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - - nodeData->setFullRateDistance(currentFullRateDistance); - nodeData->resetNumFramesSinceFRDAdjustment(); - } - } else { - nodeData->incrementNumFramesSinceFRDAdjustment(); - } - // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); @@ -233,18 +197,39 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // loop through our sorted avatars and allocate our bandwidth to them accordingly int avatarRank = 0; + + // this is overly conservative, because it includes some avatars we might not consider + // FIXME - move the ignore logic up into the sorting list so we get a better estimate + int remainingAvatars = (int)sortedAvatars.size(); + while (!sortedAvatars.empty()) { AvatarPriority sortData = sortedAvatars.top(); sortedAvatars.pop(); const auto& avatarData = sortData.avatar; avatarRank++; + remainingAvatars--; auto otherNode = avatarDataToNodes[avatarData]; + // FIXME - should't this be an assert? if (!otherNode) { continue; } + // NOTE: Here's where we determine if we are over budget and drop to bare minimum data + int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; + bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; + /** + qDebug() << "Budget debugging:" + << "numAvatarDataBytes:" << numAvatarDataBytes << "\n" + << "identityBytesSent:" << identityBytesSent << "\n" + << "remainingAvatars:" << remainingAvatars << "\n" + << "minimumBytesPerAvatar:" << minimumBytesPerAvatar << "\n" + << "minimRemainingAvatarBytes:" << minimRemainingAvatarBytes << "\n" + << "overBudget:" << overBudget << "\n" + << "overBudgetAvatars:" << overBudgetAvatars; + **/ + bool shouldConsider = false; quint64 startIgnoreCalculation = usecTimestampNow(); @@ -313,45 +298,21 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID()); // FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..." - if (otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 + if (!overBudget + && otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - sendIdentityPacket(otherNodeData, node); + identityBytesSent += sendIdentityPacket(otherNodeData, node); } const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); // Decide whether to send this avatar's data based on it's distance from us - // The full rate distance is the distance at which EVERY update will be sent for this avatar - // at twice the full rate distance, there will be a 50% chance of sending this avatar's update glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); float distanceToAvatar = glm::length(myPosition - otherPosition); - // potentially update the max full rate distance for this frame - maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - - // This code handles the random dropping of avatar data based on the ratio of - // "getFullRateDistance" to actual distance. - // - // NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that - // are out of view, this also appears to disable this random distribution. - // - // FIXME - This is the old approach for managing the outbound bandwidth. I've left it - // in for now, even though we're also directly managing budget by calculating the - // number of bytes to send per frame and simply exiting the sorted avatar list - // once that budget is surpassed. I need to remove this old logic next. [BHG 2/22/17] - if (distanceToAvatar != 0.0f - && !getsOutOfView - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) - ) { - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - shouldConsider = false; - _stats.randomDrops++; - } - if (shouldConsider) { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); @@ -377,16 +338,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { ++numAvatarsWithSkippedFrames; } - // NOTE: Here's where we determine if we are over budget and stop considering avatars after this. - // - // FIXME - revisit this code, remove the random drop logic, and move this code higher up into - // the loop so we don't bother with all these other calculations once over budget. [BHG 2/22/17] - if (numAvatarDataBytes > maxAvatarBytesPerFrame) { - overBudgetAvatars++; - _stats.overBudgetAvatars++; - shouldConsider = false; - } - // we're going to send this avatar if (shouldConsider) { @@ -396,19 +347,26 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { bool isInView = nodeData->otherAvatarInView(otherNodeBox); // this throttles the extra data to only be sent every Nth message + /** if (!isInView && !getsOutOfView && (lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0)) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; } + ***/ if (shouldConsider) { // start a new segment in the PacketList for this avatar avatarPacketList->startSegment(); AvatarData::AvatarDataDetail detail; - if (!isInView && !getsOutOfView) { + + if (overBudget) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + detail = AvatarData::NoData; + } else if (!isInView && !getsOutOfView) { detail = AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); } else { @@ -498,13 +456,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); - if (numOtherAvatars == 0) { - // update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send - nodeData->setMaxAvatarDistance(FLT_MAX); - } else { - nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); - } - quint64 endPacketSending = usecTimestampNow(); _stats.packetSendingElapsedTime += (endPacketSending - startPacketSending); } diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 1bbd367b2b..04141d9d72 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -25,7 +25,6 @@ public: int numBytesSent { 0 }; int numIdentityPackets { 0 }; int numOthersIncluded { 0 }; - int randomDrops { 0 }; int overBudgetAvatars { 0 }; quint64 ignoreCalculationElapsedTime { 0 }; @@ -46,7 +45,6 @@ public: numBytesSent = 0; numIdentityPackets = 0; numOthersIncluded = 0; - randomDrops = 0; overBudgetAvatars = 0; ignoreCalculationElapsedTime = 0; @@ -66,7 +64,6 @@ public: numBytesSent += rhs.numBytesSent; numIdentityPackets += rhs.numIdentityPackets; numOthersIncluded += rhs.numOthersIncluded; - randomDrops += rhs.randomDrops; overBudgetAvatars += rhs.overBudgetAvatars; ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime; @@ -94,7 +91,7 @@ public: void harvestStats(AvatarMixerSlaveStats& stats); private: - void sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); + int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); // frame state ConstIter _begin; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4993c0ac5d..6a01c2930c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -192,6 +192,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); unsigned char* startPosition = destinationBuffer; + // special case, if we were asked for no data, then just include the flags all set to nothing + if (dataDetail == NoData) { + AvatarDataPacket::HasFlags packetStateFlags = 0; + memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); + return avatarDataByteArray.left(sizeof(packetStateFlags)); + } + // FIXME - // // BUG -- if you enter a space bubble, and then back away, the avatar has wrong orientation until "send all" happens... diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0eddc29cda..3797132274 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -136,6 +136,7 @@ namespace AvatarDataPacket { const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const size_t AVATAR_HAS_FLAGS_SIZE = 2; // NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure. @@ -373,6 +374,7 @@ public: void setHandPosition(const glm::vec3& handPosition); typedef enum { + NoData, MinimumData, CullSmallData, IncludeSmallData, From 99e51e88ae3e8265611428efb9de1fba4ce26aed Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Fri, 3 Feb 2017 15:13:11 -0800 Subject: [PATCH 40/44] Add QMetaObject for AvatarEntityMap --- libraries/avatars/src/AvatarData.cpp | 19 +++++++++++++++++++ libraries/avatars/src/AvatarData.h | 6 ++++++ libraries/script-engine/src/ScriptEngine.cpp | 1 + 3 files changed, 26 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2263964e8f..bbd3e05994 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2309,3 +2309,22 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra vec3FromScriptValue(intersection, value.intersection); } } + +QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { + QScriptValue obj = engine->newObject(); + for (auto entityID : value.keys()) { + QScriptValue EntityProperties = engine->newVariant(QVariant::fromValue(value.value(entityID))); + QString key = entityID.toString(); + obj.setProperty(key, EntityProperties); + } + return obj; +} + +void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value) { + QScriptValueIterator itr(object); + while (itr.hasNext()) { + itr.next(); + QUuid EntityID = QUuid(itr.name()); + value[EntityID] = qvariant_cast(itr.value().data().toVariant()); + } +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 264da75de2..502ae06425 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -44,6 +44,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include #include @@ -816,6 +817,11 @@ Q_DECLARE_METATYPE(RayToAvatarIntersectionResult) QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results); void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results); +Q_DECLARE_METATYPE(AvatarEntityMap) + +QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value); +void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value); + // faux joint indexes (-1 means invalid) const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2 const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3 diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f1ff4c4686..83f2f5ccc0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -576,6 +576,7 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); qScriptRegisterMetaType(this, RayToAvatarIntersectionResultToScriptValue, RayToAvatarIntersectionResultFromScriptValue); + qScriptRegisterMetaType(this, AvatarEntityMapToScriptValue, AvatarEntityMapFromScriptValue); qScriptRegisterSequenceMetaType>(this); qScriptRegisterSequenceMetaType>(this); From 3af05ece7de2b59e5f4540f33606957a1940cf80 Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Tue, 7 Feb 2017 13:28:09 -0800 Subject: [PATCH 41/44] Made QScript Object more JS friendly --- libraries/avatars/src/AvatarData.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bbd3e05994..4ea3d8bb3e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -2313,9 +2314,18 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue obj = engine->newObject(); for (auto entityID : value.keys()) { - QScriptValue EntityProperties = engine->newVariant(QVariant::fromValue(value.value(entityID))); + QByteArray entityProperties = value.value(entityID); + QJsonDocument jsonEntityProperties = QJsonDocument::fromBinaryData(entityProperties); + if (!jsonEntityProperties.isObject()) { + qCDebug(avatars) << "bad AvatarEntityData in AvatarEntityMap" << QString(entityProperties.toHex()); + } + + QVariant variantEntityProperties = jsonEntityProperties.toVariant(); + QVariantMap entityPropertiesMap = variantEntityProperties.toMap(); + QScriptValue scriptEntityProperties = variantMapToScriptValue(entityPropertiesMap, *engine); + QString key = entityID.toString(); - obj.setProperty(key, EntityProperties); + obj.setProperty(key, scriptEntityProperties); } return obj; } @@ -2325,6 +2335,12 @@ void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& while (itr.hasNext()) { itr.next(); QUuid EntityID = QUuid(itr.name()); - value[EntityID] = qvariant_cast(itr.value().data().toVariant()); + + QScriptValue scriptEntityProperties = itr.value(); + QVariant variantEntityProperties = scriptEntityProperties.toVariant(); + QJsonDocument jsonEntityProperties = QJsonDocument::fromVariant(variantEntityProperties); + QByteArray binaryEntityProperties = jsonEntityProperties.toBinaryData(); + + value[EntityID] = binaryEntityProperties; } } From f61e16fccfe901116ffd75e56608c0be4ac7a6b5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 11:01:55 -0800 Subject: [PATCH 42/44] more aggressive out of view data culling --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 1f9fbde89a..dfc91b90c1 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -367,7 +367,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.overBudgetAvatars++; detail = AvatarData::NoData; } else if (!isInView && !getsOutOfView) { - detail = AvatarData::MinimumData; + detail = AvatarData::NoData; nodeData->incrementAvatarOutOfView(); } else { detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO From 8cb8d686ecf33798645243e11b7c7e1c9cc68993 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 13:09:39 -0800 Subject: [PATCH 43/44] more cleanup and some CR feedback fixes --- .../src/avatars/AvatarMixerSlave.cpp | 395 ++++++++---------- 1 file changed, 182 insertions(+), 213 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index dfc91b90c1..a839784cd1 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -80,13 +80,6 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; -// only send extra avatar data (avatars out of view, ignored) every Nth AvatarData frame -// Extra avatar data will be sent (AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND/EXTRA_AVATAR_DATA_FRAME_RATIO) times -// per second. -// This value should be a power of two for performance purposes, as the mixer performs a modulo operation every frame -// to determine whether the extra data should be sent. -static const int EXTRA_AVATAR_DATA_FRAME_RATIO = 16; - // FIXME - There is some old logic (unchanged as of 2/17/17) that randomly decides to send an identity // packet. That logic had the following comment about the constants it uses... // @@ -156,6 +149,20 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + // 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 + glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f; + // Set up the bounding box for the current node + AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) { + nodeBox.setScaleStayCentered(minBubbleSize); + } + // Quadruple the scale of both bounding boxes + nodeBox.embiggen(4.0f); + + // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes QList avatarList; @@ -170,7 +177,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { avatarList << otherAvatar; avatarDataToNodes[otherAvatar] = otherNode; } - }); AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); @@ -191,8 +197,87 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); }, - [thisAvatar](AvatarSharedPointer avatar)->bool{ - return (avatar == thisAvatar); // ignore ourselves... + [&](AvatarSharedPointer avatar)->bool{ + if (avatar == thisAvatar) { + return true; // ignore ourselves... + } + + bool shouldIgnore = false; + + // We will also ignore other nodes for a couple of different reasons: + // 1) ignore bubbles and ignore specific node + // 2) the node hasn't really updated it's frame data recently, this can + // happen if for example the avatar is connected on a desktop and sending + // updates at ~30hz. So every 3 frames we skip a frame. + auto avatarNode = avatarDataToNodes[avatar]; + if (avatarNode) { + const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); + if (avatarNodeData) { + quint64 startIgnoreCalculation = usecTimestampNow(); + + // make sure we have data for this avatar, that it isn't the same node, + // and isn't an avatar that the viewing node has ignored + // or that has ignored the viewing node + if (!avatarNode->getLinkedData() + || avatarNode->getUUID() == node->getUUID() + || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !getsIgnoredByMe) + || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + shouldIgnore = true; + } else { + + // Check to see if the space bubble is enabled + if (node->isIgnoreRadiusEnabled() || avatarNode->isIgnoreRadiusEnabled()) { + + // Define the scale of the box for the current other node + glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + // Set up the bounding box for the current other node + AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { + otherNodeBox.setScaleStayCentered(minBubbleSize); + } + // Quadruple the scale of both bounding boxes + otherNodeBox.embiggen(4.0f); + + // Perform the collision check between the two bounding boxes + if (nodeBox.touches(otherNodeBox)) { + nodeData->ignoreOther(node, avatarNode); + shouldIgnore = !getsAnyIgnored; + } + } + // Not close enough to ignore + if (!shouldIgnore) { + nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); + } + } + quint64 endIgnoreCalculation = usecTimestampNow(); + _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + + if (!shouldIgnore) { + AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); + AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); + + // FIXME - This code does appear to be working. But it seems brittle. + // It supports determining if the frame of data for this "other" + // avatar has already been sent to the reciever. This has been + // verified to work on a desktop display that renders at 60hz and + // therefore sends to mixer at 30hz. Each second you'd expect to + // have 15 (45hz-30hz) duplicate frames. In this case, the stat + // avg_other_av_skips_per_second does report 15. + // + // make sure we haven't already sent this data from this sender to this receiver + // or that somehow we haven't sent + if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { + ++numAvatarsHeldBack; + shouldIgnore = true; + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + // this is a skip - we still send the packet but capture the presence of the skip so we see it happening + ++numAvatarsWithSkippedFrames; + } + } + } + } + return shouldIgnore; }); // loop through our sorted avatars and allocate our bandwidth to them accordingly @@ -219,223 +304,107 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // NOTE: Here's where we determine if we are over budget and drop to bare minimum data int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; - /** - qDebug() << "Budget debugging:" - << "numAvatarDataBytes:" << numAvatarDataBytes << "\n" - << "identityBytesSent:" << identityBytesSent << "\n" - << "remainingAvatars:" << remainingAvatars << "\n" - << "minimumBytesPerAvatar:" << minimumBytesPerAvatar << "\n" - << "minimRemainingAvatarBytes:" << minimRemainingAvatarBytes << "\n" - << "overBudget:" << overBudget << "\n" - << "overBudgetAvatars:" << overBudgetAvatars; - **/ - bool shouldConsider = false; - quint64 startIgnoreCalculation = usecTimestampNow(); + quint64 startAvatarDataPacking = usecTimestampNow(); - // make sure we have data for this avatar, that it isn't the same node, - // and isn't an avatar that the viewing node has ignored - // or that has ignored the viewing node - if (!otherNode->getLinkedData() - || otherNode->getUUID() == node->getUUID() - || (node->isIgnoringNodeWithID(otherNode->getUUID()) && !getsIgnoredByMe) - || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + ++numOtherAvatars; - shouldConsider = false; + const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + + // make sure we send out identity packets to and from new arrivals. + bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID()); + + // FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..." + if (!overBudget + && otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 + && (forceSend + || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp + || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { + + identityBytesSent += sendIdentityPacket(otherNodeData, node); + } + + const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); + glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); + + // determine if avatar is in view, to determine how much data to include... + glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + bool isInView = nodeData->otherAvatarInView(otherNodeBox); + + // start a new segment in the PacketList for this avatar + avatarPacketList->startSegment(); + + AvatarData::AvatarDataDetail detail; + + if (overBudget) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + detail = AvatarData::NoData; + } else if (!isInView && !getsOutOfView) { + detail = AvatarData::NoData; + nodeData->incrementAvatarOutOfView(); } else { - const AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); - - shouldConsider = true; // assume we will consider... - - // Check to see if the space bubble is enabled - 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 - glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f; - // Define the scale of the box for the current other node - glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f; - - // Set up the bounding box for the current node - AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) { - nodeBox.setScaleStayCentered(minBubbleSize); - } - // Set up the bounding box for the current other node - AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { - otherNodeBox.setScaleStayCentered(minBubbleSize); - } - // Quadruple the scale of both bounding boxes - nodeBox.embiggen(4.0f); - otherNodeBox.embiggen(4.0f); - - // Perform the collision check between the two bounding boxes - if (nodeBox.touches(otherNodeBox)) { - nodeData->ignoreOther(node, otherNode); - shouldConsider = getsAnyIgnored; - } - } - // Not close enough to ignore - if (shouldConsider) { - nodeData->removeFromRadiusIgnoringSet(node, otherNode->getUUID()); - } - - quint64 endIgnoreCalculation = usecTimestampNow(); - _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO + ? AvatarData::SendAllData : AvatarData::CullSmallData; + nodeData->incrementAvatarInView(); } - if (shouldConsider) { - quint64 startAvatarDataPacking = usecTimestampNow(); + bool includeThisAvatar = true; + auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); + QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); + bool distanceAdjust = true; + glm::vec3 viewerPosition = myPosition; + AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray + bool dropFaceTracking = false; - ++numOtherAvatars; - - const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - - // make sure we send out identity packets to and from new arrivals. - bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID()); - - // FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..." - if (!overBudget - && otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 - && (forceSend - || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp - || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - - identityBytesSent += sendIdentityPacket(otherNodeData, node); - } - - const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); - // Decide whether to send this avatar's data based on it's distance from us - - glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); - float distanceToAvatar = glm::length(myPosition - otherPosition); - - if (shouldConsider) { - AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); - - // FIXME - This code does appear to be working. But it seems brittle. - // It supports determining if the frame of data for this "other" - // avatar has already been sent to the reciever. This has been - // verified to work on a desktop display that renders at 60hz and - // therefore sends to mixer at 30hz. Each second you'd expect to - // have 15 (45hz-30hz) duplicate frames. In this case, the stat - // avg_other_av_skips_per_second does report 15. - // - // make sure we haven't already sent this data from this sender to this receiver - // or that somehow we haven't sent - if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { - ++numAvatarsHeldBack; - - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - shouldConsider = false; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { - // this is a skip - we still send the packet but capture the presence of the skip so we see it happening - ++numAvatarsWithSkippedFrames; - } - - // we're going to send this avatar - if (shouldConsider) { - - // determine if avatar is in view, to determine how much data to include... - glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; - AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - bool isInView = nodeData->otherAvatarInView(otherNodeBox); - - // this throttles the extra data to only be sent every Nth message - /** - if (!isInView && !getsOutOfView && (lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0)) { - quint64 endAvatarDataPacking = usecTimestampNow(); - - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - shouldConsider = false; - } - ***/ - - if (shouldConsider) { - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); - - AvatarData::AvatarDataDetail detail; - - if (overBudget) { - overBudgetAvatars++; - _stats.overBudgetAvatars++; - detail = AvatarData::NoData; - } else if (!isInView && !getsOutOfView) { - detail = AvatarData::NoData; - nodeData->incrementAvatarOutOfView(); - } else { - detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO - ? AvatarData::SendAllData : AvatarData::CullSmallData; - nodeData->incrementAvatarInView(); - } - - { - bool includeThisAvatar = true; - auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); - QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); - bool distanceAdjust = true; - glm::vec3 viewerPosition = myPosition; - AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray - bool dropFaceTracking = false; - - quint64 start = usecTimestampNow(); - QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - quint64 end = usecTimestampNow(); - _stats.toByteArrayElapsedTime += (end - start); - - static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data"; - - dropFaceTracking = true; // first try dropping the facial data - bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData"; - bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther, + quint64 start = usecTimestampNow(); + QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - } + quint64 end = usecTimestampNow(); + _stats.toByteArrayElapsedTime += (end - start); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!"; - includeThisAvatar = false; - } - } + static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID); + if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { + qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data"; - if (includeThisAvatar) { - numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList->write(bytes); - _stats.numOthersIncluded++; + dropFaceTracking = true; // first try dropping the facial data + bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, + hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - // increment the number of avatars sent to this reciever - nodeData->incrementNumAvatarsSentLastFrame(); + if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { + qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData"; + bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther, + hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); + } - // set the last sent sequence number for this sender on the receiver - nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), - otherNodeData->getLastReceivedSequenceNumber()); - - // remember the last time we sent details about this other node to the receiver - nodeData->setLastBroadcastTime(otherNode->getUUID(), start); - - } - } - - avatarPacketList->endSegment(); - - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - } - } + if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { + qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!"; + includeThisAvatar = false; } } + + if (includeThisAvatar) { + numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); + numAvatarDataBytes += avatarPacketList->write(bytes); + _stats.numOthersIncluded++; + + // increment the number of avatars sent to this reciever + nodeData->incrementNumAvatarsSentLastFrame(); + + // set the last sent sequence number for this sender on the receiver + nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), + otherNodeData->getLastReceivedSequenceNumber()); + + // remember the last time we sent details about this other node to the receiver + nodeData->setLastBroadcastTime(otherNode->getUUID(), start); + + } + + avatarPacketList->endSegment(); + + quint64 endAvatarDataPacking = usecTimestampNow(); + _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); }; quint64 startPacketSending = usecTimestampNow(); From 1e91f74ce745929d6a502f7f7677640e64b617d6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 13:32:35 -0800 Subject: [PATCH 44/44] clean up some uneedded nullptr checks, make them asserts --- .../src/avatars/AvatarMixerSlave.cpp | 133 +++++++++--------- 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index a839784cd1..8d9a5e6951 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -171,6 +171,10 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { int listItem = 0; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + + // theoretically it's possible for a Node to be in the NodeList (and therefore end up here), + // but not have yet sent data that's linked to the node. Check for that case and don't + // consider those nodes. if (otherNodeData) { listItem++; AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); @@ -186,10 +190,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { [&](AvatarSharedPointer avatar)->uint64_t{ auto avatarNode = avatarDataToNodes[avatar]; - if (avatarNode) { - return nodeData->getLastBroadcastTime(avatarNode->getUUID()); - } - return 0; + assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map + return nodeData->getLastBroadcastTime(avatarNode->getUUID()); }, [&](AvatarSharedPointer avatar)->float{ @@ -210,72 +212,72 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // happen if for example the avatar is connected on a desktop and sending // updates at ~30hz. So every 3 frames we skip a frame. auto avatarNode = avatarDataToNodes[avatar]; - if (avatarNode) { - const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); - if (avatarNodeData) { - quint64 startIgnoreCalculation = usecTimestampNow(); - // make sure we have data for this avatar, that it isn't the same node, - // and isn't an avatar that the viewing node has ignored - // or that has ignored the viewing node - if (!avatarNode->getLinkedData() - || avatarNode->getUUID() == node->getUUID() - || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !getsIgnoredByMe) - || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { - shouldIgnore = true; - } else { + assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - // Check to see if the space bubble is enabled - if (node->isIgnoreRadiusEnabled() || avatarNode->isIgnoreRadiusEnabled()) { + const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); + assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data + quint64 startIgnoreCalculation = usecTimestampNow(); - // Define the scale of the box for the current other node - glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; - // Set up the bounding box for the current other node - AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { - otherNodeBox.setScaleStayCentered(minBubbleSize); - } - // Quadruple the scale of both bounding boxes - otherNodeBox.embiggen(4.0f); + // make sure we have data for this avatar, that it isn't the same node, + // and isn't an avatar that the viewing node has ignored + // or that has ignored the viewing node + if (!avatarNode->getLinkedData() + || avatarNode->getUUID() == node->getUUID() + || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !getsIgnoredByMe) + || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + shouldIgnore = true; + } else { - // Perform the collision check between the two bounding boxes - if (nodeBox.touches(otherNodeBox)) { - nodeData->ignoreOther(node, avatarNode); - shouldIgnore = !getsAnyIgnored; - } - } - // Not close enough to ignore - if (!shouldIgnore) { - nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); - } + // Check to see if the space bubble is enabled + if (node->isIgnoreRadiusEnabled() || avatarNode->isIgnoreRadiusEnabled()) { + + // Define the scale of the box for the current other node + glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + // Set up the bounding box for the current other node + AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { + otherNodeBox.setScaleStayCentered(minBubbleSize); } - quint64 endIgnoreCalculation = usecTimestampNow(); - _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + // Quadruple the scale of both bounding boxes + otherNodeBox.embiggen(4.0f); - if (!shouldIgnore) { - AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); - - // FIXME - This code does appear to be working. But it seems brittle. - // It supports determining if the frame of data for this "other" - // avatar has already been sent to the reciever. This has been - // verified to work on a desktop display that renders at 60hz and - // therefore sends to mixer at 30hz. Each second you'd expect to - // have 15 (45hz-30hz) duplicate frames. In this case, the stat - // avg_other_av_skips_per_second does report 15. - // - // make sure we haven't already sent this data from this sender to this receiver - // or that somehow we haven't sent - if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { - ++numAvatarsHeldBack; - shouldIgnore = true; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { - // this is a skip - we still send the packet but capture the presence of the skip so we see it happening - ++numAvatarsWithSkippedFrames; - } + // Perform the collision check between the two bounding boxes + if (nodeBox.touches(otherNodeBox)) { + nodeData->ignoreOther(node, avatarNode); + shouldIgnore = !getsAnyIgnored; } } + // Not close enough to ignore + if (!shouldIgnore) { + nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); + } + } + quint64 endIgnoreCalculation = usecTimestampNow(); + _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + + if (!shouldIgnore) { + AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); + AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); + + // FIXME - This code does appear to be working. But it seems brittle. + // It supports determining if the frame of data for this "other" + // avatar has already been sent to the reciever. This has been + // verified to work on a desktop display that renders at 60hz and + // therefore sends to mixer at 30hz. Each second you'd expect to + // have 15 (45hz-30hz) duplicate frames. In this case, the stat + // avg_other_av_skips_per_second does report 15. + // + // make sure we haven't already sent this data from this sender to this receiver + // or that somehow we haven't sent + if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { + ++numAvatarsHeldBack; + shouldIgnore = true; + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + // this is a skip - we still send the packet but capture the presence of the skip so we see it happening + ++numAvatarsWithSkippedFrames; + } } return shouldIgnore; }); @@ -284,7 +286,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { int avatarRank = 0; // this is overly conservative, because it includes some avatars we might not consider - // FIXME - move the ignore logic up into the sorting list so we get a better estimate int remainingAvatars = (int)sortedAvatars.size(); while (!sortedAvatars.empty()) { @@ -295,11 +296,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { remainingAvatars--; auto otherNode = avatarDataToNodes[avatarData]; - - // FIXME - should't this be an assert? - if (!otherNode) { - continue; - } + assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map // NOTE: Here's where we determine if we are over budget and drop to bare minimum data int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;