diff --git a/indra/aistatemachine/aistatemachine.cpp b/indra/aistatemachine/aistatemachine.cpp index fcdb2d9c8..fe7b4a3bd 100644 --- a/indra/aistatemachine/aistatemachine.cpp +++ b/indra/aistatemachine/aistatemachine.cpp @@ -293,7 +293,36 @@ void AIEngine::add(AIStateMachine* state_machine) } } -extern void print_statemachine_diagnostics(U64 total_clocks, U64 max_delta, AIEngine::queued_type::const_reference slowest_state_machine); +#if STATE_MACHINE_PROFILING +// Called from AIStateMachine::mainloop +void print_statemachine_diagnostics(U64 total_clocks, AIStateMachine::StateTimerBase::TimeData& slowest_timer, AIEngine::queued_type::const_reference slowest_element) +{ + AIStateMachine const& slowest_state_machine = slowest_element.statemachine(); + F64 const tfactor = 1000 / calc_clock_frequency(); + std::ostringstream msg; + + U64 max_delta = slowest_timer.GetDuration(); + + if (total_clocks > max_delta) + { + msg << "AIStateMachine::mainloop did run for " << (total_clocks * tfactor) << " ms. The slowest "; + } + else + { + msg << "AIStateMachine::mainloop: A "; + } + msg << "state machine " << "(" << slowest_state_machine.getName() << ") " << "ran for " << (max_delta * tfactor) << " ms"; + if (slowest_state_machine.getRuntime() > max_delta) + { + msg << " (" << (slowest_state_machine.getRuntime() * tfactor) << " ms in total now)"; + } + msg << ".\n"; + + AIStateMachine::StateTimerBase::DumpTimers(msg); + + llwarns << msg.str() << llendl; +} +#endif // MAIN-THREAD void AIEngine::mainloop(void) @@ -305,28 +334,33 @@ void AIEngine::mainloop(void) queued_element = engine_state_w->list.begin(); } U64 total_clocks = 0; -#ifndef LL_RELEASE_FOR_DOWNLOAD - U64 max_delta = 0; +#if STATE_MACHINE_PROFILING queued_type::value_type slowest_element(NULL); + AIStateMachine::StateTimerRoot::TimeData slowest_timer; #endif while (queued_element != end) { AIStateMachine& state_machine(queued_element->statemachine()); - U64 start = get_clock_count(); - if (!state_machine.sleep(start)) + AIStateMachine::StateTimerBase::TimeData time_data; + if (!state_machine.sleep(get_clock_count())) { - state_machine.multiplex(AIStateMachine::normal_run); + AIStateMachine::StateTimerRoot timer(state_machine.getName()); + state_machine.multiplex(AIStateMachine::normal_run); + time_data = timer.GetTimerData(); } - U64 delta = get_clock_count() - start; - state_machine.add(delta); - total_clocks += delta; -#ifndef LL_RELEASE_FOR_DOWNLOAD - if (delta > max_delta) + if (U64 delta = time_data.GetDuration()) { - max_delta = delta; - slowest_element = *queued_element; - } + state_machine.add(delta); + total_clocks += delta; +#if STATE_MACHINE_PROFILING + if (delta > slowest_timer.GetDuration()) + { + slowest_element = *queued_element; + slowest_timer = time_data; + } #endif + } + bool active = state_machine.active(this); // This locks mState shortly, so it must be called before locking mEngineState because add() locks mEngineState while holding mState. engine_state_type_wat engine_state_w(mEngineState); if (!active) @@ -340,8 +374,8 @@ void AIEngine::mainloop(void) } if (total_clocks >= sMaxCount) { -#ifndef LL_RELEASE_FOR_DOWNLOAD - print_statemachine_diagnostics(total_clocks, max_delta, slowest_element); +#if STATE_MACHINE_PROFILING + print_statemachine_diagnostics(total_clocks, slowest_timer, slowest_element); #endif Dout(dc::statemachine, "Sorting " << engine_state_w->list.size() << " state machines."); engine_state_w->list.sort(QueueElementComp()); @@ -752,6 +786,22 @@ void AIStateMachine::multiplex(event_type event) } } +#if STATE_MACHINE_PROFILING +std::vector AIStateMachine::StateTimerBase::mTimerStack; +AIStateMachine::StateTimerBase::TimeData AIStateMachine::StateTimerBase::TimeData::sRoot(""); +void AIStateMachine::StateTimer::TimeData::DumpTimer(std::ostringstream& msg, std::string prefix) +{ + F64 const tfactor = 1000 / calc_clock_frequency(); + msg << prefix << mName << " " << (mEnd - mStart)*tfactor << "ms" << std::endl; + prefix.push_back(' '); + std::vector::iterator it; + for (it = mChildren.begin(); it != mChildren.end(); ++it) + { + it->DumpTimer(msg, prefix); + } +} +#endif + AIStateMachine::state_type AIStateMachine::begin_loop(base_state_type base_state) { DoutEntering(dc::statemachine(mSMDebug), "AIStateMachine::begin_loop(" << state_str(base_state) << ") [" << (void*)this << "]"); diff --git a/indra/aistatemachine/aistatemachine.h b/indra/aistatemachine/aistatemachine.h index a9ac33d68..8c80cc01f 100644 --- a/indra/aistatemachine/aistatemachine.h +++ b/indra/aistatemachine/aistatemachine.h @@ -36,6 +36,7 @@ #include "aithreadsafe.h" #include +#include "lltimer.h" #include #include @@ -98,11 +99,140 @@ class AIEngine extern AIEngine gMainThreadEngine; extern AIEngine gStateMachineThreadEngine; +#ifndef STATE_MACHINE_PROFILING +#define STATE_MACHINE_PROFILING (LL_RELEASE_FOR_DOWNLOAD) +#endif + class AIStateMachine : public LLThreadSafeRefCount { public: typedef U32 state_type; //!< The type of run_state + // A simple timer class that will calculate time delta between ctor and GetTimerData call. + // Time data is stored as a nested TimeData object. + // If STATE_MACHINE_PROFILING is defined then a stack of all StateTimers from root is maintained for debug output. + class StateTimerBase + { + public: + class TimeData + { + friend class StateTimerBase; + public: + TimeData() : mStart(-1), mEnd(-1) {} + U64 GetDuration() { return mEnd - mStart; } + private: + U64 mStart, mEnd; + +#if !STATE_MACHINE_PROFILING + TimeData(const std::string& name) : mStart(get_clock_count()), mEnd(get_clock_count()) {} +#else + TimeData(const std::string& name) : mName(name), mStart(get_clock_count()), mEnd(get_clock_count()) {} + void DumpTimer(std::ostringstream& msg, std::string prefix); + std::vector mChildren; + std::string mName; + static TimeData sRoot; +#endif + }; + protected: +#if !STATE_MACHINE_PROFILING + StateTimerBase(const std::string& name) : mData(name) {} + ~StateTimerBase() {} + TimeData mData; + // Return a copy of the underlying timer data. + // This allows the data live beyond the scope of the state timer. + public: + const TimeData GetTimerData() + { + mData.mEnd = get_clock_count(); //set mEnd to current time, since GetTimerData() will always be called before the dtor, obv. + return mData; + } +#else + // Ctors/dtors are hidden. Only StateTimerRoot and StateTimer are permitted to access them. + StateTimerBase() : mData(NULL) {} + ~StateTimerBase() + { + // If mData is null then the timer was not registered due to being in the wrong thread or the root timer wasn't in the expected state. + if (!mData) + return; + mData->mEnd = get_clock_count(); + mTimerStack.pop_back(); + } + + // Also hide internals from everything except StateTimerRoot and StateTimer + bool AddAsRoot(const std::string& name) + { + + if (!is_main_thread()) + return true; //Ignoring this timer, but pretending it was added. + if (!mTimerStack.empty()) + return false; + TimeData::sRoot = TimeData(name); + mData = &TimeData::sRoot; + mData->mChildren.clear(); + mTimerStack.push_back(this); + return true; + } + bool AddAsChild(const std::string& name) + { + if (!is_main_thread()) + return true; //Ignoring this timer, but pretending it was added. + if (mTimerStack.empty()) + return false; + mTimerStack.back()->mData->mChildren.push_back(TimeData(name)); + mData = &mTimerStack.back()->mData->mChildren.back(); + mTimerStack.push_back(this); + return true; + } + + TimeData* mData; + static std::vector mTimerStack; + + public: + // Debug spew + static void DumpTimers(std::ostringstream& msg) + { + TimeData::sRoot.DumpTimer(msg, ""); + } + + // Return a copy of the underlying timer data. + // This allows the data live beyond the scope of the state timer. + const TimeData GetTimerData() const + { + if (mData) + { + TimeData ret = *mData; + ret.mEnd = get_clock_count(); //set mEnd to current time, since GetTimerData() will always be called before the dtor, obv. + return ret; + } + return TimeData(); + } +#endif + }; +public: +#if !STATE_MACHINE_PROFILING + typedef StateTimerBase StateTimerRoot; + typedef StateTimerBase StateTimer; +#else + class StateTimerRoot : public StateTimerBase + { //A StateTimerRoot can become a child if a root already exists. + public: + StateTimerRoot(const std::string& name) + { + if(!AddAsRoot(name)) + AddAsChild(name); + } + }; + class StateTimer : public StateTimerBase + { //A StateTimer can never become a root + public: + StateTimer(const std::string& name) + { + AddAsChild(name); + } + }; +#endif + + protected: // The type of event that causes multiplex() to be called. enum event_type { @@ -302,6 +432,9 @@ class AIStateMachine : public LLThreadSafeRefCount void add(U64 count) { mRuntime += count; } U64 getRuntime(void) const { return mRuntime; } + // For diagnostics. Every derived class must override this. + virtual const char* getName() const = 0; + protected: virtual void initialize_impl(void) = 0; virtual void multiplex_impl(state_type run_state) = 0; diff --git a/indra/aistatemachine/aistatemachinethread.h b/indra/aistatemachine/aistatemachinethread.h index d2ca65c8d..58b1e812a 100644 --- a/indra/aistatemachine/aistatemachinethread.h +++ b/indra/aistatemachine/aistatemachinethread.h @@ -232,6 +232,13 @@ class AIStateMachineThread : public AIStateMachineThreadBase { // Accessor. THREAD_IMPL& thread_impl(void) { return mThreadImpl; } + /*virtual*/ const char* getName() const + { +#define STRIZE(arg) #arg + return "AIStateMachineThread<"STRIZE(THREAD_IMPL)">"; +#undef STRIZE + } + protected: /*virtual*/ AIThreadImpl& impl(void) { return mThreadImpl; } }; diff --git a/indra/aistatemachine/aitimer.h b/indra/aistatemachine/aitimer.h index a8aeb8ca6..f3623c1d9 100644 --- a/indra/aistatemachine/aitimer.h +++ b/indra/aistatemachine/aitimer.h @@ -98,6 +98,8 @@ class AITimer : public AIStateMachine { */ F64 getInterval(void) const { return mInterval; } + /*virtual*/ const char* getName() const { return "AITimer"; } + protected: // Call finish() (or abort()), not delete. /*virtual*/ ~AITimer() { DoutEntering(dc::statemachine(mSMDebug), "~AITimer() [" << (void*)this << "]"); mFrameTimer.cancel(); } diff --git a/indra/llappearance/llavatarjoint.cpp b/indra/llappearance/llavatarjoint.cpp index 353857bc9..5153debff 100644 --- a/indra/llappearance/llavatarjoint.cpp +++ b/indra/llappearance/llavatarjoint.cpp @@ -105,8 +105,9 @@ void LLAvatarJoint::setValid( BOOL valid, BOOL recursive ) for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); ++iter) { - LLAvatarJoint* joint = (LLAvatarJoint*)(*iter); - joint->setValid(valid, TRUE); + LLAvatarJoint* joint = dynamic_cast(*iter); + if (joint) + joint->setValid(valid, TRUE); } } @@ -138,8 +139,9 @@ void LLAvatarJoint::setVisible(BOOL visible, BOOL recursive) for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); ++iter) { - LLAvatarJoint* joint = (LLAvatarJoint*)(*iter); - joint->setVisible(visible, recursive); + LLAvatarJoint* joint = dynamic_cast(*iter); + if(joint) + joint->setVisible(visible, recursive); } } } diff --git a/indra/llappearance/llavatarjointmesh.cpp b/indra/llappearance/llavatarjointmesh.cpp index 4a5cff1dc..5099e5481 100644 --- a/indra/llappearance/llavatarjointmesh.cpp +++ b/indra/llappearance/llavatarjointmesh.cpp @@ -366,8 +366,9 @@ void LLAvatarJointMesh::setupJoint(LLAvatarJoint* current_joint) for (LLJoint::child_list_t::iterator iter = current_joint->mChildren.begin(); iter != current_joint->mChildren.end(); ++iter) { - LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter); - setupJoint(child_joint); + LLAvatarJoint* child_joint = dynamic_cast(*iter); + if(child_joint) + setupJoint(child_joint); } } diff --git a/indra/llappearance/llpolyskeletaldistortion.cpp b/indra/llappearance/llpolyskeletaldistortion.cpp index efdf5c577..3157cc965 100644 --- a/indra/llappearance/llpolyskeletaldistortion.cpp +++ b/indra/llappearance/llpolyskeletaldistortion.cpp @@ -154,13 +154,13 @@ BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info) for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin(); iter != joint->mChildren.end(); ++iter) { - LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter); - if (child_joint->inheritScale()) - { - LLVector3 childDeformation = LLVector3(child_joint->getScale()); - childDeformation.scaleVec(bone_info->mScaleDeformation); - mJointScales[child_joint] = childDeformation; - } + LLAvatarJoint* child_joint = dynamic_cast(*iter); + if (child_joint && child_joint->inheritScale()) + { + LLVector3 childDeformation = LLVector3(child_joint->getScale()); + childDeformation.scaleVec(bone_info->mScaleDeformation); + mJointScales[child_joint] = childDeformation; + } } if (bone_info->mHasPositionDeformation) diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 2e3d52733..5afd5dc61 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -239,7 +239,7 @@ inline typename T::mapped_type get_ptr_in_map(const T& inmap, typename T::key_ty // //Singu note: This has been generalized to support a broader range of sequence containers template -inline typename T::iterator vector_replace_with_last(T& invec, typename T::iterator& iter) +inline typename T::iterator vector_replace_with_last(T& invec, typename T::iterator iter) { typename T::iterator last = invec.end(); if (iter == invec.end()) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 5b806161b..a112277ad 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -78,7 +78,7 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string cons LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, AIPerService::Approvement* approved, bool keepalive, bool is_auth, bool compression) : mAction(action), mURL(url), mKeepAlive(keepalive), mIsAuth(is_auth), mNoCompression(!compression), - mBody(body), mResponder(responder), mHeaders(headers), mResponderNameCache(responder ? responder->getName() : "") + mBody(body), mResponder(responder), mHeaders(headers), mResponderNameCache(std::string("LLURLRequest:") + std::string(responder ? responder->getName() : "")) { if (approved) { @@ -276,32 +276,4 @@ bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w) return rv; } -// Called from AIStateMachine::mainloop, but put here because we don't want to include llurlrequest.h there of course. -void print_statemachine_diagnostics(U64 total_clocks, U64 max_delta, AIEngine::queued_type::const_reference slowest_element) -{ - AIStateMachine const& slowest_state_machine = slowest_element.statemachine(); - LLURLRequest const* request = dynamic_cast(&slowest_state_machine); - F64 const tfactor = 1000 / calc_clock_frequency(); - std::ostringstream msg; - if (total_clocks > max_delta) - { - msg << "AIStateMachine::mainloop did run for " << (total_clocks * tfactor) << " ms. The slowest "; - } - else - { - msg << "AIStateMachine::mainloop: A "; - } - msg << "state machine "; - if (request) - { - msg << "(" << request->getResponderName() << ") "; - } - msg << "ran for " << (max_delta * tfactor) << " ms"; - if (slowest_state_machine.getRuntime() > max_delta) - { - msg << " (" << (slowest_state_machine.getRuntime() * tfactor) << " ms in total now)"; - } - msg << "."; - llwarns << msg.str() << llendl; -} diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index c888b7db3..697b42b46 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -72,7 +72,7 @@ class LLURLRequest : public AICurlEasyRequestStateMachine { /** * @brief Cached value of responder->getName() as passed to the constructor. */ - char const* getResponderName(void) const { return mResponderNameCache; } + /*virtual*/ const char* getName() const { return mResponderNameCache.c_str(); } protected: // Call abort(), not delete. @@ -113,7 +113,7 @@ class LLURLRequest : public AICurlEasyRequestStateMachine { U32 mBodySize; LLHTTPClient::ResponderPtr mResponder; AIHTTPHeaders mHeaders; - char const* mResponderNameCache; + std::string mResponderNameCache; protected: // Handle initializing the object. diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index e0ec85523..29b146f59 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1543,6 +1543,8 @@ class UpdateItemSM : public AIStateMachine static void add(UpdateItem const& ui); + /*virtual*/ const char* getName() const { return "UpdateItemSM"; } + private: static UpdateItemSM* sSelf; typedef std::deque updateQueue_type; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index d23739622..4ab6515a6 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -766,7 +766,7 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) return http_url; } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) +bool LLMeshRepoThread::getMeshHeaderInfo(const LLUUID& mesh_id, const char* block_name, MeshHeaderInfo& info) { //protected by mMutex if (!mHeaderMutex) @@ -774,229 +774,141 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) return false; } - mHeaderMutex->lock(); + LLMutexLock lock(mHeaderMutex); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); return false; } - bool ret = true ; - U32 header_size = mMeshHeaderSize[mesh_id]; - - if (header_size > 0) + if ((info.mHeaderSize = mMeshHeaderSize[mesh_id]) > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger(); + info.mVersion = mMeshHeader[mesh_id]["version"].asInteger(); + info.mOffset = info.mHeaderSize + mMeshHeader[mesh_id][block_name]["offset"].asInteger(); + info.mSize = mMeshHeader[mesh_id][block_name]["size"].asInteger(); + } + return true; +} - mHeaderMutex->unlock(); +bool LLMeshRepoThread::loadInfoFromVFS(const LLUUID& mesh_id, MeshHeaderInfo& info, boost::function fn) +{ + //check VFS for mesh skin info + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= info.mOffset + info.mSize) + { + LLMeshRepository::sCacheBytesRead += info.mSize; - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + file.seek(info.mOffset); + U8* buffer = new U8[info.mSize]; + file.read(buffer, info.mSize); + + //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(info.mSize, S32(1024)) && zero; ++i) { - //check VFS for mesh skin info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (fn(mesh_id, buffer, info.mSize)) { - LLMeshRepository::sCacheBytesRead += size; - file.seek(offset); - U8* buffer = new U8[size]; - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //attempt to parse - if (skinInfoReceived(mesh_id, buffer, size)) - { - delete[] buffer; - return true; - } - } - delete[] buffer; - } - - //reading from VFS failed for whatever reason, fetch from sim - AIHTTPHeaders headers("Accept", "application/octet-stream"); - - std::string http_url = constructUrl(mesh_id); - if (!http_url.empty()) - { - ret = LLHTTPClient::getByteRange(http_url, headers, offset, size, - new LLMeshSkinInfoResponder(mesh_id, offset, size)); - if (ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + return true; } } + + delete[] buffer; } - else - { - mHeaderMutex->unlock(); + return false; +} + +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) +{ + MeshHeaderInfo info; + if (!getMeshHeaderInfo(mesh_id, "skin", info)) + { + return false; + } + + if (info.mHeaderSize > 0 && info.mVersion <= MAX_MESH_VERSION && info.mOffset >= 0 && info.mSize > 0) + { + //check VFS for mesh skin info + if (loadInfoFromVFS(mesh_id, info, boost::bind(&LLMeshRepoThread::skinInfoReceived, this, _1, _2, _3 ))) + return true; + + //reading from VFS failed for whatever reason, fetch from sim + AIHTTPHeaders headers("Accept", "application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + if (!LLHTTPClient::getByteRange(http_url, headers, info.mOffset, info.mSize, + new LLMeshSkinInfoResponder(mesh_id, info.mOffset, info.mSize))) + return false; + LLMeshRepository::sHTTPRequestCount++; + } } //early out was not hit, effectively fetched - return ret; + return true; } bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) -{ //protected by mMutex - if (!mHeaderMutex) +{ + MeshHeaderInfo info; + if (!getMeshHeaderInfo(mesh_id, "physics_convex", info)) { return false; } - mHeaderMutex->lock(); - - if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); - return false; - } - - U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; - - if (header_size > 0) + if (info.mHeaderSize > 0 && info.mVersion <= MAX_MESH_VERSION && info.mOffset >= 0 && info.mSize > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id]["physics_convex"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["physics_convex"]["size"].asInteger(); + if (loadInfoFromVFS(mesh_id, info, boost::bind(&LLMeshRepoThread::decompositionReceived, this, _1, _2, _3 ))) + return true; - mHeaderMutex->unlock(); + //reading from VFS failed for whatever reason, fetch from sim + AIHTTPHeaders headers("Accept", "application/octet-stream"); - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) - { - //check VFS for mesh skin info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesRead += size; - - file.seek(offset); - U8* buffer = new U8[size]; - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //attempt to parse - if (decompositionReceived(mesh_id, buffer, size)) - { - delete[] buffer; - return true; - } - } - - delete[] buffer; - } - - //reading from VFS failed for whatever reason, fetch from sim - AIHTTPHeaders headers("Accept", "application/octet-stream"); - - std::string http_url = constructUrl(mesh_id); - if (!http_url.empty()) - { - ret = LLHTTPClient::getByteRange(http_url, headers, offset, size, - new LLMeshDecompositionResponder(mesh_id, offset, size)); - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } - } + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + if (!LLHTTPClient::getByteRange(http_url, headers, info.mOffset, info.mSize, + new LLMeshDecompositionResponder(mesh_id, info.mOffset, info.mSize))) + return false; + LLMeshRepository::sHTTPRequestCount++; } } - else - { - mHeaderMutex->unlock(); - } //early out was not hit, effectively fetched - return ret; + return true; } bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) -{ //protected by mMutex - if (!mHeaderMutex) +{ + MeshHeaderInfo info; + if (!getMeshHeaderInfo(mesh_id, "physics_mesh", info)) { return false; } - mHeaderMutex->lock(); - - if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing - mHeaderMutex->unlock(); - return false; - } - - U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; - - if (header_size > 0) + if (info.mHeaderSize > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id]["physics_mesh"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["physics_mesh"]["size"].asInteger(); - - mHeaderMutex->unlock(); - - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + if (info.mVersion <= MAX_MESH_VERSION && info.mOffset >= 0 && info.mSize > 0) { - //check VFS for mesh physics shape info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesRead += size; - file.seek(offset); - U8* buffer = new U8[size]; - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //attempt to parse - if (physicsShapeReceived(mesh_id, buffer, size)) - { - delete[] buffer; - return true; - } - } - - delete[] buffer; - } + if (loadInfoFromVFS(mesh_id, info, boost::bind(&LLMeshRepoThread::physicsShapeReceived, this, _1, _2, _3 ))) + return true; //reading from VFS failed for whatever reason, fetch from sim AIHTTPHeaders headers("Accept", "application/octet-stream"); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) - { - ret = LLHTTPClient::getByteRange(http_url, headers, offset, size, - new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); - - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + { + if (!LLHTTPClient::getByteRange(http_url, headers, info.mOffset, info.mSize, + new LLMeshPhysicsShapeResponder(mesh_id, info.mOffset, info.mSize))) + return false; + LLMeshRepository::sHTTPRequestCount++; } } else @@ -1004,13 +916,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) physicsShapeReceived(mesh_id, NULL, 0); } } - else - { - mHeaderMutex->unlock(); - } - + //early out was not hit, effectively fetched - return ret; + return true; } //static @@ -1086,72 +994,34 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //return false if failed to get mesh lod. bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) -{ //protected by mMutex - if (!mHeaderMutex) +{ + LLUUID mesh_id = mesh_params.getSculptID(); + MeshHeaderInfo info; + + if (!getMeshHeaderInfo(mesh_id, header_lod[lod].c_str(), info)) { return false; } - - mHeaderMutex->lock(); - - bool retval = true; - - LLUUID mesh_id = mesh_params.getSculptID(); - - U32 header_size = mMeshHeaderSize[mesh_id]; - - if (header_size > 0) + + if (info.mHeaderSize > 0) { - S32 version = mMeshHeader[mesh_id]["version"].asInteger(); - S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger(); - mHeaderMutex->unlock(); - - if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) + if(info.mVersion <= MAX_MESH_VERSION && info.mOffset >= 0 && info.mSize > 0) { - - //check VFS for mesh asset - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) - { - LLMeshRepository::sCacheBytesRead += size; - file.seek(offset); - U8* buffer = new U8[size]; - file.read(buffer, size); - - //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) - bool zero = true; - for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) - { - zero = buffer[i] > 0 ? false : true; - } - - if (!zero) - { //attempt to parse - if (lodReceived(mesh_params, lod, buffer, size)) - { - delete[] buffer; - return true; - } - } - - delete[] buffer; - } + if (loadInfoFromVFS(mesh_id, info, boost::bind(&LLMeshRepoThread::lodReceived, this, mesh_params, lod, _2, _3 ))) + return true; //reading from VFS failed for whatever reason, fetch from sim AIHTTPHeaders headers("Accept", "application/octet-stream"); std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) - { - retval = LLHTTPClient::getByteRange(constructUrl(mesh_id), headers, offset, size, - new LLMeshLODResponder(mesh_params, lod, offset, size)); - - if (retval) - { - LLMeshRepository::sHTTPRequestCount++; - } - count++; + { + count++; + if (!LLHTTPClient::getByteRange(constructUrl(mesh_id), headers, info.mOffset, info.mSize, + new LLMeshLODResponder(mesh_params, lod, info.mOffset, info.mSize))) + return false; + LLMeshRepository::sHTTPRequestCount++; + } else { @@ -1163,12 +1033,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, mUnavailableQ.push(LODRequest(mesh_params, lod)); } } - else - { - mHeaderMutex->unlock(); - } - - return retval; + + return true; } bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) @@ -1236,16 +1102,21 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) { + AIStateMachine::StateTimer timer("lodReceived"); LLPointer volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); std::string mesh_string((char*) data, data_size); std::istringstream stream(mesh_string); + AIStateMachine::StateTimer timer2("unpackVolumeFaces"); if (volume->unpackVolumeFaces(stream, data_size)) { + AIStateMachine::StateTimer timer("getNumFaces"); if (volume->getNumFaces() > 0) { + AIStateMachine::StateTimer timer("LoadedMesh"); LoadedMesh mesh(volume, mesh_params, lod); { + AIStateMachine::StateTimer timer("LLMutexLock"); LLMutexLock lock(mMutex); mLoadedQ.push(mesh); } @@ -1900,6 +1771,7 @@ void LLMeshLODResponder::completedRaw(LLChannelDescriptors const& channels, { if (is_internal_http_error_that_warrants_a_retry(mStatus) || mStatus == HTTP_SERVICE_UNAVAILABLE) { //timeout or service unavailable, try again + AIStateMachine::StateTimer timer("loadMeshLOD"); llwarns << "Timeout or service unavailable, retrying." << llendl; LLMeshRepository::sHTTPRetryCount++; gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); @@ -1918,12 +1790,14 @@ void LLMeshLODResponder::completedRaw(LLChannelDescriptors const& channels, if (data_size > 0) { + AIStateMachine::StateTimer timer("readAfter"); data = new U8[data_size]; buffer->readAfter(channels.in(), NULL, data, data_size); } if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) { + AIStateMachine::StateTimer timer("FileOpen"); //good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); @@ -1932,6 +1806,7 @@ void LLMeshLODResponder::completedRaw(LLChannelDescriptors const& channels, if (file.getSize() >= offset+size) { + AIStateMachine::StateTimer timer("WriteData"); file.seek(offset); file.write(data, size); LLMeshRepository::sCacheBytesWritten += size; @@ -2157,6 +2032,7 @@ void LLMeshHeaderResponder::completedRaw(LLChannelDescriptors const& channels, if (is_internal_http_error_that_warrants_a_retry(mStatus) || mStatus == HTTP_SERVICE_UNAVAILABLE) { //retry + AIStateMachine::StateTimer timer("Retry"); llwarns << "Timeout or service unavailable, retrying." << llendl; LLMeshRepository::sHTTPRetryCount++; LLMeshRepoThread::HeaderRequest req(mMeshParams); @@ -2173,16 +2049,19 @@ void LLMeshHeaderResponder::completedRaw(LLChannelDescriptors const& channels, S32 data_size = buffer->countAfter(channels.in(), NULL); - U8* data = NULL; + static U8 data[16384]; + llassert_always(data_size <= sizeof(data)); + memset(data + data_size, 0, sizeof(data)-data_size); if (data_size > 0) { - data = new U8[data_size]; + AIStateMachine::StateTimer timer("readAfter"); buffer->readAfter(channels.in(), NULL, data, data_size); } LLMeshRepository::sBytesReceived += llmin(data_size, 4096); + AIStateMachine::StateTimer timer("headerReceived"); bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); llassert(success); @@ -2193,7 +2072,7 @@ void LLMeshHeaderResponder::completedRaw(LLChannelDescriptors const& channels, << "Unable to parse mesh header: " << mStatus << ": " << mReason << llendl; } - else if (data && data_size > 0) + else if (data_size > 0) { //header was successfully retrieved from sim, cache in vfs LLUUID mesh_id = mMeshParams.getSculptID(); @@ -2225,33 +2104,27 @@ void LLMeshHeaderResponder::completedRaw(LLChannelDescriptors const& channels, //only allocate as much space in the VFS as is needed for the local cache data_size = llmin(data_size, bytes); + AIStateMachine::StateTimer timer("FileOpen"); LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) { LLMeshRepository::sCacheBytesWritten += data_size; - file.write((const U8*) data, data_size); - - //zero out the rest of the file - U8 block[4096]; - memset(block, 0, 4096); - - while (bytes-file.tell() > 4096) + AIStateMachine::StateTimer timer("WriteData"); + S32 bytes_remaining = bytes; + while (bytes_remaining > 0) { - file.write(block, 4096); - } - - S32 remaining = bytes-file.tell(); - - if (remaining > 0) - { - file.write(block, remaining); + const S32 bytes_written = llmin(bytes_remaining, (S32)sizeof(data)); + file.write(data, bytes_written); + if (bytes_remaining == bytes && bytes_written < bytes_remaining) + { + memset(data, 0, data_size); + } + bytes_remaining -= llmin(bytes_remaining, bytes_written); } } } } - - delete [] data; } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index ab9220cdc..de26a1ba0 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -40,6 +40,8 @@ #include "lluploadfloaterobservers.h" #include "aistatemachinethread.h" +#include + class LLVOVolume; class LLMeshResponder; class LLMutex; @@ -283,6 +285,16 @@ public: }; + struct MeshHeaderInfo + { + MeshHeaderInfo() + : mHeaderSize(0), mVersion(0), mOffset(-1), mSize(0) {} + U32 mHeaderSize; + U32 mVersion; + S32 mOffset; + S32 mSize; + }; + //set of requested skin info std::set mSkinRequests; @@ -332,6 +344,9 @@ public: bool physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); LLSD& getMeshHeader(const LLUUID& mesh_id); + bool getMeshHeaderInfo(const LLUUID& mesh_id, const char* block_name, MeshHeaderInfo& info); + bool loadInfoFromVFS(const LLUUID& mesh_id, MeshHeaderInfo& info, boost::function fn); + void notifyLoadedMeshes(); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); @@ -450,6 +465,8 @@ public: void setWholeModelUploadURL(std::string const& whole_model_upload_url) { mWholeModelUploadURL = whole_model_upload_url; } + /*virtual*/ const char* getName() const { return "AIMeshUpload"; } + protected: // Implement AIStateMachine. /*virtual*/ const char* state_str_impl(state_type run_state) const; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 03f109d12..fd3346160 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2204,8 +2204,17 @@ void reload_objects(LLTextureReloader& texture_list, LLViewerObject::const_child for (U8 i = 0; i < object->getNumTEs(); i++) { texture_list.addTexture(object->getTEImage(i)); + const LLTextureEntry* te = object->getTE(i); + if (LLMaterial* mat = te ? te->getMaterialParams().get() : NULL) + { + if (mat->getSpecularID().notNull()) + texture_list.addTexture(LLViewerTextureManager::getFetchedTexture(mat->getSpecularID())); + if (mat->getNormalID().notNull()) + texture_list.addTexture(LLViewerTextureManager::getFetchedTexture(mat->getNormalID())); + } } + if(recurse) { reload_objects(texture_list,object->getChildren(), true); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index b04c53af5..2f9317090 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -758,7 +758,7 @@ public: // show an error to the user? llwarns << "Application level error when fetching object " - << "cost. Message: " << mContent["error"]["message"].asString() + << "cost. Message: " << mContent["error"]["message"].asString() << ", identifier: " << mContent["error"]["identifier"].asString() << llendl; @@ -871,7 +871,7 @@ public: LLUUID object_id = iter->asUUID(); // Check to see if the request contains data for the object - if ( mContent.has(iter->asString()) ) + if (mContent.has(iter->asString())) { const LLSD& data = mContent[iter->asString()]; @@ -1459,8 +1459,7 @@ void LLViewerObjectList::removeFromActiveList(LLViewerObject* objectp) objectp->setListIndex(-1); - std::vector >::iterator it(mActiveObjects.begin() + idx); - std::vector >::iterator iter = vector_replace_with_last(mActiveObjects, it); + std::vector >::iterator iter = vector_replace_with_last(mActiveObjects, mActiveObjects.begin() + idx); if(iter != mActiveObjects.end()) (*iter)->setListIndex(idx); diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp index c3f49f69f..3c0c37b3e 100644 --- a/indra/newview/llviewerpartsim.cpp +++ b/indra/newview/llviewerpartsim.cpp @@ -406,8 +406,7 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt) // Kill dead particles (either flagged dead, or too old) if ((part->mLastUpdateTime > part->mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part->mFlags)) { - part_list_t::iterator it(mParticles.begin() + i); - vector_replace_with_last(mParticles, it); + vector_replace_with_last(mParticles, mParticles.begin() + i); delete part ; } else @@ -417,8 +416,7 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt) { // Transfer particles between groups LLViewerPartSim::getInstance()->put(part) ; - part_list_t::iterator it(mParticles.begin() + i); - vector_replace_with_last(mParticles, it); + vector_replace_with_last(mParticles, mParticles.begin() + i); } else { @@ -723,8 +721,7 @@ void LLViewerPartSim::updateSimulation() if (mViewerPartSources[i]->isDead()) { - source_list_t::iterator it(mViewerPartSources.begin() + i); - vector_replace_with_last(mViewerPartSources, it); + vector_replace_with_last(mViewerPartSources, mViewerPartSources.begin() + i); //mViewerPartSources.erase(it); count--; } @@ -761,8 +758,7 @@ void LLViewerPartSim::updateSimulation() if (!mViewerPartGroups[i]->getCount()) { delete mViewerPartGroups[i]; - group_list_t::iterator it(mViewerPartGroups.begin() + i); - vector_replace_with_last(mViewerPartGroups, it); + vector_replace_with_last(mViewerPartGroups, mViewerPartGroups.begin() + i); //mViewerPartGroups.erase(it); i--; count--; diff --git a/indra/newview/statemachine/aidirpicker.h b/indra/newview/statemachine/aidirpicker.h index 484a8e650..877b0571f 100644 --- a/indra/newview/statemachine/aidirpicker.h +++ b/indra/newview/statemachine/aidirpicker.h @@ -63,6 +63,8 @@ public: bool hasDirname(void) const { return hasFilename(); } std::string const& getDirname(void) const { return getFilename(); } + /*virtual*/ const char* getName() const { return "AIDirPicker"; } + public: // Basically all public members of AIStateMachine could made accessible here, // but I don't think others will ever be needed (not even these, actually). diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index 9ef02017b..08e218f71 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -188,6 +188,8 @@ public: std::string getFolder(void) const; std::vector const& getFilenames(void) const { return mFilenames; } + /*virtual*/ const char* getName() const { return "AIFilePicker"; } + // Load the sContextMap from disk. static bool loadFile(std::string const& filename); // Save the sContextMap to disk.