From a5b68f2da24d79cc2d94b55ad05aec9e76bdd465 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 2 Dec 2013 16:13:08 +0100 Subject: [PATCH 01/15] Changes to llkeyframemotionparam.cpp as result of audit. These changes were the result of my Motion leak audit. However, there is a serious problem with LLKeyframeMotionParam in that it stores plain pointers to objects that can and will be deleted by other objects. Its the classicial LL example of coding non-Object-Oriented badly maintainable and instable code. I cannot make sure this won't go wrong for the simple fact that LLKeyframeMotionParam is not used at ALL in our code. Hence, the next commit will rather delete this file. This commit is merely to have a record of this finding. --- indra/llcharacter/llkeyframemotionparam.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/indra/llcharacter/llkeyframemotionparam.cpp b/indra/llcharacter/llkeyframemotionparam.cpp index 4df1ef46b..3f3a44e52 100644 --- a/indra/llcharacter/llkeyframemotionparam.cpp +++ b/indra/llcharacter/llkeyframemotionparam.cpp @@ -68,11 +68,18 @@ LLKeyframeMotionParam::~LLKeyframeMotionParam() iter != mParameterizedMotions.end(); ++iter) { motion_list_t& motionList = iter->second; +#if 0 + // Singu note: this class is NOT responsible for cleaning up paramMotion.mMotion. + // They were obtained in LLKeyframeMotionParam::addKeyframeMotion by calling + // LLCharacter::createMotion which returns LLMotionController::createMotion + // which is responsible for cleaning up (by storing the pointers in + // LLMotionController::mAllMotions). for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) { const ParameterizedMotion& paramMotion = *iter2; delete paramMotion.mMotion; } +#endif motionList.clear(); } mParameterizedMotions.clear(); @@ -98,7 +105,11 @@ LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *ch { const ParameterizedMotion& paramMotion = *iter2; LLMotion* motion = paramMotion.mMotion; - motion->onInitialize(character); + if (motion->onInitialize(character) != STATUS_SUCCESS) + { + llwarns << "Skipping uninitialize motion!" << llendl; + continue; + } if (motion->getDuration() > mEaseInDuration) { From ba2bf9f366c6a40d2a33cce5a75665bce525ecee Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 2 Dec 2013 16:18:23 +0100 Subject: [PATCH 02/15] Get rid of unused and irrepairable code. --- indra/llcharacter/CMakeLists.txt | 2 - indra/llcharacter/llkeyframemotionparam.cpp | 467 -------------------- indra/llcharacter/llkeyframemotionparam.h | 176 -------- 3 files changed, 645 deletions(-) delete mode 100644 indra/llcharacter/llkeyframemotionparam.cpp delete mode 100644 indra/llcharacter/llkeyframemotionparam.h diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index 6652333ef..8dda476d9 100644 --- a/indra/llcharacter/CMakeLists.txt +++ b/indra/llcharacter/CMakeLists.txt @@ -29,7 +29,6 @@ set(llcharacter_SOURCE_FILES lljointsolverrp3.cpp llkeyframefallmotion.cpp llkeyframemotion.cpp - llkeyframemotionparam.cpp llkeyframestandmotion.cpp llkeyframewalkmotion.cpp llmotion.cpp @@ -57,7 +56,6 @@ set(llcharacter_HEADER_FILES lljointstate.h llkeyframefallmotion.h llkeyframemotion.h - llkeyframemotionparam.h llkeyframestandmotion.h llkeyframewalkmotion.h llmotion.h diff --git a/indra/llcharacter/llkeyframemotionparam.cpp b/indra/llcharacter/llkeyframemotionparam.cpp deleted file mode 100644 index 3f3a44e52..000000000 --- a/indra/llcharacter/llkeyframemotionparam.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/** - * @file llkeyframemotionparam.cpp - * @brief Implementation of LLKeyframeMotion class. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -//----------------------------------------------------------------------------- -// Header Files -//----------------------------------------------------------------------------- -#include "linden_common.h" - -#include "llkeyframemotionparam.h" -#include "llcharacter.h" -#include "llmath.h" -#include "m3math.h" -#include "lldir.h" -#include "llanimationstates.h" - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam class -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam() -// Class Constructor -//----------------------------------------------------------------------------- -LLKeyframeMotionParam::LLKeyframeMotionParam( const LLUUID &id) : LLMotion(id) -{ - mDefaultKeyframeMotion = NULL; - mCharacter = NULL; - - mEaseInDuration = 0.f; - mEaseOutDuration = 0.f; - mDuration = 0.f; - mPriority = LLJoint::LOW_PRIORITY; -} - - -//----------------------------------------------------------------------------- -// ~LLKeyframeMotionParam() -// Class Destructor -//----------------------------------------------------------------------------- -LLKeyframeMotionParam::~LLKeyframeMotionParam() -{ - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - motion_list_t& motionList = iter->second; -#if 0 - // Singu note: this class is NOT responsible for cleaning up paramMotion.mMotion. - // They were obtained in LLKeyframeMotionParam::addKeyframeMotion by calling - // LLCharacter::createMotion which returns LLMotionController::createMotion - // which is responsible for cleaning up (by storing the pointers in - // LLMotionController::mAllMotions). - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; - delete paramMotion.mMotion; - } -#endif - motionList.clear(); - } - mParameterizedMotions.clear(); -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onInitialize(LLCharacter *character) -//----------------------------------------------------------------------------- -LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character) -{ - mCharacter = character; - - if (!loadMotions()) - { - return STATUS_FAILURE; - } - - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - motion_list_t& motionList = iter->second; - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; - LLMotion* motion = paramMotion.mMotion; - if (motion->onInitialize(character) != STATUS_SUCCESS) - { - llwarns << "Skipping uninitialize motion!" << llendl; - continue; - } - - if (motion->getDuration() > mEaseInDuration) - { - mEaseInDuration = motion->getEaseInDuration(); - } - - if (motion->getEaseOutDuration() > mEaseOutDuration) - { - mEaseOutDuration = motion->getEaseOutDuration(); - } - - if (motion->getDuration() > mDuration) - { - mDuration = motion->getDuration(); - } - - if (motion->getPriority() > mPriority) - { - mPriority = motion->getPriority(); - } - - LLPose *pose = motion->getPose(); - - mPoseBlender.addMotion(motion); - for (LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState()) - { - LLPose *blendedPose = mPoseBlender.getBlendedPose(); - blendedPose->addJointState(jsp); - } - } - } - - return STATUS_SUCCESS; -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onActivate() -//----------------------------------------------------------------------------- -BOOL LLKeyframeMotionParam::onActivate() -{ - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - motion_list_t& motionList = iter->second; - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; - paramMotion.mMotion->activate(mActivationTimestamp); - } - } - return TRUE; -} - - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onUpdate() -//----------------------------------------------------------------------------- -BOOL LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask) -{ - F32 weightFactor = 1.f / (F32)mParameterizedMotions.size(); - - // zero out all pose weights - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - motion_list_t& motionList = iter->second; - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; -// llinfos << "Weight for pose " << paramMotion.mMotion->getName() << " is " << paramMotion.mMotion->getPose()->getWeight() << llendl; - paramMotion.mMotion->getPose()->setWeight(0.f); - } - } - - - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - const std::string& paramName = iter->first; - F32* paramValue = (F32 *)mCharacter->getAnimationData(paramName); - if (NULL == paramValue) // unexpected, but... - { - llwarns << "paramValue == NULL" << llendl; - continue; - } - - // DANGER! Do not modify mParameterizedMotions while using these pointers! - const ParameterizedMotion* firstMotion = NULL; - const ParameterizedMotion* secondMotion = NULL; - - motion_list_t& motionList = iter->second; - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; - paramMotion.mMotion->onUpdate(time, joint_mask); - - F32 distToParam = paramMotion.mParam - *paramValue; - - if ( distToParam <= 0.f) - { - // keep track of the motion closest to the parameter value - firstMotion = ¶mMotion; - } - else - { - // we've passed the parameter value - // so store the first motion we find as the second one we want to blend... - if (firstMotion && !secondMotion ) - { - secondMotion = ¶mMotion; - } - //...or, if we've seen no other motion so far, make sure we blend to this only - else if (!firstMotion) - { - firstMotion = ¶mMotion; - secondMotion = ¶mMotion; - } - } - } - - LLPose *firstPose; - LLPose *secondPose; - - if (firstMotion) - firstPose = firstMotion->mMotion->getPose(); - else - firstPose = NULL; - - if (secondMotion) - secondPose = secondMotion->mMotion->getPose(); - else - secondPose = NULL; - - // now modify weight of the subanim (only if we are blending between two motions) - if (firstMotion && secondMotion) - { - if (firstMotion == secondMotion) - { - firstPose->setWeight(weightFactor); - } - else if (firstMotion->mParam == secondMotion->mParam) - { - firstPose->setWeight(0.5f * weightFactor); - secondPose->setWeight(0.5f * weightFactor); - } - else - { - F32 first_weight = 1.f - - ((llclamp(*paramValue - firstMotion->mParam, 0.f, (secondMotion->mParam - firstMotion->mParam))) / - (secondMotion->mParam - firstMotion->mParam)); - first_weight = llclamp(first_weight, 0.f, 1.f); - - F32 second_weight = 1.f - first_weight; - - firstPose->setWeight(first_weight * weightFactor); - secondPose->setWeight(second_weight * weightFactor); - -// llinfos << "Parameter " << *paramName << ": " << *paramValue << llendl; -// llinfos << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << llendl; - } - } - else if (firstMotion && !secondMotion) - { - firstPose->setWeight(weightFactor); - } - } - - // blend poses - mPoseBlender.blendAndApply(); - - llinfos << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << llendl; - - return TRUE; -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::onDeactivate() -//----------------------------------------------------------------------------- -void LLKeyframeMotionParam::onDeactivate() -{ - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - motion_list_t& motionList = iter->second; - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; - paramMotion.mMotion->onDeactivate(); - } - } -} - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::addKeyframeMotion() -//----------------------------------------------------------------------------- -BOOL LLKeyframeMotionParam::addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value) -{ - LLMotion *newMotion = mCharacter->createMotion( id ); - - if (!newMotion) - { - return FALSE; - } - - newMotion->setName(name); - - // now add motion to this list - mParameterizedMotions[param].insert(ParameterizedMotion(newMotion, value)); - - return TRUE; -} - - -//----------------------------------------------------------------------------- -// LLKeyframeMotionParam::setDefaultKeyframeMotion() -//----------------------------------------------------------------------------- -void LLKeyframeMotionParam::setDefaultKeyframeMotion(char *name) -{ - for (motion_map_t::iterator iter = mParameterizedMotions.begin(); - iter != mParameterizedMotions.end(); ++iter) - { - motion_list_t& motionList = iter->second; - for (motion_list_t::iterator iter2 = motionList.begin(); iter2 != motionList.end(); ++iter2) - { - const ParameterizedMotion& paramMotion = *iter2; - if (paramMotion.mMotion->getName() == name) - { - mDefaultKeyframeMotion = paramMotion.mMotion; - } - } - } -} - -//----------------------------------------------------------------------------- -// loadMotions() -//----------------------------------------------------------------------------- -BOOL LLKeyframeMotionParam::loadMotions() -{ - //------------------------------------------------------------------------- - // Load named file by concatenating the character prefix with the motion name. - // Load data into a buffer to be parsed. - //------------------------------------------------------------------------- - //std::string path = gDirUtilp->getExpandedFilename(LL_PATH_MOTIONS,mCharacter->getAnimationPrefix()) - // + "_" + getName() + ".llp"; - //RN: deprecated unused reference to "motion" directory - std::string path; - - - //------------------------------------------------------------------------- - // open the file - //------------------------------------------------------------------------- - S32 fileSize = 0; - LLAPRFile infile(path, LL_APR_R, &fileSize); - apr_file_t* fp = infile.getFileHandle() ; - if (!fp || fileSize == 0) - { - llinfos << "ERROR: can't open: " << path << llendl; - return FALSE; - } - - // allocate a text buffer - try - { - std::vector text(fileSize+1); - - //------------------------------------------------------------------------- - // load data from file into buffer - //------------------------------------------------------------------------- - bool error = false; - char *p = &text[0]; - while ( 1 ) - { - if (apr_file_eof(fp) == APR_EOF) - { - break; - } - if (apr_file_gets(p, 1024, fp) != APR_SUCCESS) - { - error = true; - break; - } - while ( *(++p) ) - ; - } - - //------------------------------------------------------------------------- - // close the file - //------------------------------------------------------------------------- - infile.close(); - - //------------------------------------------------------------------------- - // check for error - //------------------------------------------------------------------------- - llassert( p <= (&text[0] + fileSize) ); - - if ( error ) - { - llinfos << "ERROR: error while reading from " << path << llendl; - return FALSE; - } - - llinfos << "Loading parametric keyframe data for: " << getName() << llendl; - - //------------------------------------------------------------------------- - // parse the text and build keyframe data structures - //------------------------------------------------------------------------- - p = &text[0]; - S32 num; - char strA[80]; /* Flawfinder: ignore */ - char strB[80]; /* Flawfinder: ignore */ - F32 floatA = 0.0f; - - - //------------------------------------------------------------------------- - // get priority - //------------------------------------------------------------------------- - BOOL isFirstMotion = TRUE; - num = sscanf(p, "%79s %79s %f", strA, strB, &floatA); /* Flawfinder: ignore */ - - while(1) - { - if (num == 0 || num == EOF) break; - if ((num != 3)) - { - llinfos << "WARNING: can't read parametric motion" << llendl; - return FALSE; - } - - addKeyframeMotion(strA, gAnimLibrary.stringToAnimState(std::string(strA)), strB, floatA); - if (isFirstMotion) - { - isFirstMotion = FALSE; - setDefaultKeyframeMotion(strA); - } - - p = strstr(p, "\n"); - if (!p) - { - break; - } - - p++; - num = sscanf(p, "%79s %79s %f", strA, strB, &floatA); /* Flawfinder: ignore */ - } - - return TRUE; - } - catch(std::bad_alloc) - { - llinfos << "ERROR: Unable to allocate keyframe text buffer." << llendl; - return FALSE; - } -} - -// End diff --git a/indra/llcharacter/llkeyframemotionparam.h b/indra/llcharacter/llkeyframemotionparam.h deleted file mode 100644 index f74aea276..000000000 --- a/indra/llcharacter/llkeyframemotionparam.h +++ /dev/null @@ -1,176 +0,0 @@ -/** - * @file llkeyframemotionparam.h - * @brief Implementation of LLKeframeMotionParam class. - * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLKEYFRAMEMOTIONPARAM_H -#define LL_LLKEYFRAMEMOTIONPARAM_H - -//----------------------------------------------------------------------------- -// Header files -//----------------------------------------------------------------------------- - -#include - -#include "llmotion.h" -#include "lljointstate.h" -#include "v3math.h" -#include "llquaternion.h" -#include "linked_lists.h" -#include "llkeyframemotion.h" - -//----------------------------------------------------------------------------- -// class LLKeyframeMotionParam -//----------------------------------------------------------------------------- -class LLKeyframeMotionParam : - public LLMotion -{ -public: - // Constructor - LLKeyframeMotionParam(const LLUUID &id); - - // Destructor - virtual ~LLKeyframeMotionParam(); - -public: - //------------------------------------------------------------------------- - // functions to support MotionController and MotionRegistry - //------------------------------------------------------------------------- - - // static constructor - // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLKeyframeMotionParam(id); } - -public: - //------------------------------------------------------------------------- - // animation callbacks to be implemented by subclasses - //------------------------------------------------------------------------- - - // motions must specify whether or not they loop - virtual BOOL getLoop() { - return TRUE; - } - - // motions must report their total duration - virtual F32 getDuration() { - return mDuration; - } - - // motions must report their "ease in" duration - virtual F32 getEaseInDuration() { - return mEaseInDuration; - } - - // motions must report their "ease out" duration. - virtual F32 getEaseOutDuration() { - return mEaseOutDuration; - } - - // motions must report their priority - virtual LLJoint::JointPriority getPriority() { - return mPriority; - } - - virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; } - - // called to determine when a motion should be activated/deactivated based on avatar pixel coverage - virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; } - - // run-time (post constructor) initialization, - // called after parameters have been set - // must return true to indicate success and be available for activation - virtual LLMotionInitStatus onInitialize(LLCharacter *character); - - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate(); - - // called per time step - // must return TRUE while it is active, and - // must return FALSE when the motion is completed. - virtual BOOL onUpdate(F32 time, U8* joint_mask); - - // called when a motion is deactivated - virtual void onDeactivate(); - - virtual LLPose* getPose() { return mPoseBlender.getBlendedPose();} - -protected: - //------------------------------------------------------------------------- - // new functions defined by this subclass - //------------------------------------------------------------------------- - struct ParameterizedMotion - { - ParameterizedMotion(LLMotion* motion, F32 param) : mMotion(motion), mParam(param) {} - LLMotion* mMotion; - F32 mParam; - }; - - // add a motion and associated parameter triplet - BOOL addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value); - - // set default motion for LOD and retrieving blend constants - void setDefaultKeyframeMotion(char *); - - BOOL loadMotions(); - -protected: - //------------------------------------------------------------------------- - // Member Data - //------------------------------------------------------------------------- - - struct compare_motions - { - bool operator() (const ParameterizedMotion& a, const ParameterizedMotion& b) const - { - if (a.mParam != b.mParam) - return (a.mParam < b.mParam); - else - return a.mMotion < b.mMotion; - } - }; - - typedef std::set < ParameterizedMotion, compare_motions > motion_list_t; - typedef std::map motion_map_t; - motion_map_t mParameterizedMotions; - LLMotion* mDefaultKeyframeMotion; - LLCharacter* mCharacter; - LLPoseBlender mPoseBlender; - - F32 mEaseInDuration; - F32 mEaseOutDuration; - F32 mDuration; - LLJoint::JointPriority mPriority; - - LLUUID mTransactionID; -}; - -#endif // LL_LLKEYFRAMEMOTIONPARAM_H From 116fe01dee505ee0ffeab1f7761b632f445c5d7c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 3 Dec 2013 02:02:07 +0100 Subject: [PATCH 03/15] Add AIDebugInstanceCounter Prints number of instances upon construction and destruction. Usage: class LLSomething : public AIDebugInstanceCounter ... --- indra/cwdebug/debug.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 3c59db690..58e0c6ac9 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -392,6 +392,40 @@ void InstanceTracker::dump(void) } // namespace debug +template +class AIDebugInstanceCounter +{ + public: + static int sInstanceCount; + + protected: + static void print_count(char const* name, int count, bool destruction); + + AIDebugInstanceCounter() + { + print_count(typeid(T).name(), ++sInstanceCount, false); + } + AIDebugInstanceCounter(AIDebugInstanceCounter const&) + { + print_count(typeid(T).name(), ++sInstanceCount, false); + } + ~AIDebugInstanceCounter() + { + print_count(typeid(T).name(), --sInstanceCount, true); + } +}; + +//static +template +int AIDebugInstanceCounter::sInstanceCount; + +//static +template +void AIDebugInstanceCounter::print_count(char const* name, int count, bool destruction) +{ + Dout(dc::notice, (destruction ? "Destructed " : "Constructing ") << name << ", now " << count << " instance" << ((count == 1) ? "." : "s.")); +} + //! Debugging macro. // // Print "Entering " << \a data to channel \a cntrl and increment From 61097dac7291cc37b8ab1240aebd2f4fd6f8d903 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 3 Dec 2013 03:15:25 +0100 Subject: [PATCH 04/15] Auto clean up motion cache. Conflicts: indra/llcharacter/llkeyframemotion.cpp indra/llcharacter/llkeyframemotion.h indra/newview/llfloaterbvhpreview.cpp --- indra/llcharacter/llcharacter.cpp | 1 - indra/llcharacter/llheadrotmotion.cpp | 3 + indra/llcharacter/lljoint.h | 2 +- indra/llcharacter/llkeyframemotion.cpp | 168 +++++++++++++++++------ indra/llcharacter/llkeyframemotion.h | 132 ++++++++++++++++-- indra/llcharacter/llmotioncontroller.cpp | 110 +++++++++------ indra/llcharacter/llmotioncontroller.h | 4 +- indra/newview/llfloaterbvhpreview.cpp | 4 + indra/newview/llvoavatar.cpp | 2 +- 9 files changed, 331 insertions(+), 95 deletions(-) diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp index 888c20cd4..25b89d2f8 100644 --- a/indra/llcharacter/llcharacter.cpp +++ b/indra/llcharacter/llcharacter.cpp @@ -136,7 +136,6 @@ LLMotion* LLCharacter::findMotion( const LLUUID &id ) //----------------------------------------------------------------------------- // createMotion() -// NOTE: Always assign the result to a LLPointer! //----------------------------------------------------------------------------- LLMotion* LLCharacter::createMotion( const LLUUID &id ) { diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp index 0ee378f3b..14017da10 100644 --- a/indra/llcharacter/llheadrotmotion.cpp +++ b/indra/llcharacter/llheadrotmotion.cpp @@ -104,7 +104,10 @@ LLHeadRotMotion::~LLHeadRotMotion() LLMotion::LLMotionInitStatus LLHeadRotMotion::onInitialize(LLCharacter *character) { if (!character) + { + llwarns << "character is NULL." << llendl; return STATUS_FAILURE; + } mCharacter = character; mPelvisJoint = character->getJoint("mPelvis"); diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h index fe14c1e44..cb55e6ca7 100644 --- a/indra/llcharacter/lljoint.h +++ b/indra/llcharacter/lljoint.h @@ -41,7 +41,7 @@ #include "lldarray.h" const S32 LL_CHARACTER_MAX_JOINTS_PER_MESH = 15; -const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 4! +const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 16! const U32 LL_HAND_JOINT_NUM = 31; const U32 LL_FACE_JOINT_NUM = 30; const S32 LL_CHARACTER_MAX_PRIORITY = 7; diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index 98906d3d4..653d3988c 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -84,38 +84,55 @@ LLKeyframeMotion::JointMotionList::~JointMotionList() for_each(mJointMotionArray.begin(), mJointMotionArray.end(), DeletePointer()); } -U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo() +//Singu: add parameter 'silent'. +U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo(bool silent) const { S32 total_size = sizeof(JointMotionList); for (U32 i = 0; i < getNumJointMotions(); i++) { - LLKeyframeMotion::JointMotion* joint_motion_p = mJointMotionArray[i]; + LLKeyframeMotion::JointMotion const* joint_motion_p = mJointMotionArray[i]; - llinfos << "\tJoint " << joint_motion_p->mJointName << llendl; + if (!silent) + { + llinfos << "\tJoint " << joint_motion_p->mJointName << llendl; + } if (joint_motion_p->mUsage & LLJointState::SCALE) { - llinfos << "\t" << joint_motion_p->mScaleCurve.mNumKeys << " scale keys at " - << joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey) << " bytes" << llendl; - + if (!silent) + { + llinfos << "\t" << joint_motion_p->mScaleCurve.mNumKeys << " scale keys at " + << joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey) << " bytes" << llendl; + } total_size += joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey); } if (joint_motion_p->mUsage & LLJointState::ROT) { - llinfos << "\t" << joint_motion_p->mRotationCurve.mNumKeys << " rotation keys at " - << joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey) << " bytes" << llendl; - + if (!silent) + { + llinfos << "\t" << joint_motion_p->mRotationCurve.mNumKeys << " rotation keys at " + << joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey) << " bytes" << llendl; + } total_size += joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey); } if (joint_motion_p->mUsage & LLJointState::POS) { - llinfos << "\t" << joint_motion_p->mPositionCurve.mNumKeys << " position keys at " - << joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey) << " bytes" << llendl; - + if (!silent) + { + llinfos << "\t" << joint_motion_p->mPositionCurve.mNumKeys << " position keys at " + << joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey) << " bytes" << llendl; + } total_size += joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey); } } - llinfos << "Size: " << total_size << " bytes" << llendl; + //Singu: Also add memory used by the constraints. + S32 constraints_size = mConstraints.size() * sizeof(constraint_list_t::value_type); + total_size += constraints_size; + if (!silent) + { + llinfos << "\t" << mConstraints.size() << " constraints at " << constraints_size << " bytes" << llendl; + llinfos << "Size: " << total_size << " bytes" << llendl; + } return total_size; } @@ -429,7 +446,6 @@ void LLKeyframeMotion::JointMotion::update(LLJointState* joint_state, F32 time, //----------------------------------------------------------------------------- LLKeyframeMotion::LLKeyframeMotion(const LLUUID &id) : LLMotion(id), - mJointMotionList(NULL), mPelvisp(NULL), mLastSkeletonSerialNum(0), mLastUpdateTime(0.f), @@ -517,6 +533,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact case ASSET_FETCHED: return STATUS_HOLD; case ASSET_FETCH_FAILED: + llwarns << "Trying to initialize a motion that failed to be fetched." << llendl; return STATUS_FAILURE; case ASSET_LOADED: return STATUS_SUCCESS; @@ -526,7 +543,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact break; } - LLKeyframeMotion::JointMotionList* joint_motion_list = LLKeyframeDataCache::getKeyframeData(getID()); + LLKeyframeMotion::JointMotionListPtr joint_motion_list = LLKeyframeDataCache::getKeyframeData(getID()); if(joint_motion_list) { @@ -1227,13 +1244,42 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8 } } +// Helper class. +template +struct AIAutoDestruct +{ + T* mPtr; + AIAutoDestruct() : mPtr(NULL) { } + ~AIAutoDestruct() { delete mPtr; } + void add(T* ptr) { mPtr = ptr; } +}; + //----------------------------------------------------------------------------- // deserialize() //----------------------------------------------------------------------------- BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp) { BOOL old_version = FALSE; - mJointMotionList = new LLKeyframeMotion::JointMotionList; + + // + + // First add a new LLKeyframeMotion::JointMotionList to the cache, then assign a pointer + // to that to mJointMotionList. In LLs code the cache is never deleted again. Now it is + // is deleted when the last mJointMotionList pointer is destructed. + // + // It is possible that we get here for an already added animation, because animations can + // be requested multiple times (we get here from LLKeyframeMotion::onLoadComplete) when + // the animation was still downloading from a previous request for another LLMotionController + // object (avatar). In that case we just overwrite the old data while decoding it again. + mJointMotionList = LLKeyframeDataCache::getKeyframeData(getID()); + bool singu_new_joint_motion_list = !mJointMotionList; + if (singu_new_joint_motion_list) + { + // Create a new JointMotionList. + mJointMotionList = LLKeyframeDataCache::createKeyframeData(getID()); + } + + // //------------------------------------------------------------------------- // get base priority @@ -1396,8 +1442,10 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp) return FALSE; } - mJointMotionList->mJointMotionArray.clear(); - mJointMotionList->mJointMotionArray.reserve(num_motions); + if (singu_new_joint_motion_list) + { + mJointMotionList->mJointMotionArray.reserve(num_motions); + } mJointStates.clear(); mJointStates.reserve(num_motions); @@ -1407,8 +1455,19 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp) for(U32 i=0; i watcher; + JointMotion* joint_motion = new JointMotion; - mJointMotionList->mJointMotionArray.push_back(joint_motion); + if (singu_new_joint_motion_list) + { + // Pass ownership to mJointMotionList. + mJointMotionList->mJointMotionArray.push_back(joint_motion); + } + else + { + // Just delete this at the end. + watcher.add(joint_motion); + } std::string joint_name; if (!dp.unpackString(joint_name, "joint_name")) @@ -1836,7 +1895,15 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp) return FALSE; } - mJointMotionList->mConstraints.push_front(constraintp); + AIAutoDestruct watcher; + if (singu_new_joint_motion_list) + { + mJointMotionList->mConstraints.push_front(constraintp); + } + else + { + watcher.add(constraintp); + } constraintp->mJointStateIndices = new S32[constraintp->mChainLength + 1]; // note: mChainLength is size-limited - comes from a byte @@ -1876,15 +1943,12 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp) if (constraintp->mJointStateIndices[i] < 0 ) { llwarns << "No joint index for constraint " << i << llendl; - delete constraintp; return FALSE; } } } } - // *FIX: support cleanup of old keyframe data - LLKeyframeDataCache::addKeyframeData(getID(), mJointMotionList); mAssetStatus = ASSET_LOADED; setupPose(); @@ -2226,16 +2290,24 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs, //-------------------------------------------------------------------- // LLKeyframeDataCache::dumpDiagInfo() //-------------------------------------------------------------------- -void LLKeyframeDataCache::dumpDiagInfo() +// +// quiet = 0 : print everything in detail. +// 1 : print the UUIDs of all animations in the cache and the total memory usage. +// 2 : only print the total memory usage. +// +void LLKeyframeDataCache::dumpDiagInfo(int quiet) { // keep track of totals U32 total_size = 0; char buf[1024]; /* Flawfinder: ignore */ - llinfos << "-----------------------------------------------------" << llendl; - llinfos << " Global Motion Table (DEBUG only)" << llendl; - llinfos << "-----------------------------------------------------" << llendl; + if (quiet < 2) + { + llinfos << "-----------------------------------------------------" << llendl; + llinfos << " Global Motion Table" << llendl; + llinfos << "-----------------------------------------------------" << llendl; + } // print each loaded mesh, and it's memory usage for (keyframe_data_map_t::iterator map_it = sKeyframeDataMap.begin(); @@ -2243,30 +2315,46 @@ void LLKeyframeDataCache::dumpDiagInfo() { U32 joint_motion_kb; - LLKeyframeMotion::JointMotionList *motion_list_p = map_it->second; + LLKeyframeMotion::JointMotionList const* motion_list_p = map_it->get(); - llinfos << "Motion: " << map_it->first << llendl; + if (quiet < 2) + { + llinfos << "Motion: " << map_it->key() << llendl; + } - joint_motion_kb = motion_list_p->dumpDiagInfo(); - - total_size += joint_motion_kb; + if (motion_list_p) + { + joint_motion_kb = motion_list_p->dumpDiagInfo(quiet); + total_size += joint_motion_kb; + } } - llinfos << "-----------------------------------------------------" << llendl; + if (quiet < 2) + { + llinfos << "-----------------------------------------------------" << llendl; + } llinfos << "Motions\tTotal Size" << llendl; snprintf(buf, sizeof(buf), "%d\t\t%d bytes", (S32)sKeyframeDataMap.size(), total_size ); /* Flawfinder: ignore */ llinfos << buf << llendl; - llinfos << "-----------------------------------------------------" << llendl; + if (quiet < 2) + { + llinfos << "-----------------------------------------------------" << llendl; + } } //-------------------------------------------------------------------- -// LLKeyframeDataCache::addKeyframeData() +// LLKeyframeDataCache::createKeyframeData() //-------------------------------------------------------------------- -void LLKeyframeDataCache::addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList* joint_motion_listp) +// This function replaces LLKeyframeDataCache::addKeyframeData and was rewritten to fix a memory leak (aka, the usage of AICachedPointer). +LLKeyframeMotion::JointMotionListPtr LLKeyframeDataCache::createKeyframeData(LLUUID const& id) { - sKeyframeDataMap[id] = joint_motion_listp; + std::pair result = + sKeyframeDataMap.insert(AICachedPointer(id, new LLKeyframeMotion::JointMotionList, &sKeyframeDataMap)); + llassert(result.second); // id may not already exist in the cache. + return &*result.first; // Construct and return a JointMotionListPt from a pointer to the actually inserted AICachedPointer. } +// //-------------------------------------------------------------------- // LLKeyframeDataCache::removeKeyframeData() @@ -2276,7 +2364,6 @@ void LLKeyframeDataCache::removeKeyframeData(const LLUUID& id) keyframe_data_map_t::iterator found_data = sKeyframeDataMap.find(id); if (found_data != sKeyframeDataMap.end()) { - delete found_data->second; sKeyframeDataMap.erase(found_data); } } @@ -2284,14 +2371,14 @@ void LLKeyframeDataCache::removeKeyframeData(const LLUUID& id) //-------------------------------------------------------------------- // LLKeyframeDataCache::getKeyframeData() //-------------------------------------------------------------------- -LLKeyframeMotion::JointMotionList* LLKeyframeDataCache::getKeyframeData(const LLUUID& id) +LLKeyframeMotion::JointMotionListPtr LLKeyframeDataCache::getKeyframeData(const LLUUID& id) { keyframe_data_map_t::iterator found_data = sKeyframeDataMap.find(id); if (found_data == sKeyframeDataMap.end()) { return NULL; } - return found_data->second; + return &*found_data; // Construct and return a JointMotionListPt from a pointer to the found AICachedPointer. } //-------------------------------------------------------------------- @@ -2307,7 +2394,6 @@ LLKeyframeDataCache::~LLKeyframeDataCache() //----------------------------------------------------------------------------- void LLKeyframeDataCache::clear() { - for_each(sKeyframeDataMap.begin(), sKeyframeDataMap.end(), DeletePairedPointer()); sKeyframeDataMap.clear(); } diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 50d9d0504..55be6df09 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -5,6 +5,7 @@ * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2009, Linden Research, Inc. + * AICachedPointer and AICachedPointPtr copyright (c) 2013, Aleric Inglewood. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -48,6 +49,7 @@ #include "v3dmath.h" #include "v3math.h" #include "llbvhconsts.h" +#include class LLKeyframeDataCache; class LLVFS; @@ -59,6 +61,112 @@ class LLDataPacker; const S32 KEYFRAME_MOTION_VERSION = 1; const S32 KEYFRAME_MOTION_SUBVERSION = 0; +//----------------------------------------------------------------------------- +// + +template +class AICachedPointer; + +template +void intrusive_ptr_add_ref(AICachedPointer const* p); + +template +void intrusive_ptr_release(AICachedPointer const* p); + +template +class AICachedPointer +{ + public: + typedef std::set > container_type; + + private: + KEY mKey; // The unique key. + LLPointer mData; // The actual data pointer. + container_type* mCache; // Pointer to the underlaying cache. + mutable int mRefCount; // Number of AICachedPointerPtr's pointing to this object. + + public: + // Construct a NULL pointer. This is needed when adding a new entry to a std::set, it is always first default constructed. + AICachedPointer(void) : mCache(NULL) { } + + // Copy constructor. This is needed to replace the std::set inserted instance with its actual value. + AICachedPointer(AICachedPointer const& cptr) : mKey(cptr.mKey), mData(cptr.mData), mCache(cptr.mCache), mRefCount(0) { } + + // Construct a AICachedPointer that points to 'ptr' with key 'key'. + AICachedPointer(KEY const& key, T* ptr, container_type* cache) : mKey(key), mData(ptr), mCache(cache), mRefCount(-1) { } + + // Construct a temporary NULL pointer that can be used in a search for a key. + AICachedPointer(KEY const& key) : mKey(key), mCache(NULL) { } + + // Accessors for key and data. + KEY const& key(void) const { return mKey; } + T const* get(void) const { return mData.get(); } + T* get(void) { return mData.get(); } + + // Order only by key. + friend bool operator<(AICachedPointer const& cp1, AICachedPointer const& cp2) { return cp1.mKey < cp2.mKey; } + + private: + friend void intrusive_ptr_add_ref<>(AICachedPointer const* p); + friend void intrusive_ptr_release<>(AICachedPointer const* p); + + private: + AICachedPointer& operator=(AICachedPointer const&); +}; + +template +void intrusive_ptr_add_ref(AICachedPointer const* p) +{ + llassert(p->mCache); + if (p->mCache) + { + p->mRefCount++; + } +} + +template +void intrusive_ptr_release(AICachedPointer const* p) +{ + llassert(p->mCache); + if (p->mCache) + { + if (--p->mRefCount == 0) + { + p->mCache->erase(p->mKey); + } + } +} + +template +class AICachedPointerPtr +{ + private: + boost::intrusive_ptr const> mPtr; + static int sCnt; + + public: + AICachedPointerPtr(void) { ++sCnt; } + AICachedPointerPtr(AICachedPointerPtr const& cpp) : mPtr(cpp.mPtr) { ++sCnt; } + AICachedPointerPtr(AICachedPointer const* cp) : mPtr(cp) { ++sCnt; } + ~AICachedPointerPtr() { --sCnt; } + + typedef boost::intrusive_ptr const> const AICachedPointerPtr::* const bool_type; + operator bool_type() const { return mPtr ? &AICachedPointerPtr::mPtr : NULL; } + + T const* operator->() const { return mPtr->get(); } + T* operator->() { return const_cast&>(*mPtr).get(); } + T const& operator*() const { return *mPtr->get(); } + T& operator*() { return *const_cast&>(*mPtr).get(); } + + AICachedPointerPtr& operator=(AICachedPointerPtr const& cpp) { mPtr = cpp.mPtr; return *this; } +}; + +template +int AICachedPointerPtr::sCnt; + +// +//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- // class LLKeyframeMotion //----------------------------------------------------------------------------- @@ -158,7 +266,7 @@ public: U32 getFileSize(); BOOL serialize(LLDataPacker& dp) const; BOOL deserialize(LLDataPacker& dp); - BOOL isLoaded() { return mJointMotionList != NULL; } + BOOL isLoaded() { return !!mJointMotionList; } // setters for modifying a keyframe animation @@ -416,19 +524,22 @@ public: public: JointMotionList(); ~JointMotionList(); - U32 dumpDiagInfo(); + U32 dumpDiagInfo(bool silent = false) const; JointMotion* getJointMotion(U32 index) const { llassert(index < mJointMotionArray.size()); return mJointMotionArray[index]; } U32 getNumJointMotions() const { return mJointMotionArray.size(); } }; + // Singu: Type of a pointer to the cached pointer (in LLKeyframeDataCache::sKeyframeDataMap) to a JointMotionList object. + typedef AICachedPointerPtr JointMotionListPtr; + protected: static LLVFS* sVFS; //------------------------------------------------------------------------- // Member Data //------------------------------------------------------------------------- - JointMotionList* mJointMotionList; + JointMotionListPtr mJointMotionList; // singu: automatically clean up cache entry when destructed. std::vector > mJointStates; LLJoint* mPelvisp; LLCharacter* mCharacter; @@ -442,21 +553,24 @@ protected: class LLKeyframeDataCache { -public: - // *FIX: implement this as an actual singleton member of LLKeyframeMotion +private: + friend class LLKeyframeMotion; LLKeyframeDataCache(){}; ~LLKeyframeDataCache(); - typedef std::map keyframe_data_map_t; +public: + typedef AICachedPointer::container_type keyframe_data_map_t; // singu: add automatic cache cleanup. static keyframe_data_map_t sKeyframeDataMap; - static void addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList*); - static LLKeyframeMotion::JointMotionList* getKeyframeData(const LLUUID& id); + // + static LLKeyframeMotion::JointMotionListPtr createKeyframeData(LLUUID const& id); // id may not exist. + static LLKeyframeMotion::JointMotionListPtr getKeyframeData(LLUUID const& id); // id may or may not exists. Returns a NULL pointer when it doesn't exist. + // static void removeKeyframeData(const LLUUID& id); //print out diagnostic info - static void dumpDiagInfo(); + static void dumpDiagInfo(int quiet = 0); // singu: added param 'quiet'. static void clear(); }; diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 35dcc1057..b5d60205c 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -168,7 +168,10 @@ void LLMotionController::deleteAllMotions() mLoadingMotions.clear(); mLoadedMotions.clear(); mActiveMotions.clear(); - + // + for_each(mDeprecatedMotions.begin(), mDeprecatedMotions.end(), DeletePointer()); + mDeprecatedMotions.clear(); + // for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer()); mAllMotions.clear(); } @@ -178,26 +181,19 @@ void LLMotionController::deleteAllMotions() //----------------------------------------------------------------------------- void LLMotionController::purgeExcessMotions() { - if (mLoadedMotions.size() > MAX_MOTION_INSTANCES) + // + // The old code attempted to remove non-active motions from mDeprecatedMotions, + // but that is nonsense: there are no motions in mDeprecatedMotions that are not active. + if (mLoadedMotions.size() <= MAX_MOTION_INSTANCES) { - // clean up deprecated motions - for (motion_set_t::iterator deprecated_motion_it = mDeprecatedMotions.begin(); - deprecated_motion_it != mDeprecatedMotions.end(); ) - { - motion_set_t::iterator cur_iter = deprecated_motion_it++; - LLMotion* cur_motionp = *cur_iter; - if (!isMotionActive(cur_motionp)) - { - // Motion is deprecated so we know it's not cannonical, - // we can safely remove the instance - removeMotionInstance(cur_motionp); // modifies mDeprecatedMotions - mDeprecatedMotions.erase(cur_iter); - } - } + // Speed up, no need to create motions_to_kill. + return; } + // std::set motions_to_kill; - if (mLoadedMotions.size() > MAX_MOTION_INSTANCES) + + if (1) // Singu: leave indentation alone... { // too many motions active this frame, kill all blenders mPoseBlender.clearBlenders(); @@ -308,24 +304,44 @@ BOOL LLMotionController::registerMotion( const LLUUID& id, LLMotionConstructor c void LLMotionController::removeMotion( const LLUUID& id) { LLMotion* motionp = findMotion(id); - mAllMotions.erase(id); - removeMotionInstance(motionp); + // + // If a motion is erased from mAllMotions, it must be deleted. + if (motionp) + { + mAllMotions.erase(id); + removeMotionInstance(motionp); + delete motionp; + } + // } // removes instance of a motion from all runtime structures, but does // not erase entry by ID, as this could be a duplicate instance -// use removeMotion(id) to remove all references to a given motion by id. +// use removeMotion(id) to remove a reference to a given motion by id +// (that will not remove (active) deprecated motions). void LLMotionController::removeMotionInstance(LLMotion* motionp) { if (motionp) { llassert(findMotion(motionp->getID()) != motionp); - if (motionp->isActive()) - motionp->deactivate(); mLoadingMotions.erase(motionp); mLoadedMotions.erase(motionp); mActiveMotions.remove(motionp); - delete motionp; + // + // Deactivation moved here. Only delete motionp when it is being removed from mDeprecatedMotions. + if (motionp->isActive()) + { + motionp->deactivate(); + // If a motion is deactivated, it must be removed from mDeprecatedMotions if there. + motion_set_t::iterator found_it = mDeprecatedMotions.find(motionp); + if (found_it != mDeprecatedMotions.end()) + { + mDeprecatedMotions.erase(found_it); + // If a motion is erased from mDeprecatedMotions, it must be deleted. + delete motionp; + } + } + // } } @@ -393,6 +409,7 @@ BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset) if (motion && !mPaused && motion->canDeprecate() + && motion->isActive() // singu: do not deprecate motions that are not active. && motion->getFadeWeight() > 0.01f // not LOD-ed out && (motion->isBlending() || motion->getStopTime() != 0.f)) { @@ -782,11 +799,10 @@ void LLMotionController::updateLoadingMotions() llinfos << "Motion " << motionp->getID() << " init failed." << llendl; sRegistry.markBad(motionp->getID()); mLoadingMotions.erase(curiter); - motion_set_t::iterator found_it = mDeprecatedMotions.find(motionp); - if (found_it != mDeprecatedMotions.end()) - { - mDeprecatedMotions.erase(found_it); - } + // Singu note: a motion in mLoadingMotions will not be in mActiveMotions + // and therefore not be in mDeprecatedMotions. So, we don't have to + // check for it's existence there. + llassert(mDeprecatedMotions.find(motionp) == mDeprecatedMotions.end()); mAllMotions.erase(motionp->getID()); delete motionp; } @@ -970,18 +986,16 @@ BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time) //----------------------------------------------------------------------------- BOOL LLMotionController::deactivateMotionInstance(LLMotion *motion) { - motion->deactivate(); - motion_set_t::iterator found_it = mDeprecatedMotions.find(motion); if (found_it != mDeprecatedMotions.end()) { // deprecated motions need to be completely excised - removeMotionInstance(motion); - mDeprecatedMotions.erase(found_it); + removeMotionInstance(motion); // singu note: this deactivates motion and removes it from mDeprecatedMotions. } else { // for motions that we are keeping, simply remove from active queue + motion->deactivate(); // singu note: moved here from the top of the function. mActiveMotions.remove(motion); } @@ -1049,11 +1063,26 @@ void LLMotionController::dumpMotions() state_string += std::string("L"); if (std::find(mActiveMotions.begin(), mActiveMotions.end(), motion)!=mActiveMotions.end()) state_string += std::string("A"); - if (mDeprecatedMotions.find(motion) != mDeprecatedMotions.end()) - state_string += std::string("D"); + llassert(mDeprecatedMotions.find(motion) == mDeprecatedMotions.end()); // singu: it's impossible that a motion is in mAllMotions and mDeprecatedMotions at the same time. llinfos << gAnimLibrary.animationName(id) << " " << state_string << llendl; - } + // + // Also dump the deprecated motions. + for (motion_set_t::iterator iter = mDeprecatedMotions.begin(); + iter != mDeprecatedMotions.end(); ++iter) + { + std::string state_string; + LLMotion* motion = *iter; + LLUUID id = motion->getID(); + llassert(mLoadingMotions.find(motion) == mLoadingMotions.end()); + if (mLoadedMotions.find(motion) != mLoadedMotions.end()) + state_string += std::string("L"); + if (std::find(mActiveMotions.begin(), mActiveMotions.end(), motion)!=mActiveMotions.end()) + state_string += std::string("A"); + state_string += "D"; + llinfos << gAnimLibrary.animationName(id) << " " << state_string << llendl; + } + // } //----------------------------------------------------------------------------- @@ -1061,11 +1090,11 @@ void LLMotionController::dumpMotions() //----------------------------------------------------------------------------- void LLMotionController::deactivateAllMotions() { - for (motion_map_t::iterator iter = mAllMotions.begin(); - iter != mAllMotions.end(); iter++) + // Singu note: this must run over mActiveMotions: other motions are not active, + // and running over mAllMotions will miss the ones in mDeprecatedMotions. + for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter) { - LLMotion* motionp = iter->second; - deactivateMotionInstance(motionp); + deactivateMotionInstance(*iter); } } @@ -1086,8 +1115,7 @@ void LLMotionController::flushAllMotions() active_motions.push_back(std::make_pair(motionp->getID(),dtime)); motionp->deactivate(); // don't call deactivateMotionInstance() because we are going to reactivate it } - mActiveMotions.clear(); - + // delete all motion instances deleteAllMotions(); diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 2de13aa36..7e0cc7ad7 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -115,7 +115,6 @@ public: // unregisters a motion with the controller // (actually just forwards call to motion registry) - // returns true if successfull void removeMotion( const LLUUID& id ); // start motion @@ -205,10 +204,13 @@ protected: // Life cycle of an animation: // // Animations are instantiated and immediately put in the mAllMotions map for their entire lifetime. +// Singu note: that is not true, they are moved to mDeprecatedMotions (often) for the last part of their lifetime. // If the animations depend on any asset data, the appropriate data is fetched from the data server, // and the animation is put on the mLoadingMotions list. // Once an animations is loaded, it will be initialized and put on the mLoadedMotions list. // Any animation that is currently playing also sits in the mActiveMotions list. +// Singu note: animations are only put in mDeprecatedMotions if and while they are playing, +// therefore animations in mDeprecatedMotions will be (must be) active and in mActiveMotions. typedef std::map motion_map_t; motion_map_t mAllMotions; diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 58e27b914..251397fa6 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -375,6 +375,10 @@ BOOL LLFloaterBvhPreview::postBuild() } } + if (motionp && mInWorld) + { + gAgentAvatarp->removeMotion(mMotionID); + } //setEnabled(FALSE); mMotionID.setNull(); mAnimPreview = NULL; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 3b15311e7..aec11e199 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3884,7 +3884,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) getOffObject(); // //Singu note: this appears to be a safety catch: - // when getParent() is NULL and we're note playing ANIM_AGENT_SIT_GROUND_CONSTRAINED then we aren't sitting! + // when getParent() is NULL and we're not playing ANIM_AGENT_SIT_GROUND_CONSTRAINED then we aren't sitting! // The previous call existed in an attempt to fix this inconsistent state by standing up from an object. // However, since getParent() is NULL that function would crash! // Since we never got crash reports regarding to this, that apparently never happened, except, I discovered From 94b42e7a9b7c692bf582a0b243d22f7ff321abf3 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 3 Dec 2013 04:41:12 +0100 Subject: [PATCH 05/15] Speed up of particular inefficient part related to motions. For these standard motions, the viewer likes to very frequently flood-call startMotion/stopMotion sometimes, which at the very least needs a LLUUID lookup in a std::map. In particular, this is done for two of them for every avatar in the sim every frame. This code makes it possible to test if that makes sense by merely doing a bit test. Conflicts: indra/llcharacter/llkeyframemotion.h --- indra/llcharacter/llcharacter.h | 1 + indra/llcharacter/lleditingmotion.cpp | 12 +- indra/llcharacter/lleditingmotion.h | 9 +- indra/llcharacter/llhandmotion.cpp | 12 +- indra/llcharacter/llhandmotion.h | 9 +- indra/llcharacter/llheadrotmotion.cpp | 36 +--- indra/llcharacter/llheadrotmotion.h | 25 +-- indra/llcharacter/llkeyframefallmotion.h | 2 +- indra/llcharacter/llkeyframemotion.cpp | 2 +- indra/llcharacter/llkeyframemotion.h | 3 +- indra/llcharacter/llkeyframestandmotion.h | 2 +- indra/llcharacter/llkeyframewalkmotion.cpp | 13 +- indra/llcharacter/llkeyframewalkmotion.h | 17 +- indra/llcharacter/llmotion.cpp | 16 ++ indra/llcharacter/llmotion.h | 42 ++++- indra/llcharacter/llmotioncontroller.cpp | 18 +- indra/llcharacter/llmotioncontroller.h | 16 +- indra/llcharacter/lltargetingmotion.cpp | 18 +- indra/llcharacter/lltargetingmotion.h | 14 +- indra/newview/llemote.h | 2 +- indra/newview/llhudeffectlookat.cpp | 5 +- indra/newview/llphysicsmotion.cpp | 15 +- indra/newview/llphysicsmotion.h | 14 +- indra/newview/llvoavatar.cpp | 202 ++++++++++++++------- indra/newview/llvoavatar.h | 9 + 25 files changed, 276 insertions(+), 238 deletions(-) diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h index 2b872effa..74cc35187 100644 --- a/indra/llcharacter/llcharacter.h +++ b/indra/llcharacter/llcharacter.h @@ -146,6 +146,7 @@ public: // is this motion active? BOOL isMotionActive( const LLUUID& id ); + bool isMotionActive(U32 bit) const { return mMotionController.isactive(bit); } // Event handler for motion deactivation. // Called when a motion has completely stopped and has been deactivated. diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp index c2a87d80a..26fca2260 100644 --- a/indra/llcharacter/lleditingmotion.cpp +++ b/indra/llcharacter/lleditingmotion.cpp @@ -49,7 +49,7 @@ S32 LLEditingMotion::sHandPosePriority = 3; // LLEditingMotion() // Class Constructor //----------------------------------------------------------------------------- -LLEditingMotion::LLEditingMotion( const LLUUID &id) : LLMotion(id) +LLEditingMotion::LLEditingMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EDITING) { mCharacter = NULL; @@ -155,7 +155,7 @@ BOOL LLEditingMotion::onActivate() mShoulderJoint.setRotation( mShoulderState->getJoint()->getRotation() ); mElbowJoint.setRotation( mElbowState->getJoint()->getRotation() ); - return TRUE; + return AIMaskedMotion::onActivate(); } //----------------------------------------------------------------------------- @@ -256,12 +256,4 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask) return result; } -//----------------------------------------------------------------------------- -// LLEditingMotion::onDeactivate() -//----------------------------------------------------------------------------- -void LLEditingMotion::onDeactivate() -{ -} - - // End diff --git a/indra/llcharacter/lleditingmotion.h b/indra/llcharacter/lleditingmotion.h index 4a83d4b9f..f880a94b7 100644 --- a/indra/llcharacter/lleditingmotion.h +++ b/indra/llcharacter/lleditingmotion.h @@ -49,11 +49,11 @@ // class LLEditingMotion //----------------------------------------------------------------------------- class LLEditingMotion : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLEditingMotion(const LLUUID &id); + LLEditingMotion(LLUUID const& id, LLMotionController& controller); // Destructor virtual ~LLEditingMotion(); @@ -65,7 +65,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLEditingMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLEditingMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -107,9 +107,6 @@ public: // must return FALSE when the motion is completed. virtual BOOL onUpdate(F32 time, U8* joint_mask); - // called when a motion is deactivated - virtual void onDeactivate(); - public: //------------------------------------------------------------------------- // joint states to be animated diff --git a/indra/llcharacter/llhandmotion.cpp b/indra/llcharacter/llhandmotion.cpp index 696dba0d9..c479f0f5f 100644 --- a/indra/llcharacter/llhandmotion.cpp +++ b/indra/llcharacter/llhandmotion.cpp @@ -61,7 +61,7 @@ const F32 HAND_MORPH_BLEND_TIME = 0.2f; // LLHandMotion() // Class Constructor //----------------------------------------------------------------------------- -LLHandMotion::LLHandMotion(const LLUUID &id) : LLMotion(id) +LLHandMotion::LLHandMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_HAND_MOTION) { mCharacter = NULL; mLastTime = 0.f; @@ -112,7 +112,7 @@ BOOL LLHandMotion::onActivate() mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f); mCharacter->updateVisualParams(); } - return TRUE; + return AIMaskedMotion::onActivate(); } @@ -235,14 +235,6 @@ BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask) return TRUE; } - -//----------------------------------------------------------------------------- -// LLHandMotion::onDeactivate() -//----------------------------------------------------------------------------- -void LLHandMotion::onDeactivate() -{ -} - //----------------------------------------------------------------------------- // LLHandMotion::getHandPoseName() //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llhandmotion.h b/indra/llcharacter/llhandmotion.h index dcf169662..582c6ee2f 100644 --- a/indra/llcharacter/llhandmotion.h +++ b/indra/llcharacter/llhandmotion.h @@ -45,7 +45,7 @@ // class LLHandMotion //----------------------------------------------------------------------------- class LLHandMotion : - public LLMotion + public AIMaskedMotion { public: typedef enum e_hand_pose @@ -68,7 +68,7 @@ public: } eHandPose; // Constructor - LLHandMotion(const LLUUID &id); + LLHandMotion(LLUUID const& id, LLMotionController& controller); // Destructor virtual ~LLHandMotion(); @@ -80,7 +80,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLHandMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLHandMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -122,9 +122,6 @@ public: // must return FALSE when the motion is completed. virtual BOOL onUpdate(F32 time, U8* joint_mask); - // called when a motion is deactivated - virtual void onDeactivate(); - virtual BOOL canDeprecate() { return FALSE; } static std::string getHandPoseName(eHandPose pose); diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp index 14017da10..7322c996a 100644 --- a/indra/llcharacter/llheadrotmotion.cpp +++ b/indra/llcharacter/llheadrotmotion.cpp @@ -76,8 +76,8 @@ const F32 EYE_BLINK_TIME_DELTA = 0.005f; // time between one eye starting a blin // LLHeadRotMotion() // Class Constructor //----------------------------------------------------------------------------- -LLHeadRotMotion::LLHeadRotMotion(const LLUUID &id) : - LLMotion(id), +LLHeadRotMotion::LLHeadRotMotion(LLUUID const& id, LLMotionController& controller) : + AIMaskedMotion(id, controller, ANIM_AGENT_HEAD_ROT), mCharacter(NULL), mTorsoJoint(NULL), mHeadJoint(NULL) @@ -172,16 +172,6 @@ LLMotion::LLMotionInitStatus LLHeadRotMotion::onInitialize(LLCharacter *characte return STATUS_SUCCESS; } - -//----------------------------------------------------------------------------- -// LLHeadRotMotion::onActivate() -//----------------------------------------------------------------------------- -BOOL LLHeadRotMotion::onActivate() -{ - return TRUE; -} - - //----------------------------------------------------------------------------- // LLHeadRotMotion::onUpdate() //----------------------------------------------------------------------------- @@ -266,19 +256,11 @@ BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask) } -//----------------------------------------------------------------------------- -// LLHeadRotMotion::onDeactivate() -//----------------------------------------------------------------------------- -void LLHeadRotMotion::onDeactivate() -{ -} - - //----------------------------------------------------------------------------- // LLEyeMotion() // Class Constructor //----------------------------------------------------------------------------- -LLEyeMotion::LLEyeMotion(const LLUUID &id) : LLMotion(id) +LLEyeMotion::LLEyeMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EYE) { mCharacter = NULL; mEyeJitterTime = 0.f; @@ -346,16 +328,6 @@ LLMotion::LLMotionInitStatus LLEyeMotion::onInitialize(LLCharacter *character) return STATUS_SUCCESS; } - -//----------------------------------------------------------------------------- -// LLEyeMotion::onActivate() -//----------------------------------------------------------------------------- -BOOL LLEyeMotion::onActivate() -{ - return TRUE; -} - - //----------------------------------------------------------------------------- // LLEyeMotion::onUpdate() //----------------------------------------------------------------------------- @@ -536,6 +508,8 @@ void LLEyeMotion::onDeactivate() { joint->setRotation(LLQuaternion::DEFAULT); } + + AIMaskedMotion::onDeactivate(); } // End diff --git a/indra/llcharacter/llheadrotmotion.h b/indra/llcharacter/llheadrotmotion.h index 97e61eac1..5342d422f 100644 --- a/indra/llcharacter/llheadrotmotion.h +++ b/indra/llcharacter/llheadrotmotion.h @@ -46,11 +46,11 @@ // class LLHeadRotMotion //----------------------------------------------------------------------------- class LLHeadRotMotion : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLHeadRotMotion(const LLUUID &id); + LLHeadRotMotion(LLUUID const& id, LLMotionController& controller); // Destructor virtual ~LLHeadRotMotion(); @@ -62,7 +62,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLHeadRotMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLHeadRotMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -94,19 +94,11 @@ public: // must return true to indicate success and be available for activation virtual LLMotionInitStatus onInitialize(LLCharacter *character); - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate(); - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. virtual BOOL onUpdate(F32 time, U8* joint_mask); - // called when a motion is deactivated - virtual void onDeactivate(); - public: //------------------------------------------------------------------------- // joint states to be animated @@ -129,11 +121,11 @@ public: // class LLEyeMotion //----------------------------------------------------------------------------- class LLEyeMotion : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLEyeMotion(const LLUUID &id); + LLEyeMotion(LLUUID const& id, LLMotionController& controller); // Destructor virtual ~LLEyeMotion(); @@ -145,7 +137,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create( const LLUUID &id) { return new LLEyeMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLEyeMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -177,11 +169,6 @@ public: // must return true to indicate success and be available for activation virtual LLMotionInitStatus onInitialize(LLCharacter *character); - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate(); - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. diff --git a/indra/llcharacter/llkeyframefallmotion.h b/indra/llcharacter/llkeyframefallmotion.h index 495be977f..c36c72798 100644 --- a/indra/llcharacter/llkeyframefallmotion.h +++ b/indra/llcharacter/llkeyframefallmotion.h @@ -59,7 +59,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLKeyframeFallMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLKeyframeFallMotion(id); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index 653d3988c..c6589ddf4 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -468,7 +468,7 @@ LLKeyframeMotion::~LLKeyframeMotion() //----------------------------------------------------------------------------- // create() //----------------------------------------------------------------------------- -LLMotion *LLKeyframeMotion::create(const LLUUID &id) +LLMotion* LLKeyframeMotion::create(LLUUID const& id, LLMotionController&) { return new LLKeyframeMotion(id); } diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 55be6df09..14fa4916c 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -54,6 +54,7 @@ class LLKeyframeDataCache; class LLVFS; class LLDataPacker; +class LLMotionController; #define MIN_REQUIRED_PIXEL_AREA_KEYFRAME (40.f) #define MAX_CHAIN_LENGTH (4) @@ -193,7 +194,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID& id); + static LLMotion* create(LLUUID const& id, LLMotionController& controller); public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframestandmotion.h b/indra/llcharacter/llkeyframestandmotion.h index b0500dc8e..1db798021 100644 --- a/indra/llcharacter/llkeyframestandmotion.h +++ b/indra/llcharacter/llkeyframestandmotion.h @@ -60,7 +60,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLKeyframeStandMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLKeyframeStandMotion(id); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframewalkmotion.cpp b/indra/llcharacter/llkeyframewalkmotion.cpp index f9c2e4766..2a12edd29 100644 --- a/indra/llcharacter/llkeyframewalkmotion.cpp +++ b/indra/llcharacter/llkeyframewalkmotion.cpp @@ -138,8 +138,8 @@ BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask) // LLWalkAdjustMotion() // Class Constructor //----------------------------------------------------------------------------- -LLWalkAdjustMotion::LLWalkAdjustMotion(const LLUUID &id) : - LLMotion(id), +LLWalkAdjustMotion::LLWalkAdjustMotion(LLUUID const& id, LLMotionController& controller) : + AIMaskedMotion(id, controller, ANIM_AGENT_WALK_ADJUST), mLastTime(0.f), mAnimSpeed(0.f), mAdjustedSpeed(0.f), @@ -193,7 +193,7 @@ BOOL LLWalkAdjustMotion::onActivate() F32 rightAnkleOffset = (mRightAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec(); mAnkleOffset = llmax(leftAnkleOffset, rightAnkleOffset); - return TRUE; + return AIMaskedMotion::onActivate(); } //----------------------------------------------------------------------------- @@ -325,13 +325,14 @@ BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask) void LLWalkAdjustMotion::onDeactivate() { mCharacter->removeAnimationData("Walk Speed"); + AIMaskedMotion::onDeactivate(); } //----------------------------------------------------------------------------- // LLFlyAdjustMotion::LLFlyAdjustMotion() //----------------------------------------------------------------------------- -LLFlyAdjustMotion::LLFlyAdjustMotion(const LLUUID &id) - : LLMotion(id), +LLFlyAdjustMotion::LLFlyAdjustMotion(LLUUID const& id, LLMotionController& controller) + : AIMaskedMotion(id, controller, ANIM_AGENT_FLY_ADJUST), mRoll(0.f) { mName = "fly_adjust"; @@ -368,7 +369,7 @@ BOOL LLFlyAdjustMotion::onActivate() mPelvisState->setPosition(LLVector3::zero); mPelvisState->setRotation(LLQuaternion::DEFAULT); mRoll = 0.f; - return TRUE; + return AIMaskedMotion::onActivate(); } //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframewalkmotion.h b/indra/llcharacter/llkeyframewalkmotion.h index b507e9423..caab2fef3 100644 --- a/indra/llcharacter/llkeyframewalkmotion.h +++ b/indra/llcharacter/llkeyframewalkmotion.h @@ -52,7 +52,7 @@ class LLKeyframeWalkMotion : friend class LLWalkAdjustMotion; public: // Constructor - LLKeyframeWalkMotion(const LLUUID &id); + LLKeyframeWalkMotion(LLUUID const& id); // Destructor virtual ~LLKeyframeWalkMotion(); @@ -64,7 +64,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLKeyframeWalkMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLKeyframeWalkMotion(id); } public: //------------------------------------------------------------------------- @@ -86,11 +86,11 @@ public: S32 mDownFoot; }; -class LLWalkAdjustMotion : public LLMotion +class LLWalkAdjustMotion : public AIMaskedMotion { public: // Constructor - LLWalkAdjustMotion(const LLUUID &id); + LLWalkAdjustMotion(LLUUID const& id, LLMotionController& controller); public: //------------------------------------------------------------------------- @@ -99,7 +99,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLWalkAdjustMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLWalkAdjustMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -136,11 +136,11 @@ public: F32 mAnkleOffset; }; -class LLFlyAdjustMotion : public LLMotion +class LLFlyAdjustMotion : public AIMaskedMotion { public: // Constructor - LLFlyAdjustMotion(const LLUUID &id); + LLFlyAdjustMotion(LLUUID const& id, LLMotionController& controller); public: //------------------------------------------------------------------------- @@ -149,7 +149,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLFlyAdjustMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLFlyAdjustMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -157,7 +157,6 @@ public: //------------------------------------------------------------------------- virtual LLMotionInitStatus onInitialize(LLCharacter *character); virtual BOOL onActivate(); - virtual void onDeactivate() {}; virtual BOOL onUpdate(F32 time, U8* joint_mask); virtual LLJoint::JointPriority getPriority(){return LLJoint::HIGHER_PRIORITY;} virtual BOOL getLoop() { return TRUE; } diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp index ce926a38a..203cf2f4c 100644 --- a/indra/llcharacter/llmotion.cpp +++ b/indra/llcharacter/llmotion.cpp @@ -37,6 +37,7 @@ #include "llmotion.h" #include "llcriticaldamp.h" +#include "llmotioncontroller.h" //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -174,4 +175,19 @@ BOOL LLMotion::canDeprecate() return TRUE; } +//----------------------------------------------------------------------------- +// AIMaskedMotion +//----------------------------------------------------------------------------- + +BOOL AIMaskedMotion::onActivate() +{ + mController.activated(mMaskBit); + return TRUE; +} + +void AIMaskedMotion::onDeactivate() +{ + mController.deactivated(mMaskBit); +} + // End diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h index d6628fd99..24504980d 100644 --- a/indra/llcharacter/llmotion.h +++ b/indra/llcharacter/llmotion.h @@ -43,6 +43,7 @@ #include "lluuid.h" class LLCharacter; +class LLMotionController; //----------------------------------------------------------------------------- // class LLMotion @@ -201,7 +202,7 @@ class LLTestMotion : public LLMotion public: LLTestMotion(const LLUUID &id) : LLMotion(id){} ~LLTestMotion() {} - static LLMotion *create(const LLUUID& id) { return new LLTestMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLTestMotion(id); } BOOL getLoop() { return FALSE; } F32 getDuration() { return 0.0f; } F32 getEaseInDuration() { return 0.0f; } @@ -225,7 +226,7 @@ class LLNullMotion : public LLMotion public: LLNullMotion(const LLUUID &id) : LLMotion(id) {} ~LLNullMotion() {} - static LLMotion *create(const LLUUID &id) { return new LLNullMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLNullMotion(id); } // motions must specify whether or not they loop /*virtual*/ BOOL getLoop() { return TRUE; } @@ -266,5 +267,42 @@ public: // called when a motion is deactivated /*virtual*/ void onDeactivate() {} }; + + +//----------------------------------------------------------------------------- +// AIMaskedMotion +//----------------------------------------------------------------------------- + +// These motions have a bit assigned in LLMotionController::mActiveMask +// that is set and uset upon activation/deactivation. + +// This must be in the same order as ANIM_AGENT_BODY_NOISE_ID through ANIM_AGENT_WALK_ADJUST_ID in llvoavatar.cpp. +U32 const ANIM_AGENT_BODY_NOISE = 0x001; +U32 const ANIM_AGENT_BREATHE_ROT = 0x002; +U32 const ANIM_AGENT_PHYSICS_MOTION = 0x004; +U32 const ANIM_AGENT_EDITING = 0x008; +U32 const ANIM_AGENT_EYE = 0x010; +U32 const ANIM_AGENT_FLY_ADJUST = 0x020; +U32 const ANIM_AGENT_HAND_MOTION = 0x040; +U32 const ANIM_AGENT_HEAD_ROT = 0x080; +U32 const ANIM_AGENT_PELVIS_FIX = 0x100; +U32 const ANIM_AGENT_TARGET = 0x200; +U32 const ANIM_AGENT_WALK_ADJUST = 0x400; + +class AIMaskedMotion : public LLMotion +{ +private: + LLMotionController& mController; + U32 mMaskBit; + +public: + AIMaskedMotion(LLUUID const& id, LLMotionController& controller, U32 mask_bit) : LLMotion(id), mController(controller), mMaskBit(mask_bit) { } + + /*virtual*/ BOOL onActivate(); + /*virtual*/ void onDeactivate(); + + U32 getMaskBit(void) const { return mMaskBit; } +}; + #endif // LL_LLMOTION_H diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index b5d60205c..4935fb3a7 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -97,7 +97,7 @@ void LLMotionRegistry::markBad( const LLUUID& id ) //----------------------------------------------------------------------------- // createMotion() //----------------------------------------------------------------------------- -LLMotion *LLMotionRegistry::createMotion( const LLUUID &id ) +LLMotion* LLMotionRegistry::createMotion(LLUUID const& id, LLMotionController& controller) { LLMotionConstructor constructor = get_if_there(mMotionTable, id, LLMotionConstructor(NULL)); LLMotion* motion = NULL; @@ -105,11 +105,11 @@ LLMotion *LLMotionRegistry::createMotion( const LLUUID &id ) if ( constructor == NULL ) { // *FIX: need to replace with a better default scheme. RN - motion = LLKeyframeMotion::create(id); + motion = LLKeyframeMotion::create(id, controller); } else { - motion = constructor(id); + motion = constructor(id, controller); } return motion; @@ -126,18 +126,19 @@ LLMotion *LLMotionRegistry::createMotion( const LLUUID &id ) // Class Constructor //----------------------------------------------------------------------------- LLMotionController::LLMotionController() - : mTimeFactor(sCurrentTimeFactor), + : mIsSelf(FALSE), + mTimeFactor(sCurrentTimeFactor), mCharacter(NULL), - mAnimTime(0.f), + mActiveMask(0), mPrevTimerElapsed(0.f), + mAnimTime(0.f), mLastTime(0.0f), mHasRunOnce(FALSE), mPaused(FALSE), mPauseTime(0.f), mTimeStep(0.f), mTimeStepCount(0), - mLastInterp(0.f), - mIsSelf(FALSE) + mLastInterp(0.f) { } @@ -169,6 +170,7 @@ void LLMotionController::deleteAllMotions() mLoadedMotions.clear(); mActiveMotions.clear(); // + mActiveMask = 0; for_each(mDeprecatedMotions.begin(), mDeprecatedMotions.end(), DeletePointer()); mDeprecatedMotions.clear(); // @@ -357,7 +359,7 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id ) if (!motion) { // look up constructor and create it - motion = sRegistry.createMotion(id); + motion = sRegistry.createMotion(id, *this); if (!motion) { return NULL; diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 7e0cc7ad7..9b6c71ef1 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -51,11 +51,12 @@ // This is necessary because llcharacter.h includes this file. //----------------------------------------------------------------------------- class LLCharacter; +class LLMotionController; //----------------------------------------------------------------------------- // LLMotionRegistry //----------------------------------------------------------------------------- -typedef LLMotion*(*LLMotionConstructor)(const LLUUID &id); +typedef LLMotion* (*LLMotionConstructor)(LLUUID const& id, LLMotionController&); class LLMotionRegistry { @@ -72,7 +73,7 @@ public: // creates a new instance of a named motion // returns NULL motion is not registered - LLMotion *createMotion( const LLUUID &id ); + LLMotion* createMotion(LLUUID const& id, LLMotionController& controller); // initialization of motion failed, don't try to create this motion again void markBad( const LLUUID& id ); @@ -149,6 +150,12 @@ public: //Flush is a liar. void deactivateAllMotions(); + // + void activated(U32 bit) { mActiveMask |= bit; } + void deactivated(U32 bit) { mActiveMask &= ~bit; } + bool isactive(U32 bit) const { return (mActiveMask & bit) != 0; } + // + // pause and continue all motions void pauseAllMotions(); void unpauseAllMotions(); @@ -219,7 +226,10 @@ protected: motion_set_t mLoadedMotions; motion_list_t mActiveMotions; motion_set_t mDeprecatedMotions; - + + // + U32 mActiveMask; + // LLFrameTimer mTimer; F32 mPrevTimerElapsed; F32 mAnimTime; diff --git a/indra/llcharacter/lltargetingmotion.cpp b/indra/llcharacter/lltargetingmotion.cpp index a330b2265..423eca363 100644 --- a/indra/llcharacter/lltargetingmotion.cpp +++ b/indra/llcharacter/lltargetingmotion.cpp @@ -52,7 +52,7 @@ const F32 TORSO_ROT_FRACTION = 0.5f; // LLTargetingMotion() // Class Constructor //----------------------------------------------------------------------------- -LLTargetingMotion::LLTargetingMotion(const LLUUID &id) : LLMotion(id) +LLTargetingMotion::LLTargetingMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_TARGET) { mCharacter = NULL; mName = "targeting"; @@ -99,14 +99,6 @@ LLMotion::LLMotionInitStatus LLTargetingMotion::onInitialize(LLCharacter *charac return STATUS_SUCCESS; } -//----------------------------------------------------------------------------- -// LLTargetingMotion::onActivate() -//----------------------------------------------------------------------------- -BOOL LLTargetingMotion::onActivate() -{ - return TRUE; -} - //----------------------------------------------------------------------------- // LLTargetingMotion::onUpdate() //----------------------------------------------------------------------------- @@ -166,12 +158,4 @@ BOOL LLTargetingMotion::onUpdate(F32 time, U8* joint_mask) return result; } -//----------------------------------------------------------------------------- -// LLTargetingMotion::onDeactivate() -//----------------------------------------------------------------------------- -void LLTargetingMotion::onDeactivate() -{ -} - - // End diff --git a/indra/llcharacter/lltargetingmotion.h b/indra/llcharacter/lltargetingmotion.h index 1ec9f80d6..8dfa32500 100644 --- a/indra/llcharacter/lltargetingmotion.h +++ b/indra/llcharacter/lltargetingmotion.h @@ -48,11 +48,11 @@ // class LLTargetingMotion //----------------------------------------------------------------------------- class LLTargetingMotion : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLTargetingMotion(const LLUUID &id); + LLTargetingMotion(LLUUID const& id, LLMotionController& controller); // Destructor virtual ~LLTargetingMotion(); @@ -64,7 +64,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLTargetingMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLTargetingMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -96,19 +96,11 @@ public: // must return true to indicate success and be available for activation virtual LLMotionInitStatus onInitialize(LLCharacter *character); - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate(); - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. virtual BOOL onUpdate(F32 time, U8* joint_mask); - // called when a motion is deactivated - virtual void onDeactivate(); - public: LLCharacter *mCharacter; diff --git a/indra/newview/llemote.h b/indra/newview/llemote.h index 99d05b9a2..eb2af5b9d 100644 --- a/indra/newview/llemote.h +++ b/indra/newview/llemote.h @@ -67,7 +67,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLEmote(id); } + static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLEmote(id); } public: //------------------------------------------------------------------------- diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp index eef650101..9f6acb60b 100644 --- a/indra/newview/llhudeffectlookat.cpp +++ b/indra/newview/llhudeffectlookat.cpp @@ -611,8 +611,9 @@ void LLHUDEffectLookAt::update() { if (calcTargetPosition()) { - LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT); - if (!head_motion || head_motion->isStopped()) + //LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT); + //if (!head_motion || head_motion->isStopped()) + // singu: startMotion does basically the same as the above two lines... it starts it, unless it was already started. { ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_HEAD_ROT); } diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp index 6fc0ab438..625199b3f 100644 --- a/indra/newview/llphysicsmotion.cpp +++ b/indra/newview/llphysicsmotion.cpp @@ -314,8 +314,8 @@ void LLPhysicsMotion::getString(std::ostringstream &oss) } } -LLPhysicsMotionController::LLPhysicsMotionController(const LLUUID &id) : - LLMotion(id), +LLPhysicsMotionController::LLPhysicsMotionController(LLUUID const& id, LLMotionController& controller) : + AIMaskedMotion(id, controller, ANIM_AGENT_PHYSICS_MOTION), mCharacter(NULL), mIsDefault(true) { @@ -332,15 +332,6 @@ LLPhysicsMotionController::~LLPhysicsMotionController() } } -BOOL LLPhysicsMotionController::onActivate() -{ - return TRUE; -} - -void LLPhysicsMotionController::onDeactivate() -{ -} - LLMotion::LLMotionInitStatus LLPhysicsMotionController::onInitialize(LLCharacter *character) { mCharacter = character; @@ -889,4 +880,4 @@ void LLPhysicsMotion::reset() mCharacter->setVisualParamWeight((*iter).mParam,(*iter).mParam->getDefaultWeight()); } } -} \ No newline at end of file +} diff --git a/indra/newview/llphysicsmotion.h b/indra/newview/llphysicsmotion.h index 7412c9d88..62e0ae658 100644 --- a/indra/newview/llphysicsmotion.h +++ b/indra/newview/llphysicsmotion.h @@ -42,14 +42,14 @@ class LLPhysicsMotion; // class LLPhysicsMotion //----------------------------------------------------------------------------- class LLPhysicsMotionController : - public LLMotion + public AIMaskedMotion { public: std::string getString(); // Constructor - LLPhysicsMotionController(const LLUUID &id); + LLPhysicsMotionController(LLUUID const& id, LLMotionController& controller); // Destructor virtual ~LLPhysicsMotionController(); @@ -61,7 +61,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLPhysicsMotionController(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLPhysicsMotionController(id, controller); } public: //------------------------------------------------------------------------- @@ -93,19 +93,11 @@ public: // must return true to indicate success and be available for activation virtual LLMotionInitStatus onInitialize(LLCharacter *character); - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate(); - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. virtual BOOL onUpdate(F32 time, U8* joint_mask); - // called when a motion is deactivated - virtual void onDeactivate(); - LLCharacter* getCharacter() { return mCharacter; } protected: diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index aec11e199..943279d6b 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -145,18 +145,104 @@ using namespace LLAvatarAppearanceDefines; //----------------------------------------------------------------------------- // Global constants //----------------------------------------------------------------------------- -const LLUUID ANIM_AGENT_BODY_NOISE = LLUUID("9aa8b0a6-0c6f-9518-c7c3-4f41f2c001ad"); //"body_noise" -const LLUUID ANIM_AGENT_BREATHE_ROT = LLUUID("4c5a103e-b830-2f1c-16bc-224aa0ad5bc8"); //"breathe_rot" -const LLUUID ANIM_AGENT_EDITING = LLUUID("2a8eba1d-a7f8-5596-d44a-b4977bf8c8bb"); //"editing" -const LLUUID ANIM_AGENT_EYE = LLUUID("5c780ea8-1cd1-c463-a128-48c023f6fbea"); //"eye" -const LLUUID ANIM_AGENT_FLY_ADJUST = LLUUID("db95561f-f1b0-9f9a-7224-b12f71af126e"); //"fly_adjust" -const LLUUID ANIM_AGENT_HAND_MOTION = LLUUID("ce986325-0ba7-6e6e-cc24-b17c4b795578"); //"hand_motion" -const LLUUID ANIM_AGENT_HEAD_ROT = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"); //"head_rot" -const LLUUID ANIM_AGENT_PELVIS_FIX = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b"); //"pelvis_fix" -const LLUUID ANIM_AGENT_TARGET = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55"); //"target" -const LLUUID ANIM_AGENT_WALK_ADJUST = LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d"); //"walk_adjust" -const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987"); //"physics_motion" +const LLUUID ANIM_AGENT_BODY_NOISE_ID = LLUUID("9aa8b0a6-0c6f-9518-c7c3-4f41f2c001ad"); //"body_noise" +const LLUUID ANIM_AGENT_BREATHE_ROT_ID = LLUUID("4c5a103e-b830-2f1c-16bc-224aa0ad5bc8"); //"breathe_rot" +const LLUUID ANIM_AGENT_PHYSICS_MOTION_ID = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987"); //"physics_motion" +const LLUUID ANIM_AGENT_EDITING_ID = LLUUID("2a8eba1d-a7f8-5596-d44a-b4977bf8c8bb"); //"editing" +const LLUUID ANIM_AGENT_EYE_ID = LLUUID("5c780ea8-1cd1-c463-a128-48c023f6fbea"); //"eye" +const LLUUID ANIM_AGENT_FLY_ADJUST_ID = LLUUID("db95561f-f1b0-9f9a-7224-b12f71af126e"); //"fly_adjust" +const LLUUID ANIM_AGENT_HAND_MOTION_ID = LLUUID("ce986325-0ba7-6e6e-cc24-b17c4b795578"); //"hand_motion" +const LLUUID ANIM_AGENT_HEAD_ROT_ID = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"); //"head_rot" +const LLUUID ANIM_AGENT_PELVIS_FIX_ID = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b"); //"pelvis_fix" +const LLUUID ANIM_AGENT_TARGET_ID = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55"); //"target" +const LLUUID ANIM_AGENT_WALK_ADJUST_ID = LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d"); //"walk_adjust" +// +// This must be in the same order as ANIM_AGENT_BODY_NOISE through ANIM_AGENT_WALK_ADJUST (see llmotion.h)! +static LLUUID const* lookup[] = { + &ANIM_AGENT_BODY_NOISE_ID, + &ANIM_AGENT_BREATHE_ROT_ID, + &ANIM_AGENT_PHYSICS_MOTION_ID, + &ANIM_AGENT_EDITING_ID, + &ANIM_AGENT_EYE_ID, + &ANIM_AGENT_FLY_ADJUST_ID, + &ANIM_AGENT_HAND_MOTION_ID, + &ANIM_AGENT_HEAD_ROT_ID, + &ANIM_AGENT_PELVIS_FIX_ID, + &ANIM_AGENT_TARGET_ID, + &ANIM_AGENT_WALK_ADJUST_ID +}; + +LLUUID const& mask2ID(U32 bit) +{ + int const lookupsize = sizeof(lookup) / sizeof(LLUUID const*); + int i = lookupsize - 1; + U32 mask = 1 << i; + for(;;) + { + if (bit == mask) + { + return *lookup[i]; + } + --i; + mask >>= 1; + llassert_always(i >= 0); + } +} + +#ifdef CWDEBUG +static char const* strlookup[] = { + "ANIM_AGENT_BODY_NOISE", + "ANIM_AGENT_BREATHE_ROT", + "ANIM_AGENT_PHYSICS_MOTION", + "ANIM_AGENT_EDITING", + "ANIM_AGENT_EYE", + "ANIM_AGENT_FLY_ADJUST", + "ANIM_AGENT_HAND_MOTION", + "ANIM_AGENT_HEAD_ROT", + "ANIM_AGENT_PELVIS_FIX", + "ANIM_AGENT_TARGET", + "ANIM_AGENT_WALK_ADJUST" +}; + +char const* mask2str(U32 bit) +{ + int const lookupsize = sizeof(lookup) / sizeof(LLUUID const*); + int i = lookupsize - 1; + U32 mask = 1 << i; + do + { + if (bit == mask) + { + return strlookup[i]; + } + --i; + mask >>= 1; + } + while(i >= 0); + return ""; +} +#endif + +// stopMotion(ANIM_AGENT_WALK_ADJUST) is called every frame, and for every avatar on the radar. +// That can be like 1000 times per second, so... speed that up a bit and lets not lookup the same LLUUID 1000 times +// per second in a std::map. Added the rest of the animations while I was at it. +void LLVOAvatar::startMotion(U32 bit, F32 time_offset) +{ + if (!isMotionActive(bit)) + { + startMotion(mask2ID(bit), time_offset); + } +} + +void LLVOAvatar::stopMotion(U32 bit, BOOL stop_immediate) +{ + if (isMotionActive(bit)) + { + stopMotion(mask2ID(bit), stop_immediate); + } +} +// //----------------------------------------------------------------------------- // Constants @@ -257,12 +343,12 @@ struct LLTextureMaskData // class LLBodyNoiseMotion //----------------------------------------------------------------------------- class LLBodyNoiseMotion : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLBodyNoiseMotion(const LLUUID &id) - : LLMotion(id) + LLBodyNoiseMotion(LLUUID const& id, LLMotionController& controller) + : AIMaskedMotion(id, controller, ANIM_AGENT_BODY_NOISE) { mName = "body_noise"; mTorsoState = new LLJointState; @@ -277,7 +363,7 @@ public: //------------------------------------------------------------------------- // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLBodyNoiseMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLBodyNoiseMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -320,11 +406,6 @@ public: return STATUS_SUCCESS; } - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate() { return TRUE; } - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. @@ -348,9 +429,6 @@ public: return TRUE; } - // called when a motion is deactivated - virtual void onDeactivate() {} - private: //------------------------------------------------------------------------- // joint states to be animated @@ -362,12 +440,12 @@ private: // class LLBreatheMotionRot //----------------------------------------------------------------------------- class LLBreatheMotionRot : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLBreatheMotionRot(const LLUUID &id) : - LLMotion(id), + LLBreatheMotionRot(LLUUID const& id, LLMotionController& controller) : + AIMaskedMotion(id, controller, ANIM_AGENT_BREATHE_ROT), mBreatheRate(1.f), mCharacter(NULL) { @@ -384,7 +462,7 @@ public: //------------------------------------------------------------------------- // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID &id) { return new LLBreatheMotionRot(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLBreatheMotionRot(id, controller); } public: //------------------------------------------------------------------------- @@ -437,11 +515,6 @@ public: } } - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate() { return TRUE; } - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. @@ -456,9 +529,6 @@ public: return TRUE; } - // called when a motion is deactivated - virtual void onDeactivate() {} - private: //------------------------------------------------------------------------- // joint states to be animated @@ -472,12 +542,12 @@ private: // class LLPelvisFixMotion //----------------------------------------------------------------------------- class LLPelvisFixMotion : - public LLMotion + public AIMaskedMotion { public: // Constructor - LLPelvisFixMotion(const LLUUID &id) - : LLMotion(id), mCharacter(NULL) + LLPelvisFixMotion(LLUUID const& id, LLMotionController& controller) + : AIMaskedMotion(id, controller, ANIM_AGENT_PELVIS_FIX), mCharacter(NULL) { mName = "pelvis_fix"; @@ -493,7 +563,7 @@ public: //------------------------------------------------------------------------- // static constructor // all subclasses must implement such a function and register it - static LLMotion *create(const LLUUID& id) { return new LLPelvisFixMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLPelvisFixMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -538,11 +608,6 @@ public: return STATUS_SUCCESS; } - // called when a motion is activated - // must return TRUE to indicate success, or else - // it will be deactivated - virtual BOOL onActivate() { return TRUE; } - // called per time step // must return TRUE while it is active, and // must return FALSE when the motion is completed. @@ -553,9 +618,6 @@ public: return TRUE; } - // called when a motion is deactivated - virtual void onDeactivate() {} - private: //------------------------------------------------------------------------- // joint states to be animated @@ -1438,17 +1500,17 @@ void LLVOAvatar::deleteCachedImages(bool clearAll) //------------------------------------------------------------------------ void LLVOAvatar::initClass() { - gAnimLibrary.animStateSetString(ANIM_AGENT_BODY_NOISE,"body_noise"); - gAnimLibrary.animStateSetString(ANIM_AGENT_BREATHE_ROT,"breathe_rot"); - gAnimLibrary.animStateSetString(ANIM_AGENT_PHYSICS_MOTION,"physics_motion"); - gAnimLibrary.animStateSetString(ANIM_AGENT_EDITING,"editing"); - gAnimLibrary.animStateSetString(ANIM_AGENT_EYE,"eye"); - gAnimLibrary.animStateSetString(ANIM_AGENT_FLY_ADJUST,"fly_adjust"); - gAnimLibrary.animStateSetString(ANIM_AGENT_HAND_MOTION,"hand_motion"); - gAnimLibrary.animStateSetString(ANIM_AGENT_HEAD_ROT,"head_rot"); - gAnimLibrary.animStateSetString(ANIM_AGENT_PELVIS_FIX,"pelvis_fix"); - gAnimLibrary.animStateSetString(ANIM_AGENT_TARGET,"target"); - gAnimLibrary.animStateSetString(ANIM_AGENT_WALK_ADJUST,"walk_adjust"); + gAnimLibrary.animStateSetString(ANIM_AGENT_BODY_NOISE_ID,"body_noise"); + gAnimLibrary.animStateSetString(ANIM_AGENT_BREATHE_ROT_ID,"breathe_rot"); + gAnimLibrary.animStateSetString(ANIM_AGENT_PHYSICS_MOTION_ID,"physics_motion"); + gAnimLibrary.animStateSetString(ANIM_AGENT_EDITING_ID,"editing"); + gAnimLibrary.animStateSetString(ANIM_AGENT_EYE_ID,"eye"); + gAnimLibrary.animStateSetString(ANIM_AGENT_FLY_ADJUST_ID,"fly_adjust"); + gAnimLibrary.animStateSetString(ANIM_AGENT_HAND_MOTION_ID,"hand_motion"); + gAnimLibrary.animStateSetString(ANIM_AGENT_HEAD_ROT_ID,"head_rot"); + gAnimLibrary.animStateSetString(ANIM_AGENT_PELVIS_FIX_ID,"pelvis_fix"); + gAnimLibrary.animStateSetString(ANIM_AGENT_TARGET_ID,"target"); + gAnimLibrary.animStateSetString(ANIM_AGENT_WALK_ADJUST_ID,"walk_adjust"); SHClientTagMgr::instance(); //Instantiate. Parse. Will fetch a new tag file if AscentUpdateTagsOnLoad is true. } @@ -1506,19 +1568,19 @@ void LLVOAvatar::initInstance(void) registerMotion( ANIM_AGENT_WALK_NEW, LLKeyframeWalkMotion::create ); //v2 // motions without a start/stop bit - registerMotion( ANIM_AGENT_BODY_NOISE, LLBodyNoiseMotion::create ); - registerMotion( ANIM_AGENT_BREATHE_ROT, LLBreatheMotionRot::create ); - registerMotion( ANIM_AGENT_PHYSICS_MOTION, LLPhysicsMotionController::create ); - registerMotion( ANIM_AGENT_EDITING, LLEditingMotion::create ); - registerMotion( ANIM_AGENT_EYE, LLEyeMotion::create ); - registerMotion( ANIM_AGENT_FLY_ADJUST, LLFlyAdjustMotion::create ); - registerMotion( ANIM_AGENT_HAND_MOTION, LLHandMotion::create ); - registerMotion( ANIM_AGENT_HEAD_ROT, LLHeadRotMotion::create ); - registerMotion( ANIM_AGENT_PELVIS_FIX, LLPelvisFixMotion::create ); - registerMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create ); - registerMotion( ANIM_AGENT_TARGET, LLTargetingMotion::create ); - registerMotion( ANIM_AGENT_WALK_ADJUST, LLWalkAdjustMotion::create ); + registerMotion( ANIM_AGENT_BODY_NOISE_ID, LLBodyNoiseMotion::create ); + registerMotion( ANIM_AGENT_BREATHE_ROT_ID, LLBreatheMotionRot::create ); + registerMotion( ANIM_AGENT_PHYSICS_MOTION_ID, LLPhysicsMotionController::create ); + registerMotion( ANIM_AGENT_EDITING_ID, LLEditingMotion::create ); + registerMotion( ANIM_AGENT_EYE_ID, LLEyeMotion::create ); + registerMotion( ANIM_AGENT_FLY_ADJUST_ID, LLFlyAdjustMotion::create ); + registerMotion( ANIM_AGENT_HAND_MOTION_ID, LLHandMotion::create ); + registerMotion( ANIM_AGENT_HEAD_ROT_ID, LLHeadRotMotion::create ); + registerMotion( ANIM_AGENT_PELVIS_FIX_ID, LLPelvisFixMotion::create ); + registerMotion( ANIM_AGENT_TARGET_ID, LLTargetingMotion::create ); + registerMotion( ANIM_AGENT_WALK_ADJUST_ID, LLWalkAdjustMotion::create ); + registerMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create ); } LLAvatarAppearance::initInstance(); diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 891fae7d9..ded74c50b 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -54,6 +54,9 @@ #include "llavatarname.h" +// +#if 0 +// Hide these: should be using the bit masks everywhere. extern const LLUUID ANIM_AGENT_BODY_NOISE; extern const LLUUID ANIM_AGENT_BREATHE_ROT; extern const LLUUID ANIM_AGENT_PHYSICS_MOTION; @@ -65,6 +68,8 @@ extern const LLUUID ANIM_AGENT_HEAD_ROT; extern const LLUUID ANIM_AGENT_PELVIS_FIX; extern const LLUUID ANIM_AGENT_TARGET; extern const LLUUID ANIM_AGENT_WALK_ADJUST; +#endif +// class LLAPRFile; class LLViewerWearable; @@ -230,6 +235,10 @@ public: /*virtual*/ LLUUID remapMotionID(const LLUUID& id); /*virtual*/ BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f); /*virtual*/ BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE); + // + void startMotion(U32 bit, F32 start_offset = 0.f); + void stopMotion(U32 bit, BOOL stop_immediate = FALSE); + // virtual void stopMotionFromSource(const LLUUID& source_id); virtual void requestStopMotion(LLMotion* motion); LLMotion* findMotion(const LLUUID& id) const; From e38ec797fda86206b01fc1d3d59b000f0efa2652 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 4 Dec 2013 22:40:07 +0100 Subject: [PATCH 06/15] Fix lookat and pointat shared experience. There was a bug that when people turned off sending viewer effects, they'd also turn off sending LookAt, while there is a seperate checkbox to turn THAT off, while still seeing their own avatar happily look around, breaking shared experience (most notably, most people will be complete oblivious of the fact that they look like zombies to others and that their friends do not see the same thing on their screen). This patch fixes this: both parties now see the same thing. Lookat and PointAt are only turned off by their respective checkboxes, and no longer silently influenced by turning off other viewer effects. --- indra/newview/llhudmanager.cpp | 25 ++++++++++++++++--- indra/newview/llvoavatarself.cpp | 6 ++--- .../en-us/panel_preferences_ascent_system.xml | 4 +-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/indra/newview/llhudmanager.cpp b/indra/newview/llhudmanager.cpp index cb9a2c1d3..15872a0db 100644 --- a/indra/newview/llhudmanager.cpp +++ b/indra/newview/llhudmanager.cpp @@ -80,14 +80,33 @@ void LLHUDManager::updateEffects() void LLHUDManager::sendEffects() { + static LLCachedControl disable_lookat_effect(gSavedSettings, "PrivateLookAt", false); + static LLCachedControl disable_pointat_effect(gSavedSettings, "DisablePointAtAndBeam", false); + static LLCachedControl broadcast_viewer_effects(gSavedSettings, "BroadcastViewerEffects", true); - if(!gSavedSettings.getBOOL("BroadcastViewerEffects")) - return; - S32 i; for (i = 0; i < mHUDEffects.count(); i++) { LLHUDEffect *hep = mHUDEffects[i]; + if (hep->mType == LLHUDObject::LL_HUD_EFFECT_LOOKAT) + { + if (disable_lookat_effect) + { + continue; + } + } + else if (hep->mType == LLHUDObject::LL_HUD_EFFECT_POINTAT || + hep->mType == LLHUDObject::LL_HUD_EFFECT_BEAM) + { + if (disable_pointat_effect) + { + continue; + } + } + else if (!broadcast_viewer_effects) + { + continue; + } if (hep->isDead()) { llwarns << "Trying to send dead effect!" << llendl; diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 6f0a4bd9f..c3ead99db 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -897,9 +897,9 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) //virtual void LLVOAvatarSelf::idleUpdateTractorBeam() { - - - if(gSavedSettings.getBOOL("DisablePointAtAndBeam")) + // + static LLCachedControl disable_pointat_effect("DisablePointAtAndBeam"); + if (disable_pointat_effect) { return; } diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_system.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_system.xml index e4d6537c0..72a5362fc 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_system.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_system.xml @@ -83,9 +83,9 @@ - + - + From 006b319c3a9be55181e0c2e02d6b9040402151f2 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 5 Dec 2013 14:53:16 +0100 Subject: [PATCH 07/15] Add AISyncClient<> (and AISyncServer). A tool to synchronize objects: Objects derived from AISyncClient can signal that they are 'ready' or 'not ready' for up to 4 events (using a bitmask) at a time. Clients that signal to be ready for anything at roughly the same time as other clients, and which return the same 'hash' (a virtual function returning a 64bit value) will be grouped together and receive events (by means of virtual functions being called) to notify them of all clients being ready or not for one of the events (syncevent1). The other three events can be polled. The memory usage is low (one pointer per client that points to its AISyncServer object), servers are released to a cache after about 100 ms (unless there is actual need for synchronization), so there aren't much of those either. The CPU usage is extremely low: all events are handled in parallel in a 32 bit value (6 bits per event to count the number of registered clients and the number of ready clients for each event, and the remaining 8 bits to count the number of reference pointers (which should only be a constant higher, so that is overkill). To signal to a server that a client has become ready or not is mostly a function call, which then takes 1 clock cycle or so before returning. Registration of a client is slightly more expensive as it requires a pointer to be added to the end of a std::list. This tool could easily be used as part of the graphics engine (not that I intend to do that :p). --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/aisyncclient.cpp | 538 ++++++++++++++++++++++++++++++++ indra/llcommon/aisyncclient.h | 417 +++++++++++++++++++++++++ 3 files changed, 957 insertions(+) create mode 100644 indra/llcommon/aisyncclient.cpp create mode 100644 indra/llcommon/aisyncclient.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 31e4fa484..fd0e8208c 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -20,6 +20,7 @@ set(llcommon_SOURCE_FILES aialert.cpp aifile.cpp aiframetimer.cpp + aisyncclient.cpp aithreadid.cpp imageids.cpp indra_constants.cpp @@ -112,6 +113,7 @@ set(llcommon_HEADER_FILES aifile.h aiframetimer.h airecursive.h + aisyncclient.h aithreadid.h aithreadsafe.h bitpack.h diff --git a/indra/llcommon/aisyncclient.cpp b/indra/llcommon/aisyncclient.cpp new file mode 100644 index 000000000..18ca33edc --- /dev/null +++ b/indra/llcommon/aisyncclient.cpp @@ -0,0 +1,538 @@ +/** + * @file aisyncclient.cpp + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 05/12/2013 + * - Initial version, written by Aleric Inglewood @ SL + */ + +#include "sys.h" +#include "aisyncclient.h" +#include +#include "debug.h" + +typedef std::deque > servers_type; +static servers_type servers[syncgroup_size]; + +//static +template +void AISyncServer::register_client(AISyncClient_ServerPtr* client) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServer::register_client(" << client << ")"); +#endif + + // Determine which server to use. + boost::intrusive_ptr server; + int expired = 0; + AISyncKey const sync_key(client->sync_key_hash()); + for (servers_type::iterator server_iter = servers[syncgroup].begin(); server_iter != servers[syncgroup].end(); ++server_iter) + { + boost::intrusive_ptr& server_ptr = *server_iter; + if (server_ptr->mSyncKey.expired()) + { + ++expired; + // If the server only contains a single client, then unregister it and put the server (back) in the server cache. + if (server_ptr->get_refcount() == 2) + { + server_ptr->unregister_last_client(); + AISyncServer::dispose_server(server_ptr); + } + continue; + } + if (server_ptr->mSyncKey.matches(sync_key)) + { + server = server_ptr; + break; + } + } + // Remove servers with expired keys. + if (expired) + { + if (expired == servers[syncgroup].size()) + { + servers[syncgroup].clear(); + } + else + { + servers[syncgroup].erase(servers[syncgroup].begin(), servers[syncgroup].begin() + expired); + } + } + if (!server) + { + AISyncServer::create_server(server, sync_key); + servers[syncgroup].push_back(server); + } + + // Sanity check: the client should never already be registered. + llassert(!client->mSyncServer); + // Recover from assertion failure. + if (client->mSyncServer) + { + if (client->mSyncServer == server) + { + return; + } + client->mSyncServer->unregister_client(client, syncgroup); + register_client(client); + return; + } + + // Obviously... + llassert(!client->mReadyEvents); + + // Check if the current clients are all ready: adding a new one will cause the group to become not-ready. + bool all_old_clients_are_ready = server->mNrReady && !((server->mNrClients - server->mNrReady) & synccountmask1); + // Add new client to the group. + client->mSyncServer = server; + server->mNrClients += syncevents; + llassert((server->mNrClients & syncoverflowbits) == 0); + if (all_old_clients_are_ready) + { + server->trigger_not_ready(); // Tell all old clients that the group is not ready. + } + server->mClients.push_back(client); // Actually add the new client to the list. +} + +void AISyncServer::unregister_client(AISyncClient_ServerPtr* client, syncgroups syncgroup) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "unregister_client(" << client << ", " << syncgroup << "), with this = " << this); +#endif + + // The client must be registered with this server. + llassert(client->mSyncServer == this); + // A client may only be unregistered after it was marked not-ready for all events. + llassert(!client->mReadyEvents); + // Run over all registered clients. + for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) + { + // Found it? + if (*client_iter == client) + { + mClients.erase(client_iter); + // Are the remaining clients ready? + if (mNrReady && !((mNrClients - syncevents - mNrReady) & synccountmask1)) + { + trigger_ready(); + } + mNrClients -= syncevents; + llassert((mNrClients & syncoverflowbits) == 0); + client->mSyncServer.reset(); // This might delete the current object. + break; + } + } + // The client must have been found. + llassert(!client->mSyncServer); +} + +void AISyncServer::unregister_last_client(void) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "unregister_last_client(), with this = " << this); +#endif + + // This function may only be called for servers with exactly one client. + llassert(mClients.size() == 1); + AISyncClient_ServerPtr* client = *mClients.begin(); +#ifdef DEBUG_SYNCOUTPUT + Dout(dc::notice, "unregistering client " << client); +#endif + mClients.clear(); + mNrClients -= syncevents; + mNrReady = 0; + llassert((mNrClients & syncoverflowbits) == 0); + client->mSyncServer.reset(); + client->deregistered(); +} + +synceventset AISyncServer::events_with_all_clients_ready(void) const +{ + synccount nrNotReady = mNrClients - mNrReady; + synceventset result1 = !(nrNotReady & synccountmask1) ? syncevent1 : 0; + synceventset result2 = !(nrNotReady & synccountmask2) ? syncevent2 : 0; + synceventset result3 = !(nrNotReady & synccountmask3) ? syncevent3 : 0; + synceventset result4 = !(nrNotReady & synccountmask4) ? syncevent4 : 0; + result1 |= result2; + result3 |= result4; + return result1 | result3; +} + +synceventset AISyncServer::events_with_at_least_one_client_ready(void) const +{ + synceventset result1 = (mNrReady & synccountmask1) ? syncevent1 : 0; + synceventset result2 = (mNrReady & synccountmask2) ? syncevent2 : 0; + synceventset result3 = (mNrReady & synccountmask3) ? syncevent3 : 0; + synceventset result4 = (mNrReady & synccountmask4) ? syncevent4 : 0; + result1 |= result2; + result3 |= result4; + return result1 | result3; +} + +#ifdef CWDEBUG +struct SyncEventSet { + synceventset mBits; + SyncEventSet(synceventset bits) : mBits(bits) { } +}; + +std::ostream& operator<<(std::ostream& os, SyncEventSet const& ses) +{ + os << ((ses.mBits & syncevent4) ? '1' : '0'); + os << ((ses.mBits & syncevent3) ? '1' : '0'); + os << ((ses.mBits & syncevent2) ? '1' : '0'); + os << ((ses.mBits & syncevent1) ? '1' : '0'); + return os; +} +#endif + +bool AISyncServer::ready(synceventset events, synceventset yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")"); +#endif + + synceventset added_events = events & yesno; + synceventset removed_events = events & ~yesno; + // May not add events that are already ready. + llassert(!(client->mReadyEvents & added_events)); + // Cannot remove events that weren't ready. + llassert((client->mReadyEvents & removed_events) == removed_events); + // Were all clients ready for event 1? + bool ready_before = !((mNrClients - mNrReady) & synccountmask1); + // Update mNrReady counters. + mNrReady += added_events; + mNrReady -= removed_events; + // Test for under and overflow, this limits the maximum number of clients to 127 instead of 255, but well :p. + llassert((mNrReady & syncoverflowbits) == 0); + // Are all clients ready for event 1? + bool ready_after = !((mNrClients - mNrReady) & synccountmask1); + if (ready_before && !ready_after) + { + trigger_not_ready(); + } +#ifdef SHOW_ASSERT + // Update debug administration. + client->mReadyEvents ^= events; +#ifdef DEBUG_SYNCOUTPUT + Dout(dc::notice, "Client " << client << " now has ready: " << SyncEventSet(client->mReadyEvents)); +#endif +#endif + if (!ready_before && ready_after) + { + trigger_ready(); + } +} + +void AISyncServer::trigger_ready(void) +{ + for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) + { + llassert(((*client_iter)->mReadyEvents & syncevent1)); + (*client_iter)->event1_ready(); + } +} + +void AISyncServer::trigger_not_ready(void) +{ + for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) + { + llassert(((*client_iter)->mReadyEvents & syncevent1)); + (*client_iter)->event1_not_ready(); + } +} + +void intrusive_ptr_add_ref(AISyncServer* server) +{ + server->mNrClients += syncrefcountunit; + llassert((server->mNrClients & syncoverflowbits) == 0); +} + +void intrusive_ptr_release(AISyncServer* server) +{ + llassert(server->mNrClients >= syncrefcountunit); + server->mNrClients -= syncrefcountunit; + // If there are no more pointers pointing to this server, then obviously it can't have any registered clients. + llassert(server->mNrClients >= syncrefcountunit || server->mNrClients == 0); + if (server->mNrClients == 0) + { + delete server; + } +} + +//static +sync_key_hash_type const AISyncKey::sNoRequirementHash = 0x91b42f98a9fef15cULL; + + +//============================================================================= +// SYNC_TESTSUITE +//============================================================================= + +#ifdef SYNC_TESTSUITE +#include +#include +#include +#include + +//static +U32 LLFrameTimer::sFrameCount; + +double innerloop_count = 0; + +double LLFrameTimer::getCurrentTime() +{ + return innerloop_count * 0.001; +} + +class TestsuiteServerBase : public AISyncServer +{ + public: + TestsuiteServerBase(AISyncKey const& sync_key) : AISyncServer(sync_key) { } + client_list_type& clients(void) { return mClients; } +}; + +class TestsuiteServer1 : public TestsuiteServerBase +{ + public: + TestsuiteServer1(AISyncKey const& sync_key) : TestsuiteServerBase(sync_key) { } +}; + +// Specialitations that link TestsuiteServer1 to syncgroup_test1. +// +//static +template<> +void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) +{ + AISyncServerCache::create_server(server, sync_key); +} +//static +template<> +void AISyncServer::dispose_server(boost::intrusive_ptr& server) +{ + AISyncServerCache::dispose_server(server); +} + +class TestsuiteServer2 : public TestsuiteServerBase +{ + public: + TestsuiteServer2(AISyncKey const& sync_key) : TestsuiteServerBase(sync_key) { } +}; + +// Specialitations that link TestsuiteServer2 to syncgroup_test2. +// +//static +template<> +void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) +{ + AISyncServerCache::create_server(server, sync_key); +} +//static +template<> +void AISyncServer::dispose_server(boost::intrusive_ptr& server) +{ + AISyncServerCache::dispose_server(server); +} + +template +class TestsuiteClient : public AISyncClient +{ + private: + int mIndex; + bool mRequestedRegistered; + synceventset mRequestedReady; + bool mActualReady1; + + public: + TestsuiteClient() : mIndex(-1), mRequestedRegistered(false), mRequestedReady(0), mActualReady1(false) { } + ~TestsuiteClient() { this->ready(mRequestedReady, (synceventset)0); } + + void setIndex(int index) { mIndex = index; } + + protected: + /*virtual*/ void event1_ready(void) + { + llassert(!mActualReady1); + mActualReady1 = true; + } + + /*virtual*/ void event1_not_ready(void) + { + llassert(mActualReady1); + mActualReady1 = false; + } + + /*virtual*/ sync_key_hash_type sync_key_hash(void) const + { + // Sync odd clients with eachother, and even clients with eachother. + return 0xb3c919ff + (mIndex & 1); + } + + // This is called when the server expired and we're the only client on it. + /*virtual*/ void deregistered(void) + { +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "TestsuiteClient<" << syncgroup << ">::deregistered(), with this = " << this); +#endif + mRequestedRegistered = false; + mRequestedReady = 0; + mActualReady1 = false; + this->mReadyEvents = 0; + } + + private: + bool is_registered(void) const { return !!this->mSyncServer; } + + public: + void change_state(unsigned long r); + bool getRequestedRegistered(void) const { return mRequestedRegistered; } + synceventset getRequestedReady(void) const { return mRequestedReady; } +}; + +TestsuiteClient* client1p; +TestsuiteClient* client2p; + +int const number_of_clients_per_syncgroup = 8; + +template +void TestsuiteClient::change_state(unsigned long r) +{ + bool change_registered = r & 1; + r >>= 1; + synceventset toggle_events = ((r & 1) ? syncevent1 : 0) | ((r & 2) ? syncevent2 : 0) | ((r & 4) ? syncevent3 : 0) | ((r & 8) ? syncevent4 : 0); + r >>= 4; + if (change_registered) + { + if (mRequestedRegistered && !mRequestedReady) + { + mRequestedRegistered = false; + this->unregister_client(); + } + } + else if (toggle_events) + { + mRequestedReady ^= toggle_events; + mRequestedRegistered = true; + this->ready(toggle_events, mRequestedReady & toggle_events); + } + llassert(mRequestedRegistered == is_registered()); + TestsuiteServerBase* server = this->server(); + llassert(!mRequestedRegistered || server->number_of_clients() == server->clients().size()); + if (mRequestedRegistered) + { + synceventset all_ready = syncevents; + synceventset any_ready = 0; + int nr = 0; + for (int cl = 0; cl < number_of_clients_per_syncgroup; ++cl) + { + if (syncgroup == syncgroup_test1) + { + if (client1p[cl].server() != server) + { + continue; + } + if (client1p[cl].getRequestedRegistered()) + { + ++nr; + all_ready &= client1p[cl].getRequestedReady(); + any_ready |= client1p[cl].getRequestedReady(); + } + } + else + { + if (client2p[cl].server() != server) + { + continue; + } + if (client2p[cl].getRequestedRegistered()) + { + ++nr; + all_ready &= client2p[cl].getRequestedReady(); + any_ready |= client2p[cl].getRequestedReady(); + } + } + } + llassert(nr == server->number_of_clients()); + llassert(!!(all_ready & syncevent1) == mActualReady1); + llassert(this->server()->events_with_all_clients_ready() == all_ready); + llassert(this->server()->events_with_at_least_one_client_ready() == any_ready); + llassert(nr == 0 || (any_ready & all_ready) == all_ready); + } + llassert(mRequestedReady == this->mReadyEvents); +} + +int main() +{ + Debug(libcw_do.on()); + Debug(dc::notice.on()); + Debug(libcw_do.set_ostream(&std::cout)); + Debug(list_channels_on(libcw_do)); + + unsigned short seed16v[3] = { 0x1234, 0xfedc, 0x7091 }; + + for (int k = 0;; ++k) + { + std::cout << "Loop: " << k << "; SEED: " << std::hex << seed16v[0] << ", " << seed16v[1] << ", " << seed16v[2] << std::dec << std::endl; + ++LLFrameTimer::sFrameCount; + + seed48(seed16v); + seed16v[0] = lrand48() & 0xffff; + seed16v[1] = lrand48() & 0xffff; + seed16v[2] = lrand48() & 0xffff; + + TestsuiteClient client1[number_of_clients_per_syncgroup]; + TestsuiteClient client2[number_of_clients_per_syncgroup]; + client1p = client1; + client2p = client2; + + for (int i = 0; i < number_of_clients_per_syncgroup; ++i) + { + client1[i].setIndex(i); + client2[i].setIndex(i); + } + + for (int j = 0; j < 1000000; ++j) + { + innerloop_count += 1; + +#ifdef DEBUG_SYNCOUTPUT + Dout(dc::notice, "Innerloop: " << j); +#endif + unsigned long r = lrand48(); + syncgroups grp = (r & 1) ? syncgroup_test1 : syncgroup_test2; + r >>= 1; + int cl = (r & 255) % number_of_clients_per_syncgroup; + r >>= 8; + switch (grp) + { + case syncgroup_test1: + client1[cl].change_state(r); + break; + case syncgroup_test2: + client2[cl].change_state(r); + break; + } + } + } +} + +#endif diff --git a/indra/llcommon/aisyncclient.h b/indra/llcommon/aisyncclient.h new file mode 100644 index 000000000..b6fcbd103 --- /dev/null +++ b/indra/llcommon/aisyncclient.h @@ -0,0 +1,417 @@ +/** + * @file aisyncclient.h + * @brief Declaration of AISyncClient. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 05/12/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AI_SYNC_CLIENT_H +#define AI_SYNC_CLIENT_H + +#ifdef SYNC_TESTSUITE +/* + * To compile the testsuite, run: + * + * cd indra/llcommon + * g++ -O3 -DCWDEBUG -DSYNC_TESTSUITE -I. -I../cwdebug aisyncclient.cpp -lcwd + */ + +#include +#include +typedef uint32_t U32; +typedef uint64_t U64; +#define LL_COMMON_API +#define SHOW_ASSERT +#define ASSERT_ONLY_COMMA(...) , __VA_ARGS__ +#define llassert assert + +struct LLFrameTimer +{ + double mStartTime; + double mExpiry; + static double getCurrentTime(void); + static U32 sFrameCount; + static U32 getFrameCount() { return sFrameCount; } + void reset(double expiration) { mStartTime = getCurrentTime(); mExpiry = mStartTime + expiration; } + bool hasExpired(void) const { return getCurrentTime() > mExpiry; } +}; + +#else // !SYNC_TESTSUITE +#include "llpreprocessor.h" +#include "stdtypes.h" +#include "llerror.h" +#include "llframetimer.h" +#endif +#include +#include + +class AISyncServer; + +/* + * AISyncClient + * + * This class allows to synchronize events between multiple client objects, + * derived from AISyncClient, where AISYNCSERVER must be + * derived from AISyncServer. + * + * Client objects call ready(mask) (or ready(event, bool), where each bit can be + * set or unset, which will result in alternating calls to event1_ready(mask) and + * event1_not_ready(mask) for all registered clients at the same time depending on + * when all clients are ready for syncevent1 or not (the other events can be + * polled, see below). + * + * For example, let a and b be two clients: + * + * { + * AISyncClient a; + * { + * AISyncClient b; + * + * a.ready(syncevent1, true); // Calls a.event1_ready(). + * a.ready(syncevent1, false); // Calls a.event1_not_ready(). + * b.ready(syncevent1, true); // Nothing happens. + * b.ready(syncevent1, false); // Nothing happens. + * b.ready(syncevent1, true); // Nothing happens. + * a.ready(syncevent1, true); // Calls a.event1_ready() and b.event1_ready(2). + * b.ready(syncevent1, false); // Calls a.event1_not_ready() and b.event1_not_ready(). + * } // Calls a.event1_ready() because b was destructed and a is still ready. + * a.ready(syncevent1, false); // Calls a.event1_not_ready(). Must set not-ready before destructing. + * } + * + * Clients can access the server by calling server() and poll it: + * + * AISyncServer* server = server(); + * + * if (server) + * { + * int n = server->number_of_clients(); + * synceventset set1 = server->events_with_all_clients_ready(); + * synceventset set2 = server->events_with_zero_clients_ready(); + * synceventset set3 = server->events_with_at_least_one_client_ready(); + * if (n == 0) + * { + * llassert(set1 == syncevents); // We define a server with no clients to have all clients ready. + * llassert(set2 == syncevents); // If there are no clients then every event has zero ready clients. + * llassert(set3 == 0); // If there are no clients then obviously set3 is the empty set. + * } + * else + * { + * llassert((set3 & set1) == set1); // set1 is a subset of set3. + * } + * llassert((set3 & (set1 & ~set2)) == (set1 & ~set2)); + * // The set of events that have all clients ready, but not zero clients, + * // is a subset of the set of events with at least one client ready. + * llassert((set2 ^ set3) == syncevents); // Each event belongs to either set2 or set3. + * } + * + * Clients can also unregister themselves without destruction, by calling unregister(); + * This might cause a call to event1_ready() for the remaining clients, if all of them + * are ready for syncevent1. + * + * Clients must be set not-ready for all events before unregistering them. + * + * Finally, it is possible to cause a server-side unregister for all clients, + * by calling server->dissolve(). This will cause an immediate call to event1_ready() + * for all clients that are ready, unless all were already ready. + */ + +enum syncgroups +{ +#ifdef SYNC_TESTSUITE + syncgroup_test1, + syncgroup_test2, +#else + syncgroup_motions, // Syncgroup used for animations. +#endif + syncgroup_size +}; + +typedef U32 sync_canonical_type; +typedef U64 sync_key_hash_type; + +int const sync_canonical_type_bits = sizeof(sync_canonical_type) * 8; +int const sync_number_of_events = 4; +int const sync_bits_per_event_counter = sync_canonical_type_bits / (sync_number_of_events + 1); + +// Each type exists of 4 event bytes. +typedef sync_canonical_type syncevent; // A single event: the least significant bit in one of the bytes. +typedef sync_canonical_type synceventset; // Zero or more events: the least significant bit in zero or more of the bytes. +typedef sync_canonical_type synccount; // A count [0, 255] in each of the bytes. +typedef sync_canonical_type synccountmask; // A single count mask: all eight bits set in one of the bytes. +typedef sync_canonical_type synccountmaskset; // Zero or more count masks: all eight bits set in zero or more of the bytes. + +syncevent const syncevent1 = 1; +syncevent const syncevent2 = syncevent1 << sync_bits_per_event_counter; +syncevent const syncevent3 = syncevent2 << sync_bits_per_event_counter; +syncevent const syncevent4 = syncevent3 << sync_bits_per_event_counter; +sync_canonical_type const syncrefcountunit = syncevent4 << sync_bits_per_event_counter; + +synccountmask const synccountmask1 = syncevent2 - 1; +synccountmask const synccountmask2 = synccountmask1 << sync_bits_per_event_counter; +synccountmask const synccountmask3 = synccountmask2 << sync_bits_per_event_counter; +synccountmask const synccountmask4 = synccountmask3 << sync_bits_per_event_counter; +synccountmask const syncrefcountmask = ((synccountmask)-1) & ~(syncrefcountunit - 1); + +synceventset const syncevents = syncevent1 | syncevent2 | syncevent3 | syncevent4; +synccountmask const syncoverflowbits = (syncevents << (sync_bits_per_event_counter - 1)) | ~(~(sync_canonical_type)0 >> 1); + +// Convert an event set into its corresponding count mask set. +inline synccountmaskset synceventset2countmaskset(synceventset events) +{ + synccountmaskset tmp1 = events << sync_bits_per_event_counter; + return (tmp1 & ~events) - (events & ~tmp1); +} + +// Convert a count mask set into an event set. +inline synceventset synccountmask2eventset(synccountmask mask) +{ + return mask & syncevents; +} + +// Interface class used to determined if a client and a server fit together. +class LL_COMMON_API AISyncKey : private LLFrameTimer +{ + private: + sync_key_hash_type mHash; + U32 mFrameCount; + + public: + // A static hash used for clients with no specific requirement. + static sync_key_hash_type const sNoRequirementHash; + + // A sync key object that returns the sNoRequirementHash. + static AISyncKey const sNoRequirement; + + public: + AISyncKey(sync_key_hash_type hash) : mHash(hash) { mFrameCount = getFrameCount(); reset(0.1); } + virtual ~AISyncKey() { } + + // Derived class should return a hash that is the same for clients which + // should be synchronized (assuming they meet the time slot requirement as well). + // The hash does not need to depend on on clock or frame number thus. + virtual sync_key_hash_type hash(void) const { return sNoRequirementHash; } + + // Return true if this key expired. + bool expired(void) const { return getFrameCount() > mFrameCount + 1 || hasExpired(); } + + // Return true if both keys are close enough to warrant synchronization. + bool matches(AISyncKey const& key) const { return key.mHash == mHash; } +}; + +class LL_COMMON_API AISyncClient_ServerPtr +{ + protected: + friend class AISyncServer; + boost::intrusive_ptr mSyncServer; + virtual ~AISyncClient_ServerPtr() { } + + // All-ready event. + virtual void event1_ready(void) = 0; // Called when, or after ready(), with the syncevent1 bit set, is called. Never called after a call to ready() with that bit unset. + // Not all-ready event. + virtual void event1_not_ready(void) = 0; // A call to event1_not_ready() follows (at most once) the call to event1_ready(), at some unspecified moment. + // Only client. Client was forcefully deregistered from expired server because it was the only client. + virtual void deregistered(void) + { +#ifdef SHOW_ASSERT + mReadyEvents = 0; +#endif + } + + // Derived classes must return a hash that determines whether or not clients can be synchronized (should be synchronized when registered at the same time). + virtual sync_key_hash_type sync_key_hash(void) const = 0; + +#ifdef SHOW_ASSERT + AISyncClient_ServerPtr(void) : mReadyEvents(0) { } + + protected: + synceventset mReadyEvents; // Used by AISyncServer for debugging only. +#endif +}; + +// AISyncServer +// +// This class represents a single group of AISyncClient's (a list of pointers to +// their AISyncClient_ServerPtr base class) that need to synchronize events. +// +class LL_COMMON_API AISyncServer +{ + public: + typedef std::list client_list_type; + + protected: + synccount mNrClients; // Four times (once in each byte) the number of registered clients (the size of mClients). + synccount mNrReady; // The number of clients that are ready (four counts, one for each syncevent). + client_list_type mClients; // The registered clients. + AISyncKey mSyncKey; // The characteristic of clients belonging to this synchronization server. + + public: + AISyncServer(AISyncKey const& sync_key) : mNrClients(0), mNrReady(0), mSyncKey(sync_key) { } + virtual ~AISyncServer() { } + + // Default creation of a server for 'syncgroup'. + template + static void create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key); + // Default removal of a server for 'syncgroup'. + template + static void dispose_server(boost::intrusive_ptr& server); + + // Called from AISyncServerCache. + void setSyncKey(AISyncKey const& sync_key) { mSyncKey = sync_key; } + + // Register client with a server from group syncgroup. + template + static void register_client(AISyncClient_ServerPtr* client); + // Unregister the client (syncgroup must be the same as what was passed to register it). + void unregister_client(AISyncClient_ServerPtr* client, syncgroups syncgroup); + + // Set readiness of all events at once. + bool ready(synceventset events, synceventset yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)); + // One event became ready or not ready. + bool ready(syncevent event, bool yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)) { return ready(event, yesno ? event : 0/*,*/ ASSERT_ONLY_COMMA(client)); } + + // Returns bitwise OR of syncevent1 through syncevent4 for + // events for which all clients are ready. + synceventset events_with_all_clients_ready(void) const; + // events for which at least one client is ready. + synceventset events_with_at_least_one_client_ready(void) const; + // events for which zero clients are ready. + synceventset events_with_zero_clients_ready(void) const { return syncevents ^ events_with_at_least_one_client_ready(); } + + // Return the number registered clients. + int number_of_clients(void) const { return (int)(mNrClients & synccountmask1); } + int get_refcount(void) const { return (int)(mNrClients >> (sync_number_of_events * sync_bits_per_event_counter)); } + + private: + void trigger_ready(void); // Calls event1_ready() for all clients. + void trigger_not_ready(void); // Calls event1_not_ready for all clients. + + void unregister_last_client(void); + + friend void intrusive_ptr_add_ref(AISyncServer* server); + friend void intrusive_ptr_release(AISyncServer* server); +}; + +template +class LL_COMMON_API AISyncClient_SyncGroup : public AISyncClient_ServerPtr +{ + public: + // Call 'ready' when you are ready (or not) to get a call to start(). + // Returns true if that call was made (immediately), otherwise it may happen later. + + bool ready(synceventset events, synceventset yesno) // Set readiness of all events at once. + { + if (!mSyncServer) + { + AISyncServer::register_client(this); + } + return mSyncServer->ready(events, yesno/*,*/ ASSERT_ONLY_COMMA(this)); + } + bool ready(syncevent event, bool yesno = true) // One event became ready or not ready. + { + if (!mSyncServer) + { + AISyncServer::register_client(this); + } + return mSyncServer->ready(event, yesno/*,*/ ASSERT_ONLY_COMMA(this)); + } + + void unregister_client(void) + { + if (mSyncServer) + { + mSyncServer->unregister_client(this, syncgroup); + } + } + + protected: + virtual ~AISyncClient_SyncGroup() { unregister_client(); } +}; + +template +class LL_COMMON_API AISyncClient : public AISyncClient_SyncGroup +{ + public: + AISYNCSERVER* server(void) { return static_cast(this->mSyncServer.get()); } + AISYNCSERVER const* server(void) const { return static_cast(this->mSyncServer.get()); } + + // By default return a general sync key that will cause synchronizing solely based on time. + /*virtual*/ AISyncKey const& getSyncKey(void) const { return AISyncKey::sNoRequirement; } +}; + +template +class AISyncServerCache +{ + private: + // The server cache exists of a few pointers to unused server instances. + // The cache is empty when sSize == 0 and full when sSize == 16; + static int const cache_size = 16; + static boost::intrusive_ptr sServerCache[cache_size]; + static int sSize; + + public: + static void create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) + { + if (sSize == 0) // Cache empty? + { + server.reset(new AISYNCSERVER(sync_key)); + } + else + { + sServerCache[--sSize].swap(server); + server->setSyncKey(sync_key); + } + } + + static void dispose_server(boost::intrusive_ptr& server) + { + if (sSize < cache_size) + { + sServerCache[sSize++].swap(server); + } + server.reset(); + } +}; + +template +boost::intrusive_ptr AISyncServerCache::sServerCache[cache_size]; + +template +int AISyncServerCache::sSize; + +template +inline void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) +{ + AISyncServerCache::create_server(server, sync_key); +} + +template +inline void AISyncServer::dispose_server(boost::intrusive_ptr& server) +{ + AISyncServerCache::dispose_server(server); +} + +#endif // AI_SYNC_CLIENT_H + From 61d365e957ed50434ea49ab28f4d69f01857dd2a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 14 Dec 2013 16:17:50 +0100 Subject: [PATCH 08/15] Removed this again... --- indra/llcommon/aisyncclient.cpp | 538 -------------------------------- indra/llcommon/aisyncclient.h | 417 ------------------------- 2 files changed, 955 deletions(-) delete mode 100644 indra/llcommon/aisyncclient.cpp delete mode 100644 indra/llcommon/aisyncclient.h diff --git a/indra/llcommon/aisyncclient.cpp b/indra/llcommon/aisyncclient.cpp deleted file mode 100644 index 18ca33edc..000000000 --- a/indra/llcommon/aisyncclient.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/** - * @file aisyncclient.cpp - * - * Copyright (c) 2013, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution. - * - * CHANGELOG - * and additional copyright holders. - * - * 05/12/2013 - * - Initial version, written by Aleric Inglewood @ SL - */ - -#include "sys.h" -#include "aisyncclient.h" -#include -#include "debug.h" - -typedef std::deque > servers_type; -static servers_type servers[syncgroup_size]; - -//static -template -void AISyncServer::register_client(AISyncClient_ServerPtr* client) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::register_client(" << client << ")"); -#endif - - // Determine which server to use. - boost::intrusive_ptr server; - int expired = 0; - AISyncKey const sync_key(client->sync_key_hash()); - for (servers_type::iterator server_iter = servers[syncgroup].begin(); server_iter != servers[syncgroup].end(); ++server_iter) - { - boost::intrusive_ptr& server_ptr = *server_iter; - if (server_ptr->mSyncKey.expired()) - { - ++expired; - // If the server only contains a single client, then unregister it and put the server (back) in the server cache. - if (server_ptr->get_refcount() == 2) - { - server_ptr->unregister_last_client(); - AISyncServer::dispose_server(server_ptr); - } - continue; - } - if (server_ptr->mSyncKey.matches(sync_key)) - { - server = server_ptr; - break; - } - } - // Remove servers with expired keys. - if (expired) - { - if (expired == servers[syncgroup].size()) - { - servers[syncgroup].clear(); - } - else - { - servers[syncgroup].erase(servers[syncgroup].begin(), servers[syncgroup].begin() + expired); - } - } - if (!server) - { - AISyncServer::create_server(server, sync_key); - servers[syncgroup].push_back(server); - } - - // Sanity check: the client should never already be registered. - llassert(!client->mSyncServer); - // Recover from assertion failure. - if (client->mSyncServer) - { - if (client->mSyncServer == server) - { - return; - } - client->mSyncServer->unregister_client(client, syncgroup); - register_client(client); - return; - } - - // Obviously... - llassert(!client->mReadyEvents); - - // Check if the current clients are all ready: adding a new one will cause the group to become not-ready. - bool all_old_clients_are_ready = server->mNrReady && !((server->mNrClients - server->mNrReady) & synccountmask1); - // Add new client to the group. - client->mSyncServer = server; - server->mNrClients += syncevents; - llassert((server->mNrClients & syncoverflowbits) == 0); - if (all_old_clients_are_ready) - { - server->trigger_not_ready(); // Tell all old clients that the group is not ready. - } - server->mClients.push_back(client); // Actually add the new client to the list. -} - -void AISyncServer::unregister_client(AISyncClient_ServerPtr* client, syncgroups syncgroup) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "unregister_client(" << client << ", " << syncgroup << "), with this = " << this); -#endif - - // The client must be registered with this server. - llassert(client->mSyncServer == this); - // A client may only be unregistered after it was marked not-ready for all events. - llassert(!client->mReadyEvents); - // Run over all registered clients. - for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) - { - // Found it? - if (*client_iter == client) - { - mClients.erase(client_iter); - // Are the remaining clients ready? - if (mNrReady && !((mNrClients - syncevents - mNrReady) & synccountmask1)) - { - trigger_ready(); - } - mNrClients -= syncevents; - llassert((mNrClients & syncoverflowbits) == 0); - client->mSyncServer.reset(); // This might delete the current object. - break; - } - } - // The client must have been found. - llassert(!client->mSyncServer); -} - -void AISyncServer::unregister_last_client(void) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "unregister_last_client(), with this = " << this); -#endif - - // This function may only be called for servers with exactly one client. - llassert(mClients.size() == 1); - AISyncClient_ServerPtr* client = *mClients.begin(); -#ifdef DEBUG_SYNCOUTPUT - Dout(dc::notice, "unregistering client " << client); -#endif - mClients.clear(); - mNrClients -= syncevents; - mNrReady = 0; - llassert((mNrClients & syncoverflowbits) == 0); - client->mSyncServer.reset(); - client->deregistered(); -} - -synceventset AISyncServer::events_with_all_clients_ready(void) const -{ - synccount nrNotReady = mNrClients - mNrReady; - synceventset result1 = !(nrNotReady & synccountmask1) ? syncevent1 : 0; - synceventset result2 = !(nrNotReady & synccountmask2) ? syncevent2 : 0; - synceventset result3 = !(nrNotReady & synccountmask3) ? syncevent3 : 0; - synceventset result4 = !(nrNotReady & synccountmask4) ? syncevent4 : 0; - result1 |= result2; - result3 |= result4; - return result1 | result3; -} - -synceventset AISyncServer::events_with_at_least_one_client_ready(void) const -{ - synceventset result1 = (mNrReady & synccountmask1) ? syncevent1 : 0; - synceventset result2 = (mNrReady & synccountmask2) ? syncevent2 : 0; - synceventset result3 = (mNrReady & synccountmask3) ? syncevent3 : 0; - synceventset result4 = (mNrReady & synccountmask4) ? syncevent4 : 0; - result1 |= result2; - result3 |= result4; - return result1 | result3; -} - -#ifdef CWDEBUG -struct SyncEventSet { - synceventset mBits; - SyncEventSet(synceventset bits) : mBits(bits) { } -}; - -std::ostream& operator<<(std::ostream& os, SyncEventSet const& ses) -{ - os << ((ses.mBits & syncevent4) ? '1' : '0'); - os << ((ses.mBits & syncevent3) ? '1' : '0'); - os << ((ses.mBits & syncevent2) ? '1' : '0'); - os << ((ses.mBits & syncevent1) ? '1' : '0'); - return os; -} -#endif - -bool AISyncServer::ready(synceventset events, synceventset yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")"); -#endif - - synceventset added_events = events & yesno; - synceventset removed_events = events & ~yesno; - // May not add events that are already ready. - llassert(!(client->mReadyEvents & added_events)); - // Cannot remove events that weren't ready. - llassert((client->mReadyEvents & removed_events) == removed_events); - // Were all clients ready for event 1? - bool ready_before = !((mNrClients - mNrReady) & synccountmask1); - // Update mNrReady counters. - mNrReady += added_events; - mNrReady -= removed_events; - // Test for under and overflow, this limits the maximum number of clients to 127 instead of 255, but well :p. - llassert((mNrReady & syncoverflowbits) == 0); - // Are all clients ready for event 1? - bool ready_after = !((mNrClients - mNrReady) & synccountmask1); - if (ready_before && !ready_after) - { - trigger_not_ready(); - } -#ifdef SHOW_ASSERT - // Update debug administration. - client->mReadyEvents ^= events; -#ifdef DEBUG_SYNCOUTPUT - Dout(dc::notice, "Client " << client << " now has ready: " << SyncEventSet(client->mReadyEvents)); -#endif -#endif - if (!ready_before && ready_after) - { - trigger_ready(); - } -} - -void AISyncServer::trigger_ready(void) -{ - for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) - { - llassert(((*client_iter)->mReadyEvents & syncevent1)); - (*client_iter)->event1_ready(); - } -} - -void AISyncServer::trigger_not_ready(void) -{ - for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) - { - llassert(((*client_iter)->mReadyEvents & syncevent1)); - (*client_iter)->event1_not_ready(); - } -} - -void intrusive_ptr_add_ref(AISyncServer* server) -{ - server->mNrClients += syncrefcountunit; - llassert((server->mNrClients & syncoverflowbits) == 0); -} - -void intrusive_ptr_release(AISyncServer* server) -{ - llassert(server->mNrClients >= syncrefcountunit); - server->mNrClients -= syncrefcountunit; - // If there are no more pointers pointing to this server, then obviously it can't have any registered clients. - llassert(server->mNrClients >= syncrefcountunit || server->mNrClients == 0); - if (server->mNrClients == 0) - { - delete server; - } -} - -//static -sync_key_hash_type const AISyncKey::sNoRequirementHash = 0x91b42f98a9fef15cULL; - - -//============================================================================= -// SYNC_TESTSUITE -//============================================================================= - -#ifdef SYNC_TESTSUITE -#include -#include -#include -#include - -//static -U32 LLFrameTimer::sFrameCount; - -double innerloop_count = 0; - -double LLFrameTimer::getCurrentTime() -{ - return innerloop_count * 0.001; -} - -class TestsuiteServerBase : public AISyncServer -{ - public: - TestsuiteServerBase(AISyncKey const& sync_key) : AISyncServer(sync_key) { } - client_list_type& clients(void) { return mClients; } -}; - -class TestsuiteServer1 : public TestsuiteServerBase -{ - public: - TestsuiteServer1(AISyncKey const& sync_key) : TestsuiteServerBase(sync_key) { } -}; - -// Specialitations that link TestsuiteServer1 to syncgroup_test1. -// -//static -template<> -void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) -{ - AISyncServerCache::create_server(server, sync_key); -} -//static -template<> -void AISyncServer::dispose_server(boost::intrusive_ptr& server) -{ - AISyncServerCache::dispose_server(server); -} - -class TestsuiteServer2 : public TestsuiteServerBase -{ - public: - TestsuiteServer2(AISyncKey const& sync_key) : TestsuiteServerBase(sync_key) { } -}; - -// Specialitations that link TestsuiteServer2 to syncgroup_test2. -// -//static -template<> -void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) -{ - AISyncServerCache::create_server(server, sync_key); -} -//static -template<> -void AISyncServer::dispose_server(boost::intrusive_ptr& server) -{ - AISyncServerCache::dispose_server(server); -} - -template -class TestsuiteClient : public AISyncClient -{ - private: - int mIndex; - bool mRequestedRegistered; - synceventset mRequestedReady; - bool mActualReady1; - - public: - TestsuiteClient() : mIndex(-1), mRequestedRegistered(false), mRequestedReady(0), mActualReady1(false) { } - ~TestsuiteClient() { this->ready(mRequestedReady, (synceventset)0); } - - void setIndex(int index) { mIndex = index; } - - protected: - /*virtual*/ void event1_ready(void) - { - llassert(!mActualReady1); - mActualReady1 = true; - } - - /*virtual*/ void event1_not_ready(void) - { - llassert(mActualReady1); - mActualReady1 = false; - } - - /*virtual*/ sync_key_hash_type sync_key_hash(void) const - { - // Sync odd clients with eachother, and even clients with eachother. - return 0xb3c919ff + (mIndex & 1); - } - - // This is called when the server expired and we're the only client on it. - /*virtual*/ void deregistered(void) - { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "TestsuiteClient<" << syncgroup << ">::deregistered(), with this = " << this); -#endif - mRequestedRegistered = false; - mRequestedReady = 0; - mActualReady1 = false; - this->mReadyEvents = 0; - } - - private: - bool is_registered(void) const { return !!this->mSyncServer; } - - public: - void change_state(unsigned long r); - bool getRequestedRegistered(void) const { return mRequestedRegistered; } - synceventset getRequestedReady(void) const { return mRequestedReady; } -}; - -TestsuiteClient* client1p; -TestsuiteClient* client2p; - -int const number_of_clients_per_syncgroup = 8; - -template -void TestsuiteClient::change_state(unsigned long r) -{ - bool change_registered = r & 1; - r >>= 1; - synceventset toggle_events = ((r & 1) ? syncevent1 : 0) | ((r & 2) ? syncevent2 : 0) | ((r & 4) ? syncevent3 : 0) | ((r & 8) ? syncevent4 : 0); - r >>= 4; - if (change_registered) - { - if (mRequestedRegistered && !mRequestedReady) - { - mRequestedRegistered = false; - this->unregister_client(); - } - } - else if (toggle_events) - { - mRequestedReady ^= toggle_events; - mRequestedRegistered = true; - this->ready(toggle_events, mRequestedReady & toggle_events); - } - llassert(mRequestedRegistered == is_registered()); - TestsuiteServerBase* server = this->server(); - llassert(!mRequestedRegistered || server->number_of_clients() == server->clients().size()); - if (mRequestedRegistered) - { - synceventset all_ready = syncevents; - synceventset any_ready = 0; - int nr = 0; - for (int cl = 0; cl < number_of_clients_per_syncgroup; ++cl) - { - if (syncgroup == syncgroup_test1) - { - if (client1p[cl].server() != server) - { - continue; - } - if (client1p[cl].getRequestedRegistered()) - { - ++nr; - all_ready &= client1p[cl].getRequestedReady(); - any_ready |= client1p[cl].getRequestedReady(); - } - } - else - { - if (client2p[cl].server() != server) - { - continue; - } - if (client2p[cl].getRequestedRegistered()) - { - ++nr; - all_ready &= client2p[cl].getRequestedReady(); - any_ready |= client2p[cl].getRequestedReady(); - } - } - } - llassert(nr == server->number_of_clients()); - llassert(!!(all_ready & syncevent1) == mActualReady1); - llassert(this->server()->events_with_all_clients_ready() == all_ready); - llassert(this->server()->events_with_at_least_one_client_ready() == any_ready); - llassert(nr == 0 || (any_ready & all_ready) == all_ready); - } - llassert(mRequestedReady == this->mReadyEvents); -} - -int main() -{ - Debug(libcw_do.on()); - Debug(dc::notice.on()); - Debug(libcw_do.set_ostream(&std::cout)); - Debug(list_channels_on(libcw_do)); - - unsigned short seed16v[3] = { 0x1234, 0xfedc, 0x7091 }; - - for (int k = 0;; ++k) - { - std::cout << "Loop: " << k << "; SEED: " << std::hex << seed16v[0] << ", " << seed16v[1] << ", " << seed16v[2] << std::dec << std::endl; - ++LLFrameTimer::sFrameCount; - - seed48(seed16v); - seed16v[0] = lrand48() & 0xffff; - seed16v[1] = lrand48() & 0xffff; - seed16v[2] = lrand48() & 0xffff; - - TestsuiteClient client1[number_of_clients_per_syncgroup]; - TestsuiteClient client2[number_of_clients_per_syncgroup]; - client1p = client1; - client2p = client2; - - for (int i = 0; i < number_of_clients_per_syncgroup; ++i) - { - client1[i].setIndex(i); - client2[i].setIndex(i); - } - - for (int j = 0; j < 1000000; ++j) - { - innerloop_count += 1; - -#ifdef DEBUG_SYNCOUTPUT - Dout(dc::notice, "Innerloop: " << j); -#endif - unsigned long r = lrand48(); - syncgroups grp = (r & 1) ? syncgroup_test1 : syncgroup_test2; - r >>= 1; - int cl = (r & 255) % number_of_clients_per_syncgroup; - r >>= 8; - switch (grp) - { - case syncgroup_test1: - client1[cl].change_state(r); - break; - case syncgroup_test2: - client2[cl].change_state(r); - break; - } - } - } -} - -#endif diff --git a/indra/llcommon/aisyncclient.h b/indra/llcommon/aisyncclient.h deleted file mode 100644 index b6fcbd103..000000000 --- a/indra/llcommon/aisyncclient.h +++ /dev/null @@ -1,417 +0,0 @@ -/** - * @file aisyncclient.h - * @brief Declaration of AISyncClient. - * - * Copyright (c) 2013, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution. - * - * CHANGELOG - * and additional copyright holders. - * - * 05/12/2013 - * Initial version, written by Aleric Inglewood @ SL - */ - -#ifndef AI_SYNC_CLIENT_H -#define AI_SYNC_CLIENT_H - -#ifdef SYNC_TESTSUITE -/* - * To compile the testsuite, run: - * - * cd indra/llcommon - * g++ -O3 -DCWDEBUG -DSYNC_TESTSUITE -I. -I../cwdebug aisyncclient.cpp -lcwd - */ - -#include -#include -typedef uint32_t U32; -typedef uint64_t U64; -#define LL_COMMON_API -#define SHOW_ASSERT -#define ASSERT_ONLY_COMMA(...) , __VA_ARGS__ -#define llassert assert - -struct LLFrameTimer -{ - double mStartTime; - double mExpiry; - static double getCurrentTime(void); - static U32 sFrameCount; - static U32 getFrameCount() { return sFrameCount; } - void reset(double expiration) { mStartTime = getCurrentTime(); mExpiry = mStartTime + expiration; } - bool hasExpired(void) const { return getCurrentTime() > mExpiry; } -}; - -#else // !SYNC_TESTSUITE -#include "llpreprocessor.h" -#include "stdtypes.h" -#include "llerror.h" -#include "llframetimer.h" -#endif -#include -#include - -class AISyncServer; - -/* - * AISyncClient - * - * This class allows to synchronize events between multiple client objects, - * derived from AISyncClient, where AISYNCSERVER must be - * derived from AISyncServer. - * - * Client objects call ready(mask) (or ready(event, bool), where each bit can be - * set or unset, which will result in alternating calls to event1_ready(mask) and - * event1_not_ready(mask) for all registered clients at the same time depending on - * when all clients are ready for syncevent1 or not (the other events can be - * polled, see below). - * - * For example, let a and b be two clients: - * - * { - * AISyncClient a; - * { - * AISyncClient b; - * - * a.ready(syncevent1, true); // Calls a.event1_ready(). - * a.ready(syncevent1, false); // Calls a.event1_not_ready(). - * b.ready(syncevent1, true); // Nothing happens. - * b.ready(syncevent1, false); // Nothing happens. - * b.ready(syncevent1, true); // Nothing happens. - * a.ready(syncevent1, true); // Calls a.event1_ready() and b.event1_ready(2). - * b.ready(syncevent1, false); // Calls a.event1_not_ready() and b.event1_not_ready(). - * } // Calls a.event1_ready() because b was destructed and a is still ready. - * a.ready(syncevent1, false); // Calls a.event1_not_ready(). Must set not-ready before destructing. - * } - * - * Clients can access the server by calling server() and poll it: - * - * AISyncServer* server = server(); - * - * if (server) - * { - * int n = server->number_of_clients(); - * synceventset set1 = server->events_with_all_clients_ready(); - * synceventset set2 = server->events_with_zero_clients_ready(); - * synceventset set3 = server->events_with_at_least_one_client_ready(); - * if (n == 0) - * { - * llassert(set1 == syncevents); // We define a server with no clients to have all clients ready. - * llassert(set2 == syncevents); // If there are no clients then every event has zero ready clients. - * llassert(set3 == 0); // If there are no clients then obviously set3 is the empty set. - * } - * else - * { - * llassert((set3 & set1) == set1); // set1 is a subset of set3. - * } - * llassert((set3 & (set1 & ~set2)) == (set1 & ~set2)); - * // The set of events that have all clients ready, but not zero clients, - * // is a subset of the set of events with at least one client ready. - * llassert((set2 ^ set3) == syncevents); // Each event belongs to either set2 or set3. - * } - * - * Clients can also unregister themselves without destruction, by calling unregister(); - * This might cause a call to event1_ready() for the remaining clients, if all of them - * are ready for syncevent1. - * - * Clients must be set not-ready for all events before unregistering them. - * - * Finally, it is possible to cause a server-side unregister for all clients, - * by calling server->dissolve(). This will cause an immediate call to event1_ready() - * for all clients that are ready, unless all were already ready. - */ - -enum syncgroups -{ -#ifdef SYNC_TESTSUITE - syncgroup_test1, - syncgroup_test2, -#else - syncgroup_motions, // Syncgroup used for animations. -#endif - syncgroup_size -}; - -typedef U32 sync_canonical_type; -typedef U64 sync_key_hash_type; - -int const sync_canonical_type_bits = sizeof(sync_canonical_type) * 8; -int const sync_number_of_events = 4; -int const sync_bits_per_event_counter = sync_canonical_type_bits / (sync_number_of_events + 1); - -// Each type exists of 4 event bytes. -typedef sync_canonical_type syncevent; // A single event: the least significant bit in one of the bytes. -typedef sync_canonical_type synceventset; // Zero or more events: the least significant bit in zero or more of the bytes. -typedef sync_canonical_type synccount; // A count [0, 255] in each of the bytes. -typedef sync_canonical_type synccountmask; // A single count mask: all eight bits set in one of the bytes. -typedef sync_canonical_type synccountmaskset; // Zero or more count masks: all eight bits set in zero or more of the bytes. - -syncevent const syncevent1 = 1; -syncevent const syncevent2 = syncevent1 << sync_bits_per_event_counter; -syncevent const syncevent3 = syncevent2 << sync_bits_per_event_counter; -syncevent const syncevent4 = syncevent3 << sync_bits_per_event_counter; -sync_canonical_type const syncrefcountunit = syncevent4 << sync_bits_per_event_counter; - -synccountmask const synccountmask1 = syncevent2 - 1; -synccountmask const synccountmask2 = synccountmask1 << sync_bits_per_event_counter; -synccountmask const synccountmask3 = synccountmask2 << sync_bits_per_event_counter; -synccountmask const synccountmask4 = synccountmask3 << sync_bits_per_event_counter; -synccountmask const syncrefcountmask = ((synccountmask)-1) & ~(syncrefcountunit - 1); - -synceventset const syncevents = syncevent1 | syncevent2 | syncevent3 | syncevent4; -synccountmask const syncoverflowbits = (syncevents << (sync_bits_per_event_counter - 1)) | ~(~(sync_canonical_type)0 >> 1); - -// Convert an event set into its corresponding count mask set. -inline synccountmaskset synceventset2countmaskset(synceventset events) -{ - synccountmaskset tmp1 = events << sync_bits_per_event_counter; - return (tmp1 & ~events) - (events & ~tmp1); -} - -// Convert a count mask set into an event set. -inline synceventset synccountmask2eventset(synccountmask mask) -{ - return mask & syncevents; -} - -// Interface class used to determined if a client and a server fit together. -class LL_COMMON_API AISyncKey : private LLFrameTimer -{ - private: - sync_key_hash_type mHash; - U32 mFrameCount; - - public: - // A static hash used for clients with no specific requirement. - static sync_key_hash_type const sNoRequirementHash; - - // A sync key object that returns the sNoRequirementHash. - static AISyncKey const sNoRequirement; - - public: - AISyncKey(sync_key_hash_type hash) : mHash(hash) { mFrameCount = getFrameCount(); reset(0.1); } - virtual ~AISyncKey() { } - - // Derived class should return a hash that is the same for clients which - // should be synchronized (assuming they meet the time slot requirement as well). - // The hash does not need to depend on on clock or frame number thus. - virtual sync_key_hash_type hash(void) const { return sNoRequirementHash; } - - // Return true if this key expired. - bool expired(void) const { return getFrameCount() > mFrameCount + 1 || hasExpired(); } - - // Return true if both keys are close enough to warrant synchronization. - bool matches(AISyncKey const& key) const { return key.mHash == mHash; } -}; - -class LL_COMMON_API AISyncClient_ServerPtr -{ - protected: - friend class AISyncServer; - boost::intrusive_ptr mSyncServer; - virtual ~AISyncClient_ServerPtr() { } - - // All-ready event. - virtual void event1_ready(void) = 0; // Called when, or after ready(), with the syncevent1 bit set, is called. Never called after a call to ready() with that bit unset. - // Not all-ready event. - virtual void event1_not_ready(void) = 0; // A call to event1_not_ready() follows (at most once) the call to event1_ready(), at some unspecified moment. - // Only client. Client was forcefully deregistered from expired server because it was the only client. - virtual void deregistered(void) - { -#ifdef SHOW_ASSERT - mReadyEvents = 0; -#endif - } - - // Derived classes must return a hash that determines whether or not clients can be synchronized (should be synchronized when registered at the same time). - virtual sync_key_hash_type sync_key_hash(void) const = 0; - -#ifdef SHOW_ASSERT - AISyncClient_ServerPtr(void) : mReadyEvents(0) { } - - protected: - synceventset mReadyEvents; // Used by AISyncServer for debugging only. -#endif -}; - -// AISyncServer -// -// This class represents a single group of AISyncClient's (a list of pointers to -// their AISyncClient_ServerPtr base class) that need to synchronize events. -// -class LL_COMMON_API AISyncServer -{ - public: - typedef std::list client_list_type; - - protected: - synccount mNrClients; // Four times (once in each byte) the number of registered clients (the size of mClients). - synccount mNrReady; // The number of clients that are ready (four counts, one for each syncevent). - client_list_type mClients; // The registered clients. - AISyncKey mSyncKey; // The characteristic of clients belonging to this synchronization server. - - public: - AISyncServer(AISyncKey const& sync_key) : mNrClients(0), mNrReady(0), mSyncKey(sync_key) { } - virtual ~AISyncServer() { } - - // Default creation of a server for 'syncgroup'. - template - static void create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key); - // Default removal of a server for 'syncgroup'. - template - static void dispose_server(boost::intrusive_ptr& server); - - // Called from AISyncServerCache. - void setSyncKey(AISyncKey const& sync_key) { mSyncKey = sync_key; } - - // Register client with a server from group syncgroup. - template - static void register_client(AISyncClient_ServerPtr* client); - // Unregister the client (syncgroup must be the same as what was passed to register it). - void unregister_client(AISyncClient_ServerPtr* client, syncgroups syncgroup); - - // Set readiness of all events at once. - bool ready(synceventset events, synceventset yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)); - // One event became ready or not ready. - bool ready(syncevent event, bool yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)) { return ready(event, yesno ? event : 0/*,*/ ASSERT_ONLY_COMMA(client)); } - - // Returns bitwise OR of syncevent1 through syncevent4 for - // events for which all clients are ready. - synceventset events_with_all_clients_ready(void) const; - // events for which at least one client is ready. - synceventset events_with_at_least_one_client_ready(void) const; - // events for which zero clients are ready. - synceventset events_with_zero_clients_ready(void) const { return syncevents ^ events_with_at_least_one_client_ready(); } - - // Return the number registered clients. - int number_of_clients(void) const { return (int)(mNrClients & synccountmask1); } - int get_refcount(void) const { return (int)(mNrClients >> (sync_number_of_events * sync_bits_per_event_counter)); } - - private: - void trigger_ready(void); // Calls event1_ready() for all clients. - void trigger_not_ready(void); // Calls event1_not_ready for all clients. - - void unregister_last_client(void); - - friend void intrusive_ptr_add_ref(AISyncServer* server); - friend void intrusive_ptr_release(AISyncServer* server); -}; - -template -class LL_COMMON_API AISyncClient_SyncGroup : public AISyncClient_ServerPtr -{ - public: - // Call 'ready' when you are ready (or not) to get a call to start(). - // Returns true if that call was made (immediately), otherwise it may happen later. - - bool ready(synceventset events, synceventset yesno) // Set readiness of all events at once. - { - if (!mSyncServer) - { - AISyncServer::register_client(this); - } - return mSyncServer->ready(events, yesno/*,*/ ASSERT_ONLY_COMMA(this)); - } - bool ready(syncevent event, bool yesno = true) // One event became ready or not ready. - { - if (!mSyncServer) - { - AISyncServer::register_client(this); - } - return mSyncServer->ready(event, yesno/*,*/ ASSERT_ONLY_COMMA(this)); - } - - void unregister_client(void) - { - if (mSyncServer) - { - mSyncServer->unregister_client(this, syncgroup); - } - } - - protected: - virtual ~AISyncClient_SyncGroup() { unregister_client(); } -}; - -template -class LL_COMMON_API AISyncClient : public AISyncClient_SyncGroup -{ - public: - AISYNCSERVER* server(void) { return static_cast(this->mSyncServer.get()); } - AISYNCSERVER const* server(void) const { return static_cast(this->mSyncServer.get()); } - - // By default return a general sync key that will cause synchronizing solely based on time. - /*virtual*/ AISyncKey const& getSyncKey(void) const { return AISyncKey::sNoRequirement; } -}; - -template -class AISyncServerCache -{ - private: - // The server cache exists of a few pointers to unused server instances. - // The cache is empty when sSize == 0 and full when sSize == 16; - static int const cache_size = 16; - static boost::intrusive_ptr sServerCache[cache_size]; - static int sSize; - - public: - static void create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) - { - if (sSize == 0) // Cache empty? - { - server.reset(new AISYNCSERVER(sync_key)); - } - else - { - sServerCache[--sSize].swap(server); - server->setSyncKey(sync_key); - } - } - - static void dispose_server(boost::intrusive_ptr& server) - { - if (sSize < cache_size) - { - sServerCache[sSize++].swap(server); - } - server.reset(); - } -}; - -template -boost::intrusive_ptr AISyncServerCache::sServerCache[cache_size]; - -template -int AISyncServerCache::sSize; - -template -inline void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) -{ - AISyncServerCache::create_server(server, sync_key); -} - -template -inline void AISyncServer::dispose_server(boost::intrusive_ptr& server) -{ - AISyncServerCache::dispose_server(server); -} - -#endif // AI_SYNC_CLIENT_H - From 66aaa9cf80561c4d22edb65cc60849879965a974 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 21 Dec 2013 18:46:19 +0100 Subject: [PATCH 09/15] Add LLMotionController* LLMotion::mMotionController This is needed for synchronization: motions will have to start (with a specific start time offset) from a point in the code where only the motion object is available. I added/used LLMotionController& in a previous commit, this was now changed into a pointer. There is (obviously) not much reason for that, but also no disadvantage: the diff relative to LLs code doesn't get larger since the references where already added, too. Conflicts: indra/llcharacter/llkeyframemotion.cpp indra/llcharacter/llkeyframemotion.h --- indra/llcharacter/lleditingmotion.cpp | 2 +- indra/llcharacter/lleditingmotion.h | 4 ++-- indra/llcharacter/llhandmotion.cpp | 2 +- indra/llcharacter/llhandmotion.h | 4 ++-- indra/llcharacter/llheadrotmotion.cpp | 4 ++-- indra/llcharacter/llheadrotmotion.h | 8 ++++---- indra/llcharacter/llkeyframefallmotion.cpp | 2 +- indra/llcharacter/llkeyframefallmotion.h | 4 ++-- indra/llcharacter/llkeyframemotion.cpp | 8 ++++---- indra/llcharacter/llkeyframemotion.h | 4 ++-- indra/llcharacter/llkeyframestandmotion.cpp | 2 +- indra/llcharacter/llkeyframestandmotion.h | 4 ++-- indra/llcharacter/llkeyframewalkmotion.cpp | 8 ++++---- indra/llcharacter/llkeyframewalkmotion.h | 12 ++++++------ indra/llcharacter/llmotion.cpp | 7 ++++--- indra/llcharacter/llmotion.h | 16 +++++++++------- indra/llcharacter/llmotioncontroller.cpp | 4 ++-- indra/llcharacter/llmotioncontroller.h | 4 ++-- indra/llcharacter/lltargetingmotion.cpp | 2 +- indra/llcharacter/lltargetingmotion.h | 4 ++-- indra/newview/llemote.cpp | 2 +- indra/newview/llemote.h | 4 ++-- indra/newview/llphysicsmotion.cpp | 2 +- indra/newview/llphysicsmotion.h | 4 ++-- indra/newview/llvoavatar.cpp | 12 ++++++------ 25 files changed, 66 insertions(+), 63 deletions(-) diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp index 26fca2260..d07f37779 100644 --- a/indra/llcharacter/lleditingmotion.cpp +++ b/indra/llcharacter/lleditingmotion.cpp @@ -49,7 +49,7 @@ S32 LLEditingMotion::sHandPosePriority = 3; // LLEditingMotion() // Class Constructor //----------------------------------------------------------------------------- -LLEditingMotion::LLEditingMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EDITING) +LLEditingMotion::LLEditingMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EDITING) { mCharacter = NULL; diff --git a/indra/llcharacter/lleditingmotion.h b/indra/llcharacter/lleditingmotion.h index f880a94b7..bc95ff682 100644 --- a/indra/llcharacter/lleditingmotion.h +++ b/indra/llcharacter/lleditingmotion.h @@ -53,7 +53,7 @@ class LLEditingMotion : { public: // Constructor - LLEditingMotion(LLUUID const& id, LLMotionController& controller); + LLEditingMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLEditingMotion(); @@ -65,7 +65,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLEditingMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLEditingMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llhandmotion.cpp b/indra/llcharacter/llhandmotion.cpp index c479f0f5f..be6dbb050 100644 --- a/indra/llcharacter/llhandmotion.cpp +++ b/indra/llcharacter/llhandmotion.cpp @@ -61,7 +61,7 @@ const F32 HAND_MORPH_BLEND_TIME = 0.2f; // LLHandMotion() // Class Constructor //----------------------------------------------------------------------------- -LLHandMotion::LLHandMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_HAND_MOTION) +LLHandMotion::LLHandMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_HAND_MOTION) { mCharacter = NULL; mLastTime = 0.f; diff --git a/indra/llcharacter/llhandmotion.h b/indra/llcharacter/llhandmotion.h index 582c6ee2f..012c3c6be 100644 --- a/indra/llcharacter/llhandmotion.h +++ b/indra/llcharacter/llhandmotion.h @@ -68,7 +68,7 @@ public: } eHandPose; // Constructor - LLHandMotion(LLUUID const& id, LLMotionController& controller); + LLHandMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLHandMotion(); @@ -80,7 +80,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLHandMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLHandMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp index 7322c996a..79912f9d5 100644 --- a/indra/llcharacter/llheadrotmotion.cpp +++ b/indra/llcharacter/llheadrotmotion.cpp @@ -76,7 +76,7 @@ const F32 EYE_BLINK_TIME_DELTA = 0.005f; // time between one eye starting a blin // LLHeadRotMotion() // Class Constructor //----------------------------------------------------------------------------- -LLHeadRotMotion::LLHeadRotMotion(LLUUID const& id, LLMotionController& controller) : +LLHeadRotMotion::LLHeadRotMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_HEAD_ROT), mCharacter(NULL), mTorsoJoint(NULL), @@ -260,7 +260,7 @@ BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask) // LLEyeMotion() // Class Constructor //----------------------------------------------------------------------------- -LLEyeMotion::LLEyeMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EYE) +LLEyeMotion::LLEyeMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_EYE) { mCharacter = NULL; mEyeJitterTime = 0.f; diff --git a/indra/llcharacter/llheadrotmotion.h b/indra/llcharacter/llheadrotmotion.h index 5342d422f..5fa4610bf 100644 --- a/indra/llcharacter/llheadrotmotion.h +++ b/indra/llcharacter/llheadrotmotion.h @@ -50,7 +50,7 @@ class LLHeadRotMotion : { public: // Constructor - LLHeadRotMotion(LLUUID const& id, LLMotionController& controller); + LLHeadRotMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLHeadRotMotion(); @@ -62,7 +62,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLHeadRotMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLHeadRotMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -125,7 +125,7 @@ class LLEyeMotion : { public: // Constructor - LLEyeMotion(LLUUID const& id, LLMotionController& controller); + LLEyeMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLEyeMotion(); @@ -137,7 +137,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLEyeMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLEyeMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframefallmotion.cpp b/indra/llcharacter/llkeyframefallmotion.cpp index 15ad1b9e9..336d79e65 100644 --- a/indra/llcharacter/llkeyframefallmotion.cpp +++ b/indra/llcharacter/llkeyframefallmotion.cpp @@ -49,7 +49,7 @@ // LLKeyframeFallMotion() // Class Constructor //----------------------------------------------------------------------------- -LLKeyframeFallMotion::LLKeyframeFallMotion(const LLUUID &id) : LLKeyframeMotion(id) +LLKeyframeFallMotion::LLKeyframeFallMotion(LLUUID const& id, LLMotionController* controller) : LLKeyframeMotion(id, controller) { mVelocityZ = 0.f; mCharacter = NULL; diff --git a/indra/llcharacter/llkeyframefallmotion.h b/indra/llcharacter/llkeyframefallmotion.h index c36c72798..0d2553ce3 100644 --- a/indra/llcharacter/llkeyframefallmotion.h +++ b/indra/llcharacter/llkeyframefallmotion.h @@ -47,7 +47,7 @@ class LLKeyframeFallMotion : { public: // Constructor - LLKeyframeFallMotion(const LLUUID &id); + LLKeyframeFallMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLKeyframeFallMotion(); @@ -59,7 +59,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLKeyframeFallMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLKeyframeFallMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index c6589ddf4..aa6e05708 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -444,8 +444,8 @@ void LLKeyframeMotion::JointMotion::update(LLJointState* joint_state, F32 time, // LLKeyframeMotion() // Class Constructor //----------------------------------------------------------------------------- -LLKeyframeMotion::LLKeyframeMotion(const LLUUID &id) - : LLMotion(id), +LLKeyframeMotion::LLKeyframeMotion(const LLUUID &id, LLMotionController* controller) + : LLMotion(id, controller), mPelvisp(NULL), mLastSkeletonSerialNum(0), mLastUpdateTime(0.f), @@ -468,9 +468,9 @@ LLKeyframeMotion::~LLKeyframeMotion() //----------------------------------------------------------------------------- // create() //----------------------------------------------------------------------------- -LLMotion* LLKeyframeMotion::create(LLUUID const& id, LLMotionController&) +LLMotion* LLKeyframeMotion::create(LLUUID const& id, LLMotionController* controller) { - return new LLKeyframeMotion(id); + return new LLKeyframeMotion(id, controller); } //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 14fa4916c..952ae0ca7 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -177,7 +177,7 @@ class LLKeyframeMotion : friend class LLKeyframeDataCache; public: // Constructor - LLKeyframeMotion(const LLUUID &id); + LLKeyframeMotion(const LLUUID &id, LLMotionController* controller); // Destructor virtual ~LLKeyframeMotion(); @@ -194,7 +194,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller); + static LLMotion* create(LLUUID const& id, LLMotionController* controller); public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframestandmotion.cpp b/indra/llcharacter/llkeyframestandmotion.cpp index 1ae0ddeea..bccc714f1 100644 --- a/indra/llcharacter/llkeyframestandmotion.cpp +++ b/indra/llcharacter/llkeyframestandmotion.cpp @@ -50,7 +50,7 @@ const F32 POSITION_THRESHOLD = 0.1f; // LLKeyframeStandMotion() // Class Constructor //----------------------------------------------------------------------------- -LLKeyframeStandMotion::LLKeyframeStandMotion(const LLUUID &id) : LLKeyframeMotion(id) +LLKeyframeStandMotion::LLKeyframeStandMotion(LLUUID const& id, LLMotionController* controller) : LLKeyframeMotion(id, controller) { mFlipFeet = FALSE; mCharacter = NULL; diff --git a/indra/llcharacter/llkeyframestandmotion.h b/indra/llcharacter/llkeyframestandmotion.h index 1db798021..346df97f0 100644 --- a/indra/llcharacter/llkeyframestandmotion.h +++ b/indra/llcharacter/llkeyframestandmotion.h @@ -48,7 +48,7 @@ class LLKeyframeStandMotion : { public: // Constructor - LLKeyframeStandMotion(const LLUUID &id); + LLKeyframeStandMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLKeyframeStandMotion(); @@ -60,7 +60,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLKeyframeStandMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLKeyframeStandMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframewalkmotion.cpp b/indra/llcharacter/llkeyframewalkmotion.cpp index 2a12edd29..21d6fce30 100644 --- a/indra/llcharacter/llkeyframewalkmotion.cpp +++ b/indra/llcharacter/llkeyframewalkmotion.cpp @@ -55,8 +55,8 @@ const F32 SPEED_ADJUST_TIME_CONSTANT = 0.1f; // time constant for speed adjustm // LLKeyframeWalkMotion() // Class Constructor //----------------------------------------------------------------------------- -LLKeyframeWalkMotion::LLKeyframeWalkMotion(const LLUUID &id) -: LLKeyframeMotion(id), +LLKeyframeWalkMotion::LLKeyframeWalkMotion(LLUUID const& id, LLMotionController* controller) +: LLKeyframeMotion(id, controller), mCharacter(NULL), mCyclePhase(0.0f), mRealTimeLast(0.0f), @@ -138,7 +138,7 @@ BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask) // LLWalkAdjustMotion() // Class Constructor //----------------------------------------------------------------------------- -LLWalkAdjustMotion::LLWalkAdjustMotion(LLUUID const& id, LLMotionController& controller) : +LLWalkAdjustMotion::LLWalkAdjustMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_WALK_ADJUST), mLastTime(0.f), mAnimSpeed(0.f), @@ -331,7 +331,7 @@ void LLWalkAdjustMotion::onDeactivate() //----------------------------------------------------------------------------- // LLFlyAdjustMotion::LLFlyAdjustMotion() //----------------------------------------------------------------------------- -LLFlyAdjustMotion::LLFlyAdjustMotion(LLUUID const& id, LLMotionController& controller) +LLFlyAdjustMotion::LLFlyAdjustMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_FLY_ADJUST), mRoll(0.f) { diff --git a/indra/llcharacter/llkeyframewalkmotion.h b/indra/llcharacter/llkeyframewalkmotion.h index caab2fef3..653c2b539 100644 --- a/indra/llcharacter/llkeyframewalkmotion.h +++ b/indra/llcharacter/llkeyframewalkmotion.h @@ -52,7 +52,7 @@ class LLKeyframeWalkMotion : friend class LLWalkAdjustMotion; public: // Constructor - LLKeyframeWalkMotion(LLUUID const& id); + LLKeyframeWalkMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLKeyframeWalkMotion(); @@ -64,7 +64,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLKeyframeWalkMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLKeyframeWalkMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -90,7 +90,7 @@ class LLWalkAdjustMotion : public AIMaskedMotion { public: // Constructor - LLWalkAdjustMotion(LLUUID const& id, LLMotionController& controller); + LLWalkAdjustMotion(LLUUID const& id, LLMotionController* controller); public: //------------------------------------------------------------------------- @@ -99,7 +99,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLWalkAdjustMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLWalkAdjustMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -140,7 +140,7 @@ class LLFlyAdjustMotion : public AIMaskedMotion { public: // Constructor - LLFlyAdjustMotion(LLUUID const& id, LLMotionController& controller); + LLFlyAdjustMotion(LLUUID const& id, LLMotionController* controller); public: //------------------------------------------------------------------------- @@ -149,7 +149,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLFlyAdjustMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLFlyAdjustMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp index 203cf2f4c..021345fb3 100644 --- a/indra/llcharacter/llmotion.cpp +++ b/indra/llcharacter/llmotion.cpp @@ -49,10 +49,11 @@ // LLMotion() // Class Constructor //----------------------------------------------------------------------------- -LLMotion::LLMotion( const LLUUID &id ) : +LLMotion::LLMotion(LLUUID const& id, LLMotionController* controller) : mStopped(TRUE), mActive(FALSE), mID(id), + mController(controller), mActivationTimestamp(0.f), mStopTimestamp(0.f), mSendStopTimestamp(F32_MAX), @@ -181,13 +182,13 @@ BOOL LLMotion::canDeprecate() BOOL AIMaskedMotion::onActivate() { - mController.activated(mMaskBit); + mController->activated(mMaskBit); return TRUE; } void AIMaskedMotion::onDeactivate() { - mController.deactivated(mMaskBit); + mController->deactivated(mMaskBit); } // End diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h index 24504980d..a67138bac 100644 --- a/indra/llcharacter/llmotion.h +++ b/indra/llcharacter/llmotion.h @@ -67,7 +67,7 @@ public: }; // Constructor - LLMotion(const LLUUID &id); + LLMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLMotion(); @@ -182,6 +182,9 @@ protected: //------------------------------------------------------------------------- std::string mName; // instance name assigned by motion controller LLUUID mID; + // + LLMotionController* mController; + // F32 mActivationTimestamp; // time when motion was activated F32 mStopTimestamp; // time when motion was told to stop @@ -200,9 +203,9 @@ protected: class LLTestMotion : public LLMotion { public: - LLTestMotion(const LLUUID &id) : LLMotion(id){} + LLTestMotion(LLUUID const& id, LLMotionController* controller) : LLMotion(id, controller){} ~LLTestMotion() {} - static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLTestMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLTestMotion(id, controller); } BOOL getLoop() { return FALSE; } F32 getDuration() { return 0.0f; } F32 getEaseInDuration() { return 0.0f; } @@ -224,9 +227,9 @@ public: class LLNullMotion : public LLMotion { public: - LLNullMotion(const LLUUID &id) : LLMotion(id) {} + LLNullMotion(LLUUID const& id, LLMotionController* controller) : LLMotion(id, controller) {} ~LLNullMotion() {} - static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLNullMotion(id); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLNullMotion(id, controller); } // motions must specify whether or not they loop /*virtual*/ BOOL getLoop() { return TRUE; } @@ -292,11 +295,10 @@ U32 const ANIM_AGENT_WALK_ADJUST = 0x400; class AIMaskedMotion : public LLMotion { private: - LLMotionController& mController; U32 mMaskBit; public: - AIMaskedMotion(LLUUID const& id, LLMotionController& controller, U32 mask_bit) : LLMotion(id), mController(controller), mMaskBit(mask_bit) { } + AIMaskedMotion(LLUUID const& id, LLMotionController* controller, U32 mask_bit) : LLMotion(id, controller), mMaskBit(mask_bit) { } /*virtual*/ BOOL onActivate(); /*virtual*/ void onDeactivate(); diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 4935fb3a7..099805676 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -97,7 +97,7 @@ void LLMotionRegistry::markBad( const LLUUID& id ) //----------------------------------------------------------------------------- // createMotion() //----------------------------------------------------------------------------- -LLMotion* LLMotionRegistry::createMotion(LLUUID const& id, LLMotionController& controller) +LLMotion* LLMotionRegistry::createMotion(LLUUID const& id, LLMotionController* controller) { LLMotionConstructor constructor = get_if_there(mMotionTable, id, LLMotionConstructor(NULL)); LLMotion* motion = NULL; @@ -359,7 +359,7 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id ) if (!motion) { // look up constructor and create it - motion = sRegistry.createMotion(id, *this); + motion = sRegistry.createMotion(id, this); if (!motion) { return NULL; diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 9b6c71ef1..04b15ff89 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -56,7 +56,7 @@ class LLMotionController; //----------------------------------------------------------------------------- // LLMotionRegistry //----------------------------------------------------------------------------- -typedef LLMotion* (*LLMotionConstructor)(LLUUID const& id, LLMotionController&); +typedef LLMotion* (*LLMotionConstructor)(LLUUID const& id, LLMotionController*); class LLMotionRegistry { @@ -73,7 +73,7 @@ public: // creates a new instance of a named motion // returns NULL motion is not registered - LLMotion* createMotion(LLUUID const& id, LLMotionController& controller); + LLMotion* createMotion(LLUUID const& id, LLMotionController* controller); // initialization of motion failed, don't try to create this motion again void markBad( const LLUUID& id ); diff --git a/indra/llcharacter/lltargetingmotion.cpp b/indra/llcharacter/lltargetingmotion.cpp index 423eca363..05b0fdfe8 100644 --- a/indra/llcharacter/lltargetingmotion.cpp +++ b/indra/llcharacter/lltargetingmotion.cpp @@ -52,7 +52,7 @@ const F32 TORSO_ROT_FRACTION = 0.5f; // LLTargetingMotion() // Class Constructor //----------------------------------------------------------------------------- -LLTargetingMotion::LLTargetingMotion(LLUUID const& id, LLMotionController& controller) : AIMaskedMotion(id, controller, ANIM_AGENT_TARGET) +LLTargetingMotion::LLTargetingMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_TARGET) { mCharacter = NULL; mName = "targeting"; diff --git a/indra/llcharacter/lltargetingmotion.h b/indra/llcharacter/lltargetingmotion.h index 8dfa32500..f7b08ee69 100644 --- a/indra/llcharacter/lltargetingmotion.h +++ b/indra/llcharacter/lltargetingmotion.h @@ -52,7 +52,7 @@ class LLTargetingMotion : { public: // Constructor - LLTargetingMotion(LLUUID const& id, LLMotionController& controller); + LLTargetingMotion(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLTargetingMotion(); @@ -64,7 +64,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLTargetingMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLTargetingMotion(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/newview/llemote.cpp b/indra/newview/llemote.cpp index c83846215..47bd57969 100644 --- a/indra/newview/llemote.cpp +++ b/indra/newview/llemote.cpp @@ -48,7 +48,7 @@ // LLEmote() // Class Constructor //----------------------------------------------------------------------------- -LLEmote::LLEmote(const LLUUID &id) : LLMotion(id) +LLEmote::LLEmote(LLUUID const& id, LLMotionController* controller) : LLMotion(id, controller) { mCharacter = NULL; diff --git a/indra/newview/llemote.h b/indra/newview/llemote.h index eb2af5b9d..173719121 100644 --- a/indra/newview/llemote.h +++ b/indra/newview/llemote.h @@ -55,7 +55,7 @@ class LLEmote : { public: // Constructor - LLEmote(const LLUUID &id); + LLEmote(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLEmote(); @@ -67,7 +67,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController&) { return new LLEmote(id); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLEmote(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp index 625199b3f..99ff385ee 100644 --- a/indra/newview/llphysicsmotion.cpp +++ b/indra/newview/llphysicsmotion.cpp @@ -314,7 +314,7 @@ void LLPhysicsMotion::getString(std::ostringstream &oss) } } -LLPhysicsMotionController::LLPhysicsMotionController(LLUUID const& id, LLMotionController& controller) : +LLPhysicsMotionController::LLPhysicsMotionController(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_PHYSICS_MOTION), mCharacter(NULL), mIsDefault(true) diff --git a/indra/newview/llphysicsmotion.h b/indra/newview/llphysicsmotion.h index 62e0ae658..2ce79b220 100644 --- a/indra/newview/llphysicsmotion.h +++ b/indra/newview/llphysicsmotion.h @@ -49,7 +49,7 @@ public: std::string getString(); // Constructor - LLPhysicsMotionController(LLUUID const& id, LLMotionController& controller); + LLPhysicsMotionController(LLUUID const& id, LLMotionController* controller); // Destructor virtual ~LLPhysicsMotionController(); @@ -61,7 +61,7 @@ public: // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLPhysicsMotionController(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLPhysicsMotionController(id, controller); } public: //------------------------------------------------------------------------- diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 943279d6b..b34291a5f 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -347,7 +347,7 @@ class LLBodyNoiseMotion : { public: // Constructor - LLBodyNoiseMotion(LLUUID const& id, LLMotionController& controller) + LLBodyNoiseMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_BODY_NOISE) { mName = "body_noise"; @@ -363,7 +363,7 @@ public: //------------------------------------------------------------------------- // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLBodyNoiseMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLBodyNoiseMotion(id, controller); } public: //------------------------------------------------------------------------- @@ -444,7 +444,7 @@ class LLBreatheMotionRot : { public: // Constructor - LLBreatheMotionRot(LLUUID const& id, LLMotionController& controller) : + LLBreatheMotionRot(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_BREATHE_ROT), mBreatheRate(1.f), mCharacter(NULL) @@ -462,7 +462,7 @@ public: //------------------------------------------------------------------------- // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLBreatheMotionRot(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLBreatheMotionRot(id, controller); } public: //------------------------------------------------------------------------- @@ -546,7 +546,7 @@ class LLPelvisFixMotion : { public: // Constructor - LLPelvisFixMotion(LLUUID const& id, LLMotionController& controller) + LLPelvisFixMotion(LLUUID const& id, LLMotionController* controller) : AIMaskedMotion(id, controller, ANIM_AGENT_PELVIS_FIX), mCharacter(NULL) { mName = "pelvis_fix"; @@ -563,7 +563,7 @@ public: //------------------------------------------------------------------------- // static constructor // all subclasses must implement such a function and register it - static LLMotion* create(LLUUID const& id, LLMotionController& controller) { return new LLPelvisFixMotion(id, controller); } + static LLMotion* create(LLUUID const& id, LLMotionController* controller) { return new LLPelvisFixMotion(id, controller); } public: //------------------------------------------------------------------------- From 1bcb6ad20d1fa1e22e22e8cb3f298d0d9c5ff77b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 21 Dec 2013 19:18:52 +0100 Subject: [PATCH 10/15] Version 2 of AISyncClient et al. A tool to synchronize objects: Objects derived from AISyncClient can signal that they are 'ready' or 'not ready' for up to 32 events (using a bitmask) at a time. Clients that are created at roughly the same time as other clients, and which return the same 'key' (a virtual function returning an AISyncKey object) will be grouped together and receive events (by means of virtual functions being called) to notify them of all clients being ready or not for one of the events (the least significant bit)). The other events can be polled. This new version does away with all the templates and explicitly remembers what events each client is ready for instead of just updating a counter of the number of clients. This was necessary because a client is removed then the server needs to know if it was ready or not when it has to be able to update those counters. This time I chose to just run over all stored clients and AND and OR the per-client-ready-masks because 1) that information is available now and 2) the lists will normally contain only one or two clients, so it's fast enough. The new version also allows for real key comparison (and derived keys) instead of just using "hash" value that is compared. --- indra/llcommon/aisyncclient.cpp | 749 ++++++++++++++++++++++++++++++++ indra/llcommon/aisyncclient.h | 296 +++++++++++++ 2 files changed, 1045 insertions(+) create mode 100644 indra/llcommon/aisyncclient.cpp create mode 100644 indra/llcommon/aisyncclient.h diff --git a/indra/llcommon/aisyncclient.cpp b/indra/llcommon/aisyncclient.cpp new file mode 100644 index 000000000..9cadb3cb1 --- /dev/null +++ b/indra/llcommon/aisyncclient.cpp @@ -0,0 +1,749 @@ +/** + * @file aisyncclient.cpp + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 13/12/2013 + * - Initial version, written by Aleric Inglewood @ SL + */ + +/* + * AISyncClient : the base class of a client object (LLMotion) that needs to be informed + * of the state of other such objects and/or to poll a server object about the state of + * other such objects, in order to be and stay synchronized with those other objects. + * In the case of an LLMotion (animation), all clients would be started and stopped at + * the same time, as long as they are part of the same synchronization group (AISyncServer). + * + * AISyncKey: object that determines what synchronization group a client belongs to. + * When a new client is created, a new AISyncKey is created too, using information from + * the client, the current frame number and the current frame time. If two AISyncKey + * compare equal, using operator==(AISyncKey const& key1, AISyncKey const& key2), + * then the clients that created them need to be synchronized. + * + * AISyncServer: object that represents a group of clients that need to be synchronized: + * it's a wrapper around a std::list with pointers to all the clients + * that need to be synchronized. It also stores the AISyncKey of the first (oldest) client + * that was added. Clients with keys that compare equal to that will be added. + * If a client is added with a synckeytype_t that is larger then it always replaces + * the existing key however. + * + * AISyncServerMap: A wrapper around std::list > + * that stores pointers to all currently existing AISyncServer objects. New entries + * are added at the end, so the oldest key is at the front. + * + * AISyncServerMap list + - - - - - - + - - - - - - + ... The AISyncServerMap is + * | | | a list with refcounted + * V V V pointers to AISyncServers. + * AISyncClient +--> AISyncServer + * | <--- list key ---> AISyncKey Each AISyncServer is a + * DerivedClient . list of normal pointers + * . AISyncClients and one + * <--- . pointer to a AISyncKey. + * Each AISyncClient is the + * base class of a DerivedClient + * and pointers back to the + * server with a refcounted + * pointer. + * + * A new client is passed to the AISyncServerMap to be stored in a new or existing AISyncServer + * object, using the key that the client produces. A boost::intrusive_ptr member + * of the client is set to point to this server. + * + * The lifetime of the server objects is determined by the intrusive_ptr objects that + * point to it: all the clients (which have an externally determined lifetime) and one + * pointer in the AISyncServerMap. However, regularly a check is done on all servers in + * the list to find expired servers: objects with keys older than two frames and older + * than 0.1 seconds; if such a server is found and it has zero or one client, then the + * client is unregistered and the pointer (and thus the server) removed from the + * AISyncServerMap. If it has two or more clients then the entry is kept until both + * clients are removed, which therefore can only be detected in intrusive_ptr_release + * which only has access to the server object. The server then is removed from the list + * by searching through it for the pointer to the server. + */ + +#include "sys.h" +#include "aisyncclient.h" +#include +#include +#include "debug.h" + +bool operator==(AISyncKey const& key1, AISyncKey const& key2) +{ + // Test if these keys match based on time. + if (std::abs((S32)(key1.mStartFrameCount - key2.mStartFrameCount)) > 1 && + std::abs(key1.mFrameTimer.getStartTime() - key2.mFrameTimer.getStartTime()) >= AISyncKey::sExpirationTime) + { + return false; + } + // Time matches, let the derived classes determine if they also match otherwise. + return key1.equals(key2); +} + +#ifdef CWDEBUG +struct SyncEventSet { + synceventset_t mBits; + SyncEventSet(synceventset_t bits) : mBits(bits) { } +}; + +std::ostream& operator<<(std::ostream& os, SyncEventSet const& ses) +{ + for (int b = sizeof(ses.mBits) * 8 - 1; b >= 0; --b) + { + int m = 1 << b; + os << ((ses.mBits & m) ? '1' : '0'); + } + return os; +} + +void print_clients(AISyncServer const* server, AISyncServer::client_list_t const& client_list) +{ + Dout(dc::notice, "Clients of server " << server << ": "); + for (AISyncServer::client_list_t::const_iterator iter = client_list.begin(); iter != client_list.end(); ++ iter) + { + llassert(iter->mClientPtr->mReadyEvents == iter->mReadyEvents); + Dout(dc::notice, "-> " << iter->mClientPtr << " : " << SyncEventSet(iter->mReadyEvents)); + } +} +#endif + +void AISyncServerMap::register_client(AISyncClient* client) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServerMap::register_client(" << client << ")"); +#endif + + // client must always be a new client that has to be stored somewhere. + llassert(client->server() == NULL); + // Obviously the client can't be ready for anything when it isn't registered yet. + llassert(!client->mReadyEvents); + + // First we need its sync key. + AISyncKey* new_key = client->createSyncKey(); + + // Find if a server with this key already exists. + AISyncServer* server = NULL; + for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end();) + { + boost::intrusive_ptr& server_ptr = *iter++; // Immediately increment iter because the call to unregister_last_client will erase it. + AISyncKey const& server_key(server_ptr->key()); + if (server_key.is_older_than(*new_key)) // This means that the server key is expired: a new key will never match. + { + if (server_ptr->never_synced()) // This means that it contains one or zero clients and will never contain more. + { + // Get rid of this server. + server_ptr->unregister_last_client(); // This will cause the server to be deleted, and erased from mServers. + } + continue; + } + if (*new_key == server_key) + { + server = server_ptr.get(); + // mServers stores new servers in strict order of the creation time of the keys, + // so once we find a server with a key that is equal, none of the remaining servers + // will have expired if they were never synced and we're done with the loop. + // Servers that synced might have been added later, but we don't unregister + // clients from those anyway because their sync partner might still show up. + break; + } + } + + if (server) + { + // A server already exists. + // Keep the oldest key, unless this new key has a synckeytype_t that is larger! + if (new_key->getkeytype() > server->key().getkeytype()) + { + server->swapkey(new_key); + } + delete new_key; + } + else + { + // Create a new server for this client. Transfers the ownership of the key allocation to the server. + server = new AISyncServer(new_key); + // Add it to mServers, before the last server that is younger then the new key. + server_list_t::iterator where = mServers.end(); // Insert the new server before 'where', +#if 0 // This is probably not necessary. + server_list_t::iterator new_where = where; + while (where != mServers.begin()) // unless there exists a server before that + { + --new_where; + if (new_key->getCreationTime() > (*new_where)->key().getCreationTime()) // and the new key is not younger then that, + { + break; + } + where = new_where; // then insert it before that element (etc). + } +#elif defined(SHOW_ASSERT) + server_list_t::iterator new_where = where; + llassert(where == mServers.begin() || new_key->getCreationTime() >= (*--new_where)->key().getCreationTime()); +#endif + // This method causes a single call to intrusive_ptr_add_ref and none to intrusive_ptr_release. + server_ptr_t server_ptr = server; + mServers.insert(where, server_ptr_t())->swap(server_ptr); + } + + // Add the client to the server. + server->add(client); +} + +#ifdef SYNC_TESTSUITE +void AISyncServer::sanity_check(void) const +{ + synceventset_t ready_events = (synceventset_t)-1; // All clients are ready. + client_list_t::const_iterator client_iter = mClients.begin(); + while (client_iter != mClients.end()) + { + ready_events &= client_iter->mReadyEvents; + ++client_iter; + } + synceventset_t pending_events = 0; // At least one client is ready. + client_iter = mClients.begin(); + while (client_iter != mClients.end()) + { + pending_events |= client_iter->mReadyEvents; + ++client_iter; + } + llassert(ready_events == mReadyEvents); + llassert(pending_events == mPendingEvents); +} +#endif + +void AISyncServer::add(AISyncClient* client) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServer::add(" << client << "), with this = " << this); + print_clients(this, getClients()); +#endif +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif + + // The client can't already be ready when it isn't even added to a server yet. + llassert(!client->mReadyEvents); + synceventset_t old_ready_events = mReadyEvents; + // A new client is not ready for anything. + mReadyEvents = 0; + // Set mSynchronized if after adding client we'll have more than 1 client (that prevents the + // server from being deleted unless all clients are actually destructed or explicitly unregistered). + if (!mSynchronized && mClients.size() > 0) + { + mSynchronized = true; + } + // Trigger the existing clients to be not-ready anymore (if we were before). + trigger(old_ready_events); + // Only then add the new client (so that it didn't get not-ready trigger). + mClients.push_back(client); + client->mServer = this; + +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif +} + +void AISyncServer::remove(AISyncClient* client) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServer::remove(" << client << "), with this = " << this); + print_clients(this, getClients()); +#endif +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif + + // A client may only be unregistered after it was marked not-ready for all events. + llassert(!client->mReadyEvents); + client_list_t::iterator client_iter = mClients.begin(); + synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready. + synceventset_t remaining_pending_events = 0; // At least one client is ready (waiting for the other clients thus). + client_list_t::iterator found_client = mClients.end(); + while (client_iter != mClients.end()) + { + if (client_iter->mClientPtr == client) + { + found_client = client_iter; + } + else + { + remaining_ready_events &= client_iter->mReadyEvents; + remaining_pending_events |= client_iter->mReadyEvents; + } + ++client_iter; + } + llassert(found_client != mClients.end()); + // This must be the same as client->mReadyEvents. + llassert(!found_client->mReadyEvents); + mClients.erase(found_client); + client->mServer.reset(); + synceventset_t old_ready_events = mReadyEvents; + mReadyEvents = remaining_ready_events; + // Since client->mReadyEvents is zero, this should be the same. + llassert(mPendingEvents == remaining_pending_events); + mPendingEvents = remaining_pending_events; + trigger(old_ready_events); + +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif +} + +void AISyncServer::unregister_last_client(void) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServer::unregister_last_client()"); + print_clients(this, getClients()); +#endif +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif + + // This function may only be called for servers with exactly one client that was never (potentially) synchronized. + llassert(!mSynchronized && mClients.size() == 1); + AISyncClient* client = mClients.begin()->mClientPtr; + mClients.clear(); + client->mServer.reset(); + llassert(mReadyEvents == client->mReadyEvents); + llassert(mPendingEvents == mReadyEvents); + // No need to update mReadyEvents/mPendingEvents because the server is going to be deleted, + // but inform the client that is no longer has a server. + client->deregistered(); + +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif +} + +synceventset_t AISyncServer::events_with_all_clients_ready(void) const +{ +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif + return mReadyEvents; +} + +synceventset_t AISyncServer::events_with_at_least_one_client_ready(void) const +{ +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif + return mPendingEvents; +} + +void AISyncServer::trigger(synceventset_t old_ready_events) +{ + // If event 1 changed, informat all clients about it. + if (((old_ready_events ^ mReadyEvents) & 1)) + { + for (client_list_t::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) + { + if ((mReadyEvents & 1)) + { + client_iter->mClientPtr->event1_ready(); + } + else + { + client_iter->mClientPtr->event1_not_ready(); + } + } + } +} + +bool AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client) +{ +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")"); + print_clients(this, getClients()); +#endif +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif + + synceventset_t added_events = events & yesno; + synceventset_t removed_events = events & ~yesno; + // May not add events that are already ready. + llassert(!(client->mReadyEvents & added_events)); + // Cannot remove events that weren't ready. + llassert((client->mReadyEvents & removed_events) == removed_events); + + // Run over all clients to find the client and calculate the current state. + synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready. + synceventset_t remaining_pending_events = 0; // At least one client is ready (waiting for the other clients thus). + client_list_t::iterator found_client = mClients.end(); + for (client_list_t::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) + { + if (client_iter->mClientPtr == client) + { + found_client = client_iter; + } + else + { + remaining_ready_events &= client_iter->mReadyEvents; + remaining_pending_events |= client_iter->mReadyEvents; + } + } + + llassert(mReadyEvents == (remaining_ready_events & found_client->mReadyEvents)); + llassert(mPendingEvents == (remaining_pending_events | found_client->mReadyEvents)); + + found_client->mReadyEvents &= ~removed_events; + found_client->mReadyEvents |= added_events; +#ifdef SHOW_ASSERT + client->mReadyEvents = found_client->mReadyEvents; +#endif + + synceventset_t old_ready_events = mReadyEvents; + + mReadyEvents = remaining_ready_events & found_client->mReadyEvents; + mPendingEvents = remaining_pending_events | found_client->mReadyEvents; + + trigger(old_ready_events); + +#ifdef SYNC_TESTSUITE + sanity_check(); +#endif +} + +void intrusive_ptr_add_ref(AISyncServer* server) +{ + server->mRefCount++; +} + +void intrusive_ptr_release(AISyncServer* server) +{ + llassert(server->mRefCount > 0); + server->mRefCount--; + if (server->mRefCount == 0) + { + delete server; + } + else if (server->mRefCount == 1) + { + // If the the last pointer to this server is in the the AISyncServerMap, then delete that too. + AISyncServerMap::instance().remove_server(server); + } +} + +void AISyncServerMap::remove_server(AISyncServer* server) +{ + for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end(); ++iter) + { + if (server == iter->get()) + { + mServers.erase(iter); // This causes server to be deleted too. + return; + } + } + // The server must be found: this function is only called from intrusive_ptr_release for servers + // with just a single server_ptr_t left, which must be the one in mServers (otherwise it wasn't + // even registered properly!) + llassert(false); +} + + +//============================================================================= +// SYNC_TESTSUITE +//============================================================================= + +#ifdef SYNC_TESTSUITE +#include +#include +#include +#include + +//static +U32 LLFrameTimer::sFrameCount; + +double innerloop_count = 0; + +double LLFrameTimer::getCurrentTime() +{ + return innerloop_count * 0.001; +} + +template +class TestsuiteKey : public AISyncKey +{ + private: + int mIndex; + + public: + TestsuiteKey(int index) : mIndex(index) { } + + int getIndex(void) const { return mIndex; } + + // Virtual functions of AISyncKey. + public: + /*virtual*/ synckeytype_t getkeytype(void) const + { + // Return a unique identifier for this class, where the low 8 bits represent the syncgroup. + return synckeytype; + } + + /*virtual*/ bool equals(AISyncKey const& key) const + { + synckeytype_t const theotherkey = (synckeytype_t)(synckeytype ^ 0x100); + switch (key.getkeytype()) + { + case synckeytype: + { + // The other key is of the same type. + TestsuiteKey const& test_key = static_cast const&>(key); + return (mIndex & 1) == (test_key.mIndex & 1); + } + case theotherkey: + { + TestsuiteKey const& test_key = static_cast const&>(key); + return (mIndex & 2) == (test_key.getIndex() & 2); + } + default: + // The keys must be in the same syncgroup. + break; + } + return false; + } +}; + +template +class TestsuiteClient : public AISyncClient +{ + // AISyncClient events. + protected: + /*virtual*/ AISyncKey* createSyncKey(void) const + { + return new TestsuiteKey(mIndex); + } + + private: + int mIndex; + bool mRequestedRegistered; + synceventset_t mRequestedReady; + bool mActualReady1; + + public: + TestsuiteClient() : mIndex(-1), mRequestedRegistered(false), mRequestedReady(0), mActualReady1(false) { } + ~TestsuiteClient() { if (is_registered()) this->ready(mRequestedReady, (synceventset_t)0); } + + void setIndex(int index) { mIndex = index; } + + protected: + /*virtual*/ void event1_ready(void) + { +#ifdef DEBUG_SYNCOUTPUT + Dout(dc::notice, "Calling TestsuiteClient<" << synckeytype << ">::event1_ready() (mIndex = " << mIndex << ") of client " << this); +#endif + llassert(!mActualReady1); + mActualReady1 = true; + } + + /*virtual*/ void event1_not_ready(void) + { +#ifdef DEBUG_SYNCOUTPUT + Dout(dc::notice, "Calling TestsuiteClient<" << synckeytype << ">::event1_not_ready() (mIndex = " << mIndex << ") of client " << this); +#endif + llassert(mActualReady1); + mActualReady1 = false; + } + + // This is called when the server expired and we're the only client on it. + /*virtual*/ void deregistered(void) + { +#ifdef DEBUG_SYNCOUTPUT + DoutEntering(dc::notice, "TestsuiteClient<" << synckeytype << ">::deregistered(), with this = " << this); +#endif + mRequestedRegistered = false; + mRequestedReady = 0; + mActualReady1 = false; + this->mReadyEvents = 0; + } + + private: + bool is_registered(void) const { return this->server(); } + + public: + void change_state(unsigned long r); + bool getRequestedRegistered(void) const { return mRequestedRegistered; } + synceventset_t getRequestedReady(void) const { return mRequestedReady; } +}; + +TestsuiteClient* client1ap; +TestsuiteClient* client1bp; +TestsuiteClient* client2ap; +TestsuiteClient* client2bp; + +int const number_of_clients_per_syncgroup = 8; + +template +void TestsuiteClient::change_state(unsigned long r) +{ + bool change_registered = r & 1; + r >>= 1; + synceventset_t toggle_events = r & 15; + r >>= 4; + if (change_registered) + { + if (mRequestedRegistered && !mRequestedReady) + { + mRequestedRegistered = false; + this->unregister_client(); + } + } + else if (toggle_events) + { + mRequestedReady ^= toggle_events; + mRequestedRegistered = true; + this->ready(toggle_events, mRequestedReady & toggle_events); + } + llassert(mRequestedRegistered == is_registered()); + AISyncServer* server = this->server(); + if (mRequestedRegistered) + { + synceventset_t all_ready = synceventset_t(-1); + synceventset_t any_ready = 0; + int nr = 0; + for (int cl = 0; cl < number_of_clients_per_syncgroup; ++cl) + { + switch ((synckeytype & 0xff)) + { + case syncgroup_test1: + { + if (client1ap[cl].server() == server) + { + if (client1ap[cl].getRequestedRegistered()) + { + ++nr; + all_ready &= client1ap[cl].getRequestedReady(); + any_ready |= client1ap[cl].getRequestedReady(); + } + } + if (client1bp[cl].server() == server) + { + if (client1bp[cl].getRequestedRegistered()) + { + ++nr; + all_ready &= client1bp[cl].getRequestedReady(); + any_ready |= client1bp[cl].getRequestedReady(); + } + } + break; + } + case syncgroup_test2: + { + if (client2ap[cl].server() == server) + { + if (client2ap[cl].getRequestedRegistered()) + { + ++nr; + all_ready &= client2ap[cl].getRequestedReady(); + any_ready |= client2ap[cl].getRequestedReady(); + } + } + if (client2bp[cl].server() == server) + { + if (client2bp[cl].getRequestedRegistered()) + { + ++nr; + all_ready &= client2bp[cl].getRequestedReady(); + any_ready |= client2bp[cl].getRequestedReady(); + } + } + break; + } + } + } + llassert(nr == server->getClients().size()); + llassert(!!(all_ready & 1) == mActualReady1); + llassert(this->server()->events_with_all_clients_ready() == all_ready); + llassert(this->server()->events_with_at_least_one_client_ready() == any_ready); + llassert(nr == 0 || (any_ready & all_ready) == all_ready); + } + llassert(mRequestedReady == this->mReadyEvents); +} + +int main() +{ + Debug(libcw_do.on()); + Debug(dc::notice.on()); + Debug(libcw_do.set_ostream(&std::cout)); + Debug(list_channels_on(libcw_do)); + + unsigned short seed16v[3] = { 0x1234, 0xfedc, 0x7091 }; + + for (int k = 0;; ++k) + { + std::cout << "Loop: " << k << "; SEED: " << std::hex << seed16v[0] << ", " << seed16v[1] << ", " << seed16v[2] << std::dec << std::endl; + ++LLFrameTimer::sFrameCount; + + seed48(seed16v); + seed16v[0] = lrand48() & 0xffff; + seed16v[1] = lrand48() & 0xffff; + seed16v[2] = lrand48() & 0xffff; + + TestsuiteClient client1a[number_of_clients_per_syncgroup]; + TestsuiteClient client1b[number_of_clients_per_syncgroup]; + TestsuiteClient client2a[number_of_clients_per_syncgroup]; + TestsuiteClient client2b[number_of_clients_per_syncgroup]; + client1ap = client1a; + client1bp = client1b; + client2ap = client2a; + client2bp = client2b; + + for (int i = 0; i < number_of_clients_per_syncgroup; ++i) + { + client1a[i].setIndex(i); + client1b[i].setIndex(i); + client2a[i].setIndex(i); + client2b[i].setIndex(i); + } + + for (int j = 0; j < 1000000; ++j) + { + innerloop_count += 1; + +#ifdef DEBUG_SYNCOUTPUT + Dout(dc::notice, "Innerloop: " << j); +#endif + unsigned long r = lrand48(); + synckeytype_t keytype = (r & 1) ? ((r & 2) ? synckeytype_test1a : synckeytype_test1b) : ((r & 2) ? synckeytype_test2a : synckeytype_test2b); + r >>= 2; + int cl = (r & 255) % number_of_clients_per_syncgroup; + r >>= 8; + switch (keytype) + { + case synckeytype_test1a: + client1a[cl].change_state(r); + break; + case synckeytype_test1b: + client1b[cl].change_state(r); + break; + case synckeytype_test2a: + client2a[cl].change_state(r); + break; + case synckeytype_test2b: + client2b[cl].change_state(r); + break; + } + } + } +} + +#endif diff --git a/indra/llcommon/aisyncclient.h b/indra/llcommon/aisyncclient.h new file mode 100644 index 000000000..34a3644a3 --- /dev/null +++ b/indra/llcommon/aisyncclient.h @@ -0,0 +1,296 @@ +/** + * @file aisyncclient.h + * @brief Declaration of AISyncClient. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 12/12/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AI_SYNC_CLIENT_H +#define AI_SYNC_CLIENT_H + +#ifdef SYNC_TESTSUITE +/* + * To compile the testsuite, run: + * + * cd indra/llcommon + * g++ -O3 -DCWDEBUG -DSYNC_TESTSUITE -I. -I../cwdebug aisyncclient.cpp -lcwd + */ + +#include +#include +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; +typedef float F32; +typedef double F64; +#define LL_COMMON_API +#define SHOW_ASSERT +#define ASSERT_ONLY_COMMA(...) , __VA_ARGS__ +#define llassert assert + +struct LLFrameTimer +{ + double mStartTime; + double mExpiry; + static double getCurrentTime(void); + static U32 sFrameCount; + static U32 getFrameCount() { return sFrameCount; } + F64 getStartTime() const { return mStartTime; } + void reset(double expiration) { mStartTime = getCurrentTime(); mExpiry = mStartTime + expiration; } + bool hasExpired(void) const { return getCurrentTime() > mExpiry; } +}; + +template +struct LLSingleton +{ + static T sInstance; + static T& instance(void) { return sInstance; } +}; + +template +T LLSingleton::sInstance; + +#else // !SYNC_TESTSUITE +#include "llsingleton.h" +#include "llframetimer.h" +#endif +#include +#include + +//--------------------------------------------------------------------------------------------------------------------- +// Keys with a different syncgroup are never equal (so they are never synchronized). +enum syncgroups +{ +#ifdef SYNC_TESTSUITE + syncgroup_test1, + syncgroup_test2, +#else + syncgroup_motions, // Syncgroup used for animations. +#endif + syncgroup_size +}; + +// Each key type must return a unique identifier that exists of its syncgroup (the least significant 8 bit) plus a few bit to make it unique (bit 9 and higher). +enum synckeytype_t +{ +#ifdef SYNC_TESTSUITE + synckeytype_test1a = 0x000 + syncgroup_test1, + synckeytype_test1b = 0x100 + syncgroup_test1, + synckeytype_test2a = 0x000 + syncgroup_test2, + synckeytype_test2b = 0x100 + syncgroup_test2, +#else + synckeytype_motion = syncgroup_motions // There is currently only one key type in the syncgroup_motions group: AISyncKeyMotion. +#endif +}; + +typedef U32 synceventset_t; // A mask where each bit represents a ready state. + +class LL_COMMON_API AISyncKey +{ + private: + LLFrameTimer mFrameTimer; // This timer is started at the moment the sync key is created. + U32 mStartFrameCount; // The frame count at which the timer was started. + static F32 const sExpirationTime = 0.1; // In seconds. + + public: + // Constructor. + AISyncKey(void) : mStartFrameCount(LLFrameTimer::getFrameCount()) + { + mFrameTimer.reset(sExpirationTime); + } + + // Destructor. + virtual ~AISyncKey() { } + + // Return true if this key expired. + bool expired(void) const + { + // The key has expired when sExpirationTime seconds have elapsed AND at least two frames have passed. + return mFrameTimer.getFrameCount() > mStartFrameCount + 1 && mFrameTimer.hasExpired(); + } + + // Returns true if this object and key would not compare equal based on time because this object is too old. + bool is_older_than(AISyncKey const& key) const + { + return key.mStartFrameCount > mStartFrameCount + 1 && key.mFrameTimer.getStartTime() > mFrameTimer.getStartTime() + sExpirationTime; + } + + // Return the creation time of this key (in number of seconds since application start). + F64 getCreationTime(void) const { return mFrameTimer.getStartTime(); } + + // Returns true if the two keys match, meaning that they should be synchronized. + friend bool operator==(AISyncKey const& key1, AISyncKey const& key2); + + // Returns an ID that uniquely identifies the derived type. + // Currently the only derived type is AISyncKeyMotion with ID synckeytype_motion. + virtual synckeytype_t getkeytype(void) const = 0; + + // Returns true if the data in the derived objects match, meaning that they should be synchronized. + virtual bool equals(AISyncKey const& key) const = 0; +}; + +// Forward declaration. +class AISyncClient; +class AISyncServer; +LL_COMMON_API extern void intrusive_ptr_add_ref(AISyncServer* server); +LL_COMMON_API extern void intrusive_ptr_release(AISyncServer* server); + +struct LL_COMMON_API AISyncClientData +{ + AISyncClient* mClientPtr; + synceventset_t mReadyEvents; + + AISyncClientData(AISyncClient* client) : mClientPtr(client), mReadyEvents(0) { } +}; + +class LL_COMMON_API AISyncServer +{ + public: + typedef std::list client_list_t; + + private: + int mRefCount; // Number of boost::intrusive_ptr objects pointing to this object. + AISyncKey* mKey; // The key of the first client that was added. + client_list_t mClients; // A list with pointers to all registered clients. + bool mSynchronized; // Set when a server gets more than one client, and not reset anymore after that. + synceventset_t mReadyEvents; // 0xFFFFFFFF bitwise-AND-ed with all clients. + synceventset_t mPendingEvents; // The bitwise-OR of all clients. + + public: + AISyncServer(AISyncKey* key) : mRefCount(0), mKey(key), mSynchronized(false), mReadyEvents((synceventset_t)-1), mPendingEvents(0) { } + ~AISyncServer() { delete mKey; } + + // Add a new client to this server. + void add(AISyncClient* client); + + // Add a new client to this server. + void remove(AISyncClient* client); + + // Return the key associated to this server (which is the key produced by the first client of the largest synckeytype_t that was added). + AISyncKey const& key(void) const { return *mKey; } + // Replace they key with another key (of larger synckeytype_t). + void swapkey(AISyncKey*& key_ptr) { AISyncKey* tmp = key_ptr; key_ptr = mKey; mKey = tmp; } + + // Returns true if this server never had more than one client. + bool never_synced(void) const { return !mSynchronized; } + + // Set readiness of all events at once. + bool ready(synceventset_t events, synceventset_t yesno, AISyncClient* client); + + // Unregister the (only) client because it's own its own and will never need synchronization. + void unregister_last_client(void); + + // Return the events that all clients for. + synceventset_t events_with_all_clients_ready(void) const; + + // Return events that at least one client is ready for. + synceventset_t events_with_at_least_one_client_ready(void) const; + +#ifdef SHOW_ASSERT + client_list_t const& getClients(void) const { return mClients; } +#endif + + private: + // Call event1_ready() or event1_not_ready() on all clients if the least significant bit of mReadyEvents changed. + void trigger(synceventset_t old_ready_events); + +#ifdef SYNC_TESTSUITE + void sanity_check(void) const; +#endif + + private: + friend void intrusive_ptr_add_ref(AISyncServer* server); + friend void intrusive_ptr_release(AISyncServer* server); +}; + +class LL_COMMON_API AISyncServerMap : public LLSingleton +{ + public: + typedef boost::intrusive_ptr server_ptr_t; // The type of a (stored) pointer to the server objects. + typedef std::list server_list_t; // The type of the list with pointers to the server objects. + + private: + server_list_t mServers; // A list with pointers to all server objects. + + public: + // Find or create a server object that the client belongs to and store the client in it. + // If a new server is created, it is stored in mServers. + void register_client(AISyncClient* client); + + private: + friend void intrusive_ptr_release(AISyncServer* server); + // Remove a server from the map, only called by intrusive_ptr_release when there is one pointer left; + // therefore, the server should not have any clients. + void remove_server(AISyncServer* server); +}; + +class LL_COMMON_API AISyncClient +{ + private: + friend class AISyncServer; + boost::intrusive_ptr mServer; // The server this client was registered with, or NULL when unregistered. + + public: +#ifdef SHOW_ASSERT + synceventset_t mReadyEvents; + AISyncClient(void) : mReadyEvents(0) { } +#endif + virtual ~AISyncClient() { } + virtual AISyncKey* createSyncKey(void) const = 0; + + virtual void event1_ready(void) = 0; + virtual void event1_not_ready(void) = 0; + + // Only client. Client was forcefully deregistered from expired server because it was the only client. + virtual void deregistered(void) + { +#ifdef SHOW_ASSERT + mReadyEvents = 0; +#endif + } + + AISyncServer* server(void) const { return mServer.get(); } + + // Add this client to a server with matching sync key. Optionally the server is first created. + void register_client(void) { AISyncServerMap::instance().register_client(this); } + + // Remove this client from its server. + void unregister_client(void) { mServer->remove(this); } + + // Call 'ready' when you are ready (or not) to get a call to start(). + // Returns true if that call was made (immediately), otherwise it may happen later. + + bool ready(synceventset_t events, synceventset_t yesno) // Set readiness of all events at once. + { + if (!mServer) + { + register_client(); + } + return mServer->ready(events, yesno, this); + } +}; + +#endif From 1c8876cead4e36bd71e02ae1364136cc579e294f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 25 Dec 2013 03:14:44 +0100 Subject: [PATCH 11/15] Synchronize looping animations that start at the same moment. --- indra/llcharacter/llcharacter.cpp | 15 +++ indra/llcharacter/llkeyframemotion.cpp | 41 ++++++- indra/llcharacter/llmotion.cpp | 137 +++++++++++++++++++++++ indra/llcharacter/llmotion.h | 87 +++++++++++++- indra/llcharacter/llmotioncontroller.cpp | 134 +++++++++++++++++++++- indra/llcharacter/llmotioncontroller.h | 34 +++++- indra/llcommon/aisyncclient.cpp | 61 +--------- indra/llcommon/aisyncclient.h | 39 ++++--- indra/llcommon/llframetimer.h | 7 ++ indra/newview/llviewermessage.cpp | 2 +- indra/newview/llvoavatar.cpp | 14 ++- 11 files changed, 484 insertions(+), 87 deletions(-) diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp index 25b89d2f8..65d37f8e0 100644 --- a/indra/llcharacter/llcharacter.cpp +++ b/indra/llcharacter/llcharacter.cpp @@ -194,11 +194,26 @@ void LLCharacter::updateMotions(e_update_t update_type) { if (update_type == HIDDEN_UPDATE) { + // + // Keep updating avatars that have at least one motion that is synchronized with a still running motion. + // This call tells the other controllers that we are in principle hidden. + // It returns false if we need to keep updating anyway. + if (!mMotionController.hidden(true)) + { + mMotionController.updateMotions(LLCharacter::NORMAL_UPDATE); + return; + } + // LLFastTimer t(FTM_UPDATE_HIDDEN_ANIMATION); mMotionController.updateMotionsMinimal(); } else { + // + // This call tells the other controllers that we are visible and that they need + // to keep updating if they are synchronized with us, even if they are hidden. + mMotionController.hidden(false); + // LLFastTimer t(FTM_UPDATE_ANIMATION); // unpause if the number of outstanding pause requests has dropped to the initial one if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1) diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index aa6e05708..711ac21e5 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -818,7 +818,44 @@ void LLKeyframeMotion::onDeactivate() //----------------------------------------------------------------------------- // setStopTime() //----------------------------------------------------------------------------- -// time is in seconds since character creation +// +// Consider a looping animation of 20 frames, where the loop in point is at 3 frames +// and the loop out point at 16 frames: +// +// The first 3 frames of the animation would be the "loop in" animation. +// The last 4 frames of the animation would be the "loop out" animation. +// Frames 4 through 15 would be the looping animation frames. +// +// If the animation would not be looping, all frames would just be played once sequentially: +// +// mActivationTimestamp -. +// v +// 0 3 15 16 20 +// | | \| | +// --------------------- +// <--> <-- mLoopInPoint (relative to mActivationTimestamp) +// <--------------> <-- mLoopOutPoint (relative to mActivationTimestamp) +// <----mDuration------> +// +// When looping the animation would repeat frames 3 to 16 (loop) a few times, for example: +// +// 0 3 15 3 15 3 15 3 15 16 20 +// | | loop 1 \| loop 2 \| loop 3 \| loop 4 \| | +// ------------------------------------------------------------ +//LOOP^ ^ LOOP +// IN | <----->| OUT +// start_loop_time loop_fraction_time-' time +// +// The time at which the animation is started corresponds to frame 0 and is stored +// in mActivationTimestamp (in seconds since character creation). +// +// If setStopTime() is called with a time somewhere inside loop 4, +// then 'loop_fraction_time' is the time from the beginning of +// loop 4 till 'time'. Thus 'time - loop_fraction_time' is the first +// frame of loop 4, and '(time - loop_fraction_time) + +// (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint)' +// would correspond to frame 20. +// void LLKeyframeMotion::setStopTime(F32 time) { LLMotion::setStopTime(time); @@ -836,6 +873,8 @@ void LLKeyframeMotion::setStopTime(F32 time) loop_fraction_time = fmod(time - start_loop_time, mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint); } + // This sets mStopTimestamp to the time that corresponds to the end of the animation (ie, frame 20 in the above example) + // minus the ease out duration, so that the animation eases out during the loop out and finishes exactly at the end. mStopTimestamp = llmax(time, (time - loop_fraction_time) + (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint) - getEaseOutDuration()); } diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp index 021345fb3..94d12c7e4 100644 --- a/indra/llcharacter/llmotion.cpp +++ b/indra/llcharacter/llmotion.cpp @@ -39,6 +39,122 @@ #include "llcriticaldamp.h" #include "llmotioncontroller.h" +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// AISyncClientMotion class +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +AISyncKey* AISyncClientMotion::createSyncKey(AISyncKey const* from_key) const +{ + // The const cast is needed because getDuration() is non-const while it should have been. + AISyncClientMotion* self = const_cast(this); + // Only synchronize motions with the same duration and loop value. + return new AISyncKeyMotion(from_key, self->getDuration(), self->getLoop()); +} + +void AISyncClientMotion::aisync_loading(void) +{ + // Register the motion for (possible) synchronization: this marks the time at which is should have started. + unregister_client(); // In case it is already registered. Getting here means we are being (re)started now, we need to synchronize with other motions that start now. + register_client(); +} + +void AISyncClientMotion::aisync_loaded(void) +{ + AISyncServer* server = this->server(); + if (!server) + { + // Already expired without being synchronized (no other motion was started at the same time). + return; + } + AISyncKey const& key = server->key(); // The allocation of this is owned by server. + // There is no need to resync if there was not another motion started at the same time and the key already expired. + bool need_resync = !(server->never_synced() && key.expired()); + AISyncKey* new_key; + if (need_resync) + { + // Create a new key using the old start time. + new_key = createSyncKey(&key); + } + server->remove(this); // This resets mServer and might even delete server. + if (need_resync) + { + // Add the client to another server (based on the new key). This takes ownership of the key allocation. + AISyncServerMap::instance().register_client(this, new_key); + } +} + +F32 LLMotion::getRuntime(void) const +{ + llassert(mActive); + return mController->getAnimTime() - mActivationTimestamp; +} + +F32 LLMotion::getAnimTime(void) const +{ + return mController->getAnimTime(); +} + +F32 LLMotion::syncActivationTime(F32 time) +{ + AISyncServer* server = this->server(); + if (!server) + { + register_client(); + server = this->server(); + } + AISyncServer::client_list_t const& clients = server->getClients(); + if (clients.size() > 1) + { + // Look for the client with the smallest runtime. + AISyncClientMotion* motion_with_smallest_runtime = NULL; + F32 runtime = 1e10; + // Run over all motions in this to be synchronized group. + for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client) + { + if ((client->mReadyEvents & 2)) // Is this motion active? Motions that aren't loaded yet are not active. + { + // Currently, if event 2 is set then this is an LLMotion. + llassert(dynamic_cast(client->mClientPtr)); + AISyncClientMotion* motion = static_cast(client->mClientPtr); + // Deactivated motions should have been deregistered, certainly not have event 2 set. + llassert(static_cast(motion)->isActive()); + if (motion->getRuntime() < runtime) + { + // This is a bit fuzzy since theoretically the runtime of all active motions in the list should be the same. + // Just use the smallest value to get rid of some randomness. We might even synchronizing with ourselves + // in which case 'time' would be set to a value such that mActivationTimestamp won't change. + // In practise however, this list will contain only two clients: this, being inactive, and our partner. + runtime = motion->getRuntime(); + motion_with_smallest_runtime = motion; + } + } + } + //----------------------------------------------------------------------------------------- + // Here is where the actual synchronization takes place. + // Current we only synchronize looped motions. + if (getLoop()) + { + if (motion_with_smallest_runtime) + { + // Pretend the motion was started in the past at the same time as the other motion(s). + time = getAnimTime() - runtime; + } + } + //----------------------------------------------------------------------------------------- + } + + return time; +} + +void AISyncClientMotion::deregistered(void) +{ + mReadyEvents = 0; +} +// + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // LLMotion class @@ -149,6 +265,19 @@ void LLMotion::activate(F32 time) { mActivationTimestamp = time; mStopped = FALSE; + // + if (mController && !mController->syncing_disabled()) // Avoid being registered when syncing is disabled for this motion. + { + if (mActive) + { + // If the motion is already active then we are being restarted. + // Unregister it first (if it is registered) so that the call to ready will cause it to be registered + // and be synchronized with other motions that are started at this moment. + unregister_client(); + } + ready(6, 2 | (mController->isHidden() ? 0 : 4)); // Signal that mActivationTimestamp is set/valid (2), and that this server has a visible motion (4) (or not). + } + // mActive = TRUE; onActivate(); } @@ -161,6 +290,14 @@ void LLMotion::deactivate() mActive = FALSE; mPose.setWeight(0.f); + // + if (server()) // Only when this motion is already registered. + { + ready(6, 0); // Signal that mActivationTimestamp is no longer valid. + unregister_client(); // No longer running, so no longer a part of this sync group. + } + // + if (mDeactivateCallback) { (*mDeactivateCallback)(mDeactivateCallbackUserData); diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h index a67138bac..a246b08c7 100644 --- a/indra/llcharacter/llmotion.h +++ b/indra/llcharacter/llmotion.h @@ -38,6 +38,7 @@ //----------------------------------------------------------------------------- #include +#include "aisyncclient.h" #include "llerror.h" #include "llpose.h" #include "lluuid.h" @@ -45,10 +46,77 @@ class LLCharacter; class LLMotionController; +//----------------------------------------------------------------------------- +// class AISync* stuff +//----------------------------------------------------------------------------- + +class AISyncKeyMotion : public AISyncKey +{ + private: + F32 mDuration; + bool mLoop; + + public: + AISyncKeyMotion(AISyncKey const* from_key, F32 duration, bool loop) : AISyncKey(from_key), mDuration(duration), mLoop(loop) { } + + // Virtual functions of AISyncKey. + public: + /*virtual*/ synckeytype_t getkeytype(void) const + { + // Return a unique identifier for this class, where the low 8 bits represent the syncgroup. + return synckeytype_motion; + } + + /*virtual*/ bool equals(AISyncKey const& key) const + { + switch (key.getkeytype()) + { + case synckeytype_motion: + { + // The other key is of the same type. + AISyncKeyMotion const& motion_key = static_cast(key); + return mLoop == motion_key.mLoop && is_approx_equal(mDuration, motion_key.mDuration); + } + default: + // The keys must be in the same syncgroup. + break; + } + return false; + } +}; + +class AISyncClientMotion : public AISyncClient +{ + protected: + // Make sure that clients that are destroyed are first unregistered. + // This is needed, for example, when loading fails or when excess motions are being purged. + /*virtual*/ ~AISyncClientMotion() { unregister_client(); } + + // AISyncClient events. + /*virtual*/ AISyncKey* createSyncKey(AISyncKey const* from_key = NULL) const; + /*virtual*/ void event1_ready(void) { } + /*virtual*/ void event1_not_ready(void) { } + /*virtual*/ void deregistered(void); + + protected: + // This is called when the server sent us a message that it wants us to play this animation, but we can't because it isn't fully downloaded yet. + void aisync_loading(void); + // This is called when that motion is successfully loaded and it has to be re-registered because now the duration etc is known. + void aisync_loaded(void); + + public: + // Virtual functions of AISyncClientMotion. + // These are defined by classes derived from LLMotion (which is derived from this class). + virtual BOOL getLoop() = 0; + virtual F32 getDuration() = 0; + virtual F32 getAnimTime(void) const = 0; + virtual F32 getRuntime(void) const = 0; +}; + //----------------------------------------------------------------------------- // class LLMotion //----------------------------------------------------------------------------- -class LLMotion +class LLMotion : public AISyncClientMotion { friend class LLMotionController; @@ -115,7 +183,22 @@ protected: BOOL isActive() { return mActive; } public: void activate(F32 time); - + + // + // Returns the time that this motion has been running. + virtual F32 getRuntime(void) const; + + // Return the current time (in seconds since creation of the controller). + virtual F32 getAnimTime(void) const; + + // This is called when a motion is to be activated, but might need synchronization. + // It adjusts the start time to match that of other motions in the same synchronization group that were already started. + F32 syncActivationTime(F32 time); + + // Accessor. + LLMotionController* getController(void) const { return mController; } + // + public: //------------------------------------------------------------------------- // animation callbacks to be implemented by subclasses diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 099805676..8f8475576 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -130,6 +130,9 @@ LLMotionController::LLMotionController() mTimeFactor(sCurrentTimeFactor), mCharacter(NULL), mActiveMask(0), + mDisableSyncing(0), + mHidden(false), + mHaveVisibleSyncedMotions(false), mPrevTimerElapsed(0.f), mAnimTime(0.f), mLastTime(0.0f), @@ -173,6 +176,10 @@ void LLMotionController::deleteAllMotions() mActiveMask = 0; for_each(mDeprecatedMotions.begin(), mDeprecatedMotions.end(), DeletePointer()); mDeprecatedMotions.clear(); + for (motion_map_t::iterator iter = mAllMotions.begin(); iter != mAllMotions.end(); ++iter) + { + iter->second->unregister_client(); + } // for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer()); mAllMotions.clear(); @@ -437,7 +444,19 @@ BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset) } // llinfos << "Starting motion " << name << llendl; - return activateMotionInstance(motion, mAnimTime - start_offset); + // + F32 start_time = mAnimTime - start_offset; + if (!mDisableSyncing) + { + start_time = motion->syncActivationTime(start_time); + } + ++mDisableSyncing; + // + BOOL res = activateMotionInstance(motion, start_time); + // + --mDisableSyncing; + // + return res; } @@ -793,7 +812,19 @@ void LLMotionController::updateLoadingMotions() // this motion should be playing if (!motionp->isStopped()) { - activateMotionInstance(motionp, mAnimTime); + // + F32 start_time = mAnimTime; + if (!mDisableSyncing) + { + motionp->aisync_loaded(); + start_time = motionp->syncActivationTime(start_time); + } + ++mDisableSyncing; + // + activateMotionInstance(motionp, start_time); + // + --mDisableSyncing; + // } } else if (status == LLMotion::STATUS_FAILURE) @@ -806,6 +837,10 @@ void LLMotionController::updateLoadingMotions() // check for it's existence there. llassert(mDeprecatedMotions.find(motionp) == mDeprecatedMotions.end()); mAllMotions.erase(motionp->getID()); + // + // Make sure we're not registered anymore. + motionp->unregister_client(); + // delete motionp; } } @@ -933,6 +968,12 @@ BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time) if (mLoadingMotions.find(motion) != mLoadingMotions.end()) { + // + if (!syncing_disabled()) + { + motion->aisync_loading(); + } + // // we want to start this motion, but we can't yet, so flag it as started motion->setStopped(FALSE); // report pending animations as activated @@ -1094,9 +1135,9 @@ void LLMotionController::deactivateAllMotions() { // Singu note: this must run over mActiveMotions: other motions are not active, // and running over mAllMotions will miss the ones in mDeprecatedMotions. - for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter) + for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end();) { - deactivateMotionInstance(*iter); + deactivateMotionInstance(*iter++); // This might invalidate iter by erasing it from mActiveMotions. } } @@ -1126,13 +1167,98 @@ void LLMotionController::flushAllMotions() mCharacter->removeAnimationData("Hand Pose"); // restart motions + // + // Because we called motionp->deactivate() above, instead of deactivateMotionInstance(), + // prevent calling AISyncClientMotion::activateInstance in startMotion below. + disable_syncing(); + // for (std::vector >::iterator iter = active_motions.begin(); iter != active_motions.end(); ++iter) { startMotion(iter->first, iter->second); } + // + enable_syncing(); + // } +// +//----------------------------------------------------------------------------- +// toggle_hidden() +//----------------------------------------------------------------------------- +void LLMotionController::toggle_hidden(void) +{ + mHaveVisibleSyncedMotions = mHidden; // Default is false if we just became invisible (otherwise this value isn't used). + mHidden = !mHidden; + synceventset_t const visible = mHidden ? 0 : 4; + + // Run over all motions. + for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter) + { + LLMotion* motionp = *iter; + AISyncServer* server = motionp->server(); + if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active. + { + bool visible_before = server->events_with_at_least_one_client_ready() & 4; + server->ready(4, visible, motionp); // Mark that now we are visible or no longer visible. + bool visible_after = server->events_with_at_least_one_client_ready() & 4; + if (visible_after) // Are there any synchronized motions (left) that ARE visible? + { + mHaveVisibleSyncedMotions = true; + } + if (visible_before != visible_after) + { + // The group as a whole now might need to change whether or not it is animated. + AISyncServer::client_list_t const& clients = server->getClients(); + for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client) + { + LLMotion* motion = dynamic_cast(client->mClientPtr); + if (!motion) + { + continue; + } + LLMotionController* controller = motion->getController(); + if (controller == this) + { + continue; + } + if (visible_after) + { + // Us becoming visible means that all synchronized avatars need to be animated again too. + controller->setHaveVisibleSyncedMotions(); + } + else + { + // Us becoming hidden means that all synchronized avatars might stop animating. + controller->refresh_hidden(); // It is extremely unlikely, but harmless, to call this twice on the same controller. + } + } + } + } + } +} + +void LLMotionController::refresh_hidden(void) +{ + mHaveVisibleSyncedMotions = !mHidden; + + // Run over all motions. + for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter) + { + LLMotion* motionp = *iter; + AISyncServer* server = motionp->server(); + if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active. + { + bool visible_after = server->events_with_at_least_one_client_ready() & 4; + if (visible_after) // Are there any synchronized motions (left) that ARE visible? + { + mHaveVisibleSyncedMotions = true; + } + } + } +} +// + //----------------------------------------------------------------------------- // pause() //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 04b15ff89..ac3d6d1f2 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -150,11 +150,11 @@ public: //Flush is a liar. void deactivateAllMotions(); - // + // void activated(U32 bit) { mActiveMask |= bit; } void deactivated(U32 bit) { mActiveMask &= ~bit; } bool isactive(U32 bit) const { return (mActiveMask & bit) != 0; } - // + // // pause and continue all motions void pauseAllMotions(); @@ -186,7 +186,10 @@ protected: // internal operations act on motion instances directly // as there can be duplicate motions per id during blending overlap void deleteAllMotions(); + // singu: LLMotion needs access to activateMotionInstance. +public: BOOL activateMotionInstance(LLMotion *motion, F32 time); +protected: BOOL deactivateMotionInstance(LLMotion *motion); void deprecateMotionInstance(LLMotion* motion); BOOL stopMotionInstance(LLMotion *motion, BOOL stop_imemdiate); @@ -227,9 +230,12 @@ protected: motion_list_t mActiveMotions; motion_set_t mDeprecatedMotions; - // + // U32 mActiveMask; - // + int mDisableSyncing; // Set while LLMotion::onActivate (and onDeactivate) are called for this controller. + bool mHidden; // The value of the last call to hidden(). + bool mHaveVisibleSyncedMotions; // Set when we are synchronized with one or more motions of a controller that is not hidden. + // LLFrameTimer mTimer; F32 mPrevTimerElapsed; F32 mAnimTime; @@ -242,6 +248,26 @@ protected: F32 mLastInterp; U8 mJointSignature[2][LL_CHARACTER_MAX_JOINTS]; + + // +public: + // Internal administration for AISync. + void disable_syncing(void) { mDisableSyncing += 100; } + void enable_syncing(void) { mDisableSyncing -= 100; } + bool syncing_disabled(void) const { return mDisableSyncing >= 100; } + + // Accessors needed for synchronization. + F32 getAnimTime(void) const { return mAnimTime; } + bool isHidden(void) const { return mHidden; } + + // Called often. Should return false if we still need to keep updating our motions even if we're not visible. + bool hidden(bool not_visible) { if (mHidden != not_visible) toggle_hidden(); return !mHaveVisibleSyncedMotions; } + +private: + void toggle_hidden(void); + void refresh_hidden(void); + void setHaveVisibleSyncedMotions(void) { mHaveVisibleSyncedMotions = true; } + // }; //----------------------------------------------------------------------------- diff --git a/indra/llcommon/aisyncclient.cpp b/indra/llcommon/aisyncclient.cpp index 9cadb3cb1..e1865fec8 100644 --- a/indra/llcommon/aisyncclient.cpp +++ b/indra/llcommon/aisyncclient.cpp @@ -126,20 +126,13 @@ void print_clients(AISyncServer const* server, AISyncServer::client_list_t const } #endif -void AISyncServerMap::register_client(AISyncClient* client) +void AISyncServerMap::register_client(AISyncClient* client, AISyncKey* new_key) { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServerMap::register_client(" << client << ")"); -#endif - // client must always be a new client that has to be stored somewhere. llassert(client->server() == NULL); // Obviously the client can't be ready for anything when it isn't registered yet. llassert(!client->mReadyEvents); - // First we need its sync key. - AISyncKey* new_key = client->createSyncKey(); - // Find if a server with this key already exists. AISyncServer* server = NULL; for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end();) @@ -183,7 +176,6 @@ void AISyncServerMap::register_client(AISyncClient* client) server = new AISyncServer(new_key); // Add it to mServers, before the last server that is younger then the new key. server_list_t::iterator where = mServers.end(); // Insert the new server before 'where', -#if 0 // This is probably not necessary. server_list_t::iterator new_where = where; while (where != mServers.begin()) // unless there exists a server before that { @@ -194,10 +186,6 @@ void AISyncServerMap::register_client(AISyncClient* client) } where = new_where; // then insert it before that element (etc). } -#elif defined(SHOW_ASSERT) - server_list_t::iterator new_where = where; - llassert(where == mServers.begin() || new_key->getCreationTime() >= (*--new_where)->key().getCreationTime()); -#endif // This method causes a single call to intrusive_ptr_add_ref and none to intrusive_ptr_release. server_ptr_t server_ptr = server; mServers.insert(where, server_ptr_t())->swap(server_ptr); @@ -231,10 +219,6 @@ void AISyncServer::sanity_check(void) const void AISyncServer::add(AISyncClient* client) { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::add(" << client << "), with this = " << this); - print_clients(this, getClients()); -#endif #ifdef SYNC_TESTSUITE sanity_check(); #endif @@ -263,16 +247,10 @@ void AISyncServer::add(AISyncClient* client) void AISyncServer::remove(AISyncClient* client) { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::remove(" << client << "), with this = " << this); - print_clients(this, getClients()); -#endif #ifdef SYNC_TESTSUITE sanity_check(); #endif - // A client may only be unregistered after it was marked not-ready for all events. - llassert(!client->mReadyEvents); client_list_t::iterator client_iter = mClients.begin(); synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready. synceventset_t remaining_pending_events = 0; // At least one client is ready (waiting for the other clients thus). @@ -292,15 +270,14 @@ void AISyncServer::remove(AISyncClient* client) } llassert(found_client != mClients.end()); // This must be the same as client->mReadyEvents. - llassert(!found_client->mReadyEvents); + llassert(found_client->mReadyEvents == client->mReadyEvents); mClients.erase(found_client); - client->mServer.reset(); synceventset_t old_ready_events = mReadyEvents; mReadyEvents = remaining_ready_events; - // Since client->mReadyEvents is zero, this should be the same. - llassert(mPendingEvents == remaining_pending_events); mPendingEvents = remaining_pending_events; trigger(old_ready_events); + client->mServer.reset(); + client->deregistered(); #ifdef SYNC_TESTSUITE sanity_check(); @@ -309,10 +286,6 @@ void AISyncServer::remove(AISyncClient* client) void AISyncServer::unregister_last_client(void) { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::unregister_last_client()"); - print_clients(this, getClients()); -#endif #ifdef SYNC_TESTSUITE sanity_check(); #endif @@ -333,22 +306,6 @@ void AISyncServer::unregister_last_client(void) #endif } -synceventset_t AISyncServer::events_with_all_clients_ready(void) const -{ -#ifdef SYNC_TESTSUITE - sanity_check(); -#endif - return mReadyEvents; -} - -synceventset_t AISyncServer::events_with_at_least_one_client_ready(void) const -{ -#ifdef SYNC_TESTSUITE - sanity_check(); -#endif - return mPendingEvents; -} - void AISyncServer::trigger(synceventset_t old_ready_events) { // If event 1 changed, informat all clients about it. @@ -368,22 +325,14 @@ void AISyncServer::trigger(synceventset_t old_ready_events) } } -bool AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client) +void AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client) { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")"); - print_clients(this, getClients()); -#endif #ifdef SYNC_TESTSUITE sanity_check(); #endif synceventset_t added_events = events & yesno; synceventset_t removed_events = events & ~yesno; - // May not add events that are already ready. - llassert(!(client->mReadyEvents & added_events)); - // Cannot remove events that weren't ready. - llassert((client->mReadyEvents & removed_events) == removed_events); // Run over all clients to find the client and calculate the current state. synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready. diff --git a/indra/llcommon/aisyncclient.h b/indra/llcommon/aisyncclient.h index 34a3644a3..b810f143a 100644 --- a/indra/llcommon/aisyncclient.h +++ b/indra/llcommon/aisyncclient.h @@ -117,9 +117,16 @@ class LL_COMMON_API AISyncKey public: // Constructor. - AISyncKey(void) : mStartFrameCount(LLFrameTimer::getFrameCount()) + AISyncKey(AISyncKey const* from_key) : mStartFrameCount(from_key ? from_key->mStartFrameCount : LLFrameTimer::getFrameCount()) { - mFrameTimer.reset(sExpirationTime); + if (from_key) + { + mFrameTimer.copy(from_key->mFrameTimer); + } + else + { + mFrameTimer.reset(sExpirationTime); + } } // Destructor. @@ -186,7 +193,7 @@ class LL_COMMON_API AISyncServer // Add a new client to this server. void add(AISyncClient* client); - // Add a new client to this server. + // Remove a client from this server. void remove(AISyncClient* client); // Return the key associated to this server (which is the key produced by the first client of the largest synckeytype_t that was added). @@ -198,20 +205,19 @@ class LL_COMMON_API AISyncServer bool never_synced(void) const { return !mSynchronized; } // Set readiness of all events at once. - bool ready(synceventset_t events, synceventset_t yesno, AISyncClient* client); + void ready(synceventset_t events, synceventset_t yesno, AISyncClient* client); // Unregister the (only) client because it's own its own and will never need synchronization. void unregister_last_client(void); // Return the events that all clients for. - synceventset_t events_with_all_clients_ready(void) const; + synceventset_t events_with_all_clients_ready(void) const { return mReadyEvents; } // Return events that at least one client is ready for. - synceventset_t events_with_at_least_one_client_ready(void) const; + synceventset_t events_with_at_least_one_client_ready(void) const { return mPendingEvents; } -#ifdef SHOW_ASSERT + // Return a list of all registered clients. client_list_t const& getClients(void) const { return mClients; } -#endif private: // Call event1_ready() or event1_not_ready() on all clients if the least significant bit of mReadyEvents changed. @@ -238,7 +244,7 @@ class LL_COMMON_API AISyncServerMap : public LLSingleton public: // Find or create a server object that the client belongs to and store the client in it. // If a new server is created, it is stored in mServers. - void register_client(AISyncClient* client); + void register_client(AISyncClient* client, AISyncKey* new_key); private: friend void intrusive_ptr_release(AISyncServer* server); @@ -258,8 +264,8 @@ class LL_COMMON_API AISyncClient synceventset_t mReadyEvents; AISyncClient(void) : mReadyEvents(0) { } #endif - virtual ~AISyncClient() { } - virtual AISyncKey* createSyncKey(void) const = 0; + virtual ~AISyncClient() { llassert(!mServer); /* If this fails then you need to add unregister_client() to the top of the destructor of the derived class that implements deregistered(). */ } + virtual AISyncKey* createSyncKey(AISyncKey const* from_key = NULL) const = 0; virtual void event1_ready(void) = 0; virtual void event1_not_ready(void) = 0; @@ -275,21 +281,22 @@ class LL_COMMON_API AISyncClient AISyncServer* server(void) const { return mServer.get(); } // Add this client to a server with matching sync key. Optionally the server is first created. - void register_client(void) { AISyncServerMap::instance().register_client(this); } + void register_client(void) { AISyncServerMap::instance().register_client(this, createSyncKey()); } - // Remove this client from its server. - void unregister_client(void) { mServer->remove(this); } + // Remove this client from its server, if any. + void unregister_client(void) { if (mServer) mServer->remove(this); } // Call 'ready' when you are ready (or not) to get a call to start(). // Returns true if that call was made (immediately), otherwise it may happen later. - bool ready(synceventset_t events, synceventset_t yesno) // Set readiness of all events at once. + // Set readiness of all events at once. + void ready(synceventset_t events, synceventset_t yesno) { if (!mServer) { register_client(); } - return mServer->ready(events, yesno, this); + mServer->ready(events, yesno, this); } }; diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index 2813150c5..30bc58dd9 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -47,6 +47,10 @@ public: // Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true). LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); } + // + void copy(LLFrameTimer const& timer) { mStartTime = timer.mStartTime; mExpiry = timer.mExpiry; mRunning = timer.mRunning; mPaused = timer.mPaused; } + // + // Atomic reads of static variables. // Return the number of seconds since the start of the application. @@ -142,6 +146,9 @@ public: bool hasExpired() const { return getElapsedSeconds() >= mExpiry; } F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); } bool getStarted() const { return mRunning; } + // + F64 getStartTime() const { llassert(!mPaused); return mStartTime; } + // // return the seconds since epoch when this timer will expire. F64 expiresAt() const; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index a2adc49a9..ceea596b0 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -5772,7 +5772,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) } } - if (num_blocks) + //if (num_blocks) Singu note: commented out; having blocks or not is totally irrelevant! { avatarp->processAnimationStateChanges(); } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index b34291a5f..8562edb7a 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -231,7 +231,9 @@ void LLVOAvatar::startMotion(U32 bit, F32 time_offset) { if (!isMotionActive(bit)) { + mMotionController.disable_syncing(); // Don't attempt to synchronize AIMaskedMotion. startMotion(mask2ID(bit), time_offset); + mMotionController.enable_syncing(); } } @@ -3810,6 +3812,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) if (LLVOAvatar::sShowAnimationDebug) { + addDebugText(llformat("at=%.1f", mMotionController.getAnimTime())); for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); iter != mMotionController.getActiveMotions().end(); ++iter) { @@ -3829,6 +3832,10 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) motionp->getName().c_str(), (U32)motionp->getPriority()); } + if (motionp->server()) + { + output += llformat(" rt=%.1f r=%d s=0x%xl", motionp->getRuntime(), motionp->mReadyEvents, motionp->server()); + } addDebugText(output); } } @@ -5499,6 +5506,7 @@ void LLVOAvatar::processAnimationStateChanges() } // clear all current animations + BOOL const AOEnabled = gSavedSettings.getBOOL("AOEnabled"); // Singu note: put this outside the loop. AnimIterator anim_it; for (anim_it = mPlayingAnimations.begin(); anim_it != mPlayingAnimations.end();) { @@ -5508,9 +5516,9 @@ void LLVOAvatar::processAnimationStateChanges() if (found_anim == mSignaledAnimations.end()) { - if (isSelf()) + if (AOEnabled && isSelf()) { - if ((gSavedSettings.getBOOL("AOEnabled")) && LLFloaterAO::stopMotion(anim_it->first, FALSE)) // if the AO replaced this anim serverside then stop it serverside + if (LLFloaterAO::stopMotion(anim_it->first, FALSE)) // if the AO replaced this anim serverside then stop it serverside { // return TRUE; //no local stop needed } @@ -5540,7 +5548,7 @@ void LLVOAvatar::processAnimationStateChanges() // if (processSingleAnimationStateChange(anim_it->first, TRUE)) { - if (isSelf() && gSavedSettings.getBOOL("AOEnabled")) // AO is only for ME + if (AOEnabled && isSelf()) // AO is only for ME { LLFloaterAO::startMotion(anim_it->first, 0,FALSE); // AO overrides the anim if needed } From b4848f308ff35668309ce7d36a10e0b6dbfe1266 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 28 Dec 2013 00:00:30 +0100 Subject: [PATCH 12/15] Compile fix for Release --- indra/llcharacter/llmotion.cpp | 2 ++ indra/llcharacter/llmotioncontroller.cpp | 4 ++-- indra/newview/llvoavatar.cpp | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp index 94d12c7e4..efd1ad158 100644 --- a/indra/llcharacter/llmotion.cpp +++ b/indra/llcharacter/llmotion.cpp @@ -151,7 +151,9 @@ F32 LLMotion::syncActivationTime(F32 time) void AISyncClientMotion::deregistered(void) { +#ifdef SHOW_ASSERT mReadyEvents = 0; +#endif } // diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 8f8475576..7c7192be0 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -1197,7 +1197,7 @@ void LLMotionController::toggle_hidden(void) { LLMotion* motionp = *iter; AISyncServer* server = motionp->server(); - if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active. + if (server && !server->never_synced() && motionp->isActive()) // Skip motions that aren't synchronized at all or that are not active. { bool visible_before = server->events_with_at_least_one_client_ready() & 4; server->ready(4, visible, motionp); // Mark that now we are visible or no longer visible. @@ -1247,7 +1247,7 @@ void LLMotionController::refresh_hidden(void) { LLMotion* motionp = *iter; AISyncServer* server = motionp->server(); - if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active. + if (server && !server->never_synced() && motionp->isActive()) // Skip motions that aren't synchronized at all or that are not active. { bool visible_after = server->events_with_at_least_one_client_ready() & 4; if (visible_after) // Are there any synchronized motions (left) that ARE visible? diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 8562edb7a..901340a57 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3834,7 +3834,11 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } if (motionp->server()) { +#ifdef SHOW_ASSERT output += llformat(" rt=%.1f r=%d s=0x%xl", motionp->getRuntime(), motionp->mReadyEvents, motionp->server()); +#else + output += llformat(" rt=%.1f s=0x%xl", motionp->getRuntime(), motionp->server()); +#endif } addDebugText(output); } From 4d2517d163cf34c9b7b8fc387e45da2ea0848213 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 29 Dec 2013 21:49:05 +0100 Subject: [PATCH 13/15] Freeze also synchronized avatars when editing an attachment. On the removal of permYouOwner(): Calling permYouOwner() to determine if an attachment is yours is not correct: grid gods (and that includes BEFORE requesting admin status) are marked as owner of everything in their sim. The reason for that is that otherwise, if they only had it when actually godding u, the viewer would have to have an option to either show the proper menus (it doesn't) or allow the sim to refresh the object flags without full object updates (it hasn't). gods often have to act fast, for instance with griefers. Having transition to god mode take as long as a full rez just isn't an option, apart from the sim load caused by resending all objects. Then i tried resending only the object that was selected but there the arriving object update would close the pie menu. So you right-click a prim and the pie menu would close again. The result has always been that if you are on your own sim, in opensim, and select an attachment on another avatar, then YOU would freeze, not the selected avatar. This patch fixes that "opensim related" bug as well. --- indra/llcharacter/llcharacter.cpp | 14 +++++++++ indra/llcharacter/llcharacter.h | 2 ++ indra/llcharacter/llmotioncontroller.cpp | 38 ++++++++++++++++++++++++ indra/llcharacter/llmotioncontroller.h | 6 ++++ indra/newview/llselectmgr.cpp | 25 +++++----------- indra/newview/llselectmgr.h | 2 +- 6 files changed, 69 insertions(+), 18 deletions(-) diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp index 65d37f8e0..12960ac01 100644 --- a/indra/llcharacter/llcharacter.cpp +++ b/indra/llcharacter/llcharacter.cpp @@ -543,3 +543,17 @@ LLAnimPauseRequest LLCharacter::requestPause() return mPauseRequest; } +void LLCharacter::requestPause(std::vector& avatar_pause_handles) +{ + mMotionController.pauseAllMotions(); + avatar_pause_handles.push_back(mPauseRequest); +} + +void LLCharacter::pauseAllSyncedCharacters(std::vector& avatar_pause_handles) +{ + // Pause this avatar. + requestPause(avatar_pause_handles); + // Also pause all avatars with synchronized motions. + mMotionController.pauseAllSyncedCharacters(avatar_pause_handles); +} + diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h index 74cc35187..f618b446f 100644 --- a/indra/llcharacter/llcharacter.h +++ b/indra/llcharacter/llcharacter.h @@ -159,6 +159,8 @@ public: void updateMotions(e_update_t update_type); LLAnimPauseRequest requestPause(); + void requestPause(std::vector& avatar_pause_handles); + void pauseAllSyncedCharacters(std::vector& avatar_pause_handles); BOOL areAnimationsPaused() const { return mMotionController.isPaused(); } void setAnimTimeFactor(F32 factor) { mMotionController.setTimeFactor(factor); } void setTimeStep(F32 time_step) { mMotionController.setTimeStep(time_step); } diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 7c7192be0..f5380957f 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -1257,6 +1257,44 @@ void LLMotionController::refresh_hidden(void) } } } + +void LLMotionController::pauseAllSyncedCharacters(std::vector& avatar_pause_handles) +{ + // Run over all motions. + for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter) + { + LLMotion* motionp = *iter; + AISyncServer* server = motionp->server(); + if (server && !server->never_synced() && motionp->isActive()) // Skip motions that aren't synchronized at all or that are not active. + { + // Run over all clients of the found servers. + AISyncServer::client_list_t const& clients = server->getClients(); + for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client) + { + LLMotion* motion = dynamic_cast(client->mClientPtr); + if (!motion) + { + continue; + } + LLMotionController* controller = motion->getController(); + if (controller == this) + { + continue; + } + controller->requestPause(avatar_pause_handles); + } + } + } +} + +void LLMotionController::requestPause(std::vector& avatar_pause_handles) +{ + if (mCharacter) + { + mCharacter->requestPause(avatar_pause_handles); + } +} + // //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index ac3d6d1f2..054f1c2f9 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -52,6 +52,8 @@ //----------------------------------------------------------------------------- class LLCharacter; class LLMotionController; +class LLPauseRequestHandle; +typedef LLPointer LLAnimPauseRequest; //----------------------------------------------------------------------------- // LLMotionRegistry @@ -160,6 +162,10 @@ public: void pauseAllMotions(); void unpauseAllMotions(); BOOL isPaused() const { return mPaused; } + // + void requestPause(std::vector& avatar_pause_handles); + void pauseAllSyncedCharacters(std::vector& avatar_pause_handles); + // void setTimeStep(F32 step); diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 4881139df..976397931 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -6506,7 +6506,7 @@ void LLSelectMgr::updateSelectionCenter() mSelectionCenterGlobal.clearVec(); mShowSelection = FALSE; mSelectionBBox = LLBBox(); - mPauseRequest = NULL; + mPauseRequests.clear(); resetAgentHUDZoom(); } @@ -6516,27 +6516,18 @@ void LLSelectMgr::updateSelectionCenter() if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid()) { - // Singu Note: Chalice Yao's pause agent on attachment selection - if (object->permYouOwner()) + // Freeze avatars with a selected attachment, and all avatars with synchronized motions, if any. + LLVOAvatar* avatar = object->getAvatar(); + // It is possible that 'avatar' is NULL despite this being an attachment because of some race condition. + // In that case just don't freeze the avatar. + if (avatar) { - mPauseRequest = gAgentAvatarp->requestPause(); - } - else if (LLViewerObject* objectp = mSelectedObjects->getPrimaryObject()) - { - while (objectp && !objectp->isAvatar()) - { - objectp = (LLViewerObject*)objectp->getParent(); - } - - if (objectp) - { - mPauseRequest = objectp->asAvatar()->requestPause(); - } + avatar->pauseAllSyncedCharacters(mPauseRequests); } } else { - mPauseRequest = NULL; + mPauseRequests.clear(); } if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && isAgentAvatarValid()) diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 16c960ba8..145c25005 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -793,7 +793,7 @@ private: LLFrameTimer mEffectsTimer; BOOL mForceSelection; - LLAnimPauseRequest mPauseRequest; + std::vector mPauseRequests; // Selected avatar and all synchronized avatars. friend class LLObjectBackup; }; From 2fb945a4890caa6d1a2fd154b126fada42f7db8d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 26 Jan 2014 20:59:01 +0100 Subject: [PATCH 14/15] Fix floating point round off error bug that caused mAnimTime to be decreased in value. Conflicts: indra/llcharacter/llmotioncontroller.cpp --- indra/llcharacter/llmotioncontroller.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index f5380957f..d12b3ddb6 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -874,8 +874,15 @@ void LLMotionController::updateMotions(bool force_update) { F32 time_interval = fmodf(update_time, mTimeStep); - // always animate *ahead* of actual time - S32 quantum_count = llmax(0, llfloor((update_time - time_interval) / mTimeStep)) + 1; + // + // This old code is nonsense. + //S32 quantum_count = llmax(0, llround((update_time - time_interval) / mTimeStep)) + 1; + // (update_time - time_interval) / mTimeStep is an integer! We need llround to get rid of floating point errors, not llfloor. + // Moreover, just rounding off to the nearest integer with llround(update_time / mTimeStep) makes a lot more sense: + // it is the best we can do to get as close to what we should draw as possible. + // However, mAnimTime may only be incremented; therefore make sure of that with the llmax. + S32 quantum_count = llmax(llround(update_time / mTimeStep), llceil(mAnimTime / mTimeStep)); + // if (quantum_count == mTimeStepCount) { // we're still in same time quantum as before, so just interpolate and exit @@ -901,7 +908,8 @@ void LLMotionController::updateMotions(bool force_update) } else { - mAnimTime = update_time; + // Singu note: mAnimTime may never be set back in time. + mAnimTime = llmax(mAnimTime, update_time); } } From a332a7fc3572406874c76cba32bc7ab671be14eb Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 4 Apr 2014 17:33:45 +0200 Subject: [PATCH 15/15] Compile fix for motions backport. --- indra/llcharacter/llkeyframemotion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 952ae0ca7..4c54d5500 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -502,7 +502,7 @@ public: //------------------------------------------------------------------------- // JointMotionList //------------------------------------------------------------------------- - class JointMotionList + class JointMotionList : public LLRefCount { public: std::vector mJointMotionArray;