Merge branch 'master' of https://github.com/worklist/hifi into 19507

This commit is contained in:
stojce 2014-03-22 10:19:01 +01:00
commit 51db3dc3e1
15 changed files with 199 additions and 128 deletions

View file

@ -63,4 +63,5 @@ To test things out you'll want to run the Interface client.
To access your local domain in Interface, open your Preferences -- on OS X this is available in the Interface menu, on Linux you'll find it in the File menu. Enter "localhost" in the "Domain server" field.
If everything worked you should see that you are connected to at least one server. Nice work!
If everything worked you should see that you are connected to at least one server.
Nice work!

View file

@ -189,4 +189,9 @@ void Agent::run() {
_scriptEngine.setScriptContents(scriptContents);
_scriptEngine.run();
setFinished(true);
}
void Agent::aboutToFinish() {
_scriptEngine.stop();
}

View file

@ -41,6 +41,8 @@ public:
bool isListeningToAudioStream() const { return _scriptEngine.isListeningToAudioStream(); }
void setIsListeningToAudioStream(bool isListeningToAudioStream)
{ _scriptEngine.setIsListeningToAudioStream(isListeningToAudioStream); }
virtual void aboutToFinish();
public slots:
void run();

View file

@ -66,7 +66,8 @@ void attachNewBufferToNode(Node *newNode) {
AudioMixer::AudioMixer(const QByteArray& packet) :
ThreadedAssignment(packet),
_trailingSleepRatio(1.0f),
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f)
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
_numClientsMixedInFrame(0)
{
}
@ -80,15 +81,22 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
if (bufferToAdd != listeningNodeBuffer) {
// if the two buffer pointers do not match then these are different buffers
glm::vec3 relativePosition = bufferToAdd->getPosition() - listeningNodeBuffer->getPosition();
if (bufferToAdd->getAverageLoudness() / glm::length(relativePosition) <= _minAudibilityThreshold) {
float distanceBetween = glm::length(relativePosition);
if (distanceBetween < EPSILON) {
distanceBetween = EPSILON;
}
if (bufferToAdd->getAverageLoudness() / distanceBetween <= _minAudibilityThreshold) {
// according to mixer performance we have decided this does not get to be mixed in
// bail out
return;
}
++_numClientsMixedInFrame;
glm::quat inverseOrientation = glm::inverse(listeningNodeBuffer->getOrientation());
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
@ -364,6 +372,9 @@ void AudioMixer::run() {
int usecToSleep = BUFFER_SEND_INTERVAL_USECS;
float audabilityCutoffRatio = 0;
const int TRAILING_AVERAGE_FRAMES = 100;
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
while (!_isFinished) {
@ -374,10 +385,9 @@ void AudioMixer::run() {
}
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.30f;
const float CUTOFF_EPSILON = 0.0001f;
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
const float CUTOFF_DELTA = 0.02f;
const int TRAILING_AVERAGE_FRAMES = 100;
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
@ -391,30 +401,40 @@ void AudioMixer::run() {
float lastCutoffRatio = audabilityCutoffRatio;
bool hasRatioChanged = false;
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
// we're struggling - change our min required loudness to reduce some load
audabilityCutoffRatio += (1.0f - audabilityCutoffRatio) / 2.0f;
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
<< lastCutoffRatio << "and is now" << audabilityCutoffRatio;
hasRatioChanged = true;
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && audabilityCutoffRatio != 0) {
// we've recovered and can back off the required loudness
audabilityCutoffRatio -= audabilityCutoffRatio / 2.0f;
if (audabilityCutoffRatio < CUTOFF_EPSILON) {
audabilityCutoffRatio = 0.0f;
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
// we're struggling - change our min required loudness to reduce some load
audabilityCutoffRatio += CUTOFF_DELTA;
if (audabilityCutoffRatio >= 1) {
audabilityCutoffRatio = 1 - CUTOFF_DELTA;
}
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
<< lastCutoffRatio << "and is now" << audabilityCutoffRatio;
hasRatioChanged = true;
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && audabilityCutoffRatio != 0) {
// we've recovered and can back off the required loudness
audabilityCutoffRatio -= CUTOFF_DELTA;
if (audabilityCutoffRatio < 0) {
audabilityCutoffRatio = 0;
}
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
<< lastCutoffRatio << "and is now" << audabilityCutoffRatio;
hasRatioChanged = true;
}
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
<< lastCutoffRatio << "and is now" << audabilityCutoffRatio;
hasRatioChanged = true;
}
if (hasRatioChanged) {
// set out min required loudness from the new ratio
_minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - audabilityCutoffRatio));
qDebug() << "Minimum loudness required to be mixed is now" << _minAudibilityThreshold;
if (hasRatioChanged) {
// set out min audability threshold from the new ratio
_minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - audabilityCutoffRatio));
qDebug() << "Minimum audability required to be mixed is now" << _minAudibilityThreshold;
framesSinceCutoffEvent = 0;
}
} else {
framesSinceCutoffEvent++;
}
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
@ -428,6 +448,8 @@ void AudioMixer::run() {
nodeList->writeDatagram(clientMixBuffer, NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader, node);
}
}
_numClientsMixedInFrame = 0;
// push forward the next output pointers for any audio buffers we used
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
@ -446,10 +468,7 @@ void AudioMixer::run() {
if (usecToSleep > 0) {
usleep(usecToSleep);
} else {
qDebug() << "AudioMixer loop took" << -usecToSleep << "of extra time. Not sleeping.";
}
}
delete[] clientMixBuffer;

View file

@ -42,6 +42,7 @@ private:
float _trailingSleepRatio;
float _minAudibilityThreshold;
int _numClientsMixedInFrame;
};
#endif /* defined(__hifi__AudioMixer__) */

View file

@ -67,6 +67,14 @@ OctreeQueryNode::~OctreeQueryNode() {
}
void OctreeQueryNode::deleteLater() {
_isShuttingDown = true;
if (_octreeSendThread) {
_octreeSendThread->setIsShuttingDown();
}
OctreeQuery::deleteLater();
}
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) {
// Create octree sending thread...

View file

@ -27,6 +27,7 @@ class OctreeQueryNode : public OctreeQuery {
public:
OctreeQueryNode();
virtual ~OctreeQueryNode();
virtual void deleteLater();
void init(); // called after creation to set up some virtual items
virtual PacketType getMyPacketType() const = 0;

View file

@ -5,6 +5,8 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QMutexLocker>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
@ -21,7 +23,9 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer
_nodeUUID(nodeUUID),
_myServer(myServer),
_packetData(),
_nodeMissingCount(0)
_nodeMissingCount(0),
_processLock(),
_isShuttingDown(false)
{
QString safeServerName("Octree");
if (_myServer) {
@ -43,8 +47,19 @@ OctreeSendThread::~OctreeSendThread() {
OctreeServer::clientDisconnected();
}
void OctreeSendThread::setIsShuttingDown() {
QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete
_isShuttingDown = true;
}
bool OctreeSendThread::process() {
QMutexLocker locker(&_processLock);
if (_isShuttingDown) {
return false; // exit early if we're shutting down
}
const int MAX_NODE_MISSING_CHECKS = 10;
if (_nodeMissingCount > MAX_NODE_MISSING_CHECKS) {
qDebug() << "our target node:" << _nodeUUID << "has been missing the last" << _nodeMissingCount
@ -56,7 +71,10 @@ bool OctreeSendThread::process() {
// don't do any send processing until the initial load of the octree is complete...
if (_myServer->isInitialLoadComplete()) {
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
// see if we can get access to our node, but don't wait on the lock, if the nodeList is busy
// it might not return a node that is known, but that's ok we can handle that case.
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false);
if (node) {
_nodeMissingCount = 0;
@ -113,19 +131,6 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
bool packetSent = false; // did we send a packet?
int packetsSent = 0;
// double check that the node has an active socket, otherwise, don't send...
quint64 lockWaitStart = usecTimestampNow();
QMutexLocker locker(&node->getMutex());
quint64 lockWaitEnd = usecTimestampNow();
float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
OctreeServer::trackNodeWaitTime(lockWaitElapsedUsec);
const HifiSockAddr* nodeAddress = node->getActiveSocket();
if (!nodeAddress) {
return packetsSent; // without sending...
}
// Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
// this rate control savings.

View file

@ -23,6 +23,8 @@ class OctreeSendThread : public GenericThread {
public:
OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer);
virtual ~OctreeSendThread();
void setIsShuttingDown();
static quint64 _totalBytes;
static quint64 _totalWastedBytes;
@ -45,6 +47,8 @@ private:
OctreePacketData _packetData;
int _nodeMissingCount;
QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing
bool _isShuttingDown;
};
#endif // __octree_server__OctreeSendThread__

View file

@ -32,8 +32,6 @@ var MIN_PASTE_VOXEL_SCALE = .256;
var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting
var previewLineWidth = 1.5;
var oldMode = Camera.getMode();
var trackAsOrbitOrPan = false;
var isAdding = false;
var isExtruding = false;
var extrudeDirection = { x: 0, y: 0, z: 0 };
@ -614,8 +612,6 @@ function showPreviewVoxel() {
var guidePosition;
if (trackAsRecolor || recolorToolSelected || trackAsEyedropper || eyedropperToolSelected) {
Overlays.editOverlay(voxelPreview, { visible: true });
} else if (trackAsOrbitOrPan) {
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (voxelToolSelected && !isExtruding) {
Overlays.editOverlay(voxelPreview, { visible: true });
} else if (isExtruding) {
@ -706,15 +702,12 @@ function showPreviewGuides() {
}
function trackMouseEvent(event) {
if (!trackAsOrbitOrPan) {
trackLastMouseX = event.x;
trackLastMouseY = event.y;
trackAsDelete = event.isControl;
trackAsRecolor = event.isShifted;
trackAsEyedropper = event.isMeta;
trackAsOrbitOrPan = event.isAlt; // TODO: double check this...??
showPreviewGuides();
}
trackLastMouseX = event.x;
trackLastMouseY = event.y;
trackAsDelete = event.isControl;
trackAsRecolor = event.isShifted;
trackAsEyedropper = event.isMeta;
showPreviewGuides();
}
function trackKeyPressEvent(event) {
@ -742,6 +735,7 @@ function trackKeyReleaseEvent(event) {
if (event.text == "TAB") {
editToolsOn = !editToolsOn;
moveTools();
showPreviewGuides();
Audio.playSound(clickSound, audioOptions);
}
@ -788,67 +782,64 @@ function mousePressEvent(event) {
return;
}
// no clicking on overlays while in panning mode
if (!trackAsOrbitOrPan) {
var clickedOnSomething = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
// If the user clicked on the thumb, handle the slider logic
if (clickedOverlay == thumb) {
isMovingSlider = true;
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
clickedOnSomething = true;
Overlays.editOverlay(thumb, { imageURL: toolIconUrl + "voxel-size-slider-handle.svg", });
} else if (clickedOverlay == voxelTool) {
voxelToolSelected = true;
recolorToolSelected = false;
eyedropperToolSelected = false;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == recolorTool) {
voxelToolSelected = false;
recolorToolSelected = true;
eyedropperToolSelected = false;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == eyedropperTool) {
voxelToolSelected = false;
recolorToolSelected = false;
eyedropperToolSelected = true;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == slider) {
if (event.x < sliderX + minThumbX) {
thumbX -= thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
if (event.x > sliderX + maxThumbX) {
thumbX += thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
moveTools();
clickedOnSomething = true;
} else {
// if the user clicked on one of the color swatches, update the selectedSwatch
for (s = 0; s < numColors; s++) {
if (clickedOverlay == swatches[s]) {
whichColor = s;
moveTools();
clickedOnSomething = true;
break;
}
}
var clickedOnSomething = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
// If the user clicked on the thumb, handle the slider logic
if (clickedOverlay == thumb) {
isMovingSlider = true;
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
clickedOnSomething = true;
Overlays.editOverlay(thumb, { imageURL: toolIconUrl + "voxel-size-slider-handle.svg", });
} else if (clickedOverlay == voxelTool) {
voxelToolSelected = true;
recolorToolSelected = false;
eyedropperToolSelected = false;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == recolorTool) {
voxelToolSelected = false;
recolorToolSelected = true;
eyedropperToolSelected = false;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == eyedropperTool) {
voxelToolSelected = false;
recolorToolSelected = false;
eyedropperToolSelected = true;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == slider) {
if (event.x < sliderX + minThumbX) {
thumbX -= thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
if (clickedOnSomething) {
return; // no further processing
if (event.x > sliderX + maxThumbX) {
thumbX += thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
moveTools();
clickedOnSomething = true;
} else {
// if the user clicked on one of the color swatches, update the selectedSwatch
for (s = 0; s < numColors; s++) {
if (clickedOverlay == swatches[s]) {
whichColor = s;
moveTools();
clickedOnSomething = true;
break;
}
}
}
if (clickedOnSomething) {
return; // no further processing
}
// TODO: does any of this stuff need to execute if we're panning or orbiting?
trackMouseEvent(event); // used by preview support
mouseX = event.x;
@ -1071,7 +1062,7 @@ function mouseMoveEvent(event) {
}
if (!trackAsOrbitOrPan && isMovingSlider) {
if (isMovingSlider) {
thumbX = (event.x - thumbClickOffsetX) - sliderX;
if (thumbX < minThumbX) {
thumbX = minThumbX;
@ -1081,7 +1072,7 @@ function mouseMoveEvent(event) {
}
calcScaleFromThumb(thumbX);
} else if (!trackAsOrbitOrPan && isAdding) {
} else if (isAdding) {
// Watch the drag direction to tell which way to 'extrude' this voxel
if (!isExtruding) {
var pickRay = Camera.computePickRay(event.x, event.y);

View file

@ -14,9 +14,11 @@
// Dragging the mouse will move your camera according to the mode you are in.
//
var PI = 3.14 // No need for something more precise
var AZIMUTH_RATE = 90.0;
var ALTITUDE_RATE = 200.0;
var RADIUS_RATE = 20.0;
var RADIUS_RATE = 1.0 / 100.0;
var PAN_RATE = 50.0;
var alt = false;
@ -46,7 +48,7 @@ var altitude = 0.0;
function handleRadialMode(dx, dy) {
azimuth += dx / AZIMUTH_RATE;
radius += radius * dy / RADIUS_RATE;
radius += radius * dy * RADIUS_RATE;
if (radius < 1) {
radius = 1;
}
@ -61,6 +63,12 @@ function handleRadialMode(dx, dy) {
function handleOrbitMode(dx, dy) {
azimuth += dx / AZIMUTH_RATE;
altitude += dy / ALTITUDE_RATE;
if (altitude > PI / 2.0) {
altitude = PI / 2.0;
}
if (altitude < -PI / 2.0) {
altitude = -PI / 2.0;
}
vector = { x:(Math.cos(altitude) * Math.cos(azimuth)) * radius,
y:Math.sin(altitude) * radius,
@ -165,7 +173,7 @@ function keyReleaseEvent(event) {
}
function mousePressEvent(event) {
if (alt) {
if (alt && !isActive) {
isActive = true;
mouseLastX = event.x;
mouseLastY = event.y;

View file

@ -3252,6 +3252,9 @@ void Application::domainChanged(const QString& domainHostname) {
// reset the particle renderer
_particles.clear();
// reset the voxels renderer
_voxels.killLocalVoxels();
}
void Application::connectedToDomain(const QString& hostname) {

View file

@ -67,7 +67,20 @@ void AudioRingBuffer::updateAverageLoudnessForBoundarySamples(int numSamples) {
nextLoudness /= numSamples;
nextLoudness /= MAX_SAMPLE_VALUE;
_averageLoudness = nextLoudness;
const int TRAILING_AVERAGE_FRAMES = 100;
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
const float LOUDNESS_EPSILON = 0.01f;
if (nextLoudness >= _averageLoudness) {
_averageLoudness = nextLoudness;
} else {
_averageLoudness = (_averageLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness);
if (_averageLoudness < LOUDNESS_EPSILON) {
_averageLoudness = 0;
}
}
}
qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) {

View file

@ -308,9 +308,18 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
return 0;
}
SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) {
QMutexLocker locker(&_nodeHashMutex);
return _nodeHash.value(nodeUUID);
SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) {
SharedNodePointer node;
// if caller wants us to block and guarantee the correct answer, then honor that request
if (blockingLock) {
// this will block till we can get access
QMutexLocker locker(&_nodeHashMutex);
node = _nodeHash.value(nodeUUID);
} else if (_nodeHashMutex.tryLock()) { // some callers are willing to get wrong answers but not block
node = _nodeHash.value(nodeUUID);
_nodeHashMutex.unlock();
}
return node;
}
SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) {

View file

@ -101,7 +101,8 @@ public:
QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node);
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
/// passing false for blockingLock, will tryLock, and may return NULL when a node with the UUID actually does exist
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true);
SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType,