Merge branch 'master' of https://github.com/highfidelity/hifi into orange

This commit is contained in:
samcake 2016-04-20 09:37:54 -07:00
commit d471cc4ec5
12 changed files with 103 additions and 106 deletions

View file

@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <thread>
#ifdef _WIN32
#include <math.h>
@ -577,15 +578,15 @@ void AudioMixer::domainSettingsRequestComplete() {
void AudioMixer::broadcastMixes() {
auto nodeList = DependencyManager::get<NodeList>();
int64_t nextFrame = 0;
QElapsedTimer timer;
timer.start();
int64_t usecToSleep = AudioConstants::NETWORK_FRAME_USECS;
auto nextFrameTimestamp = p_high_resolution_clock::now();
auto timeToSleep = std::chrono::microseconds(0);
const int TRAILING_AVERAGE_FRAMES = 100;
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
int currentFrame { 1 };
int numFramesPerSecond { (int) ceil(AudioConstants::NETWORK_FRAMES_PER_SEC) };
while (!_isFinished) {
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
@ -595,12 +596,12 @@ void AudioMixer::broadcastMixes() {
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
if (usecToSleep < 0) {
usecToSleep = 0;
if (timeToSleep.count() < 0) {
timeToSleep = std::chrono::microseconds(0);
}
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS);
+ (timeToSleep.count() * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS);
float lastCutoffRatio = _performanceThrottlingRatio;
bool hasRatioChanged = false;
@ -694,10 +695,11 @@ void AudioMixer::broadcastMixes() {
nodeList->sendPacket(std::move(mixPacket), *node);
nodeData->incrementOutgoingMixedAudioSequenceNumber();
static const int FRAMES_PER_SECOND = int(ceilf(1.0f / AudioConstants::NETWORK_FRAME_SECS));
// send an audio stream stats packet to the client approximately every second
if (nextFrame % FRAMES_PER_SECOND == 0) {
++currentFrame;
currentFrame %= numFramesPerSecond;
if (nodeData->shouldSendStats(currentFrame)) {
nodeData->sendAudioStreamStatsPackets(node);
}
@ -718,11 +720,14 @@ void AudioMixer::broadcastMixes() {
break;
}
usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - (timer.nsecsElapsed() / 1000);
// push the next frame timestamp to when we should send the next
nextFrameTimestamp += std::chrono::microseconds(AudioConstants::NETWORK_FRAME_USECS);
if (usecToSleep > 0) {
usleep(usecToSleep);
}
// sleep as long as we need until next frame, if we can
auto now = p_high_resolution_clock::now();
timeToSleep = std::chrono::duration_cast<std::chrono::microseconds>(nextFrameTimestamp - now);
std::this_thread::sleep_for(timeToSleep);
}
}

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <random>
#include <QtCore/QDebug>
#include <QtCore/QJsonArray>
@ -26,7 +28,14 @@ AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) :
_outgoingMixedAudioSequenceNumber(0),
_downstreamAudioStreamStats()
{
// of the ~94 blocks in a second of audio sent from the AudioMixer, pick a random one to send out a stats packet on
// this ensures we send out stats to this client around every second
// but do not send all of the stats packets out at the same time
std::random_device randomDevice;
std::mt19937 numberGenerator { randomDevice() };
std::uniform_int_distribution<> distribution { 1, (int) ceil(1.0f / AudioConstants::NETWORK_FRAME_SECS) };
_frameToSendStats = distribution(numberGenerator);
}
AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() {
@ -180,6 +189,10 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
}
}
bool AudioMixerClientData::shouldSendStats(int frameNumber) {
return frameNumber == _frameToSendStats;
}
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) {
auto nodeList = DependencyManager::get<NodeList>();

View file

@ -58,6 +58,9 @@ public:
void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; }
quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; }
// uses randomization to have the AudioMixer send a stats packet to this node around every second
bool shouldSendStats(int frameNumber);
signals:
void injectorStreamFinished(const QUuid& streamIdentifier);
@ -72,6 +75,8 @@ private:
quint16 _outgoingMixedAudioSequenceNumber;
AudioStreamStats _downstreamAudioStreamStats;
int _frameToSendStats { 0 };
};
#endif // hifi_AudioMixerClientData_h

View file

@ -36,14 +36,7 @@ const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXE
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
ThreadedAssignment(message),
_broadcastThread(),
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
_trailingSleepRatio(1.0f),
_performanceThrottlingRatio(0.0f),
_sumListeners(0),
_numStatFrames(0),
_sumBillboardPackets(0),
_sumIdentityPackets(0)
_broadcastThread()
{
// make sure we hear about node kills so we can tell the other nodes
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
@ -51,7 +44,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket");
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "handleAvatarBillboardPacket");
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
}
@ -66,13 +58,18 @@ AvatarMixer::~AvatarMixer() {
// An 80% chance of sending a identity packet within a 5 second interval.
// assuming 60 htz update rate.
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
const float IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
// NOTE: some additional optimizations to consider.
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
// if the avatar is not in view or in the keyhole.
void AvatarMixer::broadcastAvatarData() {
int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp;
int idleTime = AVATAR_DATA_SEND_INTERVAL_MSECS;
if (_lastFrameTimestamp.time_since_epoch().count() > 0) {
auto idleDuration = p_high_resolution_clock::now() - _lastFrameTimestamp;
idleTime = std::chrono::duration_cast<std::chrono::microseconds>(idleDuration).count();
}
++_numStatFrames;
@ -245,32 +242,13 @@ void AvatarMixer::broadcastAvatarData() {
return;
}
// make sure we send out identity and billboard packets to and from new arrivals.
// make sure we send out identity packets to and from new arrivals.
bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID());
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
billboardPacket->write(rfcUUID);
billboardPacket->write(billboard);
nodeList->sendPacket(std::move(billboardPacket), *node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
if (otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|| distribution(generator) < IDENTITY_SEND_PROBABILITY)) {
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
@ -385,7 +363,7 @@ void AvatarMixer::broadcastAvatarData() {
otherAvatar.doneEncoding(false);
});
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
_lastFrameTimestamp = p_high_resolution_clock::now();
}
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
@ -438,26 +416,12 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
// parse the identity packet and update the change timestamp if appropriate
if (avatar.hasIdentityChangedAfterParsing(message->getMessage())) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
nodeData->flagIdentityChange();
}
}
}
}
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
if (nodeData) {
AvatarData& avatar = nodeData->getAvatar();
// parse the billboard packet and update the change timestamp if appropriate
if (avatar.hasBillboardChangedAfterParsing(message->getMessage())) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
}
}
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message) {
DependencyManager::get<NodeList>()->processKillNode(*message);
}
@ -466,7 +430,6 @@ void AvatarMixer::sendStatsPacket() {
QJsonObject statsObject;
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames;
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
@ -507,7 +470,6 @@ void AvatarMixer::sendStatsPacket() {
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
_sumListeners = 0;
_sumBillboardPackets = 0;
_sumIdentityPackets = 0;
_numStatFrames = 0;
}

View file

@ -15,6 +15,8 @@
#ifndef hifi_AvatarMixer_h
#define hifi_AvatarMixer_h
#include <PortableHighResolutionClock.h>
#include <ThreadedAssignment.h>
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
@ -34,7 +36,6 @@ public slots:
private slots:
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
void domainSettingsRequestComplete();
@ -44,15 +45,14 @@ private:
QThread _broadcastThread;
quint64 _lastFrameTimestamp;
p_high_resolution_clock::time_point _lastFrameTimestamp;
float _trailingSleepRatio;
float _performanceThrottlingRatio;
float _trailingSleepRatio { 1.0f };
float _performanceThrottlingRatio { 0.0f };
int _sumListeners;
int _numStatFrames;
int _sumBillboardPackets;
int _sumIdentityPackets;
int _sumListeners { 0 };
int _numStatFrames { 0 };
int _sumIdentityPackets { 0 };
float _maxKbpsPerNode = 0.0f;

View file

@ -24,6 +24,7 @@
#include <NodeData.h>
#include <NumericalConstants.h>
#include <udt/PacketHeaders.h>
#include <PortableHighResolutionClock.h>
#include <SimpleMovingAverage.h>
#include <UUIDHasher.h>
@ -33,6 +34,8 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
class AvatarMixerClientData : public NodeData {
Q_OBJECT
public:
using HRCTime = p_high_resolution_clock::time_point;
int parseData(ReceivedMessage& message) override;
AvatarData& getAvatar() { return *_avatar; }
@ -45,11 +48,8 @@ public:
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); }
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
float getFullRateDistance() const { return _fullRateDistance; }
@ -86,8 +86,7 @@ private:
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
quint64 _billboardChangeTimestamp = 0;
quint64 _identityChangeTimestamp = 0;
HRCTime _identityChangeTimestamp;
float _fullRateDistance = FLT_MAX;
float _maxAvatarDistance = FLT_MAX;

View file

@ -40,11 +40,10 @@ const int UP_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * UP_SHIFT_WINDOW_IN_SECS;
const float ADJUST_LOD_DOWN_BY = 0.9f;
const float ADJUST_LOD_UP_BY = 1.1f;
// This controls how low the auto-adjust LOD will go a value of 1 means it will adjust to a point where you must be 0.25
// meters away from an object of TREE_SCALE before you can see it (which is effectively completely blind). The default value
// DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
const float ADJUST_LOD_MIN_SIZE_SCALE = 1.0f;
// The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
// This controls how low the auto-adjust LOD will go. We want a minimum vision of ~20:500 or 0.04 of default
const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
class RenderArgs;
class AABox;

View file

@ -51,8 +51,8 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) :
connect(_manualLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust()));
_lodSize = new QSlider(Qt::Horizontal, this);
const int MAX_LOD_SIZE = MAX_LOD_SIZE_MULTIPLIER;
const int MIN_LOD_SIZE = ADJUST_LOD_MIN_SIZE_SCALE;
const int MAX_LOD_SIZE = 2000; // ~20:4 vision -- really good.
const int MIN_LOD_SIZE = 5; // ~20:1600 vision -- really bad!
const int STEP_LOD_SIZE = 1;
const int PAGE_STEP_LOD_SIZE = 100;
const int SLIDER_WIDTH = 300;

View file

@ -29,6 +29,7 @@ namespace AudioConstants {
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
const float NETWORK_FRAME_SECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / float(AudioConstants::SAMPLE_RATE));
const float NETWORK_FRAME_MSECS = NETWORK_FRAME_SECS * 1000.0f;
const float NETWORK_FRAMES_PER_SEC = 1.0f / NETWORK_FRAME_SECS;
// be careful with overflows when using this constant
const int NETWORK_FRAME_USECS = static_cast<int>(NETWORK_FRAME_MSECS * 1000.0f);

View file

@ -38,7 +38,7 @@ bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const s
GLuint glshader = glCreateShader(shaderDomain);
if (!glshader) {
qCDebug(gpulogging) << "GLShader::compileShader - failed to create the gl shader object";
return nullptr;
return false;
}
// Assign the source

View file

@ -329,10 +329,21 @@ void SendQueue::run() {
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
// sleep as long as we need until next packet send, if we can
// sleep as long as we need for next packet send, if we can
auto now = p_high_resolution_clock::now();
auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - now);
// we use nextPacketTimestamp so that we don't fall behind, not to force long sleeps
// we'll never allow nextPacketTimestamp to force us to sleep for more than nextPacketDelta
// so cap it to that value
if (timeToSleep > std::chrono::microseconds(nextPacketDelta)) {
// reset the nextPacketTimestamp so that it is correct next time we come around
nextPacketTimestamp = now + std::chrono::microseconds(nextPacketDelta);
timeToSleep = std::chrono::microseconds(nextPacketDelta);
}
// we're seeing SendQueues sleep for a long period of time here,
// which can lock the NodeList if it's attempting to clear connections
// for now we guard this by capping the time this thread and sleep for

View file

@ -10,6 +10,7 @@
#include <atomic>
#include <Windows.h>
#include <QtCore/QLoggingCategory>
#include <QtCore/QFile>
#include <QtCore/QDir>
@ -41,23 +42,28 @@ void logFatal(const char* what) {
qFatal(error.c_str());
}
static const QString OCULUS_RUNTIME_PATH { "C:\\Program Files (x86)\\Oculus\\Support\\oculus-runtime" };
static const QString GOOD_OCULUS_RUNTIME_FILE { OCULUS_RUNTIME_PATH + "\\LibOVRRT64_1.dll" };
static wchar_t* REQUIRED_OCULUS_DLL = L"LibOVRRT64_1.dll";
static wchar_t FOUND_PATH[MAX_PATH];
bool oculusAvailable() {
ovrDetectResult detect = ovr_Detect(0);
if (!detect.IsOculusServiceRunning || !detect.IsOculusHMDConnected) {
return false;
}
static std::once_flag once;
static bool result { false };
std::call_once(once, [&] {
ovrDetectResult detect = ovr_Detect(0);
if (!detect.IsOculusServiceRunning || !detect.IsOculusHMDConnected) {
return;
}
// HACK Explicitly check for the presence of the 1.0 runtime DLL, and fail if it
// doesn't exist
if (!QFile(GOOD_OCULUS_RUNTIME_FILE).exists()) {
qCWarning(oculus) << "Oculus Runtime detected, but no 1.x DLL present: \"" + GOOD_OCULUS_RUNTIME_FILE + "\"";
return false;
}
DWORD searchResult = SearchPathW(NULL, REQUIRED_OCULUS_DLL, NULL, MAX_PATH, FOUND_PATH, NULL);
if (searchResult <= 0) {
return;
}
return true;
result = true;
});
return result;
}
ovrSession acquireOculusSession() {
@ -67,10 +73,6 @@ ovrSession acquireOculusSession() {
}
if (!session) {
ovrInitParams init = {};
init.Flags = 0;
init.ConnectionTimeoutMS = 0;
init.LogCallback = nullptr;
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
logWarning("Failed to initialize Oculus SDK");
return session;