mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
removed not yet ready for prime time libraries
This commit is contained in:
parent
2b27c91550
commit
bc5317f20b
14 changed files with 0 additions and 2424 deletions
|
@ -1,42 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ../..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||
|
||||
set(TARGET_NAME particle-server)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
|
||||
# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS
|
||||
FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*)
|
||||
|
||||
setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS})
|
||||
|
||||
include_directories(${ROOT_DIR}/externals/civetweb/include)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Widgets)
|
||||
|
||||
# inluce GLM
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link in the shared library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link ZLIB
|
||||
find_package(ZLIB)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
|
||||
# link in the hifi octree library
|
||||
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link in the hifi particles library
|
||||
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
//
|
||||
// ParticlePersistThread.cpp
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded particle persistence
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include <NodeList.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "ParticlePersistThread.h"
|
||||
#include "ParticleServer.h"
|
||||
|
||||
ParticlePersistThread::ParticlePersistThread(ParticleTree* tree, const char* filename, int persistInterval) :
|
||||
_tree(tree),
|
||||
_filename(filename),
|
||||
_persistInterval(persistInterval),
|
||||
_initialLoadComplete(false),
|
||||
_loadTimeUSecs(0) {
|
||||
}
|
||||
|
||||
bool ParticlePersistThread::process() {
|
||||
|
||||
if (!_initialLoadComplete) {
|
||||
uint64_t loadStarted = usecTimestampNow();
|
||||
qDebug("loading particles from file: %s...\n", _filename);
|
||||
|
||||
bool persistantFileRead;
|
||||
|
||||
_tree->lockForWrite();
|
||||
{
|
||||
PerformanceWarning warn(true, "Loading Particle File", true);
|
||||
persistantFileRead = _tree->readFromSVOFile(_filename);
|
||||
}
|
||||
_tree->unlock();
|
||||
|
||||
_loadCompleted = time(0);
|
||||
uint64_t loadDone = usecTimestampNow();
|
||||
_loadTimeUSecs = loadDone - loadStarted;
|
||||
|
||||
_tree->clearDirtyBit(); // the tree is clean since we just loaded it
|
||||
qDebug("DONE loading particles from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
|
||||
|
||||
unsigned long nodeCount = ParticleNode::getNodeCount();
|
||||
unsigned long internalNodeCount = ParticleNode::getInternalNodeCount();
|
||||
unsigned long leafNodeCount = ParticleNode::getLeafNodeCount();
|
||||
qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
|
||||
|
||||
double usecPerGet = (double)ParticleNode::getGetChildAtIndexTime() / (double)ParticleNode::getGetChildAtIndexCalls();
|
||||
qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n",
|
||||
ParticleNode::getGetChildAtIndexTime(), ParticleNode::getGetChildAtIndexCalls(), usecPerGet);
|
||||
|
||||
double usecPerSet = (double)ParticleNode::getSetChildAtIndexTime() / (double)ParticleNode::getSetChildAtIndexCalls();
|
||||
qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n",
|
||||
ParticleNode::getSetChildAtIndexTime(), ParticleNode::getSetChildAtIndexCalls(), usecPerSet);
|
||||
|
||||
_initialLoadComplete = true;
|
||||
_lastCheck = usecTimestampNow(); // we just loaded, no need to save again
|
||||
}
|
||||
|
||||
if (isStillRunning()) {
|
||||
uint64_t MSECS_TO_USECS = 1000;
|
||||
uint64_t USECS_TO_SLEEP = 100 * MSECS_TO_USECS; // every 100ms
|
||||
usleep(USECS_TO_SLEEP);
|
||||
uint64_t now = usecTimestampNow();
|
||||
uint64_t sinceLastSave = now - _lastCheck;
|
||||
uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS;
|
||||
|
||||
if (sinceLastSave > intervalToCheck) {
|
||||
// check the dirty bit and persist here...
|
||||
_lastCheck = usecTimestampNow();
|
||||
if (_tree->isDirty()) {
|
||||
qDebug("saving particles to file %s...\n",_filename);
|
||||
_tree->writeToSVOFile(_filename);
|
||||
_tree->clearDirtyBit(); // tree is clean after saving
|
||||
qDebug("DONE saving particles to file...\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// ParticlePersistThread.h
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded particle persistence
|
||||
//
|
||||
|
||||
#ifndef __particle_server__ParticlePersistThread__
|
||||
#define __particle_server__ParticlePersistThread__
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include <NetworkPacket.h>
|
||||
#include <ParticleTree.h>
|
||||
|
||||
/// Generalized threaded processor for handling received inbound packets.
|
||||
class ParticlePersistThread : public virtual GenericThread {
|
||||
public:
|
||||
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
|
||||
|
||||
ParticlePersistThread(ParticleTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
|
||||
|
||||
bool isInitialLoadComplete() const { return _initialLoadComplete; }
|
||||
|
||||
time_t* getLoadCompleted() { return &_loadCompleted; }
|
||||
uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; }
|
||||
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
private:
|
||||
ParticleTree* _tree;
|
||||
const char* _filename;
|
||||
int _persistInterval;
|
||||
bool _initialLoadComplete;
|
||||
|
||||
time_t _loadCompleted;
|
||||
uint64_t _loadTimeUSecs;
|
||||
uint64_t _lastCheck;
|
||||
};
|
||||
|
||||
#endif // __particle_server__ParticlePersistThread__
|
|
@ -1,276 +0,0 @@
|
|||
//
|
||||
// ParticleReceiverData.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "ParticleReceiverData.h"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include "ParticleSendThread.h"
|
||||
|
||||
ParticleReceiverData::ParticleReceiverData(Node* owningNode) :
|
||||
ParticleQuery(owningNode),
|
||||
_viewSent(false),
|
||||
_particlePacketAvailableBytes(MAX_PACKET_SIZE),
|
||||
_maxSearchLevel(1),
|
||||
_maxLevelReachedInLastSearch(1),
|
||||
_lastTimeBagEmpty(0),
|
||||
_viewFrustumChanging(false),
|
||||
_viewFrustumJustStoppedChanging(true),
|
||||
_currentPacketIsColor(true),
|
||||
_currentPacketIsCompressed(false),
|
||||
_particleSendThread(NULL),
|
||||
_lastClientBoundaryLevelAdjust(0),
|
||||
_lastClientParticleSizeScale(DEFAULT_PARTICLE_SIZE_SCALE),
|
||||
_lodChanged(false),
|
||||
_lodInitialized(false)
|
||||
{
|
||||
_particlePacket = new unsigned char[MAX_PACKET_SIZE];
|
||||
_particlePacketAt = _particlePacket;
|
||||
_lastParticlePacket = new unsigned char[MAX_PACKET_SIZE];
|
||||
_lastParticlePacketLength = 0;
|
||||
_duplicatePacketCount = 0;
|
||||
_sequenceNumber = 0;
|
||||
resetParticlePacket(true); // don't bump sequence
|
||||
}
|
||||
|
||||
void ParticleReceiverData::initializeParticleSendThread(ParticleServer* particleServer) {
|
||||
// Create particle sending thread...
|
||||
QUuid nodeUUID = getOwningNode()->getUUID();
|
||||
_particleSendThread = new ParticleSendThread(nodeUUID, particleServer);
|
||||
_particleSendThread->initialize(true);
|
||||
}
|
||||
|
||||
bool ParticleReceiverData::packetIsDuplicate() const {
|
||||
// since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp
|
||||
// of the entire packet, we need to compare only the packet content...
|
||||
if (_lastParticlePacketLength == getPacketLength()) {
|
||||
return memcmp(_lastParticlePacket + PARTICLE_PACKET_HEADER_SIZE,
|
||||
_particlePacket+PARTICLE_PACKET_HEADER_SIZE , getPacketLength() - PARTICLE_PACKET_HEADER_SIZE) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParticleReceiverData::shouldSuppressDuplicatePacket() {
|
||||
bool shouldSuppress = false; // assume we won't suppress
|
||||
|
||||
// only consider duplicate packets
|
||||
if (packetIsDuplicate()) {
|
||||
_duplicatePacketCount++;
|
||||
|
||||
// If this is the first suppressed packet, remember our time...
|
||||
if (_duplicatePacketCount == 1) {
|
||||
_firstSuppressedPacket = usecTimestampNow();
|
||||
}
|
||||
|
||||
// How long has it been since we've sent one, if we're still under our max time, then keep considering
|
||||
// this packet for suppression
|
||||
uint64_t now = usecTimestampNow();
|
||||
long sinceFirstSuppressedPacket = now - _firstSuppressedPacket;
|
||||
const long MAX_TIME_BETWEEN_DUPLICATE_PACKETS = 1000 * 1000; // 1 second.
|
||||
|
||||
if (sinceFirstSuppressedPacket < MAX_TIME_BETWEEN_DUPLICATE_PACKETS) {
|
||||
// Finally, if we know we've sent at least one duplicate out, then suppress the rest...
|
||||
if (_duplicatePacketCount >= 1) {
|
||||
shouldSuppress = true;
|
||||
}
|
||||
} else {
|
||||
// Reset our count, we've reached our maximum time.
|
||||
_duplicatePacketCount = 0;
|
||||
}
|
||||
} else {
|
||||
// Reset our count, it wasn't a duplicate
|
||||
_duplicatePacketCount = 0;
|
||||
}
|
||||
return shouldSuppress;
|
||||
}
|
||||
|
||||
void ParticleReceiverData::resetParticlePacket(bool lastWasSurpressed) {
|
||||
// Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has
|
||||
// changed since we last reset it. Since we know that no two packets can ever be identical without being the same
|
||||
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
|
||||
// packet send rate.
|
||||
_lastParticlePacketLength = getPacketLength();
|
||||
memcpy(_lastParticlePacket, _particlePacket, _lastParticlePacketLength);
|
||||
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
_currentPacketIsColor = getWantColor();
|
||||
_currentPacketIsCompressed = getWantCompression();
|
||||
PARTICLE_PACKET_FLAGS flags = 0;
|
||||
if (_currentPacketIsColor) {
|
||||
setAtBit(flags,PACKET_IS_COLOR_BIT);
|
||||
}
|
||||
if (_currentPacketIsCompressed) {
|
||||
setAtBit(flags,PACKET_IS_COMPRESSED_BIT);
|
||||
}
|
||||
|
||||
_particlePacketAvailableBytes = MAX_PACKET_SIZE;
|
||||
int numBytesPacketHeader = populateTypeAndVersion(_particlePacket, PACKET_TYPE_PARTICLE_DATA);
|
||||
_particlePacketAt = _particlePacket + numBytesPacketHeader;
|
||||
_particlePacketAvailableBytes -= numBytesPacketHeader;
|
||||
|
||||
// pack in flags
|
||||
PARTICLE_PACKET_FLAGS* flagsAt = (PARTICLE_PACKET_FLAGS*)_particlePacketAt;
|
||||
*flagsAt = flags;
|
||||
_particlePacketAt += sizeof(PARTICLE_PACKET_FLAGS);
|
||||
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_FLAGS);
|
||||
|
||||
// pack in sequence number
|
||||
PARTICLE_PACKET_SEQUENCE* sequenceAt = (PARTICLE_PACKET_SEQUENCE*)_particlePacketAt;
|
||||
*sequenceAt = _sequenceNumber;
|
||||
_particlePacketAt += sizeof(PARTICLE_PACKET_SEQUENCE);
|
||||
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_SEQUENCE);
|
||||
if (!(lastWasSurpressed || _lastParticlePacketLength == PARTICLE_PACKET_HEADER_SIZE)) {
|
||||
_sequenceNumber++;
|
||||
}
|
||||
|
||||
// pack in timestamp
|
||||
PARTICLE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
PARTICLE_PACKET_SENT_TIME* timeAt = (PARTICLE_PACKET_SENT_TIME*)_particlePacketAt;
|
||||
*timeAt = now;
|
||||
_particlePacketAt += sizeof(PARTICLE_PACKET_SENT_TIME);
|
||||
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_SENT_TIME);
|
||||
|
||||
_particlePacketWaiting = false;
|
||||
}
|
||||
|
||||
void ParticleReceiverData::writeToPacket(const unsigned char* buffer, int bytes) {
|
||||
// compressed packets include lead bytes which contain compressed size, this allows packing of
|
||||
// multiple compressed portions together
|
||||
if (_currentPacketIsCompressed) {
|
||||
*(PARTICLE_PACKET_INTERNAL_SECTION_SIZE*)_particlePacketAt = bytes;
|
||||
_particlePacketAt += sizeof(PARTICLE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
}
|
||||
if (bytes <= _particlePacketAvailableBytes) {
|
||||
memcpy(_particlePacketAt, buffer, bytes);
|
||||
_particlePacketAvailableBytes -= bytes;
|
||||
_particlePacketAt += bytes;
|
||||
_particlePacketWaiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
ParticleReceiverData::~ParticleReceiverData() {
|
||||
delete[] _particlePacket;
|
||||
delete[] _lastParticlePacket;
|
||||
|
||||
if (_particleSendThread) {
|
||||
_particleSendThread->terminate();
|
||||
delete _particleSendThread;
|
||||
}
|
||||
}
|
||||
|
||||
bool ParticleReceiverData::updateCurrentViewFrustum() {
|
||||
bool currentViewFrustumChanged = false;
|
||||
ViewFrustum newestViewFrustum;
|
||||
// get position and orientation details from the camera
|
||||
newestViewFrustum.setPosition(getCameraPosition());
|
||||
newestViewFrustum.setOrientation(getCameraOrientation());
|
||||
|
||||
// Also make sure it's got the correct lens details from the camera
|
||||
float originalFOV = getCameraFov();
|
||||
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
|
||||
|
||||
newestViewFrustum.setFieldOfView(wideFOV); // hack
|
||||
newestViewFrustum.setAspectRatio(getCameraAspectRatio());
|
||||
newestViewFrustum.setNearClip(getCameraNearClip());
|
||||
newestViewFrustum.setFarClip(getCameraFarClip());
|
||||
newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition());
|
||||
|
||||
// if there has been a change, then recalculate
|
||||
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
|
||||
_currentViewFrustum = newestViewFrustum;
|
||||
_currentViewFrustum.calculate();
|
||||
currentViewFrustumChanged = true;
|
||||
}
|
||||
|
||||
// Also check for LOD changes from the client
|
||||
if (_lodInitialized) {
|
||||
if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
|
||||
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||
_lodChanged = true;
|
||||
}
|
||||
if (_lastClientParticleSizeScale != getParticleSizeScale()) {
|
||||
_lastClientParticleSizeScale = getParticleSizeScale();
|
||||
_lodChanged = true;
|
||||
}
|
||||
} else {
|
||||
_lodInitialized = true;
|
||||
_lastClientParticleSizeScale = getParticleSizeScale();
|
||||
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||
_lodChanged = false;
|
||||
}
|
||||
|
||||
// When we first detect that the view stopped changing, we record this.
|
||||
// but we don't change it back to false until we've completely sent this
|
||||
// scene.
|
||||
if (_viewFrustumChanging && !currentViewFrustumChanged) {
|
||||
_viewFrustumJustStoppedChanging = true;
|
||||
}
|
||||
_viewFrustumChanging = currentViewFrustumChanged;
|
||||
return currentViewFrustumChanged;
|
||||
}
|
||||
|
||||
void ParticleReceiverData::setViewSent(bool viewSent) {
|
||||
_viewSent = viewSent;
|
||||
if (viewSent) {
|
||||
_viewFrustumJustStoppedChanging = false;
|
||||
_lodChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleReceiverData::updateLastKnownViewFrustum() {
|
||||
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
|
||||
|
||||
if (frustumChanges) {
|
||||
// save our currentViewFrustum into our lastKnownViewFrustum
|
||||
_lastKnownViewFrustum = _currentViewFrustum;
|
||||
}
|
||||
|
||||
// save that we know the view has been sent.
|
||||
uint64_t now = usecTimestampNow();
|
||||
setLastTimeBagEmpty(now); // is this what we want? poor names
|
||||
}
|
||||
|
||||
|
||||
bool ParticleReceiverData::moveShouldDump() const {
|
||||
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
|
||||
glm::vec3 newPosition = _currentViewFrustum.getPosition();
|
||||
|
||||
// theoretically we could make this slightly larger but relative to avatar scale.
|
||||
const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f;
|
||||
if (glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ParticleReceiverData::dumpOutOfView() {
|
||||
int stillInView = 0;
|
||||
int outOfView = 0;
|
||||
ParticleNodeBag tempBag;
|
||||
while (!nodeBag.isEmpty()) {
|
||||
ParticleNode* node = nodeBag.extract();
|
||||
if (node->isInView(_currentViewFrustum)) {
|
||||
tempBag.insert(node);
|
||||
stillInView++;
|
||||
} else {
|
||||
outOfView++;
|
||||
}
|
||||
}
|
||||
if (stillInView > 0) {
|
||||
while (!tempBag.isEmpty()) {
|
||||
ParticleNode* node = tempBag.extract();
|
||||
if (node->isInView(_currentViewFrustum)) {
|
||||
nodeBag.insert(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
//
|
||||
// ParticleReceiverData.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __hifi__ParticleReceiverData__
|
||||
#define __hifi__ParticleReceiverData__
|
||||
|
||||
#include <iostream>
|
||||
#include <NodeData.h>
|
||||
#include <ParticlePacketData.h>
|
||||
#include <ParticleQuery.h>
|
||||
|
||||
#include <CoverageMap.h>
|
||||
#include <ParticleConstants.h>
|
||||
#include <ParticleNodeBag.h>
|
||||
#include <ParticleSceneStats.h>
|
||||
|
||||
class ParticleSendThread;
|
||||
class ParticleServer;
|
||||
|
||||
class ParticleReceiverData : public ParticleQuery {
|
||||
public:
|
||||
ParticleReceiverData(Node* owningNode);
|
||||
virtual ~ParticleReceiverData();
|
||||
|
||||
void resetParticlePacket(bool lastWasSurpressed = false); // resets particle packet to after "V" header
|
||||
|
||||
void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet
|
||||
|
||||
const unsigned char* getPacket() const { return _particlePacket; }
|
||||
int getPacketLength() const { return (MAX_PACKET_SIZE - _particlePacketAvailableBytes); }
|
||||
bool isPacketWaiting() const { return _particlePacketWaiting; }
|
||||
|
||||
bool packetIsDuplicate() const;
|
||||
bool shouldSuppressDuplicatePacket();
|
||||
|
||||
int getAvailable() const { return _particlePacketAvailableBytes; }
|
||||
int getMaxSearchLevel() const { return _maxSearchLevel; }
|
||||
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
|
||||
void incrementMaxSearchLevel() { _maxSearchLevel++; }
|
||||
|
||||
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }
|
||||
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
|
||||
|
||||
ParticleNodeBag nodeBag;
|
||||
CoverageMap map;
|
||||
|
||||
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
|
||||
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }
|
||||
|
||||
// These are not classic setters because they are calculating and maintaining state
|
||||
// which is set asynchronously through the network receive
|
||||
bool updateCurrentViewFrustum();
|
||||
void updateLastKnownViewFrustum();
|
||||
|
||||
bool getViewSent() const { return _viewSent; }
|
||||
void setViewSent(bool viewSent);
|
||||
|
||||
bool getViewFrustumChanging() const { return _viewFrustumChanging; }
|
||||
bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; }
|
||||
|
||||
bool moveShouldDump() const;
|
||||
|
||||
uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
|
||||
void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }
|
||||
|
||||
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }
|
||||
bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; }
|
||||
bool getCurrentPacketFormatMatches() {
|
||||
return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression());
|
||||
}
|
||||
|
||||
bool hasLodChanged() const { return _lodChanged; };
|
||||
|
||||
ParticleSceneStats stats;
|
||||
|
||||
void initializeParticleSendThread(ParticleServer* particleServer);
|
||||
bool isParticleSendThreadInitalized() { return _particleSendThread; }
|
||||
|
||||
void dumpOutOfView();
|
||||
|
||||
private:
|
||||
ParticleReceiverData(const ParticleReceiverData &);
|
||||
ParticleReceiverData& operator= (const ParticleReceiverData&);
|
||||
|
||||
bool _viewSent;
|
||||
unsigned char* _particlePacket;
|
||||
unsigned char* _particlePacketAt;
|
||||
int _particlePacketAvailableBytes;
|
||||
bool _particlePacketWaiting;
|
||||
|
||||
unsigned char* _lastParticlePacket;
|
||||
int _lastParticlePacketLength;
|
||||
int _duplicatePacketCount;
|
||||
uint64_t _firstSuppressedPacket;
|
||||
|
||||
int _maxSearchLevel;
|
||||
int _maxLevelReachedInLastSearch;
|
||||
ViewFrustum _currentViewFrustum;
|
||||
ViewFrustum _lastKnownViewFrustum;
|
||||
uint64_t _lastTimeBagEmpty;
|
||||
bool _viewFrustumChanging;
|
||||
bool _viewFrustumJustStoppedChanging;
|
||||
bool _currentPacketIsColor;
|
||||
bool _currentPacketIsCompressed;
|
||||
|
||||
ParticleSendThread* _particleSendThread;
|
||||
|
||||
// watch for LOD changes
|
||||
int _lastClientBoundaryLevelAdjust;
|
||||
float _lastClientParticleSizeScale;
|
||||
bool _lodChanged;
|
||||
bool _lodInitialized;
|
||||
|
||||
PARTICLE_PACKET_SEQUENCE _sequenceNumber;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleReceiverData__) */
|
|
@ -1,551 +0,0 @@
|
|||
//
|
||||
// ParticleSendThread.cpp
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded particle packet sender
|
||||
//
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "ParticleSendThread.h"
|
||||
#include "ParticleServer.h"
|
||||
#include "ParticleServerConsts.h"
|
||||
|
||||
|
||||
uint64_t startSceneSleepTime = 0;
|
||||
uint64_t endSceneSleepTime = 0;
|
||||
|
||||
|
||||
ParticleSendThread::ParticleSendThread(const QUuid& nodeUUID, ParticleServer* myServer) :
|
||||
_nodeUUID(nodeUUID),
|
||||
_myServer(myServer),
|
||||
_packetData()
|
||||
{
|
||||
}
|
||||
|
||||
bool ParticleSendThread::process() {
|
||||
uint64_t start = usecTimestampNow();
|
||||
bool gotLock = false;
|
||||
|
||||
// don't do any send processing until the initial load of the particles is complete...
|
||||
if (_myServer->isInitialLoadComplete()) {
|
||||
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
|
||||
|
||||
if (node) {
|
||||
// make sure the node list doesn't kill our node while we're using it
|
||||
if (node->trylock()) {
|
||||
gotLock = true;
|
||||
ParticleReceiverData* nodeData = NULL;
|
||||
|
||||
nodeData = (ParticleReceiverData*) node->getLinkedData();
|
||||
|
||||
int packetsSent = 0;
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
|
||||
}
|
||||
packetsSent = deepestLevelParticleDistributor(node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
|
||||
node->unlock(); // we're done with this node for now.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
qDebug("ParticleSendThread::process() waiting for isInitialLoadComplete()\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap
|
||||
if (isStillRunning() && gotLock) {
|
||||
// dynamically sleep until we need to fire off the next set of particles
|
||||
int elapsed = (usecTimestampNow() - start);
|
||||
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
PerformanceWarning warn(false,"ParticleSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
std::cout << "Last send took too much time, not sleeping!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
||||
|
||||
uint64_t ParticleSendThread::_usleepTime = 0;
|
||||
uint64_t ParticleSendThread::_usleepCalls = 0;
|
||||
|
||||
uint64_t ParticleSendThread::_totalBytes = 0;
|
||||
uint64_t ParticleSendThread::_totalWastedBytes = 0;
|
||||
uint64_t ParticleSendThread::_totalPackets = 0;
|
||||
|
||||
int ParticleSendThread::handlePacketSend(Node* node, ParticleReceiverData* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
bool debug = _myServer->wantsDebugParticleSending();
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
||||
bool packetSent = false; // did we send a packet?
|
||||
int packetsSent = 0;
|
||||
// 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.
|
||||
if (nodeData->shouldSuppressDuplicatePacket()) {
|
||||
nodeData->resetParticlePacket(true); // we still need to reset it though!
|
||||
return packetsSent; // without sending...
|
||||
}
|
||||
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(messageData);
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(VOXEL_PACKET_FLAGS);
|
||||
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
|
||||
|
||||
|
||||
// If we've got a stats message ready to send, then see if we can piggyback them together
|
||||
if (nodeData->stats.isReadyToSend()) {
|
||||
// Send the stats message to the client
|
||||
unsigned char* statsMessage = nodeData->stats.getStatsMessage();
|
||||
int statsMessageLength = nodeData->stats.getStatsMessageLength();
|
||||
|
||||
// If the size of the stats message and the particle message will fit in a packet, then piggyback them
|
||||
if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) {
|
||||
|
||||
// copy particle message to back of stats message
|
||||
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
|
||||
statsMessageLength += nodeData->getPacketLength();
|
||||
|
||||
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
|
||||
// there was nothing else to send.
|
||||
int thisWastedBytes = 0;
|
||||
_totalWastedBytes += thisWastedBytes;
|
||||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
qDebug("Adding stats to packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n",
|
||||
now,
|
||||
_totalPackets,
|
||||
sequence, nodeData->getPacketLength(), _totalBytes,
|
||||
thisWastedBytes, _totalWastedBytes);
|
||||
}
|
||||
|
||||
// actually send it
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
packetSent = true;
|
||||
} else {
|
||||
// not enough room in the packet, send two packets
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
|
||||
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
|
||||
// there was nothing else to send.
|
||||
int thisWastedBytes = 0;
|
||||
_totalWastedBytes += thisWastedBytes;
|
||||
_totalBytes += statsMessageLength;
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
qDebug("Sending separate stats packet at %llu [%llu]: size:%d [%llu] wasted bytes:%d [%llu]\n",
|
||||
now,
|
||||
_totalPackets,
|
||||
statsMessageLength, _totalBytes,
|
||||
thisWastedBytes, _totalWastedBytes);
|
||||
}
|
||||
|
||||
trueBytesSent += statsMessageLength;
|
||||
truePacketsSent++;
|
||||
packetsSent++;
|
||||
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
|
||||
packetSent = true;
|
||||
|
||||
thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
|
||||
_totalWastedBytes += thisWastedBytes;
|
||||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
qDebug("Sending packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n",
|
||||
now,
|
||||
_totalPackets,
|
||||
sequence, nodeData->getPacketLength(), _totalBytes,
|
||||
thisWastedBytes, _totalWastedBytes);
|
||||
}
|
||||
}
|
||||
nodeData->stats.markAsSent();
|
||||
} else {
|
||||
// If there's actually a packet waiting, then send it.
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
// just send the particle packet
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
packetSent = true;
|
||||
|
||||
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
|
||||
_totalWastedBytes += thisWastedBytes;
|
||||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
qDebug("Sending packet at %llu [%llu]: sequence:%d size:%d [%llu] wasted bytes:%d [%llu]\n",
|
||||
now,
|
||||
_totalPackets,
|
||||
sequence, nodeData->getPacketLength(), _totalBytes,
|
||||
thisWastedBytes, _totalWastedBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
// remember to track our stats
|
||||
if (packetSent) {
|
||||
nodeData->stats.packetSent(nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
packetsSent++;
|
||||
nodeData->resetParticlePacket();
|
||||
}
|
||||
|
||||
return packetsSent;
|
||||
}
|
||||
|
||||
/// Version of particle distributor that sends the deepest LOD level at once
|
||||
int ParticleSendThread::deepestLevelParticleDistributor(Node* node, ParticleReceiverData* nodeData, bool viewFrustumChanged) {
|
||||
|
||||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
bool somethingToSend = true; // assume we have something
|
||||
|
||||
// FOR NOW... node tells us if it wants to receive only view frustum deltas
|
||||
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
|
||||
|
||||
// If our packet already has content in it, then we must use the color choice of the waiting packet.
|
||||
// If we're starting a fresh packet, then...
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
bool wantColor = nodeData->getWantColor();
|
||||
bool wantCompression = nodeData->getWantCompression();
|
||||
|
||||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
// then let's just send that waiting packet.
|
||||
if (!nodeData->getCurrentPacketFormatMatches()) {
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(wantCompression),
|
||||
debug::valueOf(nodeData->getCurrentPacketIsColor()),
|
||||
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
|
||||
}
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
} else {
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(wantCompression),
|
||||
debug::valueOf(nodeData->getCurrentPacketIsColor()),
|
||||
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
|
||||
}
|
||||
nodeData->resetParticlePacket();
|
||||
}
|
||||
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
|
||||
if (wantCompression) {
|
||||
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
|
||||
}
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
|
||||
debug::valueOf(wantCompression), targetSize);
|
||||
}
|
||||
|
||||
_packetData.changeSettings(wantCompression, targetSize);
|
||||
}
|
||||
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
|
||||
debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()),
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
|
||||
}
|
||||
|
||||
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
|
||||
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("deepestLevelParticleDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
|
||||
debug::valueOf(nodeData->getViewSent())
|
||||
);
|
||||
}
|
||||
|
||||
// If the current view frustum has changed OR we have nothing to send, then search against
|
||||
// the current view frustum for things to send.
|
||||
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
|
||||
if (nodeData->getLastTimeBagEmpty() > 0) {
|
||||
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
|
||||
if (viewFrustumChanged) {
|
||||
printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
} else {
|
||||
printf("elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
}
|
||||
printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n",
|
||||
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
|
||||
debug::valueOf(wantColor));
|
||||
}
|
||||
}
|
||||
|
||||
// if our view has changed, we need to reset these things...
|
||||
if (viewFrustumChanged) {
|
||||
if (_myServer->wantDumpParticlesOnMove() || nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
|
||||
nodeData->dumpOutOfView();
|
||||
}
|
||||
nodeData->map.erase();
|
||||
}
|
||||
|
||||
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
|
||||
// only set our last sent time if we weren't resetting due to frustum change
|
||||
uint64_t now = usecTimestampNow();
|
||||
nodeData->setLastTimeBagEmpty(now);
|
||||
}
|
||||
|
||||
// track completed scenes and send out the stats packet accordingly
|
||||
nodeData->stats.sceneCompleted();
|
||||
::endSceneSleepTime = _usleepTime;
|
||||
unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime;
|
||||
|
||||
unsigned long encodeTime = nodeData->stats.getTotalEncodeTime();
|
||||
unsigned long elapsedTime = nodeData->stats.getElapsedTime();
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
|
||||
if (_myServer->wantsDebugParticleSending()) {
|
||||
qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n",
|
||||
usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes);
|
||||
}
|
||||
|
||||
if (_myServer->wantDisplayParticleStats()) {
|
||||
nodeData->stats.printDebugDetails();
|
||||
}
|
||||
|
||||
// start tracking our stats
|
||||
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta())
|
||||
&& nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
|
||||
|
||||
// If we're starting a full scene, then definitely we want to empty the nodeBag
|
||||
if (isFullScene) {
|
||||
nodeData->nodeBag.deleteAll();
|
||||
}
|
||||
|
||||
if (_myServer->wantsDebugParticleSending()) {
|
||||
qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n",
|
||||
usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes);
|
||||
}
|
||||
|
||||
::startSceneSleepTime = _usleepTime;
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
|
||||
_myServer->getServerTree().rootNode, _myServer->getJurisdiction());
|
||||
|
||||
// This is the start of "resending" the scene.
|
||||
bool dontRestartSceneOnMove = false; // this is experimental
|
||||
if (dontRestartSceneOnMove) {
|
||||
if (nodeData->nodeBag.isEmpty()) {
|
||||
nodeData->nodeBag.insert(_myServer->getServerTree().rootNode); // only in case of empty
|
||||
}
|
||||
} else {
|
||||
nodeData->nodeBag.insert(_myServer->getServerTree().rootNode); // original behavior, reset on move or empty
|
||||
}
|
||||
}
|
||||
|
||||
// If we have something in our nodeBag, then turn them into packets and send them out...
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
int bytesWritten = 0;
|
||||
uint64_t start = usecTimestampNow();
|
||||
uint64_t startCompressTimeMsecs = ParticlePacketData::getCompressContentTime() / 1000;
|
||||
uint64_t startCompressCalls = ParticlePacketData::getCompressContentCalls();
|
||||
|
||||
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxParticlePacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
|
||||
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
|
||||
nodeData->getMaxParticlePacketsPerSecond(), clientMaxPacketsPerInterval);
|
||||
}
|
||||
|
||||
int extraPackingAttempts = 0;
|
||||
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) {
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
|
||||
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
|
||||
nodeData->getMaxParticlePacketsPerSecond(), clientMaxPacketsPerInterval);
|
||||
}
|
||||
|
||||
bool lastNodeDidntFit = false; // assume each node fits
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
ParticleNode* subTree = nodeData->nodeBag.extract();
|
||||
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
|
||||
|
||||
float particleSizeScale = nodeData->getParticleSizeScale();
|
||||
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
|
||||
|
||||
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
|
||||
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
|
||||
|
||||
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) &&
|
||||
nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, particleSizeScale,
|
||||
nodeData->getLastTimeBagEmpty(),
|
||||
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
|
||||
|
||||
|
||||
_myServer->getServerTree().lockForRead();
|
||||
nodeData->stats.encodeStarted();
|
||||
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
|
||||
|
||||
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
|
||||
if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) {
|
||||
if (_packetData.hasContent() && bytesWritten == 0 &&
|
||||
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
}
|
||||
} else {
|
||||
// in compressed mode and we are trying to pack more... and we don't care if the _packetData has
|
||||
// content or not... because in this case even if we were unable to pack any data, we want to drop
|
||||
// below to our sendNow logic, but we do want to track that we attempted to pack extra
|
||||
extraPackingAttempts++;
|
||||
if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
}
|
||||
}
|
||||
|
||||
nodeData->stats.encodeStopped();
|
||||
_myServer->getServerTree().unlock();
|
||||
} else {
|
||||
// If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0
|
||||
bytesWritten = 0;
|
||||
somethingToSend = false; // this will cause us to drop out of the loop...
|
||||
}
|
||||
|
||||
// If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a
|
||||
// little bit more in this packet. To do this we
|
||||
|
||||
// We only consider sending anything if there is something in the _packetData to send... But
|
||||
// if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases
|
||||
// mean we should send the previous packet contents and reset it.
|
||||
if (lastNodeDidntFit) {
|
||||
if (_packetData.hasContent()) {
|
||||
// if for some reason the finalized size is greater than our available size, then probably the "compressed"
|
||||
// form actually inflated beyond our padding, and in this case we will send the current packet, then
|
||||
// write to out new packet...
|
||||
int writtenSize = _packetData.getFinalizedSize()
|
||||
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0);
|
||||
|
||||
|
||||
if (writtenSize > nodeData->getAvailable()) {
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n",
|
||||
writtenSize, nodeData->getAvailable());
|
||||
}
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
}
|
||||
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n",
|
||||
nodeData->getAvailable(), _packetData.getFinalizedSize(),
|
||||
_packetData.getUncompressedSize(), _packetData.getTargetSize());
|
||||
}
|
||||
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
|
||||
extraPackingAttempts = 0;
|
||||
}
|
||||
|
||||
// If we're not running compressed, the we know we can just send now. Or if we're running compressed, but
|
||||
// the packet doesn't have enough space to bother attempting to pack more...
|
||||
bool sendNow = true;
|
||||
|
||||
if (nodeData->getCurrentPacketIsCompressed() &&
|
||||
nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
|
||||
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
|
||||
sendNow = false; // try to pack more
|
||||
}
|
||||
|
||||
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
|
||||
if (sendNow) {
|
||||
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
if (wantCompression) {
|
||||
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
|
||||
}
|
||||
} else {
|
||||
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
|
||||
// but we've finalized the _packetData, so we want to start a new section, we will do that by
|
||||
// resetting the packet settings with the max uncompressed size of our current available space
|
||||
// in the wire packet. We also include room for our section header, and a little bit of padding
|
||||
// to account for the fact that whenc compressing small amounts of data, we sometimes end up with
|
||||
// a larger compressed size then uncompressed size
|
||||
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
|
||||
}
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
|
||||
debug::valueOf(nodeData->getWantCompression()), targetSize);
|
||||
}
|
||||
_packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
|
||||
uint64_t endCompressCalls = ParticlePacketData::getCompressContentCalls();
|
||||
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
|
||||
|
||||
uint64_t endCompressTimeMsecs = ParticlePacketData::getCompressContentTime() / 1000;
|
||||
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
|
||||
|
||||
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
int elapsedsec = (end - start)/1000000;
|
||||
printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n",
|
||||
elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
} else {
|
||||
printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
|
||||
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
}
|
||||
} else if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
|
||||
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
}
|
||||
|
||||
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
|
||||
// the particles from the current view frustum
|
||||
if (nodeData->nodeBag.isEmpty()) {
|
||||
nodeData->updateLastKnownViewFrustum();
|
||||
nodeData->setViewSent(true);
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
nodeData->map.printStats();
|
||||
}
|
||||
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
|
||||
}
|
||||
|
||||
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
|
||||
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
|
||||
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
|
||||
nodeData->getMaxParticlePacketsPerSecond(), clientMaxPacketsPerInterval);
|
||||
}
|
||||
|
||||
} // end if bag wasn't empty, and so we sent stuff...
|
||||
|
||||
return truePacketsSent;
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
//
|
||||
// ParticleSendThread.h
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded object for sending particles to a client
|
||||
//
|
||||
|
||||
#ifndef __particle_server__ParticleSendThread__
|
||||
#define __particle_server__ParticleSendThread__
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include <NetworkPacket.h>
|
||||
#include <ParticleTree.h>
|
||||
#include <ParticleNodeBag.h>
|
||||
#include "ParticleReceiverData.h"
|
||||
#include "ParticleServer.h"
|
||||
|
||||
/// Threaded processor for sending particle packets to a single client
|
||||
class ParticleSendThread : public virtual GenericThread {
|
||||
public:
|
||||
ParticleSendThread(const QUuid& nodeUUID, ParticleServer* myServer);
|
||||
|
||||
static uint64_t _totalBytes;
|
||||
static uint64_t _totalWastedBytes;
|
||||
static uint64_t _totalPackets;
|
||||
|
||||
static uint64_t _usleepTime;
|
||||
static uint64_t _usleepCalls;
|
||||
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
|
||||
private:
|
||||
QUuid _nodeUUID;
|
||||
ParticleServer* _myServer;
|
||||
|
||||
int handlePacketSend(Node* node, ParticleReceiverData* nodeData, int& trueBytesSent, int& truePacketsSent);
|
||||
int deepestLevelParticleDistributor(Node* node, ParticleReceiverData* nodeData, bool viewFrustumChanged);
|
||||
|
||||
ParticlePacketData _packetData;
|
||||
};
|
||||
|
||||
#endif // __particle_server__ParticleSendThread__
|
|
@ -1,802 +0,0 @@
|
|||
//
|
||||
// ParticleSever.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <time.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <Logging.h>
|
||||
#include <OctalCode.h>
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
//#include <ParticleTree.h>
|
||||
#include "ParticleReceiverData.h"
|
||||
#include <SharedUtil.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SceneUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <JurisdictionSender.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#include "Systime.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include "ParticleSever.h"
|
||||
#include "ParticleSeverConsts.h"
|
||||
|
||||
const char* LOCAL_PARTICLES_PERSIST_FILE = "resources/particles.svo";
|
||||
const char* PARTICLES_PERSIST_FILE = "/etc/highfidelity/particle-server/resources/particles.svo";
|
||||
|
||||
void attachParticleReceiverDataToNode(Node* newNode) {
|
||||
if (newNode->getLinkedData() == NULL) {
|
||||
ParticleReceiverData* particleNodeData = new ParticleReceiverData(newNode);
|
||||
newNode->setLinkedData(particleNodeData);
|
||||
}
|
||||
}
|
||||
|
||||
ParticleSever* ParticleSever::_theInstance = NULL;
|
||||
|
||||
ParticleSever::ParticleSever(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes),
|
||||
_serverTree(true) {
|
||||
_argc = 0;
|
||||
_argv = NULL;
|
||||
|
||||
_packetsPerClientPerInterval = 10;
|
||||
_wantParticlePersist = true;
|
||||
_wantLocalDomain = false;
|
||||
_debugParticleSending = false;
|
||||
_shouldShowAnimationDebug = false;
|
||||
_displayParticleStats = false;
|
||||
_debugParticleReceiving = false;
|
||||
_sendEnvironments = true;
|
||||
_sendMinimalEnvironment = false;
|
||||
_dumpParticlesOnMove = false;
|
||||
_verboseDebug = false;
|
||||
_jurisdiction = NULL;
|
||||
_jurisdictionSender = NULL;
|
||||
_ParticleSeverPacketProcessor = NULL;
|
||||
_particlePersistThread = NULL;
|
||||
_parsedArgV = NULL;
|
||||
|
||||
_started = time(0);
|
||||
_startedUSecs = usecTimestampNow();
|
||||
|
||||
_theInstance = this;
|
||||
}
|
||||
|
||||
ParticleSever::~ParticleSever() {
|
||||
if (_parsedArgV) {
|
||||
for (int i = 0; i < _argc; i++) {
|
||||
delete[] _parsedArgV[i];
|
||||
}
|
||||
delete[] _parsedArgV;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSever::initMongoose(int port) {
|
||||
// setup the mongoose web server
|
||||
struct mg_callbacks callbacks = {};
|
||||
|
||||
QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
|
||||
QString listenPort = QString("%1").arg(port);
|
||||
|
||||
|
||||
// list of options. Last element must be NULL.
|
||||
const char* options[] = {
|
||||
"listening_ports", listenPort.toLocal8Bit().constData(),
|
||||
"document_root", documentRoot.toLocal8Bit().constData(),
|
||||
NULL };
|
||||
|
||||
callbacks.begin_request = civetwebRequestHandler;
|
||||
|
||||
// Start the web server.
|
||||
mg_start(&callbacks, NULL, options);
|
||||
}
|
||||
|
||||
int ParticleSever::civetwebRequestHandler(struct mg_connection* connection) {
|
||||
const struct mg_request_info* ri = mg_get_request_info(connection);
|
||||
|
||||
ParticleSever* theServer = GetInstance();
|
||||
|
||||
#ifdef FORCE_CRASH
|
||||
if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) {
|
||||
qDebug() << "About to force a crash!\n";
|
||||
int foo;
|
||||
int* forceCrash = &foo;
|
||||
mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
||||
mg_printf(connection, "%s", "forcing a crash....\r\n");
|
||||
delete[] forceCrash;
|
||||
mg_printf(connection, "%s", "did it crash....\r\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool showStats = false;
|
||||
if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) {
|
||||
showStats = true;
|
||||
}
|
||||
|
||||
if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) {
|
||||
theServer->_ParticleSeverPacketProcessor->resetStats();
|
||||
showStats = true;
|
||||
}
|
||||
|
||||
if (showStats) {
|
||||
uint64_t checkSum;
|
||||
// return a 200
|
||||
mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n");
|
||||
mg_printf(connection, "%s", "Content-Type: text/html\r\n\r\n");
|
||||
mg_printf(connection, "%s", "<html><doc>\r\n");
|
||||
mg_printf(connection, "%s", "<pre>\r\n");
|
||||
mg_printf(connection, "%s", "<b>Your Particle Server is running... <a href='/'>[RELOAD]</a></b>\r\n");
|
||||
|
||||
tm* localtm = localtime(&theServer->_started);
|
||||
const int MAX_TIME_LENGTH = 128;
|
||||
char buffer[MAX_TIME_LENGTH];
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
|
||||
mg_printf(connection, "Running since: %s", buffer);
|
||||
|
||||
// Convert now to tm struct for UTC
|
||||
tm* gmtm = gmtime(&theServer->_started);
|
||||
if (gmtm != NULL) {
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
|
||||
mg_printf(connection, " [%s UTM] ", buffer);
|
||||
}
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
const int USECS_PER_MSEC = 1000;
|
||||
uint64_t msecsElapsed = (now - theServer->_startedUSecs) / USECS_PER_MSEC;
|
||||
const int MSECS_PER_SEC = 1000;
|
||||
const int SECS_PER_MIN = 60;
|
||||
const int MIN_PER_HOUR = 60;
|
||||
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
||||
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
mg_printf(connection, "%s", "Uptime: ");
|
||||
if (hours > 0) {
|
||||
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
|
||||
}
|
||||
if (minutes > 0) {
|
||||
mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
|
||||
}
|
||||
if (seconds > 0) {
|
||||
mg_printf(connection, "%.3f seconds ", seconds);
|
||||
}
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
|
||||
// display particle file load time
|
||||
if (theServer->isInitialLoadComplete()) {
|
||||
time_t* loadCompleted = theServer->getLoadCompleted();
|
||||
if (loadCompleted) {
|
||||
tm* particlesLoadedAtLocal = localtime(loadCompleted);
|
||||
const int MAX_TIME_LENGTH = 128;
|
||||
char buffer[MAX_TIME_LENGTH];
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", particlesLoadedAtLocal);
|
||||
mg_printf(connection, "Particles Loaded At: %s", buffer);
|
||||
|
||||
// Convert now to tm struct for UTC
|
||||
tm* particlesLoadedAtUTM = gmtime(theServer->getLoadCompleted());
|
||||
if (gmtm != NULL) {
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", particlesLoadedAtUTM);
|
||||
mg_printf(connection, " [%s UTM] ", buffer);
|
||||
}
|
||||
} else {
|
||||
mg_printf(connection, "%s", "Particle Persist Disabled...\r\n");
|
||||
}
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
|
||||
uint64_t msecsElapsed = theServer->getLoadElapsedTime() / USECS_PER_MSEC;;
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
mg_printf(connection, "%s", "Particles Load Took: ");
|
||||
if (hours > 0) {
|
||||
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
|
||||
}
|
||||
if (minutes > 0) {
|
||||
mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
|
||||
}
|
||||
if (seconds >= 0) {
|
||||
mg_printf(connection, "%.3f seconds", seconds);
|
||||
}
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
} else {
|
||||
mg_printf(connection, "%s", "Particles not yet loaded...\r\n");
|
||||
}
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
mg_printf(connection, "%s", "<b>Configuration:</b>\r\n");
|
||||
|
||||
for (int i = 1; i < theServer->_argc; i++) {
|
||||
mg_printf(connection, "%s ", theServer->_argv[i]);
|
||||
}
|
||||
mg_printf(connection, "%s", "\r\n"); // one to end the config line
|
||||
mg_printf(connection, "%s", "\r\n"); // two more for spacing
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
// display scene stats
|
||||
unsigned long nodeCount = ParticleNode::getNodeCount();
|
||||
unsigned long internalNodeCount = ParticleNode::getInternalNodeCount();
|
||||
unsigned long leafNodeCount = ParticleNode::getLeafNodeCount();
|
||||
|
||||
QLocale locale(QLocale::English);
|
||||
const float AS_PERCENT = 100.0;
|
||||
mg_printf(connection, "%s", "<b>Current Nodes in scene:</b>\r\n");
|
||||
mg_printf(connection, " Total Nodes: %s nodes\r\n",
|
||||
locale.toString((uint)nodeCount).rightJustified(16, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Internal Nodes: %s nodes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||
((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, " Leaf Nodes: %s nodes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
|
||||
// display outbound packet stats
|
||||
mg_printf(connection, "%s", "<b>Particle Packet Statistics...</b>\r\n");
|
||||
uint64_t totalOutboundPackets = ParticleSendThread::_totalPackets;
|
||||
uint64_t totalOutboundBytes = ParticleSendThread::_totalBytes;
|
||||
uint64_t totalWastedBytes = ParticleSendThread::_totalWastedBytes;
|
||||
uint64_t totalBytesOfOctalCodes = ParticlePacketData::getTotalBytesOfOctalCodes();
|
||||
uint64_t totalBytesOfBitMasks = ParticlePacketData::getTotalBytesOfBitMasks();
|
||||
uint64_t totalBytesOfColor = ParticlePacketData::getTotalBytesOfColor();
|
||||
|
||||
const int COLUMN_WIDTH = 10;
|
||||
mg_printf(connection, " Total Outbound Packets: %s packets\r\n",
|
||||
locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Total Outbound Bytes: %s bytes\r\n",
|
||||
locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Total Wasted Bytes: %s bytes\r\n",
|
||||
locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfOctalCodes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
mg_printf(connection, " Total BitMasks Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfBitMasks).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
mg_printf(connection, " Total Color Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
// display inbound packet stats
|
||||
mg_printf(connection, "%s", "<b>Particle Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n");
|
||||
uint64_t averageTransitTimePerPacket = theServer->_ParticleSeverPacketProcessor->getAverageTransitTimePerPacket();
|
||||
uint64_t averageProcessTimePerPacket = theServer->_particleServerPacketProcessor->getAverageProcessTimePerPacket();
|
||||
uint64_t averageLockWaitTimePerPacket = theServer->_particleServerPacketProcessor->getAverageLockWaitTimePerPacket();
|
||||
uint64_t averageProcessTimePerParticle = theServer->_particleServerPacketProcessor->getAverageProcessTimePerParticle();
|
||||
uint64_t averageLockWaitTimePerParticle = theServer->_particleServerPacketProcessor->getAverageLockWaitTimePerParticle();
|
||||
uint64_t totalParticlesProcessed = theServer->_particleServerPacketProcessor->getTotalParticlesProcessed();
|
||||
uint64_t totalPacketsProcessed = theServer->_particleServerPacketProcessor->getTotalPacketsProcessed();
|
||||
|
||||
float averageParticlesPerPacket = totalPacketsProcessed == 0 ? 0 : totalParticlesProcessed / totalPacketsProcessed;
|
||||
|
||||
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
|
||||
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Total Inbound Particles: %s particles\r\n",
|
||||
locale.toString((uint)totalParticlesProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Inbound Particles/Packet: %f particles/packet\r\n", averageParticlesPerPacket);
|
||||
mg_printf(connection, " Average Transit Time/Packet: %s usecs\r\n",
|
||||
locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Process Time/Packet: %s usecs\r\n",
|
||||
locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Wait Lock Time/Packet: %s usecs\r\n",
|
||||
locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Process Time/Particle: %s usecs\r\n",
|
||||
locale.toString((uint)averageProcessTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Wait Lock Time/Particle: %s usecs\r\n",
|
||||
locale.toString((uint)averageLockWaitTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
|
||||
|
||||
int senderNumber = 0;
|
||||
NodeToSenderStatsMap& allSenderStats = theServer->_particleServerPacketProcessor->getSingleSenderStats();
|
||||
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
|
||||
senderNumber++;
|
||||
QUuid senderID = i->first;
|
||||
SingleSenderStats& senderStats = i->second;
|
||||
|
||||
mg_printf(connection, "\r\n Stats for sender %d uuid: %s\r\n", senderNumber,
|
||||
senderID.toString().toLocal8Bit().constData());
|
||||
|
||||
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
|
||||
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
|
||||
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
|
||||
averageProcessTimePerParticle = senderStats.getAverageProcessTimePerParticle();
|
||||
averageLockWaitTimePerParticle = senderStats.getAverageLockWaitTimePerParticle();
|
||||
totalParticlesProcessed = senderStats.getTotalParticlesProcessed();
|
||||
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
|
||||
|
||||
averageParticlesPerPacket = totalPacketsProcessed == 0 ? 0 : totalParticlesProcessed / totalPacketsProcessed;
|
||||
|
||||
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
|
||||
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Total Inbound Particles: %s particles\r\n",
|
||||
locale.toString((uint)totalParticlesProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Inbound Particles/Packet: %f particles/packet\r\n", averageParticlesPerPacket);
|
||||
mg_printf(connection, " Average Transit Time/Packet: %s usecs\r\n",
|
||||
locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Process Time/Packet: %s usecs\r\n",
|
||||
locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Wait Lock Time/Packet: %s usecs\r\n",
|
||||
locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Process Time/Particle: %s usecs\r\n",
|
||||
locale.toString((uint)averageProcessTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
mg_printf(connection, " Average Wait Lock Time/Particle: %s usecs\r\n",
|
||||
locale.toString((uint)averageLockWaitTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
|
||||
|
||||
}
|
||||
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
// display memory usage stats
|
||||
mg_printf(connection, "%s", "<b>Current Memory Usage Statistics</b>\r\n");
|
||||
mg_printf(connection, "\r\nParticleNode size... %ld bytes\r\n", sizeof(ParticleNode));
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
|
||||
const char* memoryScaleLabel;
|
||||
const float MEGABYTES = 1000000.f;
|
||||
const float GIGABYTES = 1000000000.f;
|
||||
float memoryScale;
|
||||
if (ParticleNode::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
|
||||
memoryScaleLabel = "MB";
|
||||
memoryScale = MEGABYTES;
|
||||
} else {
|
||||
memoryScaleLabel = "GB";
|
||||
memoryScale = GIGABYTES;
|
||||
}
|
||||
|
||||
mg_printf(connection, "Particle Node Memory Usage: %8.2f %s\r\n",
|
||||
ParticleNode::getParticleMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
mg_printf(connection, "Octcode Memory Usage: %8.2f %s\r\n",
|
||||
ParticleNode::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
mg_printf(connection, "External Children Memory Usage: %8.2f %s\r\n",
|
||||
ParticleNode::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
mg_printf(connection, "%s", " -----------\r\n");
|
||||
mg_printf(connection, " Total: %8.2f %s\r\n",
|
||||
ParticleNode::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "ParticleNode Children Population Statistics...\r\n");
|
||||
checkSum = 0;
|
||||
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
|
||||
checkSum += ParticleNode::getChildrenCount(i);
|
||||
mg_printf(connection, " Nodes with %d children: %s nodes (%5.2f%%)\r\n", i,
|
||||
locale.toString((uint)ParticleNode::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||
((float)ParticleNode::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
|
||||
}
|
||||
mg_printf(connection, "%s", " ----------------------\r\n");
|
||||
mg_printf(connection, " Total: %s nodes\r\n",
|
||||
locale.toString((uint)checkSum).rightJustified(16, ' ').toLocal8Bit().constData());
|
||||
|
||||
#ifdef BLENDED_UNION_CHILDREN
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "ParticleNode Children Encoding Statistics...\r\n");
|
||||
|
||||
mg_printf(connection, " Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
|
||||
ParticleNode::getSingleChildrenCount(), ((float)ParticleNode::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, " Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
||||
ParticleNode::getTwoChildrenOffsetCount(),
|
||||
((float)ParticleNode::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, " Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
||||
ParticleNode::getTwoChildrenExternalCount(),
|
||||
((float)ParticleNode::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, " Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
||||
ParticleNode::getThreeChildrenOffsetCount(),
|
||||
((float)ParticleNode::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, " Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
||||
ParticleNode::getThreeChildrenExternalCount(),
|
||||
((float)ParticleNode::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
||||
mg_printf(connection, " Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
|
||||
ParticleNode::getExternalChildrenCount(),
|
||||
((float)ParticleNode::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
||||
|
||||
checkSum = ParticleNode::getSingleChildrenCount() +
|
||||
ParticleNode::getTwoChildrenOffsetCount() + ParticleNode::getTwoChildrenExternalCount() +
|
||||
ParticleNode::getThreeChildrenOffsetCount() + ParticleNode::getThreeChildrenExternalCount() +
|
||||
ParticleNode::getExternalChildrenCount();
|
||||
|
||||
mg_printf(connection, "%s", " ----------------\r\n");
|
||||
mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum);
|
||||
mg_printf(connection, " Expected: %10.lu nodes\r\n", nodeCount);
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "In other news....\r\n");
|
||||
mg_printf(connection, "could store 4 children internally: %10.llu nodes\r\n",
|
||||
ParticleNode::getCouldStoreFourChildrenInternally());
|
||||
mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n",
|
||||
ParticleNode::getCouldNotStoreFourChildrenInternally());
|
||||
#endif
|
||||
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "\r\n");
|
||||
mg_printf(connection, "%s", "</pre>\r\n");
|
||||
|
||||
mg_printf(connection, "%s", "</doc></html>");
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
// have mongoose process this request from the document_root
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParticleSever::setArguments(int argc, char** argv) {
|
||||
_argc = argc;
|
||||
_argv = const_cast<const char**>(argv);
|
||||
|
||||
qDebug("ParticleSever::setArguments()\n");
|
||||
for (int i = 0; i < _argc; i++) {
|
||||
qDebug("_argv[%d]=%s\n", i, _argv[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ParticleSever::parsePayload() {
|
||||
|
||||
if (getNumPayloadBytes() > 0) {
|
||||
QString config((const char*) _payload);
|
||||
|
||||
// Now, parse the config
|
||||
QStringList configList = config.split(" ");
|
||||
|
||||
int argCount = configList.size() + 1;
|
||||
|
||||
qDebug("ParticleSever::parsePayload()... argCount=%d\n",argCount);
|
||||
|
||||
_parsedArgV = new char*[argCount];
|
||||
const char* dummy = "config-from-payload";
|
||||
_parsedArgV[0] = new char[strlen(dummy) + sizeof(char)];
|
||||
strcpy(_parsedArgV[0], dummy);
|
||||
|
||||
for (int i = 1; i < argCount; i++) {
|
||||
QString configItem = configList.at(i-1);
|
||||
_parsedArgV[i] = new char[configItem.length() + sizeof(char)];
|
||||
strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData());
|
||||
qDebug("ParticleSever::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
|
||||
}
|
||||
|
||||
setArguments(argCount, _parsedArgV);
|
||||
}
|
||||
}
|
||||
|
||||
//int main(int argc, const char * argv[]) {
|
||||
void ParticleSever::run() {
|
||||
|
||||
const char PARTICLE_SERVER_LOGGING_TARGET_NAME[] = "particle-server";
|
||||
|
||||
// change the logging target name while this is running
|
||||
Logging::setTargetName(PARTICLE_SERVER_LOGGING_TARGET_NAME);
|
||||
|
||||
// Now would be a good time to parse our arguments, if we got them as assignment
|
||||
if (getNumPayloadBytes() > 0) {
|
||||
parsePayload();
|
||||
}
|
||||
|
||||
qInstallMessageHandler(Logging::verboseMessageHandler);
|
||||
|
||||
const char* STATUS_PORT = "--statusPort";
|
||||
const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT);
|
||||
if (statusPort) {
|
||||
int statusPortNumber = atoi(statusPort);
|
||||
initMongoose(statusPortNumber);
|
||||
}
|
||||
|
||||
|
||||
const char* JURISDICTION_FILE = "--jurisdictionFile";
|
||||
const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE);
|
||||
if (jurisdictionFile) {
|
||||
qDebug("jurisdictionFile=%s\n", jurisdictionFile);
|
||||
|
||||
qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
|
||||
_jurisdiction = new JurisdictionMap(jurisdictionFile);
|
||||
qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
|
||||
} else {
|
||||
const char* JURISDICTION_ROOT = "--jurisdictionRoot";
|
||||
const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT);
|
||||
if (jurisdictionRoot) {
|
||||
qDebug("jurisdictionRoot=%s\n", jurisdictionRoot);
|
||||
}
|
||||
|
||||
const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes";
|
||||
const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES);
|
||||
if (jurisdictionEndNodes) {
|
||||
qDebug("jurisdictionEndNodes=%s\n", jurisdictionEndNodes);
|
||||
}
|
||||
|
||||
if (jurisdictionRoot || jurisdictionEndNodes) {
|
||||
_jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes);
|
||||
}
|
||||
}
|
||||
|
||||
// should we send environments? Default is yes, but this command line suppresses sending
|
||||
const char* DUMP_PARTICLES_ON_MOVE = "--dumpParticlesOnMove";
|
||||
_dumpParticlesOnMove = cmdOptionExists(_argc, _argv, DUMP_PARTICLES_ON_MOVE);
|
||||
qDebug("dumpParticlesOnMove=%s\n", debug::valueOf(_dumpParticlesOnMove));
|
||||
|
||||
// should we send environments? Default is yes, but this command line suppresses sending
|
||||
const char* SEND_ENVIRONMENTS = "--sendEnvironments";
|
||||
bool dontSendEnvironments = !cmdOptionExists(_argc, _argv, SEND_ENVIRONMENTS);
|
||||
if (dontSendEnvironments) {
|
||||
qDebug("Sending environments suppressed...\n");
|
||||
_sendEnvironments = false;
|
||||
} else {
|
||||
// should we send environments? Default is yes, but this command line suppresses sending
|
||||
const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment";
|
||||
_sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT);
|
||||
qDebug("Using Minimal Environment=%s\n", debug::valueOf(_sendMinimalEnvironment));
|
||||
}
|
||||
qDebug("Sending environments=%s\n", debug::valueOf(_sendEnvironments));
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_PARTICLE_SERVER);
|
||||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER};
|
||||
nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// tell our NodeList about our desire to get notifications
|
||||
nodeList->addHook(&_nodeWatcher);
|
||||
nodeList->linkedDataCreateCallback = &attachParticleReceiverDataToNode;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
srand((unsigned)time(0));
|
||||
|
||||
const char* DISPLAY_PARTICLE_STATS = "--displayParticleStats";
|
||||
_displayParticleStats = cmdOptionExists(_argc, _argv, DISPLAY_PARTICLE_STATS);
|
||||
qDebug("displayParticleStats=%s\n", debug::valueOf(_displayParticleStats));
|
||||
|
||||
const char* VERBOSE_DEBUG = "--verboseDebug";
|
||||
_verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG);
|
||||
qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug));
|
||||
|
||||
const char* DEBUG_PARTICLE_SENDING = "--debugParticleSending";
|
||||
_debugParticleSending = cmdOptionExists(_argc, _argv, DEBUG_PARTICLE_SENDING);
|
||||
qDebug("debugParticleSending=%s\n", debug::valueOf(_debugParticleSending));
|
||||
|
||||
const char* DEBUG_PARTICLE_RECEIVING = "--debugParticleReceiving";
|
||||
_debugParticleReceiving = cmdOptionExists(_argc, _argv, DEBUG_PARTICLE_RECEIVING);
|
||||
qDebug("debugParticleReceiving=%s\n", debug::valueOf(_debugParticleReceiving));
|
||||
|
||||
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
|
||||
_shouldShowAnimationDebug = cmdOptionExists(_argc, _argv, WANT_ANIMATION_DEBUG);
|
||||
qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug));
|
||||
|
||||
// By default we will particle persist, if you want to disable this, then pass in this parameter
|
||||
const char* NO_PARTICLE_PERSIST = "--NoParticlePersist";
|
||||
if (cmdOptionExists(_argc, _argv, NO_PARTICLE_PERSIST)) {
|
||||
_wantParticlePersist = false;
|
||||
}
|
||||
qDebug("wantParticlePersist=%s\n", debug::valueOf(_wantParticlePersist));
|
||||
|
||||
// if we want Particle Persistence, set up the local file and persist thread
|
||||
if (_wantParticlePersist) {
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
const char* PARTICLES_PERSIST_FILENAME = "--particlesPersistFilename";
|
||||
const char* particlesPersistFilenameParameter = getCmdOption(_argc, _argv, PARTICLES_PERSIST_FILENAME);
|
||||
if (particlesPersistFilenameParameter) {
|
||||
strcpy(_particlePersistFilename, particlesPersistFilenameParameter);
|
||||
} else {
|
||||
//strcpy(particlePersistFilename, _wantLocalDomain ? LOCAL_PARTICLES_PERSIST_FILE : PARTICLES_PERSIST_FILE);
|
||||
strcpy(_particlePersistFilename, LOCAL_PARTICLES_PERSIST_FILE);
|
||||
}
|
||||
|
||||
qDebug("particlePersistFilename=%s\n", _particlePersistFilename);
|
||||
|
||||
// now set up ParticlePersistThread
|
||||
_particlePersistThread = new ParticlePersistThread(&_serverTree, _particlePersistFilename);
|
||||
if (_particlePersistThread) {
|
||||
_particlePersistThread->initialize(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if the user passed in a command line option for loading an old style local
|
||||
// Particle File. If so, load it now. This is not the same as a particle persist file
|
||||
const char* INPUT_FILE = "-i";
|
||||
const char* particlesFilename = getCmdOption(_argc, _argv, INPUT_FILE);
|
||||
if (particlesFilename) {
|
||||
_serverTree.readFromSVOFile(particlesFilename);
|
||||
}
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
|
||||
const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND);
|
||||
if (packetsPerSecond) {
|
||||
_packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND;
|
||||
if (_packetsPerClientPerInterval < 1) {
|
||||
_packetsPerClientPerInterval = 1;
|
||||
}
|
||||
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
|
||||
}
|
||||
|
||||
sockaddr senderAddress;
|
||||
|
||||
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
|
||||
ssize_t packetLength;
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
|
||||
// set up our jurisdiction broadcaster...
|
||||
_jurisdictionSender = new JurisdictionSender(_jurisdiction);
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->initialize(true);
|
||||
}
|
||||
|
||||
// set up our ParticleSeverPacketProcessor
|
||||
_particleServerPacketProcessor = new ParticleSeverPacketProcessor(this);
|
||||
if (_particleServerPacketProcessor) {
|
||||
_particleServerPacketProcessor->initialize(true);
|
||||
}
|
||||
|
||||
// Convert now to tm struct for local timezone
|
||||
tm* localtm = localtime(&_started);
|
||||
const int MAX_TIME_LENGTH = 128;
|
||||
char localBuffer[MAX_TIME_LENGTH] = { 0 };
|
||||
char utcBuffer[MAX_TIME_LENGTH] = { 0 };
|
||||
strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
|
||||
// Convert now to tm struct for UTC
|
||||
tm* gmtm = gmtime(&_started);
|
||||
if (gmtm != NULL) {
|
||||
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
|
||||
}
|
||||
qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n";
|
||||
|
||||
|
||||
// loop to send to nodes requesting data
|
||||
while (true) {
|
||||
// check for >= in case one gets past the goalie
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
// ping our inactive nodes to punch holes with them
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_PARTICLE_QUERY) {
|
||||
bool debug = false;
|
||||
if (debug) {
|
||||
qDebug("Got PACKET_TYPE_PARTICLE_QUERY at %llu.\n", usecTimestampNow());
|
||||
}
|
||||
|
||||
// If we got a PACKET_TYPE_PARTICLE_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* node = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (node) {
|
||||
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active socket for this node, but they're talking to us
|
||||
// this means they've heard from us and can reply, let's assume public is active
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
ParticleReceiverData* nodeData = (ParticleReceiverData*) node->getLinkedData();
|
||||
if (nodeData && !nodeData->isParticleSendThreadInitalized()) {
|
||||
nodeData->initializeParticleSendThread(this);
|
||||
}
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_PARTICLE_JURISDICTION_REQUEST) {
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
|
||||
}
|
||||
} else if (_particleServerPacketProcessor &&
|
||||
(packetData[0] == PACKET_TYPE_SET_VOXEL
|
||||
|| packetData[0] == PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE
|
||||
|| packetData[0] == PACKET_TYPE_ERASE_VOXEL)) {
|
||||
|
||||
|
||||
const char* messageName;
|
||||
switch (packetData[0]) {
|
||||
case PACKET_TYPE_SET_VOXEL:
|
||||
messageName = "PACKET_TYPE_SET_VOXEL";
|
||||
break;
|
||||
case PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE:
|
||||
messageName = "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE";
|
||||
break;
|
||||
case PACKET_TYPE_ERASE_VOXEL:
|
||||
messageName = "PACKET_TYPE_ERASE_VOXEL";
|
||||
break;
|
||||
}
|
||||
_particleServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
|
||||
// properly shutdown and cleaned up.
|
||||
NodeList::getInstance()->clear();
|
||||
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->terminate();
|
||||
delete _jurisdictionSender;
|
||||
}
|
||||
|
||||
if (_particleServerPacketProcessor) {
|
||||
_particleServerPacketProcessor->terminate();
|
||||
delete _particleServerPacketProcessor;
|
||||
}
|
||||
|
||||
if (_particlePersistThread) {
|
||||
_particlePersistThread->terminate();
|
||||
delete _particlePersistThread;
|
||||
}
|
||||
|
||||
// tell our NodeList we're done with notifications
|
||||
nodeList->removeHook(&_nodeWatcher);
|
||||
|
||||
delete _jurisdiction;
|
||||
_jurisdiction = NULL;
|
||||
|
||||
qDebug() << "ParticleSever::run()... DONE\n";
|
||||
}
|
||||
|
||||
|
||||
void ParticleSever::nodeAdded(Node* node) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ParticleSever::nodeKilled(Node* node) {
|
||||
// Use this to cleanup our node
|
||||
if (node->getType() == NODE_TYPE_AGENT) {
|
||||
ParticleReceiverData* nodeData = (ParticleReceiverData*)node->getLinkedData();
|
||||
if (nodeData) {
|
||||
node->setLinkedData(NULL);
|
||||
delete nodeData;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,91 +0,0 @@
|
|||
//
|
||||
// ParticleServer.h
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __particle_server__ParticleServer__
|
||||
#define __particle_server__ParticleServer__
|
||||
|
||||
#include <QStringList>
|
||||
#include <QDateTime>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <Assignment.h>
|
||||
|
||||
#include "civetweb.h"
|
||||
|
||||
#include "ParticlePersistThread.h"
|
||||
#include "ParticleSendThread.h"
|
||||
#include "ParticleServerConsts.h"
|
||||
#include "ParticleServerPacketProcessor.h"
|
||||
|
||||
/// Handles assignments of type ParticleServer - sending particles to various clients.
|
||||
class ParticleServer : public Assignment {
|
||||
public:
|
||||
ParticleServer(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
~ParticleServer();
|
||||
|
||||
/// runs the particle server assignment
|
||||
void run();
|
||||
|
||||
/// allows setting of run arguments
|
||||
void setArguments(int argc, char** argv);
|
||||
|
||||
bool wantsDebugParticleSending() const { return _debugParticleSending; }
|
||||
bool wantsDebugParticleReceiving() const { return _debugParticleReceiving; }
|
||||
bool wantsVerboseDebug() const { return _verboseDebug; }
|
||||
bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; }
|
||||
bool wantDumpParticlesOnMove() const { return _dumpParticlesOnMove; }
|
||||
bool wantDisplayParticleStats() const { return _displayParticleStats; }
|
||||
|
||||
ParticleTree& getServerTree() { return _serverTree; }
|
||||
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
|
||||
|
||||
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
|
||||
|
||||
static ParticleServer* GetInstance() { return _theInstance; }
|
||||
|
||||
bool isInitialLoadComplete() const { return (_particlePersistThread) ? _particlePersistThread->isInitialLoadComplete() : true; }
|
||||
time_t* getLoadCompleted() { return (_particlePersistThread) ? _particlePersistThread->getLoadCompleted() : NULL; }
|
||||
uint64_t getLoadElapsedTime() const { return (_particlePersistThread) ? _particlePersistThread->getLoadElapsedTime() : 0; }
|
||||
|
||||
private:
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
char** _parsedArgV;
|
||||
|
||||
char _particlePersistFilename[MAX_FILENAME_LENGTH];
|
||||
int _packetsPerClientPerInterval;
|
||||
ParticleTree _serverTree; // this IS a reaveraging tree
|
||||
bool _wantParticlePersist;
|
||||
bool _wantLocalDomain;
|
||||
bool _debugParticleSending;
|
||||
bool _shouldShowAnimationDebug;
|
||||
bool _displayParticleStats;
|
||||
bool _debugParticleReceiving;
|
||||
bool _dumpParticlesOnMove;
|
||||
bool _verboseDebug;
|
||||
JurisdictionMap* _jurisdiction;
|
||||
JurisdictionSender* _jurisdictionSender;
|
||||
ParticleServerPacketProcessor* _particleServerPacketProcessor;
|
||||
ParticlePersistThread* _particlePersistThread;
|
||||
|
||||
NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed
|
||||
|
||||
void parsePayload();
|
||||
|
||||
void initMongoose(int port);
|
||||
|
||||
static int civetwebRequestHandler(struct mg_connection *connection);
|
||||
static ParticleServer* _theInstance;
|
||||
|
||||
time_t _started;
|
||||
uint64_t _startedUSecs;
|
||||
};
|
||||
|
||||
#endif // __particle_server__ParticleServer__
|
|
@ -1,28 +0,0 @@
|
|||
// ParticleServerConsts.h
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __particle_server__ParticleServerConsts__
|
||||
#define __particle_server__ParticleServerConsts__
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <NodeList.h> // for MAX_PACKET_SIZE
|
||||
#include <JurisdictionSender.h>
|
||||
#include <ParticleTree.h>
|
||||
|
||||
#include "ParticleServerPacketProcessor.h"
|
||||
|
||||
const int MAX_FILENAME_LENGTH = 1024;
|
||||
const int INTERVALS_PER_SECOND = 60;
|
||||
const int PARTICLE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
|
||||
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating particles
|
||||
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
|
||||
|
||||
extern const char* LOCAL_PARTICLES_PERSIST_FILE;
|
||||
extern const char* PARTICLES_PERSIST_FILE;
|
||||
|
||||
#endif // __particle_server__ParticleServerConsts__
|
|
@ -1,222 +0,0 @@
|
|||
//
|
||||
// ParticleServerPacketProcessor.cpp
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/2/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded network packet processor for the particle-server
|
||||
//
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "ParticleServer.h"
|
||||
#include "ParticleServerConsts.h"
|
||||
#include "ParticleServerPacketProcessor.h"
|
||||
|
||||
static QUuid DEFAULT_NODE_ID_REF;
|
||||
|
||||
ParticleServerPacketProcessor::ParticleServerPacketProcessor(ParticleServer* myServer) :
|
||||
_myServer(myServer),
|
||||
_receivedPacketCount(0),
|
||||
_totalTransitTime(0),
|
||||
_totalProcessTime(0),
|
||||
_totalLockWaitTime(0),
|
||||
_totalParticlesInPacket(0),
|
||||
_totalPackets(0)
|
||||
{
|
||||
}
|
||||
|
||||
void ParticleServerPacketProcessor::resetStats() {
|
||||
_totalTransitTime = 0;
|
||||
_totalProcessTime = 0;
|
||||
_totalLockWaitTime = 0;
|
||||
_totalParticlesInPacket = 0;
|
||||
_totalPackets = 0;
|
||||
|
||||
_singleSenderStats.clear();
|
||||
}
|
||||
|
||||
|
||||
void ParticleServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
|
||||
bool debugProcessPacket = _myServer->wantsVerboseDebug();
|
||||
|
||||
if (debugProcessPacket) {
|
||||
printf("ParticleServerPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
|
||||
}
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_SET_PARTICLE || packetData[0] == PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE) {
|
||||
bool destructive = (packetData[0] == PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE);
|
||||
PerformanceWarning warn(_myServer->wantShowAnimationDebug(),
|
||||
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
|
||||
_myServer->wantShowAnimationDebug());
|
||||
|
||||
_receivedPacketCount++;
|
||||
|
||||
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
|
||||
uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence))));
|
||||
uint64_t arrivedAt = usecTimestampNow();
|
||||
uint64_t transitTime = arrivedAt - sentAt;
|
||||
int particlesInPacket = 0;
|
||||
uint64_t processTime = 0;
|
||||
uint64_t lockWaitTime = 0;
|
||||
|
||||
if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugParticleReceiving()) {
|
||||
printf("PROCESSING THREAD: got %s - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
|
||||
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
|
||||
_receivedPacketCount, packetLength, sequence, transitTime);
|
||||
}
|
||||
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
|
||||
unsigned char* particleData = (unsigned char*)&packetData[atByte];
|
||||
while (atByte < packetLength) {
|
||||
int maxSize = packetLength - atByte;
|
||||
|
||||
if (debugProcessPacket) {
|
||||
printf("ParticleServerPacketProcessor::processPacket() %s packetData=%p packetLength=%ld particleData=%p atByte=%d maxSize=%d\n",
|
||||
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
|
||||
packetData, packetLength, particleData, atByte, maxSize);
|
||||
}
|
||||
|
||||
int octets = numberOfThreeBitSectionsInCode(particleData, maxSize);
|
||||
|
||||
if (octets == OVERFLOWED_OCTCODE_BUFFER) {
|
||||
printf("WARNING! Got particle edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
|
||||
printf("bailing processing of packet!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
const int COLOR_SIZE_IN_BYTES = 3;
|
||||
int particleDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
|
||||
int particleCodeSize = bytesRequiredForCodeLength(octets);
|
||||
|
||||
if (atByte + particleDataSize <= packetLength) {
|
||||
if (_myServer->wantShowAnimationDebug()) {
|
||||
int red = particleData[particleCodeSize + RED_INDEX];
|
||||
int green = particleData[particleCodeSize + GREEN_INDEX];
|
||||
int blue = particleData[particleCodeSize + BLUE_INDEX];
|
||||
|
||||
float* vertices = firstVertexForCode(particleData);
|
||||
printf("inserting particle: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
|
||||
delete[] vertices;
|
||||
}
|
||||
|
||||
uint64_t startLock = usecTimestampNow();
|
||||
_myServer->getServerTree().lockForWrite();
|
||||
uint64_t startProcess = usecTimestampNow();
|
||||
_myServer->getServerTree().readCodeColorBufferToTree(particleData, destructive);
|
||||
_myServer->getServerTree().unlock();
|
||||
uint64_t endProcess = usecTimestampNow();
|
||||
|
||||
particlesInPacket++;
|
||||
|
||||
uint64_t thisProcessTime = endProcess - startProcess;
|
||||
uint64_t thisLockWaitTime = startProcess - startLock;
|
||||
|
||||
processTime += thisProcessTime;
|
||||
lockWaitTime += thisLockWaitTime;
|
||||
|
||||
// skip to next particle edit record in the packet
|
||||
particleData += particleDataSize;
|
||||
atByte += particleDataSize;
|
||||
} else {
|
||||
printf("WARNING! Got particle edit record that would overflow buffer, bailing processing of packet!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (debugProcessPacket) {
|
||||
printf("ParticleServerPacketProcessor::processPacket() DONE LOOPING FOR %s packetData=%p packetLength=%ld particleData=%p atByte=%d\n",
|
||||
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
|
||||
packetData, packetLength, particleData, atByte);
|
||||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* senderNode = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
QUuid& nodeUUID = DEFAULT_NODE_ID_REF;
|
||||
if (senderNode) {
|
||||
senderNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
nodeUUID = senderNode->getUUID();
|
||||
if (debugProcessPacket) {
|
||||
qDebug() << "sender has uuid=" << nodeUUID << "\n";
|
||||
}
|
||||
} else {
|
||||
if (debugProcessPacket) {
|
||||
qDebug() << "sender has no known nodeUUID.\n";
|
||||
}
|
||||
}
|
||||
trackInboundPackets(nodeUUID, sequence, transitTime, particlesInPacket, processTime, lockWaitTime);
|
||||
|
||||
} else if (packetData[0] == PACKET_TYPE_ERASE_PARTICLE) {
|
||||
|
||||
_receivedPacketCount++;
|
||||
|
||||
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
|
||||
uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence))));
|
||||
uint64_t arrivedAt = usecTimestampNow();
|
||||
uint64_t transitTime = arrivedAt - sentAt;
|
||||
|
||||
if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugParticleReceiving()) {
|
||||
printf("PROCESSING THREAD: got PACKET_TYPE_ERASE_PARTICLE - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
|
||||
_receivedPacketCount, packetLength, sequence, transitTime);
|
||||
}
|
||||
|
||||
// Send these bits off to the ParticleTree class to process them
|
||||
_myServer->getServerTree().lockForWrite();
|
||||
_myServer->getServerTree().processRemoveParticleBitstream((unsigned char*)packetData, packetLength);
|
||||
_myServer->getServerTree().unlock();
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
} else {
|
||||
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleServerPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
|
||||
int particlesInPacket, uint64_t processTime, uint64_t lockWaitTime) {
|
||||
|
||||
_totalTransitTime += transitTime;
|
||||
_totalProcessTime += processTime;
|
||||
_totalLockWaitTime += lockWaitTime;
|
||||
_totalParticlesInPacket += particlesInPacket;
|
||||
_totalPackets++;
|
||||
|
||||
// find the individual senders stats and track them there too...
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
|
||||
SingleSenderStats stats;
|
||||
|
||||
stats._totalTransitTime += transitTime;
|
||||
stats._totalProcessTime += processTime;
|
||||
stats._totalLockWaitTime += lockWaitTime;
|
||||
stats._totalParticlesInPacket += particlesInPacket;
|
||||
stats._totalPackets++;
|
||||
|
||||
_singleSenderStats[nodeUUID] = stats;
|
||||
} else {
|
||||
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
|
||||
stats._totalTransitTime += transitTime;
|
||||
stats._totalProcessTime += processTime;
|
||||
stats._totalLockWaitTime += lockWaitTime;
|
||||
stats._totalParticlesInPacket += particlesInPacket;
|
||||
stats._totalPackets++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SingleSenderStats::SingleSenderStats() {
|
||||
_totalTransitTime = 0;
|
||||
_totalProcessTime = 0;
|
||||
_totalLockWaitTime = 0;
|
||||
_totalParticlesInPacket = 0;
|
||||
_totalPackets = 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
//
|
||||
// ParticleServerPacketProcessor.h
|
||||
// particle-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded network packet processor for the particle-server
|
||||
//
|
||||
|
||||
#ifndef __particle_server__ParticleServerPacketProcessor__
|
||||
#define __particle_server__ParticleServerPacketProcessor__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <ReceivedPacketProcessor.h>
|
||||
class ParticleServer;
|
||||
|
||||
class SingleSenderStats {
|
||||
public:
|
||||
SingleSenderStats();
|
||||
|
||||
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
|
||||
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
|
||||
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
|
||||
uint64_t getTotalParticlesProcessed() const { return _totalParticlesInPacket; }
|
||||
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
|
||||
uint64_t getAverageProcessTimePerParticle() const
|
||||
{ return _totalParticlesInPacket == 0 ? 0 : _totalProcessTime / _totalParticlesInPacket; }
|
||||
uint64_t getAverageLockWaitTimePerParticle() const
|
||||
{ return _totalParticlesInPacket == 0 ? 0 : _totalLockWaitTime / _totalParticlesInPacket; }
|
||||
|
||||
uint64_t _totalTransitTime;
|
||||
uint64_t _totalProcessTime;
|
||||
uint64_t _totalLockWaitTime;
|
||||
uint64_t _totalParticlesInPacket;
|
||||
uint64_t _totalPackets;
|
||||
};
|
||||
|
||||
typedef std::map<QUuid, SingleSenderStats> NodeToSenderStatsMap;
|
||||
typedef std::map<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterator;
|
||||
|
||||
|
||||
/// Handles processing of incoming network packets for the particle-server. As with other ReceivedPacketProcessor classes
|
||||
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
||||
class ParticleServerPacketProcessor : public ReceivedPacketProcessor {
|
||||
|
||||
public:
|
||||
ParticleServerPacketProcessor(ParticleServer* myServer);
|
||||
|
||||
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
|
||||
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
|
||||
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
|
||||
uint64_t getTotalParticlesProcessed() const { return _totalParticlesInPacket; }
|
||||
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
|
||||
uint64_t getAverageProcessTimePerParticle() const
|
||||
{ return _totalParticlesInPacket == 0 ? 0 : _totalProcessTime / _totalParticlesInPacket; }
|
||||
uint64_t getAverageLockWaitTimePerParticle() const
|
||||
{ return _totalParticlesInPacket == 0 ? 0 : _totalLockWaitTime / _totalParticlesInPacket; }
|
||||
|
||||
void resetStats();
|
||||
|
||||
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
|
||||
|
||||
protected:
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
private:
|
||||
void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
|
||||
int particlesInPacket, uint64_t processTime, uint64_t lockWaitTime);
|
||||
|
||||
ParticleServer* _myServer;
|
||||
int _receivedPacketCount;
|
||||
|
||||
uint64_t _totalTransitTime;
|
||||
uint64_t _totalProcessTime;
|
||||
uint64_t _totalLockWaitTime;
|
||||
uint64_t _totalParticlesInPacket;
|
||||
uint64_t _totalPackets;
|
||||
|
||||
NodeToSenderStatsMap _singleSenderStats;
|
||||
};
|
||||
#endif // __particle_server__ParticleServerPacketProcessor__
|
|
@ -1,28 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ../..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||
|
||||
set(TARGET_NAME particles)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Widgets)
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link ZLIB
|
||||
find_package(ZLIB)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
|
@ -1 +0,0 @@
|
|||
// ParticleTree.cpp
|
Loading…
Reference in a new issue