Merge branch 'master' of github.com:highfidelity/hifi into fix-logging-crash
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 51 KiB |
|
@ -240,25 +240,26 @@ class DeadlockWatchdogThread : public QThread {
|
|||
public:
|
||||
static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
|
||||
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
|
||||
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 15 * USECS_PER_SECOND;
|
||||
|
||||
static const unsigned long HEARTBEAT_REPORT_INTERVAL_USECS = 5 * USECS_PER_SECOND;
|
||||
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND;
|
||||
static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large
|
||||
static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples
|
||||
|
||||
// Set the heartbeat on launch
|
||||
DeadlockWatchdogThread() {
|
||||
setObjectName("Deadlock Watchdog");
|
||||
QTimer* heartbeatTimer = new QTimer();
|
||||
// Give the heartbeat an initial value
|
||||
updateHeartbeat();
|
||||
connect(heartbeatTimer, &QTimer::timeout, [this] {
|
||||
updateHeartbeat();
|
||||
});
|
||||
heartbeatTimer->start(HEARTBEAT_UPDATE_INTERVAL_SECS * MSECS_PER_SECOND);
|
||||
_heartbeat = usecTimestampNow();
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, [this] {
|
||||
_quit = true;
|
||||
});
|
||||
}
|
||||
|
||||
void updateHeartbeat() {
|
||||
_heartbeat = usecTimestampNow();
|
||||
auto now = usecTimestampNow();
|
||||
auto elapsed = now - _heartbeat;
|
||||
_movingAverage.addSample(elapsed);
|
||||
_heartbeat = now;
|
||||
}
|
||||
|
||||
void deadlockDetectionCrash() {
|
||||
|
@ -269,10 +270,52 @@ public:
|
|||
void run() override {
|
||||
while (!_quit) {
|
||||
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
|
||||
#ifdef NDEBUG
|
||||
auto now = usecTimestampNow();
|
||||
auto lastHeartbeatAge = now - _heartbeat;
|
||||
|
||||
// in the unlikely event that now is less than _heartbeat, don't rollover and confuse ourselves
|
||||
auto lastHeartbeatAge = (now > _heartbeat) ? now - _heartbeat : 0;
|
||||
auto sinceLastReport = (now > _lastReport) ? now - _lastReport : 0;
|
||||
auto elapsedMovingAverage = _movingAverage.getAverage();
|
||||
|
||||
if (elapsedMovingAverage > _maxElapsedAverage) {
|
||||
qDebug() << "DEADLOCK WATCHDOG NEW maxElapsedAverage:"
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "NEW maxElapsedAverage:" << elapsedMovingAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_maxElapsedAverage = elapsedMovingAverage;
|
||||
}
|
||||
if (lastHeartbeatAge > _maxElapsed) {
|
||||
qDebug() << "DEADLOCK WATCHDOG NEW maxElapsed:"
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "PREVIOUS maxElapsed:" << _maxElapsed
|
||||
<< "NEW maxElapsed:" << lastHeartbeatAge
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_maxElapsed = lastHeartbeatAge;
|
||||
}
|
||||
if ((sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) || (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT)) {
|
||||
qDebug() << "DEADLOCK WATCHDOG STATUS -- lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_lastReport = now;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) {
|
||||
qDebug() << "DEADLOCK DETECTED -- "
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "[ _heartbeat:" << _heartbeat
|
||||
<< "now:" << now << " ]"
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
deadlockDetectionCrash();
|
||||
}
|
||||
#endif
|
||||
|
@ -280,10 +323,19 @@ public:
|
|||
}
|
||||
|
||||
static std::atomic<uint64_t> _heartbeat;
|
||||
static std::atomic<uint64_t> _lastReport;
|
||||
static std::atomic<uint64_t> _maxElapsed;
|
||||
static std::atomic<int> _maxElapsedAverage;
|
||||
static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage;
|
||||
|
||||
bool _quit { false };
|
||||
};
|
||||
|
||||
std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat;
|
||||
std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport;
|
||||
std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
|
||||
std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
|
||||
ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class MyNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
|
@ -1381,6 +1433,8 @@ void Application::initializeUi() {
|
|||
|
||||
void Application::paintGL() {
|
||||
|
||||
updateHeartbeat();
|
||||
|
||||
// Some plugins process message events, potentially leading to
|
||||
// re-entering a paint event. don't allow further processing if this
|
||||
// happens
|
||||
|
@ -2502,6 +2556,8 @@ static uint32_t _renderedFrameIndex { INVALID_FRAME };
|
|||
|
||||
void Application::idle(uint64_t now) {
|
||||
|
||||
updateHeartbeat();
|
||||
|
||||
if (_aboutToQuit || _inPaint) {
|
||||
return; // bail early, nothing to do here.
|
||||
}
|
||||
|
|
|
@ -611,7 +611,7 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j
|
|||
return loadNode(rootVal.toObject(), jsonUrl);
|
||||
}
|
||||
|
||||
void AnimNodeLoader::onRequestDone(const QByteArray& data) {
|
||||
void AnimNodeLoader::onRequestDone(const QByteArray data) {
|
||||
auto node = load(data, _url);
|
||||
if (node) {
|
||||
emit success(node);
|
||||
|
|
|
@ -36,7 +36,7 @@ protected:
|
|||
static AnimNode::Pointer load(const QByteArray& contents, const QUrl& jsonUrl);
|
||||
|
||||
protected slots:
|
||||
void onRequestDone(const QByteArray& data);
|
||||
void onRequestDone(const QByteArray data);
|
||||
void onRequestError(QNetworkReply::NetworkError error);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -354,7 +354,9 @@ void RenderableModelEntityItem::updateModelBounds() {
|
|||
bool movingOrAnimating = isMovingRelativeToParent() || isAnimatingSomething();
|
||||
if ((movingOrAnimating ||
|
||||
_needsInitialSimulation ||
|
||||
_needsJointSimulation ||
|
||||
_model->getTranslation() != getPosition() ||
|
||||
_model->getScaleToFitDimensions() != getDimensions() ||
|
||||
_model->getRotation() != getRotation() ||
|
||||
_model->getRegistrationPoint() != getRegistrationPoint())
|
||||
&& _model->isActive() && _dimensionsInitialized) {
|
||||
|
@ -370,6 +372,7 @@ void RenderableModelEntityItem::updateModelBounds() {
|
|||
}
|
||||
|
||||
_needsInitialSimulation = false;
|
||||
_needsJointSimulation = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -770,6 +773,7 @@ bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index,
|
|||
_absoluteJointRotationsInObjectFrameSet[index] = true;
|
||||
_absoluteJointRotationsInObjectFrameDirty[index] = true;
|
||||
result = true;
|
||||
_needsJointSimulation = true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
@ -785,11 +789,33 @@ bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int ind
|
|||
_absoluteJointTranslationsInObjectFrameSet[index] = true;
|
||||
_absoluteJointTranslationsInObjectFrameDirty[index] = true;
|
||||
result = true;
|
||||
_needsJointSimulation = true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setJointRotations(const QVector<glm::quat>& rotations) {
|
||||
ModelEntityItem::setJointRotations(rotations);
|
||||
_needsJointSimulation = true;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setJointRotationsSet(const QVector<bool>& rotationsSet) {
|
||||
ModelEntityItem::setJointRotationsSet(rotationsSet);
|
||||
_needsJointSimulation = true;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setJointTranslations(const QVector<glm::vec3>& translations) {
|
||||
ModelEntityItem::setJointTranslations(translations);
|
||||
_needsJointSimulation = true;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setJointTranslationsSet(const QVector<bool>& translationsSet) {
|
||||
ModelEntityItem::setJointTranslationsSet(translationsSet);
|
||||
_needsJointSimulation = true;
|
||||
}
|
||||
|
||||
|
||||
void RenderableModelEntityItem::locationChanged() {
|
||||
EntityItem::locationChanged();
|
||||
if (_model && _model->isActive()) {
|
||||
|
|
|
@ -69,6 +69,11 @@ public:
|
|||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override;
|
||||
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override;
|
||||
|
||||
virtual void setJointRotations(const QVector<glm::quat>& rotations) override;
|
||||
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet) override;
|
||||
virtual void setJointTranslations(const QVector<glm::vec3>& translations) override;
|
||||
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet) override;
|
||||
|
||||
virtual void loader() override;
|
||||
virtual void locationChanged() override;
|
||||
|
||||
|
@ -97,6 +102,8 @@ private:
|
|||
bool _showCollisionHull = false;
|
||||
|
||||
bool getAnimationFrame();
|
||||
|
||||
bool _needsJointSimulation { false };
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableModelEntityItem_h
|
||||
|
|
|
@ -48,6 +48,8 @@ public:
|
|||
|
||||
virtual ~RenderablePolyVoxEntityItem();
|
||||
|
||||
void initializePolyVox();
|
||||
|
||||
virtual void somethingChangedNotification() {
|
||||
// This gets called from EnityItem::readEntityDataFromBuffer every time a packet describing
|
||||
// this entity comes from the entity-server. It gets called even if nothing has actually changed
|
||||
|
@ -114,17 +116,28 @@ public:
|
|||
virtual void setYPNeighborID(const EntityItemID& yPNeighborID);
|
||||
virtual void setZPNeighborID(const EntityItemID& zPNeighborID);
|
||||
|
||||
virtual void rebakeMesh();
|
||||
|
||||
virtual void updateRegistrationPoint(const glm::vec3& value);
|
||||
|
||||
void setVoxelsFromData(QByteArray uncompressedData, quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize);
|
||||
void forEachVoxelValue(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize,
|
||||
std::function<void(int, int, int, uint8_t)> thunk);
|
||||
|
||||
void setMesh(model::MeshPointer mesh);
|
||||
void setCollisionPoints(const QVector<QVector<glm::vec3>> points, AABox box);
|
||||
PolyVox::SimpleVolume<uint8_t>* getVolData() { return _volData; }
|
||||
|
||||
uint8_t getVoxelInternal(int x, int y, int z);
|
||||
bool setVoxelInternal(int x, int y, int z, uint8_t toValue);
|
||||
|
||||
void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); }
|
||||
|
||||
private:
|
||||
// The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions
|
||||
// may not match _voxelVolumeSize.
|
||||
|
||||
model::MeshPointer _mesh;
|
||||
bool _meshDirty; // does collision-shape need to be recomputed?
|
||||
mutable QReadWriteLock _meshLock{QReadWriteLock::Recursive};
|
||||
bool _meshDirty { true }; // does collision-shape need to be recomputed?
|
||||
bool _meshInitialized { false };
|
||||
|
||||
NetworkTexturePointer _xTexture;
|
||||
NetworkTexturePointer _yTexture;
|
||||
|
@ -135,44 +148,35 @@ private:
|
|||
static gpu::PipelinePointer _pipeline;
|
||||
|
||||
ShapeInfo _shapeInfo;
|
||||
mutable QReadWriteLock _shapeInfoLock;
|
||||
|
||||
PolyVox::SimpleVolume<uint8_t>* _volData = nullptr;
|
||||
mutable QReadWriteLock _volDataLock{QReadWriteLock::Recursive}; // lock for _volData
|
||||
bool _volDataDirty = false; // does getMesh need to be called?
|
||||
int _onCount; // how many non-zero voxels are in _volData
|
||||
|
||||
bool inUserBounds(const PolyVox::SimpleVolume<uint8_t>* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle,
|
||||
int x, int y, int z) const;
|
||||
uint8_t getVoxelInternal(int x, int y, int z);
|
||||
bool setVoxelInternal(int x, int y, int z, uint8_t toValue);
|
||||
bool _neighborsNeedUpdate { false };
|
||||
|
||||
bool updateOnCount(int x, int y, int z, uint8_t toValue);
|
||||
PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const;
|
||||
|
||||
// these are run off the main thread
|
||||
void decompressVolumeData();
|
||||
void decompressVolumeDataAsync();
|
||||
void compressVolumeDataAndSendEditPacket();
|
||||
void compressVolumeDataAndSendEditPacketAsync();
|
||||
void getMesh();
|
||||
void getMeshAsync();
|
||||
virtual void getMesh(); // recompute mesh
|
||||
void computeShapeInfoWorker();
|
||||
void computeShapeInfoWorkerAsync();
|
||||
|
||||
QSemaphore _threadRunning{1};
|
||||
|
||||
// these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID
|
||||
EntityItemWeakPointer _xNNeighbor; // neighor found by going along negative X axis
|
||||
EntityItemWeakPointer _xNNeighbor; // neighbor found by going along negative X axis
|
||||
EntityItemWeakPointer _yNNeighbor;
|
||||
EntityItemWeakPointer _zNNeighbor;
|
||||
EntityItemWeakPointer _xPNeighbor; // neighor found by going along positive X axis
|
||||
EntityItemWeakPointer _xPNeighbor; // neighbor found by going along positive X axis
|
||||
EntityItemWeakPointer _yPNeighbor;
|
||||
EntityItemWeakPointer _zPNeighbor;
|
||||
void clearOutOfDateNeighbors();
|
||||
void cacheNeighbors();
|
||||
void copyUpperEdgesFromNeighbors();
|
||||
void bonkNeighbors();
|
||||
};
|
||||
|
||||
bool inUserBounds(const PolyVox::SimpleVolume<uint8_t>* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle,
|
||||
int x, int y, int z);
|
||||
|
||||
#endif // hifi_RenderablePolyVoxEntityItem_h
|
||||
|
|
|
@ -101,12 +101,15 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
|||
prepareEntityForDelete(entity);
|
||||
} else {
|
||||
if (expiry < _nextExpiry) {
|
||||
// remeber the smallest _nextExpiry so we know when to start the next search
|
||||
// remember the smallest _nextExpiry so we know when to start the next search
|
||||
_nextExpiry = expiry;
|
||||
}
|
||||
++itemItr;
|
||||
}
|
||||
}
|
||||
if (_mortalEntities.size() < 1) {
|
||||
_nextExpiry = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,10 +120,10 @@ public:
|
|||
virtual glm::vec3 getJointPosition(int jointIndex) const { return glm::vec3(); }
|
||||
virtual glm::quat getJointRotation(int jointIndex) const { return glm::quat(); }
|
||||
|
||||
void setJointRotations(const QVector<glm::quat>& rotations);
|
||||
void setJointRotationsSet(const QVector<bool>& rotationsSet);
|
||||
void setJointTranslations(const QVector<glm::vec3>& translations);
|
||||
void setJointTranslationsSet(const QVector<bool>& translationsSet);
|
||||
virtual void setJointRotations(const QVector<glm::quat>& rotations);
|
||||
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet);
|
||||
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
||||
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet);
|
||||
QVector<glm::quat> getJointRotations() const;
|
||||
QVector<bool> getJointRotationsSet() const;
|
||||
QVector<glm::vec3> getJointTranslations() const;
|
||||
|
|
|
@ -64,44 +64,47 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID) :
|
|||
}
|
||||
|
||||
void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) {
|
||||
QWriteLocker(&this->_voxelDataLock);
|
||||
withWriteLock([&] {
|
||||
assert((int)_voxelVolumeSize.x == _voxelVolumeSize.x);
|
||||
assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y);
|
||||
assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z);
|
||||
|
||||
assert((int)_voxelVolumeSize.x == _voxelVolumeSize.x);
|
||||
assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y);
|
||||
assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z);
|
||||
_voxelVolumeSize = glm::vec3(roundf(voxelVolumeSize.x), roundf(voxelVolumeSize.y), roundf(voxelVolumeSize.z));
|
||||
if (_voxelVolumeSize.x < 1) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1";
|
||||
_voxelVolumeSize.x = 1;
|
||||
}
|
||||
if (_voxelVolumeSize.x > MAX_VOXEL_DIMENSION) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to max";
|
||||
_voxelVolumeSize.x = MAX_VOXEL_DIMENSION;
|
||||
}
|
||||
|
||||
_voxelVolumeSize = glm::vec3(roundf(voxelVolumeSize.x), roundf(voxelVolumeSize.y), roundf(voxelVolumeSize.z));
|
||||
if (_voxelVolumeSize.x < 1) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1";
|
||||
_voxelVolumeSize.x = 1;
|
||||
}
|
||||
if (_voxelVolumeSize.x > MAX_VOXEL_DIMENSION) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to max";
|
||||
_voxelVolumeSize.x = MAX_VOXEL_DIMENSION;
|
||||
}
|
||||
if (_voxelVolumeSize.y < 1) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to 1";
|
||||
_voxelVolumeSize.y = 1;
|
||||
}
|
||||
if (_voxelVolumeSize.y > MAX_VOXEL_DIMENSION) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to max";
|
||||
_voxelVolumeSize.y = MAX_VOXEL_DIMENSION;
|
||||
}
|
||||
|
||||
if (_voxelVolumeSize.y < 1) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to 1";
|
||||
_voxelVolumeSize.y = 1;
|
||||
}
|
||||
if (_voxelVolumeSize.y > MAX_VOXEL_DIMENSION) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to max";
|
||||
_voxelVolumeSize.y = MAX_VOXEL_DIMENSION;
|
||||
}
|
||||
|
||||
if (_voxelVolumeSize.z < 1) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to 1";
|
||||
_voxelVolumeSize.z = 1;
|
||||
}
|
||||
if (_voxelVolumeSize.z > MAX_VOXEL_DIMENSION) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to max";
|
||||
_voxelVolumeSize.z = MAX_VOXEL_DIMENSION;
|
||||
}
|
||||
if (_voxelVolumeSize.z < 1) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to 1";
|
||||
_voxelVolumeSize.z = 1;
|
||||
}
|
||||
if (_voxelVolumeSize.z > MAX_VOXEL_DIMENSION) {
|
||||
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to max";
|
||||
_voxelVolumeSize.z = MAX_VOXEL_DIMENSION;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const glm::vec3& PolyVoxEntityItem::getVoxelVolumeSize() const {
|
||||
QWriteLocker locker(&this->_voxelDataLock);
|
||||
return _voxelVolumeSize;
|
||||
glm::vec3 PolyVoxEntityItem::getVoxelVolumeSize() const {
|
||||
glm::vec3 voxelVolumeSize;
|
||||
withReadLock([&] {
|
||||
voxelVolumeSize = _voxelVolumeSize;
|
||||
});
|
||||
return voxelVolumeSize;
|
||||
}
|
||||
|
||||
|
||||
|
@ -226,12 +229,16 @@ void PolyVoxEntityItem::debugDump() const {
|
|||
}
|
||||
|
||||
void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) {
|
||||
QWriteLocker(&this->_voxelDataLock);
|
||||
_voxelData = voxelData;
|
||||
_voxelDataDirty = true;
|
||||
withWriteLock([&] {
|
||||
_voxelData = voxelData;
|
||||
_voxelDataDirty = true;
|
||||
});
|
||||
}
|
||||
|
||||
const QByteArray PolyVoxEntityItem::getVoxelData() const {
|
||||
QReadLocker(&this->_voxelDataLock);
|
||||
return _voxelData;
|
||||
QByteArray voxelDataCopy;
|
||||
withReadLock([&] {
|
||||
voxelDataCopy = _voxelData;
|
||||
});
|
||||
return voxelDataCopy;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
virtual void debugDump() const;
|
||||
|
||||
virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
|
||||
virtual const glm::vec3& getVoxelVolumeSize() const;
|
||||
virtual glm::vec3 getVoxelVolumeSize() const;
|
||||
|
||||
virtual void setVoxelData(QByteArray voxelData);
|
||||
virtual const QByteArray getVoxelData() const;
|
||||
|
@ -128,12 +128,14 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
|
||||
virtual void rebakeMesh() {};
|
||||
|
||||
void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); }
|
||||
virtual void getMesh() {}; // recompute mesh
|
||||
|
||||
protected:
|
||||
glm::vec3 _voxelVolumeSize; // this is always 3 bytes
|
||||
|
||||
mutable QReadWriteLock _voxelDataLock;
|
||||
QByteArray _voxelData;
|
||||
bool _voxelDataDirty;
|
||||
bool _voxelDataDirty; // _voxelData has changed, things that depend on it should be updated
|
||||
|
||||
PolyVoxSurfaceStyle _voxelSurfaceStyle;
|
||||
|
||||
|
|
|
@ -293,7 +293,7 @@ void NetworkGeometry::requestModel(const QUrl& url) {
|
|||
connect(_resource, &Resource::failed, this, &NetworkGeometry::modelRequestError);
|
||||
}
|
||||
|
||||
void NetworkGeometry::mappingRequestDone(const QByteArray& data) {
|
||||
void NetworkGeometry::mappingRequestDone(const QByteArray data) {
|
||||
assert(_state == RequestMappingState);
|
||||
|
||||
// parse the mapping file
|
||||
|
@ -325,7 +325,7 @@ void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) {
|
|||
emit onFailure(*this, MappingRequestError);
|
||||
}
|
||||
|
||||
void NetworkGeometry::modelRequestDone(const QByteArray& data) {
|
||||
void NetworkGeometry::modelRequestDone(const QByteArray data) {
|
||||
assert(_state == RequestModelState);
|
||||
|
||||
_state = ParsingModelState;
|
||||
|
|
|
@ -113,10 +113,10 @@ public slots:
|
|||
void textureLoaded(const QWeakPointer<NetworkTexture>& networkTexture);
|
||||
|
||||
protected slots:
|
||||
void mappingRequestDone(const QByteArray& data);
|
||||
void mappingRequestDone(const QByteArray data);
|
||||
void mappingRequestError(QNetworkReply::NetworkError error);
|
||||
|
||||
void modelRequestDone(const QByteArray& data);
|
||||
void modelRequestDone(const QByteArray data);
|
||||
void modelRequestError(QNetworkReply::NetworkError error);
|
||||
|
||||
void modelParseSuccess(FBXGeometry* geometry);
|
||||
|
|
|
@ -81,6 +81,8 @@ AccountManager::AccountManager() :
|
|||
|
||||
qRegisterMetaType<QHttpMultiPart*>("QHttpMultiPart*");
|
||||
|
||||
qRegisterMetaType<AccountManagerAuth::Type>();
|
||||
|
||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||
}
|
||||
|
||||
|
@ -215,12 +217,13 @@ void AccountManager::sendRequest(const QString& path,
|
|||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "sendRequest",
|
||||
Q_ARG(const QString&, path),
|
||||
Q_ARG(AccountManagerAuth::Type, AccountManagerAuth::Required),
|
||||
Q_ARG(AccountManagerAuth::Type, authType),
|
||||
Q_ARG(QNetworkAccessManager::Operation, operation),
|
||||
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
||||
Q_ARG(const QByteArray&, dataByteArray),
|
||||
Q_ARG(QHttpMultiPart*, dataMultiPart),
|
||||
Q_ARG(QVariantMap, propertyMap));
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
|
|
@ -423,12 +423,12 @@ void Resource::handleReplyFinished() {
|
|||
|
||||
auto result = _request->getResult();
|
||||
if (result == ResourceRequest::Success) {
|
||||
_data = _request->getData();
|
||||
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
|
||||
qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
|
||||
|
||||
emit loaded(_data);
|
||||
downloadFinished(_data);
|
||||
auto data = _request->getData();
|
||||
emit loaded(data);
|
||||
downloadFinished(data);
|
||||
} else {
|
||||
switch (result) {
|
||||
case ResourceRequest::Result::Timeout: {
|
||||
|
|
|
@ -194,12 +194,11 @@ public:
|
|||
Q_INVOKABLE void allReferencesCleared();
|
||||
|
||||
const QUrl& getURL() const { return _url; }
|
||||
const QByteArray& getData() const { return _data; }
|
||||
|
||||
signals:
|
||||
/// Fired when the resource has been downloaded.
|
||||
/// This can be used instead of downloadFinished to access data before it is processed.
|
||||
void loaded(const QByteArray& request);
|
||||
void loaded(const QByteArray request);
|
||||
|
||||
/// Fired when the resource has finished loading.
|
||||
void finished(bool success);
|
||||
|
@ -235,7 +234,6 @@ protected:
|
|||
QHash<QPointer<QObject>, float> _loadPriorities;
|
||||
QWeakPointer<Resource> _self;
|
||||
QPointer<ResourceCache> _cache;
|
||||
QByteArray _data;
|
||||
|
||||
private slots:
|
||||
void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal);
|
||||
|
|
|
@ -58,8 +58,6 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
|
|||
|
||||
// if no callbacks specified, call our owns
|
||||
if (params.isEmpty()) {
|
||||
params.jsonCallbackReceiver = this;
|
||||
params.jsonCallbackMethod = "requestFinished";
|
||||
params.errorCallbackReceiver = this;
|
||||
params.errorCallbackMethod = "requestError";
|
||||
}
|
||||
|
@ -70,10 +68,6 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
|
|||
params, NULL, multipart);
|
||||
}
|
||||
|
||||
void UserActivityLogger::requestFinished(QNetworkReply& requestReply) {
|
||||
// qCDebug(networking) << object;
|
||||
}
|
||||
|
||||
void UserActivityLogger::requestError(QNetworkReply& errorReply) {
|
||||
qCDebug(networking) << errorReply.error() << "-" << errorReply.errorString();
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ public slots:
|
|||
void wentTo(QString destinationType, QString destinationName);
|
||||
|
||||
private slots:
|
||||
void requestFinished(QNetworkReply& requestReply);
|
||||
void requestError(QNetworkReply& errorReply);
|
||||
|
||||
private:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <LogHandler.h>
|
||||
|
@ -27,6 +28,7 @@
|
|||
#include "ControlPacket.h"
|
||||
#include "Packet.h"
|
||||
#include "PacketList.h"
|
||||
#include "../UserActivityLogger.h"
|
||||
#include "Socket.h"
|
||||
|
||||
using namespace udt;
|
||||
|
@ -328,7 +330,39 @@ void SendQueue::run() {
|
|||
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
||||
|
||||
// sleep as long as we need until next packet send, if we can
|
||||
const auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - p_high_resolution_clock::now());
|
||||
auto now = p_high_resolution_clock::now();
|
||||
auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - now);
|
||||
|
||||
// we're seeing SendQueues sleep for a long period of time here,
|
||||
// which can lock the NodeList if it's attempting to clear connections
|
||||
// for now we guard this by capping the time this thread and sleep for
|
||||
|
||||
const microseconds MAX_SEND_QUEUE_SLEEP_USECS { 2000000 };
|
||||
if (timeToSleep > MAX_SEND_QUEUE_SLEEP_USECS) {
|
||||
qWarning() << "udt::SendQueue wanted to sleep for" << timeToSleep.count() << "microseconds";
|
||||
qWarning() << "Capping sleep to" << MAX_SEND_QUEUE_SLEEP_USECS.count();
|
||||
qWarning() << "PSP:" << _packetSendPeriod << "NPD:" << nextPacketDelta
|
||||
<< "NPT:" << nextPacketTimestamp.time_since_epoch().count()
|
||||
<< "NOW:" << now.time_since_epoch().count();
|
||||
|
||||
// alright, we're in a weird state
|
||||
// we want to know why this is happening so we can implement a better fix than this guard
|
||||
// send some details up to the API (if the user allows us) that indicate how we could such a large timeToSleep
|
||||
static const QString SEND_QUEUE_LONG_SLEEP_ACTION = "sendqueue-sleep";
|
||||
|
||||
// setup a json object with the details we want
|
||||
QJsonObject longSleepObject;
|
||||
longSleepObject["timeToSleep"] = qint64(timeToSleep.count());
|
||||
longSleepObject["packetSendPeriod"] = _packetSendPeriod.load();
|
||||
longSleepObject["nextPacketDelta"] = nextPacketDelta;
|
||||
longSleepObject["nextPacketTimestamp"] = qint64(nextPacketTimestamp.time_since_epoch().count());
|
||||
longSleepObject["then"] = qint64(now.time_since_epoch().count());
|
||||
|
||||
// hopefully send this event using the user activity logger
|
||||
UserActivityLogger::getInstance().logAction(SEND_QUEUE_LONG_SLEEP_ACTION, longSleepObject);
|
||||
|
||||
timeToSleep = MAX_SEND_QUEUE_SLEEP_USECS;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(timeToSleep);
|
||||
}
|
||||
|
|
|
@ -57,16 +57,18 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isSimulated()) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
_outgoingChanges.remove(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
} else {
|
||||
_entitiesToDelete.insert(entity);
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
_outgoingChanges.remove(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
} else {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +177,7 @@ void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionState
|
|||
_entitiesToRelease.insert(entity);
|
||||
}
|
||||
|
||||
if (entity->isSimulated() && entity->isDead()) {
|
||||
if (entity->isDead()) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +192,7 @@ void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() {
|
|||
entity->setPhysicsInfo(nullptr);
|
||||
delete motionState;
|
||||
|
||||
if (entity->isSimulated() && entity->isDead()) {
|
||||
if (entity->isDead()) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#ifndef hifi_SimpleMovingAverage_h
|
||||
#define hifi_SimpleMovingAverage_h
|
||||
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
|
||||
class SimpleMovingAverage {
|
||||
|
@ -64,4 +65,45 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <class T, int MAX_NUM_SAMPLES> class ThreadSafeMovingAverage {
|
||||
public:
|
||||
void clear() {
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
_samples = 0;
|
||||
}
|
||||
|
||||
bool isAverageValid() const {
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
return (_samples > 0);
|
||||
}
|
||||
|
||||
void addSample(T sample) {
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
if (_samples > 0) {
|
||||
_average = (sample * WEIGHTING) + (_average * ONE_MINUS_WEIGHTING);
|
||||
} else {
|
||||
_average = sample;
|
||||
}
|
||||
_samples++;
|
||||
}
|
||||
|
||||
T getAverage() const {
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
return _average;
|
||||
}
|
||||
|
||||
size_t getSamples() const {
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
return _samples;
|
||||
}
|
||||
|
||||
private:
|
||||
const float WEIGHTING = 1.0f / (float)MAX_NUM_SAMPLES;
|
||||
const float ONE_MINUS_WEIGHTING = 1.0f - WEIGHTING;
|
||||
size_t _samples { 0 };
|
||||
T _average;
|
||||
mutable std::mutex _lock;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_SimpleMovingAverage_h
|
||||
|
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 78 KiB |