Merge pull request #12904 from AndrewMeadows/lhf-needsToCallUpdate

LHF optimiaztion: only call ModelEntityItem::update() on instances that actually need it
This commit is contained in:
Sam Gateau 2018-04-24 11:22:48 -07:00 committed by GitHub
commit 2cae294ab4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 147 additions and 163 deletions

View file

@ -69,7 +69,6 @@ private:
graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); }
graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; }
bool _needsInitialSimulation{ true };
glm::vec3 _lastPosition;
glm::vec3 _lastDimensions;
glm::quat _lastRotation;

View file

@ -216,7 +216,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
int bytesRead = 0;
bool overwriteLocalData = true;
bool somethingChanged = false;
@ -360,3 +359,21 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold);
return bytesRead;
}
float AnimationPropertyGroup::getNumFrames() const {
return _lastFrame - _firstFrame + 1.0f;
}
float AnimationPropertyGroup::computeLoopedFrame(float frame) const {
float numFrames = getNumFrames();
if (numFrames > 1.0f) {
frame = getFirstFrame() + fmodf(frame - getFirstFrame(), numFrames);
} else {
frame = getFirstFrame();
}
return frame;
}
bool AnimationPropertyGroup::isValidAndRunning() const {
return getRunning() && (getFPS() > 0.0f) && (getNumFrames() > 1.0f) && !(getURL().isEmpty());
}

View file

@ -77,6 +77,10 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) override;
float getNumFrames() const;
float computeLoopedFrame(float frame) const;
bool isValidAndRunning() const;
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, "");
DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f);
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f);

View file

@ -597,7 +597,7 @@ protected:
//
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
uint32_t _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
std::atomic_uint _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
// these backpointers are only ever set/cleared by friends:
EntityTreeElementPointer _element; // set by EntityTreeElement

View file

@ -82,12 +82,12 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
bool somethingChangedInAnimations = _animationProperties.setProperties(properties);
if (somethingChangedInAnimations) {
_flags |= Simulation::DIRTY_UPDATEABLE;
}
somethingChanged = somethingChanged || somethingChangedInAnimations;
withWriteLock([&] {
AnimationPropertyGroup animationProperties = _animationProperties;
animationProperties.setProperties(properties);
bool somethingChangedInAnimations = applyNewAnimationProperties(animationProperties);
somethingChanged = somethingChanged || somethingChangedInAnimations;
});
if (somethingChanged) {
bool wantDebug = false;
@ -118,12 +118,16 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
// grab a local copy of _animationProperties to avoid multiple locks
int bytesFromAnimation;
withWriteLock([&] {
// Note: since we've associated our _animationProperties with our _animationLoop, the readEntitySubclassDataFromBuffer()
// will automatically read into the animation loop
bytesFromAnimation = _animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
withReadLock([&] {
AnimationPropertyGroup animationProperties = _animationProperties;
bytesFromAnimation = animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, animationPropertiesChanged);
if (animationPropertiesChanged) {
applyNewAnimationProperties(animationProperties);
somethingChanged = true;
}
});
bytesRead += bytesFromAnimation;
@ -131,11 +135,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
if (animationPropertiesChanged) {
_flags |= Simulation::DIRTY_UPDATEABLE;
somethingChanged = true;
}
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector<bool>, setJointRotationsSet);
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector<glm::quat>, setJointRotations);
READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
@ -194,98 +193,38 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
// added update function back for property fix
void ModelEntityItem::update(const quint64& now) {
assert(_lastAnimated > 0);
{
auto currentAnimationProperties = this->getAnimationProperties();
if (_previousAnimationProperties != currentAnimationProperties) {
withWriteLock([&] {
// if we hit start animation or change the first or last frame then restart the animation
if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) ||
(currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) ||
(currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) {
// when we start interface and the property is are set then the current frame is initialized to -1
if (_currentFrame < 0) {
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
_currentFrame = currentAnimationProperties.getCurrentFrame();
setAnimationCurrentFrame(_currentFrame);
} else {
_lastAnimated = usecTimestampNow();
_currentFrame = currentAnimationProperties.getFirstFrame();
setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame());
}
} else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) {
_currentFrame = currentAnimationProperties.getFirstFrame();
setAnimationCurrentFrame(_currentFrame);
} else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) {
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
_currentFrame = currentAnimationProperties.getCurrentFrame();
}
});
_previousAnimationProperties = this->getAnimationProperties();
}
if (isAnimatingSomething()) {
if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) {
updateFrameCount();
}
}
}
EntityItem::update(now);
}
bool ModelEntityItem::needsToCallUpdate() const {
return true;
}
void ModelEntityItem::updateFrameCount() {
if (_currentFrame < 0.0f) {
return;
}
if (!_lastAnimated) {
_lastAnimated = usecTimestampNow();
return;
}
auto now = usecTimestampNow();
// update the interval since the last animation.
// increment timestamp before checking "hold"
auto interval = now - _lastAnimated;
_lastAnimated = now;
// if fps is negative then increment timestamp and return.
if (getAnimationFPS() < 0.0f) {
// grab a local copy of _animationProperties to avoid multiple locks
auto animationProperties = getAnimationProperties();
// bail on "hold"
if (animationProperties.getHold()) {
return;
}
int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1;
if (!getAnimationHold() && getAnimationIsPlaying()) {
float deltaTime = (float)interval / (float)USECS_PER_SECOND;
_currentFrame += (deltaTime * getAnimationFPS());
if (_currentFrame > getAnimationLastFrame() + 1) {
if (getAnimationLoop() && getAnimationFirstFrame() != getAnimationLastFrame()) {
_currentFrame = getAnimationFirstFrame() + (int)(_currentFrame - getAnimationFirstFrame()) % updatedFrameCount;
} else {
_currentFrame = getAnimationLastFrame();
}
} else if (_currentFrame < getAnimationFirstFrame()) {
if (getAnimationFirstFrame() < 0) {
_currentFrame = 0;
} else {
_currentFrame = getAnimationFirstFrame();
}
// increment animation frame
_currentFrame += (animationProperties.getFPS() * ((float)interval) / (float)USECS_PER_SECOND);
if (_currentFrame > animationProperties.getLastFrame() + 1.0f) {
if (animationProperties.getLoop()) {
_currentFrame = animationProperties.computeLoopedFrame(_currentFrame);
} else {
_currentFrame = animationProperties.getLastFrame();
}
} else if (_currentFrame < animationProperties.getFirstFrame()) {
if (animationProperties.getFirstFrame() < 0.0f) {
_currentFrame = 0.0f;
} else {
_currentFrame = animationProperties.getFirstFrame();
}
// qCDebug(entities) << "in update frame " << _currentFrame;
setAnimationCurrentFrame(_currentFrame);
}
setAnimationCurrentFrame(_currentFrame);
EntityItem::update(now);
}
void ModelEntityItem::debugDump() const {
@ -361,67 +300,61 @@ void ModelEntityItem::setAnimationURL(const QString& url) {
}
void ModelEntityItem::setAnimationSettings(const QString& value) {
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, currentFrame, or running, those values will be parsed out and
// will over ride the regular animation settings
// NOTE: this method only called for old bitstream format
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object();
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
if (settingsMap.contains("fps")) {
float fps = settingsMap["fps"].toFloat();
setAnimationFPS(fps);
}
withWriteLock([&] {
auto animationProperties = _animationProperties;
// old settings used frameIndex
if (settingsMap.contains("frameIndex")) {
float currentFrame = settingsMap["frameIndex"].toFloat();
#ifdef WANT_DEBUG
if (!getAnimationURL().isEmpty()) {
qCDebug(entities) << "ModelEntityItem::setAnimationSettings() calling setAnimationFrameIndex()...";
qCDebug(entities) << " model URL:" << getModelURL();
qCDebug(entities) << " animation URL:" << getAnimationURL();
qCDebug(entities) << " settings:" << value;
qCDebug(entities) << " settingsMap[frameIndex]:" << settingsMap["frameIndex"];
qCDebug(entities" currentFrame: %20.5f", currentFrame);
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, currentFrame, or running, those values will be parsed out and
// will over ride the regular animation settings
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object();
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
if (settingsMap.contains("fps")) {
float fps = settingsMap["fps"].toFloat();
animationProperties.setFPS(fps);
}
#endif
setAnimationCurrentFrame(currentFrame);
}
if (settingsMap.contains("running")) {
bool running = settingsMap["running"].toBool();
if (running != getAnimationIsPlaying()) {
setAnimationIsPlaying(running);
// old settings used frameIndex
if (settingsMap.contains("frameIndex")) {
float currentFrame = settingsMap["frameIndex"].toFloat();
animationProperties.setCurrentFrame(currentFrame);
}
}
if (settingsMap.contains("firstFrame")) {
float firstFrame = settingsMap["firstFrame"].toFloat();
setAnimationFirstFrame(firstFrame);
}
if (settingsMap.contains("running")) {
bool running = settingsMap["running"].toBool();
if (running != animationProperties.getRunning()) {
animationProperties.setRunning(running);
}
}
if (settingsMap.contains("lastFrame")) {
float lastFrame = settingsMap["lastFrame"].toFloat();
setAnimationLastFrame(lastFrame);
}
if (settingsMap.contains("firstFrame")) {
float firstFrame = settingsMap["firstFrame"].toFloat();
animationProperties.setFirstFrame(firstFrame);
}
if (settingsMap.contains("loop")) {
bool loop = settingsMap["loop"].toBool();
setAnimationLoop(loop);
}
if (settingsMap.contains("lastFrame")) {
float lastFrame = settingsMap["lastFrame"].toFloat();
animationProperties.setLastFrame(lastFrame);
}
if (settingsMap.contains("hold")) {
bool hold = settingsMap["hold"].toBool();
setAnimationHold(hold);
}
if (settingsMap.contains("loop")) {
bool loop = settingsMap["loop"].toBool();
animationProperties.setLoop(loop);
}
if (settingsMap.contains("allowTranslation")) {
bool allowTranslation = settingsMap["allowTranslation"].toBool();
setAnimationAllowTranslation(allowTranslation);
}
_flags |= Simulation::DIRTY_UPDATEABLE;
if (settingsMap.contains("hold")) {
bool hold = settingsMap["hold"].toBool();
animationProperties.setHold(hold);
}
if (settingsMap.contains("allowTranslation")) {
bool allowTranslation = settingsMap["allowTranslation"].toBool();
animationProperties.setAllowTranslation(allowTranslation);
}
applyNewAnimationProperties(animationProperties);
});
}
void ModelEntityItem::setAnimationIsPlaying(bool value) {
@ -713,11 +646,45 @@ float ModelEntityItem::getAnimationFPS() const {
});
}
bool ModelEntityItem::isAnimatingSomething() const {
return resultWithReadLock<bool>([&] {
return !_animationProperties.getURL().isEmpty() &&
_animationProperties.getRunning() &&
(_animationProperties.getFPS() != 0.0f);
});
return _animationProperties.isValidAndRunning();
});
}
bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProperties) {
// call applyNewAnimationProperties() whenever trying to update _animationProperties
// because there is some reset logic we need to do whenever the animation "config" properties change
// NOTE: this private method is always called inside withWriteLock()
// if we hit start animation or change the first or last frame then restart the animation
if ((newProperties.getFirstFrame() != _animationProperties.getFirstFrame()) ||
(newProperties.getLastFrame() != _animationProperties.getLastFrame()) ||
(newProperties.getRunning() && !_animationProperties.getRunning())) {
// when we start interface and the property is are set then the current frame is initialized to -1
if (_currentFrame < 0.0f) {
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
_currentFrame = newProperties.getCurrentFrame();
newProperties.setCurrentFrame(_currentFrame);
} else {
_lastAnimated = usecTimestampNow();
_currentFrame = newProperties.getFirstFrame();
newProperties.setCurrentFrame(newProperties.getFirstFrame());
}
} else if (!newProperties.getRunning() && _animationProperties.getRunning()) {
_currentFrame = newProperties.getFirstFrame();
newProperties.setCurrentFrame(_currentFrame);
} else if (newProperties.getCurrentFrame() != _animationProperties.getCurrentFrame()) {
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
_currentFrame = newProperties.getCurrentFrame();
}
// finally apply the changes
bool somethingChanged = newProperties != _animationProperties;
if (somethingChanged) {
_animationProperties = newProperties;
_flags |= Simulation::DIRTY_UPDATEABLE;
}
return somethingChanged;
}

View file

@ -46,10 +46,9 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) override;
// update() and needstocallupdate() added back for the entity property fix
virtual void update(const quint64& now) override;
virtual bool needsToCallUpdate() const override;
void updateFrameCount();
bool needsToCallUpdate() const override { return isAnimatingSomething(); }
virtual void debugDump() const override;
@ -132,6 +131,7 @@ public:
private:
void setAnimationSettings(const QString& value); // only called for old bitstream format
bool applyNewAnimationProperties(AnimationPropertyGroup newProperties);
ShapeType computeTrueShapeType() const;
protected:
@ -172,7 +172,6 @@ protected:
private:
uint64_t _lastAnimated{ 0 };
AnimationPropertyGroup _previousAnimationProperties;
float _currentFrame{ -1.0f };
};

View file

@ -77,8 +77,6 @@ class PolyLineEntityItem : public EntityItem {
QString getTextures() const;
void setTextures(const QString& textures);
virtual bool needsToCallUpdate() const override { return true; }
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; }
bool pointsChanged() const { return _pointsChanged; }