From 78dbaf2ed16bb901ac7121a1f25ff40329af6c10 Mon Sep 17 00:00:00 2001 From: Latif Khalifa Date: Mon, 4 Feb 2013 00:47:19 +0100 Subject: [PATCH 01/41] Moved RLVa menu back to advanced by default --- indra/newview/app_settings/settings_rlv.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings_rlv.xml b/indra/newview/app_settings/settings_rlv.xml index bb385a033..7dd83af41 100644 --- a/indra/newview/app_settings/settings_rlv.xml +++ b/indra/newview/app_settings/settings_rlv.xml @@ -209,7 +209,7 @@ Type Boolean Value - 1 + 0 RLVaWearReplaceUnlocked From db5846c1456de27be8a572f1262d944c12c1f54c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 7 Feb 2013 17:42:54 +0100 Subject: [PATCH 02/41] Added documentation on LLHTTPClient Responders. --- doc/responders.txt | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 doc/responders.txt diff --git a/doc/responders.txt b/doc/responders.txt new file mode 100644 index 000000000..b1bf31f50 --- /dev/null +++ b/doc/responders.txt @@ -0,0 +1,82 @@ +All Responders are derived from ResponderBase, however you normally do never derived from that directly yourself. +Instead, Responder classes are derived from one of: + +1. Responder base classes + + ResponderHeadersOnly -- Derived classes are used with HTTPClient::head or HTTPClient::getHeaderOnly. + ResponderWithCompleted -- Derived classes implement completed(U32, std::string const&, LLSD const&), + or completedRaw(U32, std::string const&, LLChannelDescriptors const&, buffer_ptr_t const&) + if the response is not (always) LLSD. + ResponderWithResult -- Derived classes implement result(LLSD const&) and optionally + errorWithContent(U32, std::string const&, LLSD const&) OR error(U32, std::string const&). + +2. Special base classes + + ResponderIgnoreBody -- Same as ResponderWithResult but already implements result() that ignored the body. + LLAssetUploadResponder -- Derived from ResponderWithResult. Base class for responders that upload assets via capabilities. + LegacyPolledResponder -- Used for old code that needs polling (do not use). + + +There is one non-base class Responder with a more general purpose: + +3. Special purpose responders: + + ResponderIgnore -- Derived from ResponderIgnoreBody. Used for "fire and forget" requests as it ignores any response. + + +4. Signatures. + +Every final (derived) responder class must implement 'getName(void) const' and 'getHTTPTimeoutPolicy(void)', +except the base classes (this is to alert the developer they have to implement getName as it is pure virtual). + +For example: + + extern AIHTTPTimeoutPolicy myResponder_timeout; // Add 'P(myResponder)' to indra/llmessage/aihttptimeoutpolicy.cpp. + + class MyResponder : public LLHTTPClient::SomeResponderBaseClass { + ... + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return myResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "MyResponder"; } + }; + +Note the convention that the name of a AIHTTPTimeoutPolicy (what goes between the brackets of P()) is +the class name minus any 'AI' or 'LL' prefix and starting with a lowercase character. + +Then, depending on the three main base classes that was derived from, the signatures should be: + + class MyResponder1 : public LLHTTPClient::ResponderHeadersOnly { + /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers); + // See for example PostImageResponder + ... + }; + + class MyResponder2 : public LLHTTPClient::ResponderWithCompleted { + /*virtual*/ void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer); + // See for example PostImageRedirectResponder + >>>OR<<< + + /*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content); // See for example LLImportPostResponder + + }; + + class MyResponder3 : public LLHTTPClient::ResponderWithResult { + /*virtual*/ void result(const LLSD& content); // See for example LLInventoryModelFetchItemResponder + /*virtual*/ void error(U32 status, const std::string& reason); + >>>OR instead error()<<< + /*virtual*/ void errorWithContent(U32 status, const std::string& reason, const LLSD& content); + // See for example LLSDMessage::EventResponder + }; + +Finally, if a responder derived from ResponderWithCompleted or ResponderWithResult needs to process +individual headers, you need to override 'needsHeaders': + + /*virtual*/ bool needsHeaders(void) const { return true; } // See for example LLWebProfileResponders::PostImageResponder + // which will cause this to be called: + /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers); + +And if it needs redirection to work (you'll get an assert if you forget this and it is being redirected): + + /*virtual*/ bool followRedir(void) const { return true; } // See for example LLWebProfileResponders::ConfigResponder + +This is not necessary for ResponderHeadersOnly because that already defines both. + From 56b2c10312bff41296e03816dc759529d3209253 Mon Sep 17 00:00:00 2001 From: Latif Khalifa Date: Thu, 7 Feb 2013 18:17:15 +0100 Subject: [PATCH 03/41] Renamed deferred rendering switch from 'Lightning and Shadows' to 'Advanced Lighting Model' as per upstream --- .../skins/default/xui/en-us/panel_preferences_graphics1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_graphics1.xml index bfe2e285e..939e27d86 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_graphics1.xml @@ -52,7 +52,7 @@ - + Terrain Scale: From adf4c9a0ce29e8685d298e49c22a8e12d038c46e Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 4 Feb 2013 22:41:46 +0100 Subject: [PATCH 04/41] Make the type of the read/write lock of AIThreadSafe a template parameter. This allows passing a different type than the default AIRWLock for debugging purposes. The signature of the class used for RWLOCK should be: struct RWLock { // Default constructor. RWLock(void); // Block until it's safe to read the data. // high_priority is a hint that granting this thread the read lock is more important than granting another thread a write lock. void rdlock(bool high_priority = false); // Release the obtained read lock. void rdunlock(); // Block until it's safe to write to the data. void wrlock(); // Release the obtained write lock. void wrunlock(); // Convert the obtained write lock into a read lock. void wr2rdlock(); // Block until it is possible to convert the obtained read lock into a write lock. void rd2wrlock(); // Return true when a read or write is obtained and subsequent calls to release the lock are expected. bool isLocked() const; }; --- indra/llcommon/aithreadsafe.h | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index 425741601..461bb4cc5 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -106,9 +106,9 @@ // on a compiler that doesn't need this hack. #define AI_NEED_ACCESS_CC (defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || (__GNUC__ < 4))) -template struct AIReadAccessConst; -template struct AIReadAccess; -template struct AIWriteAccess; +template struct AIReadAccessConst; +template struct AIReadAccess; +template struct AIWriteAccess; template struct AIAccessConst; template struct AIAccess; template struct AISTAccessConst; @@ -225,17 +225,17 @@ protected: * * */ -template +template class AIThreadSafe : public AIThreadSafeBits { protected: // Only these may access the object (through ptr()). - friend struct AIReadAccessConst; - friend struct AIReadAccess; - friend struct AIWriteAccess; + friend struct AIReadAccessConst; + friend struct AIReadAccess; + friend struct AIWriteAccess; // Locking control. - AIRWLock mRWLock; + RWLOCK mRWLock; // For use by AIThreadSafeDC AIThreadSafe(void) { } @@ -310,20 +310,20 @@ public: * * which is not possible with AITHREADSAFE. */ -template -class AIThreadSafeDC : public AIThreadSafe +template +class AIThreadSafeDC : public AIThreadSafe { public: // Construct a wrapper around a default constructed object. - AIThreadSafeDC(void) { new (AIThreadSafe::ptr()) T; } + AIThreadSafeDC(void) { new (AIThreadSafe::ptr()) T; } // Allow an arbitrary parameter to be passed for construction. - template AIThreadSafeDC(T2 const& val) { new (AIThreadSafe::ptr()) T(val); } + template AIThreadSafeDC(T2 const& val) { new (AIThreadSafe::ptr()) T(val); } }; /** * @brief Read lock object and provide read access. */ -template +template struct AIReadAccessConst { //! Internal enum for the lock-type of the AI*Access object. @@ -336,8 +336,8 @@ struct AIReadAccessConst }; //! Construct a AIReadAccessConst from a constant AIThreadSafe. - AIReadAccessConst(AIThreadSafe const& wrapper, bool high_priority = false) - : mWrapper(const_cast&>(wrapper)), + AIReadAccessConst(AIThreadSafe const& wrapper, bool high_priority = false) + : mWrapper(const_cast&>(wrapper)), mState(readlocked) #if AI_NEED_ACCESS_CC , mIsCopyConstructed(false) @@ -369,14 +369,14 @@ struct AIReadAccessConst protected: //! Constructor used by AIReadAccess. - AIReadAccessConst(AIThreadSafe& wrapper, state_type state) + AIReadAccessConst(AIThreadSafe& wrapper, state_type state) : mWrapper(wrapper), mState(state) #if AI_NEED_ACCESS_CC , mIsCopyConstructed(false) #endif { } - AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to. + AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to. state_type const mState; //!< The lock state that mWrapper is in. #if AI_NEED_ACCESS_CC @@ -393,39 +393,39 @@ private: /** * @brief Read lock object and provide read access, with possible promotion to write access. */ -template -struct AIReadAccess : public AIReadAccessConst +template +struct AIReadAccess : public AIReadAccessConst { - typedef typename AIReadAccessConst::state_type state_type; - using AIReadAccessConst::readlocked; + typedef typename AIReadAccessConst::state_type state_type; + using AIReadAccessConst::readlocked; //! Construct a AIReadAccess from a non-constant AIThreadSafe. - AIReadAccess(AIThreadSafe& wrapper, bool high_priority = false) : AIReadAccessConst(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(high_priority); } + AIReadAccess(AIThreadSafe& wrapper, bool high_priority = false) : AIReadAccessConst(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(high_priority); } protected: //! Constructor used by AIWriteAccess. - AIReadAccess(AIThreadSafe& wrapper, state_type state) : AIReadAccessConst(wrapper, state) { } + AIReadAccess(AIThreadSafe& wrapper, state_type state) : AIReadAccessConst(wrapper, state) { } - friend struct AIWriteAccess; + friend struct AIWriteAccess; }; /** * @brief Write lock object and provide read/write access. */ -template -struct AIWriteAccess : public AIReadAccess +template +struct AIWriteAccess : public AIReadAccess { - using AIReadAccessConst::readlocked; - using AIReadAccessConst::read2writelocked; - using AIReadAccessConst::writelocked; - using AIReadAccessConst::write2writelocked; + using AIReadAccessConst::readlocked; + using AIReadAccessConst::read2writelocked; + using AIReadAccessConst::writelocked; + using AIReadAccessConst::write2writelocked; //! Construct a AIWriteAccess from a non-constant AIThreadSafe. - AIWriteAccess(AIThreadSafe& wrapper) : AIReadAccess(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();} + AIWriteAccess(AIThreadSafe& wrapper) : AIReadAccess(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();} //! Promote read access to write access. - explicit AIWriteAccess(AIReadAccess& access) - : AIReadAccess(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked) + explicit AIWriteAccess(AIReadAccess& access) + : AIReadAccess(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked) { if (this->mState == read2writelocked) { From e7aeb3feafb28a5353691bfdb0abd7dea8c58b77 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 4 Feb 2013 22:56:40 +0100 Subject: [PATCH 05/41] Add AINRLock for debugging purposes. This class can be used as RWLOCK parameter to AIThreadSafe to check that data is only accessed by a single thread (like AIThreadSafeSingleThreaded) AND is never write locked when a read or write lock was already obtained (by the same thread). It doesn't actually lock anything, it just keeps track if the "lock" was obtained before. The use case is to check if STL containers aren't being used (read or write locked) by a calling function when additional write access is necessary, as write access might invalidate iterator that point to the container (where the previous lock was taken). --- indra/llcommon/llthread.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 08d6e2110..f64200c95 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -397,6 +397,43 @@ public: #endif }; +#if LL_DEBUG +class LL_COMMON_API AINRLock +{ +private: + int read_locked; + int write_locked; + + mutable bool mAccessed; + mutable AIThreadID mTheadID; + + void accessed(void) const + { + if (!mAccessed) + { + mAccessed = true; + mTheadID.reset(); + } + else + { + llassert_always(mTheadID.equals_current_thread()); + } + } + +public: + AINRLock(void) : read_locked(false), write_locked(false), mAccessed(false) { } + + bool isLocked() const { return read_locked || write_locked; } + + void rdlock(bool high_priority = false) { accessed(); ++read_locked; } + void rdunlock() { --read_locked; } + void wrlock() { llassert(!isLocked()); accessed(); ++write_locked; } + void wrunlock() { --write_locked; } + void wr2rdlock() { llassert(false); } + void rd2wrlock() { llassert(false); } +}; +#endif + //============================================================================ void LLThread::lockData() From c1e6812f412a66939750af01feb45922f336c245 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Feb 2013 02:03:14 +0100 Subject: [PATCH 06/41] Remove unused variable. --- indra/newview/llfolderview.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 82e12f525..bea49eb6b 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1513,12 +1513,6 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) LLMenuGL::sMenuContainer->hideMenus(); } - LLView *item = NULL; - if (getChildCount() > 0) - { - item = *(getChildList()->begin()); - } - switch( key ) { case KEY_F2: From ea114986bee76f63eab7cdbf22cbd7c61b587ba6 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 5 Feb 2013 21:08:57 +0100 Subject: [PATCH 07/41] Use viewList_t and child_list_t in appropriate places. Both types are currently the same, but soon they will be made different. Currently they are used a bit mixed up. This patch fixes that. --- indra/llui/lluictrl.cpp | 14 +++++++------- indra/llui/llview.cpp | 20 ++++++++++---------- indra/llui/llview.h | 4 ++-- indra/llui/llviewquery.cpp | 8 ++++---- indra/llui/llviewquery.h | 2 +- indra/newview/llfloatereditui.cpp | 6 +++--- indra/newview/llpanelmediahud.cpp | 4 ++-- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 58cb3e5fa..5e388e233 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -302,8 +302,8 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) LLCtrlQuery query = getTabOrderQuery(); // sort things such that the default tab group is at the front query.setSorter(DefaultTabGroupFirstSorter::getInstance()); - child_list_t result = query(this); - if(result.size() > 0) + viewList_t result = query(this); + if(!result.empty()) { LLUICtrl * ctrl = static_cast(result.front()); if(!ctrl->hasFocus()) @@ -322,7 +322,7 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash) { LLCtrlQuery query = getTabOrderQuery(); query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - child_list_t result = query(this); + viewList_t result = query(this); if(result.size() > 0) { LLUICtrl * ctrl = static_cast(result.front()); @@ -358,7 +358,7 @@ BOOL LLUICtrl::focusLastItem(BOOL prefer_text_fields) { LLCtrlQuery query = getTabOrderQuery(); query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); - child_list_t result = query(this); + viewList_t result = query(this); if(result.size() > 0) { LLUICtrl * ctrl = static_cast(result.back()); @@ -372,7 +372,7 @@ BOOL LLUICtrl::focusLastItem(BOOL prefer_text_fields) } } // no text field found, or we don't care about text fields - child_list_t result = getTabOrderQuery().run(this); + viewList_t result = getTabOrderQuery().run(this); if(result.size() > 0) { LLUICtrl * ctrl = static_cast(result.back()); @@ -395,7 +395,7 @@ BOOL LLUICtrl::focusNextItem(BOOL text_fields_only) { query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); } - child_list_t result = query(this); + viewList_t result = query(this); return focusNext(result); } @@ -407,7 +407,7 @@ BOOL LLUICtrl::focusPrevItem(BOOL text_fields_only) { query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance()); } - child_list_t result = query(this); + viewList_t result = query(this); return focusPrev(result); } diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 07f99132b..40ee816a7 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -518,21 +518,21 @@ LLRect LLView::getRequiredRect() BOOL LLView::focusNextRoot() { - LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); + viewList_t result = LLView::getFocusRootsQuery().run(this); return LLView::focusNext(result); } BOOL LLView::focusPrevRoot() { - LLView::child_list_t result = LLView::getFocusRootsQuery().run(this); + viewList_t result = LLView::getFocusRootsQuery().run(this); return LLView::focusPrev(result); } // static -BOOL LLView::focusNext(LLView::child_list_t & result) +BOOL LLView::focusNext(viewList_t& result) { - LLView::child_list_iter_t focused = result.end(); - for(LLView::child_list_iter_t iter = result.begin(); + viewList_t::iterator focused = result.end(); + for(viewList_t::iterator iter = result.begin(); iter != result.end(); ++iter) { @@ -542,7 +542,7 @@ BOOL LLView::focusNext(LLView::child_list_t & result) break; } } - LLView::child_list_iter_t next = focused; + viewList_t::iterator next = focused; next = (next == result.end()) ? result.begin() : ++next; while(next != focused) { @@ -565,10 +565,10 @@ BOOL LLView::focusNext(LLView::child_list_t & result) } // static -BOOL LLView::focusPrev(LLView::child_list_t & result) +BOOL LLView::focusPrev(viewList_t& result) { - LLView::child_list_reverse_iter_t focused = result.rend(); - for(LLView::child_list_reverse_iter_t iter = result.rbegin(); + viewList_t::reverse_iterator focused = result.rend(); + for(viewList_t::reverse_iterator iter = result.rbegin(); iter != result.rend(); ++iter) { @@ -578,7 +578,7 @@ BOOL LLView::focusPrev(LLView::child_list_t & result) break; } } - LLView::child_list_reverse_iter_t next = focused; + viewList_t::reverse_iterator next = focused; next = (next == result.rend()) ? result.rbegin() : ++next; while(next != focused) { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 233e56bbc..fbd132a8a 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -582,9 +582,9 @@ public: static std::string escapeXML(const std::string& xml, std::string& indent); // focuses the item in the list after the currently-focused item, wrapping if necessary - static BOOL focusNext(LLView::child_list_t & result); + static BOOL focusNext(viewList_t& result); // focuses the item in the list before the currently-focused item, wrapping if necessary - static BOOL focusPrev(LLView::child_list_t & result); + static BOOL focusPrev(viewList_t& result); // returns query for iterating over controls in tab order static const LLCtrlQuery & getTabOrderQuery(); diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index 1c9ba6b8f..2c1b9c2cd 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -113,12 +113,12 @@ viewList_t LLViewQuery::run(LLView* view) const void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const { - LLView::child_list_t views(*(view->getChildList())); + viewList_t views(*(view->getChildList())); if (mSorterp) { (*mSorterp)(view, views); // sort the children per the sorter } - for(LLView::child_list_iter_t iter = views.begin(); + for(viewList_t::iterator iter = views.begin(); iter != views.end(); iter++) { @@ -127,7 +127,7 @@ void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) } } -filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const +filterResult_t LLViewQuery::runFilters(LLView* view, viewList_t const& children, filterList_t const& filters) const { filterResult_t result = filterResult_t(TRUE, TRUE); for(filterList_const_iter_t iter = filters.begin(); @@ -143,7 +143,7 @@ filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, class SortByTabOrder : public LLQuerySorter, public LLSingleton { - /*virtual*/ void operator() (LLView * parent, LLView::child_list_t &children) const + /*virtual*/ void operator() (LLView* parent, viewList_t& children) const { children.sort(LLCompareByTabOrder(parent->getCtrlOrder())); } diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 98d9bf879..e058985cc 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -126,7 +126,7 @@ public: private: - filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const; + filterResult_t runFilters(LLView* view, viewList_t const& children, filterList_t const& filters) const; filterList_t mPreFilters; filterList_t mPostFilters; diff --git a/indra/newview/llfloatereditui.cpp b/indra/newview/llfloatereditui.cpp index ff67a2069..fbef26b18 100644 --- a/indra/newview/llfloatereditui.cpp +++ b/indra/newview/llfloatereditui.cpp @@ -56,9 +56,9 @@ void LLFloaterEditUI::navigateHierarchyButtonPressed(void* data) const LLView::child_list_t* viewChildren = view->getChildList(); const LLView::child_list_t* parentChildren = parent->getChildList(); //LLView::child_list_t::iterator - std::list::const_iterator itor; - std::list::size_type idx; - std::list::size_type sidx; + LLView::child_list_t::const_iterator itor; + LLView::child_list_t::size_type idx; + LLView::child_list_t::size_type sidx; for(idx = 0,itor = parentChildren->begin();itor!=parentChildren->end();itor++,idx++){ if((*itor)==view)break; } diff --git a/indra/newview/llpanelmediahud.cpp b/indra/newview/llpanelmediahud.cpp index 27d1473d6..e56e976f5 100644 --- a/indra/newview/llpanelmediahud.cpp +++ b/indra/newview/llpanelmediahud.cpp @@ -424,8 +424,8 @@ void LLPanelMediaHUD::setAlpha(F32 alpha) LLViewQuery query; LLView* query_view = mMediaFocus ? getChildView("media_focused_controls") : getChildView("media_hover_controls"); - child_list_t children = query(query_view); - for (child_list_iter_t child_iter = children.begin(); + viewList_t children = query(query_view); + for (viewList_t::iterator child_iter = children.begin(); child_iter != children.end(); ++child_iter) { LLUICtrl* ctrl = dynamic_cast(*child_iter); From baab1d81a7f7633aa123e45e563054cf22508d40 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 6 Feb 2013 03:06:06 +0100 Subject: [PATCH 08/41] Wrap LLView::child_list_t in a class AIList. So far not doing anything. Iterators already keep a pointer to the container they belong to. --- indra/llui/CMakeLists.txt | 1 + indra/llui/ailist.h | 191 +++++++++++++++++++++++++++++++++++++ indra/llui/llview.h | 3 +- indra/llui/llviewquery.cpp | 4 +- 4 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 indra/llui/ailist.h diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 6aa14e8d6..e35d5b5dd 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -82,6 +82,7 @@ set(llui_SOURCE_FILES set(llui_HEADER_FILES CMakeLists.txt + ailist.h llalertdialog.h llbutton.h llcallbackmap.h diff --git a/indra/llui/ailist.h b/indra/llui/ailist.h new file mode 100644 index 000000000..821f679cb --- /dev/null +++ b/indra/llui/ailist.h @@ -0,0 +1,191 @@ +/** + * @file ailist.h + * @brief A linked list with iterators that advance when their element is deleted. + * + * 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/02/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AILIST_H +#define AILIST_H + +#include + +template +class AIConstListIterator; + +template +class AIListIterator { + private: + typedef AIListIterator _Self; + typedef std::list _Container; + typedef typename _Container::iterator _Iterator; + + _Container* mContainer; + _Iterator mIterator; + + public: + typedef typename _Iterator::difference_type difference_type; + typedef typename _Iterator::iterator_category iterator_category; + typedef typename _Iterator::value_type value_type; + typedef typename _Iterator::pointer pointer; + typedef typename _Iterator::reference reference; + + AIListIterator(void) : mContainer(NULL) { } + AIListIterator(_Container* __c, _Iterator const& __i) : mContainer(__c), mIterator(__i) { } + + _Self& operator=(_Self const& __x) { mContainer = __x.mContainer; mIterator = __x.mIterator; return *this; } + + reference operator*() const { return *mIterator; } + pointer operator->() const { return mIterator.operator->(); } + _Self& operator++() { ++mIterator; return *this; } + _Self operator++(int) { _Self tmp = *this; ++mIterator; return tmp; } + _Self& operator--() { --mIterator; return *this; } + _Self operator--(int) { _Self tmp = *this; --mIterator; return tmp; } + + bool operator==(_Self const& __x) const { return mIterator == __x.mIterator; } + bool operator!=(_Self const& __x) const { return mIterator != __x.mIterator; } + + template friend class AIConstListIterator; + template friend bool operator==(AIListIterator const& __x, AIConstListIterator const& __y); + template friend bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y); +}; + +template +class AIConstListIterator { + private: + typedef AIConstListIterator _Self; + typedef std::list _Container; + typedef typename _Container::const_iterator _ConstIterator; + typedef AIListIterator iterator; + + _Container const* mContainer; + _ConstIterator mConstIterator; + + public: + typedef typename _ConstIterator::difference_type difference_type; + typedef typename _ConstIterator::iterator_category iterator_category; + typedef typename _ConstIterator::value_type value_type; + typedef typename _ConstIterator::pointer pointer; + typedef typename _ConstIterator::reference reference; + + AIConstListIterator(void) : mContainer(NULL) { } + AIConstListIterator(_Container const* __c, _ConstIterator const& __i) : mContainer(__c), mConstIterator(__i) { } + AIConstListIterator(iterator const& __x) : mContainer(__x.mContainer), mConstIterator(__x.mIterator) { } + + _Self& operator=(_Self const& __x) { mContainer = __x.mContainer; mConstIterator = __x.mConstIterator; return *this; } + _Self& operator=(iterator const& __x) { mContainer = __x.mContainer; mConstIterator = __x.mIterator; return *this; } + + reference operator*() const { return *mConstIterator; } + pointer operator->() const { return mConstIterator.operator->(); } + _Self& operator++() { ++mConstIterator; return *this; } + _Self operator++(int) { _Self tmp = *this; ++mConstIterator; return tmp; } + _Self& operator--() { --mConstIterator; return *this; } + _Self operator--(int) { _Self tmp = *this; --mConstIterator; return tmp; } + + bool operator==(_Self const& __x) const { return mConstIterator == __x.mConstIterator; } + bool operator!=(_Self const& __x) const { return mConstIterator != __x.mConstIterator; } + + template friend bool operator==(AIListIterator const& __x, AIConstListIterator const& __y); + template friend bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y); +}; + +template +inline bool operator==(AIListIterator const& __x, AIConstListIterator const& __y) +{ + return __x.mIterator == __y.mConstIterator; +} + +template +inline bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y) +{ + return __x.mIterator != __y.mConstIterator; +} + +template +class AIList { + private: + std::list mContainer; + + public: + typedef T value_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef AIListIterator iterator; + typedef AIConstListIterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + AIList(void) { } + //AIList(std::list const& __list) : mContainer(__list) { } + + explicit AIList(size_type __n, value_type const& __value = value_type()) : mContainer(__n, __value) { } + AIList(AIList const& __list) : mContainer(__list.mContainer) { } + + template + AIList(_InputIterator __first, _InputIterator __last) : mContainer(__first, __last) { } + + AIList& operator=(AIList const& __list) { mContainer = __list.mContainer; return *this; } + + iterator begin() { return iterator(&mContainer, mContainer.begin()); } + const_iterator begin() const { return const_iterator(&mContainer, mContainer.begin()); } + iterator end() { return iterator(&mContainer, mContainer.end()); } + const_iterator end() const { return const_iterator(&mContainer, mContainer.end()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + bool empty() const { return mContainer.empty(); } + size_type size() const { return mContainer.size(); } + size_type max_size() const { return mContainer.max_size(); } + + reference front() { return mContainer.front(); } + const_reference front() const { return mContainer.front(); } + reference back() { return mContainer.back(); } + const_reference back() const { return mContainer.back(); } + + void push_front(value_type const& __x) { mContainer.push_front(__x); } + void pop_front() { mContainer.pop_front(); } + void push_back(value_type const& __x) { mContainer.push_back(__x); } + void pop_back() { mContainer.pop_back(); } + iterator insert(iterator __position, value_type const& __x) { return iterator(&mContainer, mContainer.insert(__position, __x)); } + iterator erase(iterator __position) { return iterator(&mContainer, mContainer.erase(__position)); } + void clear() { mContainer.clear(); } + void remove(value_type const& __value) { mContainer.remove(__value); } + + // Use this with care. No iterators should be left pointing at elements after the code returns. + std::list const& get_std_list(void) const { return mContainer; } + + void sort() { mContainer.sort(); } + template + void sort(_StrictWeakOrdering pred) { mContainer.sort(pred); } +}; + +#endif diff --git a/indra/llui/llview.h b/indra/llui/llview.h index fbd132a8a..2ec5ce58c 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -57,6 +57,7 @@ #include "llinitparam.h" #include "llfocusmgr.h" #include +#include "ailist.h" const U32 FOLLOWS_NONE = 0x00; const U32 FOLLOWS_LEFT = 0x01; @@ -231,7 +232,7 @@ public: SNAP_BOTTOM }; - typedef std::list child_list_t; + typedef AIList child_list_t; typedef child_list_t::iterator child_list_iter_t; typedef child_list_t::const_iterator child_list_const_iter_t; typedef child_list_t::reverse_iterator child_list_reverse_iter_t; diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index 2c1b9c2cd..7faf53726 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -76,7 +76,7 @@ viewList_t LLViewQuery::run(LLView* view) const viewList_t result; // prefilter gets immediate children of view - filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters); + filterResult_t pre = runFilters(view, view->getChildList()->get_std_list(), mPreFilters); if(!pre.first && !pre.second) { // not including ourselves or the children @@ -113,7 +113,7 @@ viewList_t LLViewQuery::run(LLView* view) const void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const { - viewList_t views(*(view->getChildList())); + viewList_t views(view->getChildList()->get_std_list()); if (mSorterp) { (*mSorterp)(view, views); // sort the children per the sorter From d5482e6c74cabb2b9e79c76d259ed13acc3873aa Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 6 Feb 2013 03:09:17 +0100 Subject: [PATCH 09/41] Do not pre- increment the loop iterator in LLView::drawChildren This is in fact much safer, because the only way to invalidate an interator in the first place (in this code) is by calling removeChild, which *already* has an assert never to remove a child that is being iterated over (by means of the mInDraw flag). --- indra/llui/llview.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 40ee816a7..6c1bce689 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1170,16 +1170,13 @@ void LLView::drawChildren() LLView* rootp = getRootView(); ++sDepth; - for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) + for (child_list_const_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter) { - child_list_reverse_iter_t child = child_iter++; - LLView *viewp = *child; - - if (viewp == NULL) - { - continue; - } - + LLView *viewp = *child_iter; + if (viewp == NULL) + { + continue; + } if (viewp->getVisible() && /*viewp != focus_view && */viewp->getRect().isValid()) { From dd6f95cd330422a69d193f3e423ae35d9b33969a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 8 Feb 2013 14:13:54 +0100 Subject: [PATCH 10/41] Make AIList resilient against invalidating iterators. This adds a counter and a 'dead' flag to the data stored in the linked list. The counter counts the number of iterators (still) pointing to an element and the dead flag is set to indicate the element was erased when iterators are still pointing to it. As a result, iterators are NEVER invalidated. Of course, such elements are subsequentially skipped when iterating over the list. Assertions protect against dereferencing an erased iterator, but incrementing or decremention still works: it is still well-defined what the next (non erased) element is, assuming the element wasn't erased (yet), but would be erased delayed - or assuming the iterator would have been incremented (decremented) in advance to erasing the element. --- indra/llui/ailist.h | 580 ++++++++++++++++++++++++++++---- indra/llui/llviewquery.cpp | 9 +- indra/newview/lltextureview.cpp | 4 +- 3 files changed, 524 insertions(+), 69 deletions(-) diff --git a/indra/llui/ailist.h b/indra/llui/ailist.h index 821f679cb..be2142f7e 100644 --- a/indra/llui/ailist.h +++ b/indra/llui/ailist.h @@ -33,83 +33,372 @@ #include +template +class AIList; + template class AIConstListIterator; +/* + * AINode + * + * The actual data stored in a std::list when using AIList. + * T is the template parameter used with AIList. + * count are the number of iterators that currently point to this element. + * dead is 0 or 1, where 1 means that the element was erased from the AIList + * (but not yet from the internal std::list, so that any iterators to it + * are NOT invalidated). + */ +template +struct AINode { + T mElement; // The user visual data. + mutable unsigned short count; // Number of iterators pointing to this element. + mutable unsigned short dead; // Whether or not the element is "erased". + + AINode(void) : count(0), dead(0) { } + explicit AINode(T const& __value) : mElement(__value), count(0), dead(0) { } + + // Equivalence operators. + // __node may not be dead. Dead nodes in the list are "skipped" (obviously), meaning that they are never equal or always unequal. + bool operator==(AINode const& __node) const { llassert(!__node.dead); return mElement == __node.mElement && !dead; } + bool operator!=(AINode const& __node) const { llassert(!__node.dead); return mElement != __node.mElement || dead; } + + // Default ordering for sort(). + bool operator<(AINode const& __node) const { return mElement < __node.mElement; } +}; + +/* + * AIListIterator + * + * A non-const iterator to an element of AIList. + */ template class AIListIterator { private: typedef AIListIterator _Self; - typedef std::list _Container; + typedef std::list > _Container; typedef typename _Container::iterator _Iterator; - _Container* mContainer; - _Iterator mIterator; + _Container* mContainer; // A pointer to the associated container, or NULL when (still) singular. + // Note that this code does not allow a container to be destructed while + // any non-end iterators still exist (although that could be legal). + // If an iterator points to end() and the container is destructed then + // this pointer is NOT reset to NULL. + _Iterator mIterator; // Internal iterator to element of mContainer (or singular when mContainer is NULL). + + // Increment reference counter for mIterator (if not singular and not end()). + void ref(void) + { + if (mContainer && mContainer->end() != mIterator) + { + // It would be bad a new iterator was created that pointed to a dead element. + // Also, as a sanity check, make sure there aren't a ridiculous number of iterators + // pointing to this element. + llassert(!mIterator->dead && mIterator->count < 100); + ++(mIterator->count); + } + } + + // Decrement reference counter for mIterator (if not singular and not end()). + // If this was the last iterator pointing to a dead element, then really erase it. + void unref(void) + { + if (mContainer && mContainer->end() != mIterator) + { + llassert(mIterator->count > 0); + if (--(mIterator->count) == 0 && mIterator->dead) + { + mContainer->erase(mIterator); + } + } + } public: + // Some standard typedefs that have to exist for iterators. typedef typename _Iterator::difference_type difference_type; typedef typename _Iterator::iterator_category iterator_category; - typedef typename _Iterator::value_type value_type; - typedef typename _Iterator::pointer pointer; - typedef typename _Iterator::reference reference; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + // Construct a singular iterator. AIListIterator(void) : mContainer(NULL) { } - AIListIterator(_Container* __c, _Iterator const& __i) : mContainer(__c), mIterator(__i) { } - _Self& operator=(_Self const& __x) { mContainer = __x.mContainer; mIterator = __x.mIterator; return *this; } + // Construct an iterator to a given element of std::list. Only for internal use by AIList. + AIListIterator(_Container* __c, _Iterator const& __i) : mContainer(__c), mIterator(__i) + { + llassert(mContainer); + ref(); + } - reference operator*() const { return *mIterator; } - pointer operator->() const { return mIterator.operator->(); } - _Self& operator++() { ++mIterator; return *this; } - _Self operator++(int) { _Self tmp = *this; ++mIterator; return tmp; } - _Self& operator--() { --mIterator; return *this; } - _Self operator--(int) { _Self tmp = *this; --mIterator; return tmp; } + // Copy constructor. + AIListIterator(AIListIterator const& __i) : mContainer(__i.mContainer), mIterator(__i.mIterator) + { + ref(); + } - bool operator==(_Self const& __x) const { return mIterator == __x.mIterator; } - bool operator!=(_Self const& __x) const { return mIterator != __x.mIterator; } + // Destructor. + ~AIListIterator() + { + unref(); + } - template friend class AIConstListIterator; + // Assignment operator. + _Self& operator=(_Self const& __x) + { + unref(); // We no longer point to whatever we were pointing. + mContainer = __x.mContainer; + mIterator = __x.mIterator; + llassert(mContainer); + ref(); // We now point an(other) element. + return *this; + } + + // Dereference operator. + reference operator*() const + { + // Iterator may not be singular or dead. + llassert(mContainer && !mIterator->dead); + return mIterator->mElement; + } + + // Dereference operator. + pointer operator->() const + { + // Iterator may not be singular or dead. + llassert(mContainer && !mIterator->dead); + return &mIterator->mElement; + } + + // Pre-increment operator (not being singular is implied). + _Self& operator++() + { + _Iterator cur = mIterator; // Make copy of mIterator. + ++cur; // Advance it. + unref(); // We will no longer be pointing to this element. This might invalidate mIterator! + while(cur != mContainer->end() && cur->dead) // Advance till the first non-dead element. + { + ++cur; + } + mIterator = cur; // Put result back into mIterator. + ref(); // We are now pointing to a valid element again. + return *this; + } + + // Post-increment operator (not being singular is implied). + _Self operator++(int) + { + _Self tmp = *this; + this->operator++(); + return tmp; + } + + // Pre-decrement operator (not being singular is implied). + _Self& operator--() + { + _Iterator cur = mIterator; // See operator++(). + --cur; + unref(); + while(cur->dead) + { + --cur; + } + mIterator = cur; + ref(); + return *this; + } + + // Post-decrement operator (not being singular is implied). + _Self operator--(int) + { + _Self tmp = *this; + this->operator--(); + return tmp; + } + + // Equivalence operators. + // We allow comparing with dead iterators, because if one of them is not-dead + // then the result is "unequal" anyway, which is probably what you want. + bool operator==(_Self const& __x) const { return mIterator == __x.mIterator; } + bool operator!=(_Self const& __x) const { return mIterator != __x.mIterator; } + + friend class AIList; + friend class AIConstListIterator; template friend bool operator==(AIListIterator const& __x, AIConstListIterator const& __y); template friend bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y); + + // Return the total number of iterators pointing the element that this iterator is pointing to. + // Unless the iterator points to end, a positive number will be returned since this iterator is + // pointing to the element. If it points to end (or is singular) then 0 is returned. + int count(void) const + { + if (mContainer && mIterator != mContainer->end()) + { + return mIterator->count; + } + return 0; + } }; +/* + * AIConstListIterator + * + * A const iterator to an element of AIList. + * + * Because this class is very simular to AIListIterator, see above for detailed comments. + */ template class AIConstListIterator { private: typedef AIConstListIterator _Self; - typedef std::list _Container; + typedef std::list > _Container; + typedef typename _Container::iterator _Iterator; typedef typename _Container::const_iterator _ConstIterator; typedef AIListIterator iterator; _Container const* mContainer; - _ConstIterator mConstIterator; + _Iterator mConstIterator; // This has to be an _Iterator instead of _ConstIterator, because the compiler doesn't accept a const_iterator for erase yet (C++11 does). + + void ref(void) + { + if (mContainer && mContainer->end() != mConstIterator) + { + llassert(mConstIterator->count < 100); + mConstIterator->count++; + } + } + + void unref(void) + { + if (mContainer && mContainer->end() != mConstIterator) + { + llassert(mConstIterator->count > 0); + mConstIterator->count--; + if (mConstIterator->count == 0 && mConstIterator->dead) + { + const_cast<_Container*>(mContainer)->erase(mConstIterator); + } + } + } public: typedef typename _ConstIterator::difference_type difference_type; typedef typename _ConstIterator::iterator_category iterator_category; - typedef typename _ConstIterator::value_type value_type; - typedef typename _ConstIterator::pointer pointer; - typedef typename _ConstIterator::reference reference; + typedef T value_type; + typedef T const* pointer; + typedef T const& reference; AIConstListIterator(void) : mContainer(NULL) { } - AIConstListIterator(_Container const* __c, _ConstIterator const& __i) : mContainer(__c), mConstIterator(__i) { } - AIConstListIterator(iterator const& __x) : mContainer(__x.mContainer), mConstIterator(__x.mIterator) { } + AIConstListIterator(_Container const* __c, _Iterator const& __i) : mContainer(__c), mConstIterator(__i) + { + llassert(mContainer); + ref(); + } + // Allow to construct a const_iterator from an iterator. + AIConstListIterator(iterator const& __x) : mContainer(__x.mContainer), mConstIterator(__x.mIterator) + { + ref(); + } + AIConstListIterator(AIConstListIterator const& __i) : mContainer(__i.mContainer), mConstIterator(__i.mConstIterator) + { + ref(); + } + ~AIConstListIterator() + { + unref(); + } - _Self& operator=(_Self const& __x) { mContainer = __x.mContainer; mConstIterator = __x.mConstIterator; return *this; } - _Self& operator=(iterator const& __x) { mContainer = __x.mContainer; mConstIterator = __x.mIterator; return *this; } + _Self& operator=(_Self const& __x) + { + unref(); + mContainer = __x.mContainer; + mConstIterator = __x.mConstIterator; + llassert(mContainer); + ref(); + return *this; + } - reference operator*() const { return *mConstIterator; } - pointer operator->() const { return mConstIterator.operator->(); } - _Self& operator++() { ++mConstIterator; return *this; } - _Self operator++(int) { _Self tmp = *this; ++mConstIterator; return tmp; } - _Self& operator--() { --mConstIterator; return *this; } - _Self operator--(int) { _Self tmp = *this; --mConstIterator; return tmp; } + // Allow to assign from a non-const iterator. + _Self& operator=(iterator const& __x) + { + unref(); + mContainer = __x.mContainer; + mConstIterator = __x.mIterator; + llassert(mContainer); + ref(); + return *this; + } - bool operator==(_Self const& __x) const { return mConstIterator == __x.mConstIterator; } - bool operator!=(_Self const& __x) const { return mConstIterator != __x.mConstIterator; } + reference operator*() const + { + llassert(mContainer && !mConstIterator->dead); + return mConstIterator->mElement; + } + + pointer operator->() const + { + llassert(mContainer && !mConstIterator->dead); + return &mConstIterator->mElement; + } + + _Self& operator++() + { + _Iterator cur = mConstIterator; + ++cur; + unref(); + while(cur != mContainer->end() && cur->dead) + { + ++cur; + } + mConstIterator = cur; + ref(); + return *this; + } + + _Self operator++(int) + { + _Self tmp = *this; + this->operator++(); + return tmp; + } + + _Self& operator--() + { + _Iterator cur = mConstIterator; + --cur; + unref(); + while(cur->dead) + { + --cur; + } + mConstIterator = cur; + ref(); + return *this; + } + + _Self operator--(int) + { + _Self tmp = *this; + this->operator--(); + return tmp; + } + + bool operator==(_Self const& __x) const { return mConstIterator == __x.mConstIterator; } + bool operator!=(_Self const& __x) const { return mConstIterator != __x.mConstIterator; } + bool operator==(iterator const& __x) const { return mConstIterator == __x.mIterator; } + bool operator!=(iterator const& __x) const { return mConstIterator != __x.mIterator; } template friend bool operator==(AIListIterator const& __x, AIConstListIterator const& __y); template friend bool operator!=(AIListIterator const& __x, AIConstListIterator const& __y); + + int count(void) const + { + if (mContainer && mConstIterator != mContainer->end()) + { + return mConstIterator->count; + } + return 0; + } }; template @@ -124,10 +413,31 @@ inline bool operator!=(AIListIterator const& __x, AIConstListIterator cons return __x.mIterator != __y.mConstIterator; } +/* + * AIList + * + * A linked list that allows elements to be erased while one or more iterators + * are still pointing to that element, after which pre-increment and pre-decrement + * operators still work. + * + * For example: + * + * for (AIList::iterator i = l.begin(); i != l.end(); ++i) + * { + * int x = *i; + * f(i); // Might erase any element of list l. + * // Should not dereference i anymore here (because it might be erased). + * } + */ template class AIList { private: - std::list mContainer; + typedef std::list > _Container; + typedef typename _Container::iterator _Iterator; + typedef typename _Container::const_iterator _ConstIterator; + + _Container mContainer; + size_t mSize; public: typedef T value_type; @@ -142,50 +452,194 @@ class AIList { typedef size_t size_type; typedef ptrdiff_t difference_type; - AIList(void) { } - //AIList(std::list const& __list) : mContainer(__list) { } + // Default constructor. Create an empty list. + AIList(void) : mSize(0) { } +#ifdef LL_DEBUG + // Destructor calls clear() to check if there are no iterators left pointing to this list. Destructing an empty list is trivial. + ~AIList() { clear(); } +#endif - explicit AIList(size_type __n, value_type const& __value = value_type()) : mContainer(__n, __value) { } - AIList(AIList const& __list) : mContainer(__list.mContainer) { } + // Construct a list with __n elements of __value. + explicit AIList(size_type __n, value_type const& __value = value_type()) : mContainer(__n, AINode(__value)), mSize(__n) { } + // Copy constructor. + AIList(AIList const& __list) : mSize(0) + { + for (_ConstIterator __i = __list.mContainer.begin(); __i != __list.mContainer.end(); ++__i) + { + if (!__i->dead) + { + mContainer.push_back(AINode(__i->mElement)); + ++mSize; + } + } + } + + // Construct a list from the range [__first, __last>. template - AIList(_InputIterator __first, _InputIterator __last) : mContainer(__first, __last) { } + AIList(_InputIterator __first, _InputIterator __last) : mSize(0) + { + for (_InputIterator __i = __first; __i != __last; ++__i) + { + mContainer.push_back(AINode(*__i)); + ++mSize; + } + } - AIList& operator=(AIList const& __list) { mContainer = __list.mContainer; return *this; } + // Assign from another list. + AIList& operator=(AIList const& __list) + { + clear(); + for (_ConstIterator __i = __list.mContainer.begin(); __i != __list.mContainer.end(); ++__i) + { + if (!__i->dead) + { + mContainer.push_back(AINode(__i->mElement)); + ++mSize; + } + } + return *this; + } + + iterator begin() + { + _Iterator __i = mContainer.begin(); + while(__i != mContainer.end() && __i->dead) + { + ++__i; + } + return iterator(&mContainer, __i); + } + + const_iterator begin() const + { + _Iterator __i = const_cast<_Container&>(mContainer).begin(); + while(__i != mContainer.end() && __i->dead) + { + ++__i; + } + return const_iterator(&mContainer, __i); + } - iterator begin() { return iterator(&mContainer, mContainer.begin()); } - const_iterator begin() const { return const_iterator(&mContainer, mContainer.begin()); } iterator end() { return iterator(&mContainer, mContainer.end()); } - const_iterator end() const { return const_iterator(&mContainer, mContainer.end()); } + const_iterator end() const { return const_iterator(&mContainer, const_cast<_Container&>(mContainer).end()); } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - bool empty() const { return mContainer.empty(); } - size_type size() const { return mContainer.size(); } - size_type max_size() const { return mContainer.max_size(); } + bool empty() const { return mSize == 0; } + size_type size() const { return mSize; } + size_type max_size() const { return mContainer.max_size(); } - reference front() { return mContainer.front(); } - const_reference front() const { return mContainer.front(); } - reference back() { return mContainer.back(); } - const_reference back() const { return mContainer.back(); } + reference front() { iterator __i = begin(); return *__i; } + const_reference front() const { const_iterator __i = begin(); return *__i; } + reference back() { iterator __i = end(); --__i; return *__i; } + const_reference back() const { const_iterator __i = end(); --__i; return *__i; } - void push_front(value_type const& __x) { mContainer.push_front(__x); } - void pop_front() { mContainer.pop_front(); } - void push_back(value_type const& __x) { mContainer.push_back(__x); } - void pop_back() { mContainer.pop_back(); } - iterator insert(iterator __position, value_type const& __x) { return iterator(&mContainer, mContainer.insert(__position, __x)); } - iterator erase(iterator __position) { return iterator(&mContainer, mContainer.erase(__position)); } - void clear() { mContainer.clear(); } - void remove(value_type const& __value) { mContainer.remove(__value); } + void push_front(value_type const& __x) + { + mContainer.push_front(AINode(__x)); + ++mSize; + } + void pop_front() + { + iterator __i = begin(); + erase(__i); + } + void push_back(value_type const& __x) + { + mContainer.push_back(AINode(__x)); + ++mSize; + } + void pop_back() + { + iterator __i = end(); + --__i; + erase(__i); + } - // Use this with care. No iterators should be left pointing at elements after the code returns. - std::list const& get_std_list(void) const { return mContainer; } + iterator insert(iterator __position, value_type const& __x) + { + ++mSize; + return iterator(&mContainer, mContainer.insert(__position.mIterator, AINode(__x))); + } - void sort() { mContainer.sort(); } + void clear() + { +#ifdef LL_DEBUG + // There should be no iterators left pointing at any element here. + for (_Iterator __i = mContainer.begin(); __i != mContainer.end(); ++__i) + { + llassert(__i->count == 0); + } +#endif + mContainer.clear(); + mSize = 0; + } + + iterator erase(iterator __position) + { + // Mark the element __position points to as being erased. + // Iterator may not be singular, point to end, or be dead already. + // Obviously count must be larger than zero since __position is still pointing to it. + llassert(__position.mContainer == &mContainer && __position.mIterator != mContainer.end() && __position.mIterator->count > 0 && !__position.mIterator->dead); + __position.mIterator->dead = 1; + --mSize; + return ++__position; + } + + // Remove all elements, designated by the iterator where, for which *where == __value. + void remove(value_type const& __value) + { + _Iterator const __e = mContainer.end(); + for (_Iterator __i = mContainer.begin(); __i != __e;) + { + if (!__i->dead && __i->mElement == __value) + { + --mSize; + if (__i->count == 0) + { + mContainer.erase(__i++); + continue; + } + // Mark the element as being erased. + __i->dead = 1; + } + ++__i; + } + } + + void sort() + { +#ifdef LL_DEBUG + // There should be no iterators left pointing at any element here. + for (_Iterator __i = mContainer.begin(); __i != mContainer.end(); ++__i) + { + llassert(__i->count == 0); + } +#endif + mContainer.sort(); + } template - void sort(_StrictWeakOrdering pred) { mContainer.sort(pred); } + struct PredWrapper + { + _StrictWeakOrdering mPred; + PredWrapper(_StrictWeakOrdering const& pred) : mPred(pred) { } + bool operator()(AINode const& __x, AINode const& __y) const { return mPred(__x.mElement, __y.mElement); } + }; + template + void sort(_StrictWeakOrdering const& pred) + { +#ifdef LL_DEBUG + // There should be no iterators left pointing at any element here. + for (_Iterator __i = mContainer.begin(); __i != mContainer.end(); ++__i) + { + llassert(__i->count == 0); + } +#endif + mContainer.sort(PredWrapper<_StrictWeakOrdering>(pred)); + } }; #endif diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index 7faf53726..bb3bab353 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -74,9 +74,10 @@ filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewLis viewList_t LLViewQuery::run(LLView* view) const { viewList_t result; + viewList_t const child_list(view->getChildList()->begin(), view->getChildList()->end()); // prefilter gets immediate children of view - filterResult_t pre = runFilters(view, view->getChildList()->get_std_list(), mPreFilters); + filterResult_t pre = runFilters(view, child_list, mPreFilters); if(!pre.first && !pre.second) { // not including ourselves or the children @@ -113,14 +114,14 @@ viewList_t LLViewQuery::run(LLView* view) const void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const { - viewList_t views(view->getChildList()->get_std_list()); + viewList_t views(view->getChildList()->begin(), view->getChildList()->end()); if (mSorterp) { (*mSorterp)(view, views); // sort the children per the sorter } for(viewList_t::iterator iter = views.begin(); iter != views.end(); - iter++) + ++iter) { viewList_t indiv_children = this->run(*iter); filtered_children.splice(filtered_children.end(), indiv_children); @@ -132,7 +133,7 @@ filterResult_t LLViewQuery::runFilters(LLView* view, viewList_t const& children, filterResult_t result = filterResult_t(TRUE, TRUE); for(filterList_const_iter_t iter = filters.begin(); iter != filters.end(); - iter++) + ++iter) { filterResult_t filtered = (**iter)(view, children); result.first = result.first && filtered.first; diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 0a0eb5f08..e57fbc3a2 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -101,7 +101,7 @@ public: // Used for sorting struct sort { - bool operator()(const LLView* i1, const LLView* i2) + bool operator()(const LLView* i1, const LLView* i2) const { LLTextureBar* bar1p = (LLTextureBar*)i1; LLTextureBar* bar2p = (LLTextureBar*)i2; @@ -120,7 +120,7 @@ public: struct sort_fetch { - bool operator()(const LLView* i1, const LLView* i2) + bool operator()(const LLView* i1, const LLView* i2) const { LLTextureBar* bar1p = (LLTextureBar*)i1; LLTextureBar* bar2p = (LLTextureBar*)i2; From 2d7ab61c6e496fbee7fd03d015da37c9338c4174 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 8 Feb 2013 18:40:14 +0100 Subject: [PATCH 11/41] Make keyboard focus more robust under closed floaters. --- indra/llui/llfloater.cpp | 2 ++ indra/llui/llfocusmgr.cpp | 35 +++++++++++++++++++++++++++-- indra/llui/llfocusmgr.h | 5 ++++- indra/newview/llfloatersnapshot.cpp | 5 +---- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 1cbb52dab..b91b0df43 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -911,6 +911,8 @@ void LLFloater::setMinimized(BOOL minimize) // Lose keyboard focus when minimized releaseFocus(); + // Also reset mLockedView and mLastKeyboardFocus, to avoid that we get focus back somehow. + gFocusMgr.removeKeyboardFocusWithoutCallback(this); for (S32 i = 0; i < 4; i++) { diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 66834a6a1..7817fd0d5 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -55,6 +55,8 @@ BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_pa // virtual LLFocusableElement::~LLFocusableElement() { + // Make sure nothing is pointing to us anymore! + gFocusMgr.removeKeyboardFocusWithoutCallback(this); delete mFocusLostCallback; delete mFocusReceivedCallback; delete mFocusChangedCallback; @@ -131,6 +133,7 @@ LLFocusMgr::LLFocusMgr() mKeyboardFocus( NULL ), mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), + mLastDefaultKeyboardFocus( NULL ), mKeystrokesOnly(FALSE), mTopCtrl( NULL ), mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true @@ -171,6 +174,23 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) } } +void LLFocusMgr::restoreDefaultKeyboardFocus(LLFocusableElement* current_default_focus) +{ + if (current_default_focus && mDefaultKeyboardFocus == current_default_focus) + { + setDefaultKeyboardFocus(mLastDefaultKeyboardFocus); + mLastDefaultKeyboardFocus = NULL; + } +} + +void LLFocusMgr::restoreKeyboardFocus(LLFocusableElement* current_focus) +{ + if (current_focus && mKeyboardFocus == current_focus) + { + setKeyboardFocus(mLastKeyboardFocus); + mLastKeyboardFocus = NULL; + } +} void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only) { @@ -328,11 +348,22 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* f { mLockedView = NULL; } - - if( mKeyboardFocus == focus ) + if (mKeyboardFocus == focus) { mKeyboardFocus = NULL; } + if (mLastKeyboardFocus == focus) + { + mLastKeyboardFocus = NULL; + } + if (mDefaultKeyboardFocus == focus) + { + mDefaultKeyboardFocus = NULL; + } + if (mLastDefaultKeyboardFocus == focus) + { + mLastDefaultKeyboardFocus = NULL; + } } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 14c711b5e..5f1466f5d 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -84,6 +84,7 @@ public: // Keyboard Focus void setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus. + void restoreKeyboardFocus(LLFocusableElement* current_focus); LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; } LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; } BOOL childHasKeyboardFocus( const LLView* parent ) const; @@ -103,7 +104,8 @@ public: // If setKeyboardFocus(NULL) is called, and there is a non-NULL default // keyboard focus view, focus goes there. JC - void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; } + void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mLastDefaultKeyboardFocus = mDefaultKeyboardFocus; mDefaultKeyboardFocus = default_focus; } + void restoreDefaultKeyboardFocus(LLFocusableElement* current_default_focus); LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; } @@ -132,6 +134,7 @@ private: LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object LLFocusableElement* mLastKeyboardFocus; // who last had focus LLFocusableElement* mDefaultKeyboardFocus; + LLFocusableElement* mLastDefaultKeyboardFocus; BOOL mKeystrokesOnly; // Top View diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 735b033b1..d3f4ed3f6 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -345,7 +345,6 @@ public: LLToolset* mLastToolset; boost::signals2::connection mQualityMouseUpConnection; - LLFocusableElement* mPrevDefaultKeyboardFocus; }; //---------------------------------------------------------------------------- @@ -1716,14 +1715,12 @@ void LLFloaterSnapshot::Impl::freezeTime(bool on) } // Make sure the floater keeps focus so that pressing ESC stops Freeze Time mode. - sInstance->impl.mPrevDefaultKeyboardFocus = gFocusMgr.getDefaultKeyboardFocus(); gFocusMgr.setDefaultKeyboardFocus(sInstance); } else if (gSavedSettings.getBOOL("FreezeTime")) // turning off freeze frame mode { // Restore default keyboard focus. - gFocusMgr.setDefaultKeyboardFocus(sInstance->impl.mPrevDefaultKeyboardFocus); - sInstance->impl.mPrevDefaultKeyboardFocus = NULL; + gFocusMgr.restoreDefaultKeyboardFocus(sInstance); gSnapshotFloaterView->setMouseOpaque(FALSE); From 2a177c6614cb3be21728923c9f128b663e36bbee Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 10 Feb 2013 18:18:50 +0100 Subject: [PATCH 12/41] Fix for libcwd configured with --disable-location --- indra/cwdebug/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 329f0448e..6c6d71908 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -173,8 +173,8 @@ extern LL_COMMON_API fake_channel const notice; #include #if CWDEBUG_LOCATION #include // Needed for 'backtrace'. -#include "llpreprocessor.h" #endif +#include "llpreprocessor.h" // LL_COMMON_API #include #define CWD_API __attribute__ ((visibility("default"))) From c18b156d8b5df7668b305e32b47652bdbdd329ca Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 11 Feb 2013 03:36:46 +0100 Subject: [PATCH 13/41] Bug fix for scrolling folder views. An LLFolderView is added as child to LLScrollableContainerView, but also adds the LLScrollableContainerView to it's mScrollContainer. As a result, when scrolling inside a LLFolderView the event is passed to the mScrollContainer, which then passes it first on to it's children (see the "Bad UI design" remark in the code), causing an infinite loop. This patch breaks that loop for those objects that have a mScrollContainer: LLFolderView and LLContainerView. --- indra/llui/llscrollcontainer.cpp | 5 +++-- indra/llui/llscrollcontainer.h | 2 ++ indra/newview/llcontainerview.cpp | 7 +++++++ indra/newview/llcontainerview.h | 2 +- indra/newview/llfolderview.cpp | 6 ++++++ indra/newview/llfolderview.h | 2 +- 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 8e740384a..1206d3b76 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -77,7 +77,8 @@ LLScrollableContainerView::LLScrollableContainerView( const std::string& name, mReserveScrollCorner( FALSE ), mMinAutoScrollRate( MIN_AUTO_SCROLL_RATE ), mMaxAutoScrollRate( MAX_AUTO_SCROLL_RATE ), - mScrolledView( scrolled_view ) + mScrolledView( scrolled_view ), + mPassBackToChildren(true) { if( mScrolledView ) { @@ -218,7 +219,7 @@ BOOL LLScrollableContainerView::handleScrollWheel( S32 x, S32 y, S32 clicks ) { // Give event to my child views - they may have scroll bars // (Bad UI design, but technically possible.) - if (LLUICtrl::handleScrollWheel(x,y,clicks)) + if (mPassBackToChildren && LLUICtrl::handleScrollWheel(x,y,clicks)) return TRUE; // When the vertical scrollbar is visible, scroll wheel diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index a5002a341..2e1cf1dd3 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -70,6 +70,7 @@ public: virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } void setBorderVisible( BOOL b ); + void setPassBackToChildren(bool b) { mPassBackToChildren = b; } void scrollToShowRect( const LLRect& rect, const LLRect& constraint); void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); } @@ -128,6 +129,7 @@ private: F32 mMinAutoScrollRate; F32 mMaxAutoScrollRate; bool mHideScrollbar; + bool mPassBackToChildren; }; diff --git a/indra/newview/llcontainerview.cpp b/indra/newview/llcontainerview.cpp index be7be72c7..e6d9b0119 100644 --- a/indra/newview/llcontainerview.cpp +++ b/indra/newview/llcontainerview.cpp @@ -278,3 +278,10 @@ void LLContainerView::setDisplayChildren(const BOOL displayChildren) childp->setVisible(mDisplayChildren); } } + +void LLContainerView::setScrollContainer(LLScrollableContainerView* scroll) +{ + mScrollContainer = scroll; + scroll->setPassBackToChildren(false); +} + diff --git a/indra/newview/llcontainerview.h b/indra/newview/llcontainerview.h index 37c2d97f3..1b337b09b 100644 --- a/indra/newview/llcontainerview.h +++ b/indra/newview/llcontainerview.h @@ -61,7 +61,7 @@ public: void showLabel(BOOL show) { mShowLabel = show; } void setDisplayChildren(const BOOL displayChildren); BOOL getDisplayChildren() { return mDisplayChildren; } - void setScrollContainer(LLScrollableContainerView* scroll) {mScrollContainer = scroll;} + void setScrollContainer(LLScrollableContainerView* scroll); private: LLScrollableContainerView* mScrollContainer; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index bea49eb6b..e474dec9e 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -2068,6 +2068,12 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr } } +void LLFolderView::setScrollContainer(LLScrollableContainerView* parent) +{ + mScrollContainer = parent; + parent->setPassBackToChildren(false); +} + LLRect LLFolderView::getVisibleRect() { S32 visible_height = mScrollContainer->getRect().getHeight(); diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 0d36bfe3b..c2771ed77 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -217,7 +217,7 @@ public: void scrollToShowSelection(); void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); - void setScrollContainer( LLScrollableContainerView* parent ) { mScrollContainer = parent; } + void setScrollContainer(LLScrollableContainerView* parent); LLRect getVisibleRect(); BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward); From b1892eb238a646e2b540b59b517e8a4fde5b1819 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 11 Feb 2013 20:08:58 +0100 Subject: [PATCH 14/41] DoutEntering was hiding variable 'on' --- indra/cwdebug/debug.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 6c6d71908..9ca1886da 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -387,21 +387,21 @@ void InstanceTracker::dump(void) // Print "Entering " << \a data to channel \a cntrl and increment // debugging output indentation until the end of the current scope. #define DoutEntering(cntrl, data) \ - int __slviewer_debug_indentation = 2; \ + int __slviewer_debug_indentation = 2; \ { \ LIBCWD_TSD_DECLARATION; \ if (LIBCWD_DO_TSD_MEMBER_OFF(::libcwd::libcw_do) < 0) \ { \ ::libcwd::channel_set_bootstrap_st __libcwd_channel_set(LIBCWD_DO_TSD(::libcwd::libcw_do) LIBCWD_COMMA_TSD); \ - bool on; \ + bool __slviewer_debug_on; \ { \ using namespace LIBCWD_DEBUGCHANNELS; \ - on = (__libcwd_channel_set|cntrl).on; \ + __slviewer_debug_on = (__libcwd_channel_set|cntrl).on; \ } \ - if (on) \ + if (__slviewer_debug_on) \ Dout(cntrl, "Entering " << data); \ else \ - __slviewer_debug_indentation = 0; \ + __slviewer_debug_indentation = 0; \ } \ } \ debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation); From 69c43947edb584e4ad72e82ff473f12e392782b7 Mon Sep 17 00:00:00 2001 From: Latif Khalifa Date: Tue, 12 Feb 2013 03:30:13 +0100 Subject: [PATCH 15/41] Don't include warnings of singleton access from initSingleton() to the end users (release build) --- indra/llcommon/llsingleton.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 6fac2c8a9..45aae60e8 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -226,7 +226,7 @@ void LLSingleton::createInstance(SingletonInstanceData& data) if (data.mInitState == INITIALIZING) { - llwarns << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from initSingleton(), using half-initialized object" << llendl; + lldebugs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from initSingleton(), using half-initialized object" << llendl; return; } From c799f9f7ebca856889600a82ac12ac9256a876ee Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 14:46:31 -0600 Subject: [PATCH 16/41] Avoid sculpt discard-level 5 spam. --- indra/newview/llvovolume.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index bd3fb344a..2af2dc14b 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -926,20 +926,32 @@ void LLVOVolume::sculpt() if (discard_level > max_discard) discard_level = max_discard; // clamp to the best we can do - S32 current_discard = getVolume()->getSculptLevel(); + S32 current_discard = getVolume()->getSculptLevel() ; if(current_discard < -2) { - llwarns << "WARNING!!: Current discard of sculpty at " << current_discard - << " is less than -2." << llendl; + static S32 low_sculpty_discard_warning_count = 100; + if (++low_sculpty_discard_warning_count >= 100) + { // Log first time, then every 100 afterwards otherwise this can flood the logs + llwarns << "WARNING!!: Current discard for sculpty " << mSculptTexture->getID() + << " at " << current_discard + << " is less than -2." << llendl; + low_sculpty_discard_warning_count = 0; + } // corrupted volume... don't update the sculpty return; } else if (current_discard > MAX_DISCARD_LEVEL) { - llwarns << "WARNING!!: Current discard of sculpty at " << current_discard - << " is more than than allowed max of " << MAX_DISCARD_LEVEL << llendl; - + static S32 high_sculpty_discard_warning_count = 100; + if (++high_sculpty_discard_warning_count >= 100) + { // Log first time, then every 100 afterwards otherwise this can flood the logs + llwarns << "WARNING!!: Current discard for sculpty " << mSculptTexture->getID() + << " at " << current_discard + << " is more than than allowed max of " << MAX_DISCARD_LEVEL << llendl; + high_sculpty_discard_warning_count = 0; + } + // corrupted volume... don't update the sculpty return; } From 0759f2dae8508d26bf60288b688142159fd56240 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 14:47:59 -0600 Subject: [PATCH 17/41] Actually use the namecache callback results when displaying currency transaction notifications. --- indra/newview/llviewermessage.cpp | 42 ++++++++++++------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index b0395da79..eabb0a7c5 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -5799,6 +5799,18 @@ static std::string reason_from_transaction_type(S32 transaction_type, } } +static void process_money_group_name_reply(const std::string& name, const std::string notification, LLSD args, LLSD payload) +{ + args["NAME"] = name; + LLNotificationsUtil::add(notification,args,payload); +} +static void process_money_avatar_name_reply(const LLAvatarName& name, const std::string notification, LLSD args, LLSD payload) +{ + std::string av_name; + LLAvatarNameCache::getPNSName(name,av_name); + args["NAME"] = av_name; + LLNotificationsUtil::add(notification,args,payload); +} static void process_money_balance_reply_extended(LLMessageSystem* msg) { // Added in server 1.40 and viewer 2.1, support for localization @@ -5831,26 +5843,6 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) return; } - std::string source_slurl; - if (is_source_group) - { - gCacheName->getGroupName(source_id, source_slurl); - } - else - { - LLAvatarNameCache::getPNSName(source_id, source_slurl); - } - - std::string dest_slurl; - if (is_dest_group) - { - gCacheName->getGroupName(dest_id, dest_slurl); - } - else - { - LLAvatarNameCache::getPNSName(dest_id, dest_slurl); - } - std::string reason = reason_from_transaction_type(transaction_type, item_description); @@ -5871,7 +5863,6 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) bool you_paid_someone = (source_id == gAgentID); if (you_paid_someone) { - args["NAME"] = dest_slurl; is_name_group = is_dest_group; name_id = dest_id; if (!reason.empty()) @@ -5907,7 +5898,6 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) else { // ...someone paid you - args["NAME"] = source_slurl; is_name_group = is_source_group; name_id = source_id; if (!reason.empty()) @@ -5930,14 +5920,14 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) if (is_name_group) { gCacheName->getGroup(name_id, - boost::bind(&LLNotificationsUtil::add, - notification, final_args, payload)); + boost::bind(&process_money_group_name_reply, + _2, notification, final_args, payload)); } else { LLAvatarNameCache::get(name_id, - boost::bind(&LLNotificationsUtil::add, - notification, final_args, payload)); + boost::bind(&process_money_avatar_name_reply, + _2, notification, final_args, payload)); } } From 5180fbfc23e0ce73b5c8664c928518833b011db4 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 14:50:29 -0600 Subject: [PATCH 18/41] Clean up llfloaterurlentry --- indra/newview/llfloaterurlentry.cpp | 67 ++++++++++++++--------------- indra/newview/llfloaterurlentry.h | 2 +- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index 122cfd60f..dd6ea2864 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -103,27 +103,6 @@ LLFloaterURLEntry::LLFloaterURLEntry(LLHandle parent) mPanelLandMediaHandle(parent) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_url_entry.xml"); - - mMediaURLEdit = getChild("media_entry"); - - // Cancel button - childSetAction("cancel_btn", onBtnCancel, this); - - // Cancel button - childSetAction("clear_btn", onBtnClear, this); - - // clear media list button - LLSD parcel_history = LLURLHistory::getURLHistory("parcel"); - bool enable_clear_button = parcel_history.size() > 0 ? true : false; - childSetEnabled( "clear_btn", enable_clear_button ); - - // OK button - childSetAction("ok_btn", onBtnOK, this); - - setDefaultBtn("ok_btn"); - buildURLHistory(); - - sInstance = this; } //----------------------------------------------------------------------------- @@ -134,6 +113,28 @@ LLFloaterURLEntry::~LLFloaterURLEntry() sInstance = NULL; } +BOOL LLFloaterURLEntry::postBuild() +{ + mMediaURLEdit = getChild("media_entry"); + + // Cancel button + childSetAction("cancel_btn", onBtnCancel, this); + + // Cancel button + childSetAction("clear_btn", onBtnClear, this); + // clear media list button + LLSD parcel_history = LLURLHistory::getURLHistory("parcel"); + bool enable_clear_button = parcel_history.size() > 0 ? true : false; + getChildView("clear_btn")->setEnabled(enable_clear_button ); + + // OK button + childSetAction("ok_btn", onBtnOK, this); + + setDefaultBtn("ok_btn"); + buildURLHistory(); + + return TRUE; +} void LLFloaterURLEntry::buildURLHistory() { LLCtrlListInterface* url_list = childGetListInterface("media_entry"); @@ -157,7 +158,7 @@ void LLFloaterURLEntry::buildURLHistory() void LLFloaterURLEntry::headerFetchComplete(U32 status, const std::string& mime_type) { - LLPanelLandMedia* panel_media = (LLPanelLandMedia*)mPanelLandMediaHandle.get(); + LLPanelLandMedia* panel_media = dynamic_cast(mPanelLandMediaHandle.get()); if (panel_media) { // status is ignored for now -- error = "none/none" @@ -166,21 +167,18 @@ void LLFloaterURLEntry::headerFetchComplete(U32 status, const std::string& mime_ } // Decrement the cursor getWindow()->decBusyCount(); - childSetVisible("loading_label", false); + getChildView("loading_label")->setVisible( false); close(); } // static LLHandle LLFloaterURLEntry::show(LLHandle parent) { - if (sInstance) - { - sInstance->open(); - } - else + if (!sInstance) { sInstance = new LLFloaterURLEntry(parent); } + sInstance->open(); sInstance->updateFromLandMediaPanel(); return sInstance->getHandle(); } @@ -239,7 +237,8 @@ void LLFloaterURLEntry::onBtnOK( void* userdata ) } // Discover the MIME type only for "http" scheme. - if(scheme == "http" || scheme == "https") + if(!media_url.empty() && + (scheme == "http" || scheme == "https")) { LLHTTPClient::getHeaderOnly( media_url, new LLMediaTypeResponder(self->getHandle())); @@ -250,13 +249,13 @@ void LLFloaterURLEntry::onBtnOK( void* userdata ) } // Grey the buttons until we get the header response - self->childSetEnabled("ok_btn", false); - self->childSetEnabled("cancel_btn", false); - self->childSetEnabled("media_entry", false); + self->getChildView("ok_btn")->setEnabled(false); + self->getChildView("cancel_btn")->setEnabled(false); + self->getChildView("media_entry")->setEnabled(false); // show progress bar here? getWindow()->incBusyCount(); - self->childSetVisible("loading_label", true); + self->getChildView("loading_label")->setVisible( true); } // static @@ -298,7 +297,7 @@ bool LLFloaterURLEntry::callback_clear_url_list(const LLSD& notification, const LLURLHistory::clear("parcel"); // cleared the list so disable Clear button - childSetEnabled( "clear_btn", false ); + getChildView("clear_btn")->setEnabled(false ); } return false; } diff --git a/indra/newview/llfloaterurlentry.h b/indra/newview/llfloaterurlentry.h index dbc345a1c..0aeca823b 100644 --- a/indra/newview/llfloaterurlentry.h +++ b/indra/newview/llfloaterurlentry.h @@ -45,7 +45,7 @@ public: // Can only be shown by LLPanelLandMedia, and pushes data back into // that panel via the handle. static LLHandle show(LLHandle panel_land_media_handle); - + /*virtual*/ BOOL postBuild(); void updateFromLandMediaPanel(); void headerFetchComplete(U32 status, const std::string& mime_type); From 0a8147a9dafcde49e1981fcd5c8052bc094c741c Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 14:52:53 -0600 Subject: [PATCH 19/41] Merged some alignment adjustments from sunshine-external --- indra/llappearance/lldriverparam.h | 5 +++-- indra/llappearance/llpolymorph.h | 6 ++++-- indra/llappearance/llpolyskeletaldistortion.h | 5 +++-- indra/llappearance/lltexlayerparams.h | 18 +++++++++++------- indra/llappearance/llviewervisualparam.h | 3 ++- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/indra/llappearance/lldriverparam.h b/indra/llappearance/lldriverparam.h index 1a24cefa1..65dd3cdde 100644 --- a/indra/llappearance/lldriverparam.h +++ b/indra/llappearance/lldriverparam.h @@ -76,6 +76,7 @@ protected: //----------------------------------------------------------------------------- +LL_ALIGN_PREFIX(16) class LLDriverParam : public LLViewerVisualParam { friend class LLPhysicsMotion; @@ -133,13 +134,13 @@ protected: void setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake); - LLVector4a mDefaultVec; // temp holder + LL_ALIGN_16(LLVector4a mDefaultVec); // temp holder typedef std::vector entry_list_t; entry_list_t mDriven; LLViewerVisualParam* mCurrentDistortionParam; // Backlink only; don't make this an LLPointer. LLAvatarAppearance* mAvatarAppearance; LLWearable* mWearablep; -}; +} LL_ALIGN_POSTFIX(16); #endif // LL_LLDRIVERPARAM_H diff --git a/indra/llappearance/llpolymorph.h b/indra/llappearance/llpolymorph.h index f7782ffb3..03915d3da 100644 --- a/indra/llappearance/llpolymorph.h +++ b/indra/llappearance/llpolymorph.h @@ -41,6 +41,7 @@ class LLWearable; //----------------------------------------------------------------------------- // LLPolyMorphData() //----------------------------------------------------------------------------- +LL_ALIGN_PREFIX(16) class LLPolyMorphData { public: @@ -79,12 +80,13 @@ public: F32 mTotalDistortion; // vertex distortion summed over entire morph F32 mMaxDistortion; // maximum single vertex distortion in a given morph - LLVector4a mAvgDistortion; // average vertex distortion, to infer directionality of the morph + LL_ALIGN_16(LLVector4a mAvgDistortion); // average vertex distortion, to infer directionality of the morph LLPolyMeshSharedData* mMesh; private: void freeData(); -}; +} LL_ALIGN_POSTFIX(16); + //----------------------------------------------------------------------------- // LLPolyVertexMask() diff --git a/indra/llappearance/llpolyskeletaldistortion.h b/indra/llappearance/llpolyskeletaldistortion.h index b9c3c3628..774bc7dfa 100644 --- a/indra/llappearance/llpolyskeletaldistortion.h +++ b/indra/llappearance/llpolyskeletaldistortion.h @@ -63,6 +63,7 @@ struct LLPolySkeletalBoneInfo BOOL mHasPositionDeformation; }; +LL_ALIGN_PREFIX(16) class LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo { friend class LLPolySkeletalDistortion; @@ -118,13 +119,13 @@ public: /*virtual*/ const LLVector4a* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return NULL;}; protected: + LL_ALIGN_16(LLVector4a mDefaultVec); typedef std::map joint_vec_map_t; joint_vec_map_t mJointScales; joint_vec_map_t mJointOffsets; - LLVector4a mDefaultVec; // Backlink only; don't make this an LLPointer. LLAvatarAppearance *mAvatar; -}; +} LL_ALIGN_POSTFIX(16); #endif // LL_LLPOLYSKELETALDISTORTION_H diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h index ca1497ebf..b38d28d3e 100644 --- a/indra/llappearance/lltexlayerparams.h +++ b/indra/llappearance/lltexlayerparams.h @@ -60,6 +60,7 @@ protected: // LLTexLayerParamAlpha // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL_ALIGN_PREFIX(16) class LLTexLayerParamAlpha : public LLTexLayerParam { public: @@ -67,8 +68,6 @@ public: LLTexLayerParamAlpha( LLAvatarAppearance* appearance ); /*virtual*/ ~LLTexLayerParamAlpha(); - /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const; - void* operator new(size_t size) { return ll_aligned_malloc_16(size); @@ -79,6 +78,8 @@ public: ll_aligned_free_16(ptr); } + /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const; + // LLVisualParam Virtual functions ///*virtual*/ BOOL parseData(LLXmlTreeNode* node); /*virtual*/ void apply( ESex avatar_sex ) {} @@ -106,7 +107,7 @@ private: LLPointer mStaticImageRaw; BOOL mNeedsCreateTexture; BOOL mStaticImageInvalid; - LLVector4a mAvgDistortionVec; + LL_ALIGN_16(LLVector4a mAvgDistortionVec); F32 mCachedEffectiveWeight; public: @@ -116,7 +117,7 @@ public: typedef std::list< LLTexLayerParamAlpha* > param_alpha_ptr_list_t; static param_alpha_ptr_list_t sInstances; -}; +} LL_ALIGN_POSTFIX(16); class LLTexLayerParamAlphaInfo : public LLViewerVisualParamInfo { friend class LLTexLayerParamAlpha; @@ -140,6 +141,8 @@ private: // LLTexLayerParamColor // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +LL_ALIGN_PREFIX(16) class LLTexLayerParamColor : public LLTexLayerParam { public: @@ -153,7 +156,6 @@ public: LLTexLayerParamColor( LLTexLayerInterface* layer ); LLTexLayerParamColor( LLAvatarAppearance* appearance ); - /* virtual */ ~LLTexLayerParamColor(); void* operator new(size_t size) { @@ -165,6 +167,8 @@ public: ll_aligned_free_16(ptr); } + /* virtual */ ~LLTexLayerParamColor(); + /*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const; // LLVisualParam Virtual functions @@ -188,8 +192,8 @@ public: protected: virtual void onGlobalColorChanged(bool upload_bake) {} private: - LLVector4a mAvgDistortionVec; -}; + LL_ALIGN_16(LLVector4a mAvgDistortionVec); +} LL_ALIGN_POSTFIX(16); class LLTexLayerParamColorInfo : public LLViewerVisualParamInfo { diff --git a/indra/llappearance/llviewervisualparam.h b/indra/llappearance/llviewervisualparam.h index 259c0d6ee..64364c881 100644 --- a/indra/llappearance/llviewervisualparam.h +++ b/indra/llappearance/llviewervisualparam.h @@ -66,6 +66,7 @@ protected: // VIRTUAL CLASS // a viewer side interface class for a generalized parametric modification of the avatar mesh //----------------------------------------------------------------------------- +LL_ALIGN_PREFIX(16) class LLViewerVisualParam : public LLVisualParam { public: @@ -106,6 +107,6 @@ public: BOOL getCrossWearable() const { return getInfo()->mCrossWearable; } -}; +} LL_ALIGN_POSTFIX(16); #endif // LL_LLViewerVisualParam_H From 655c9348dec2e90fe724fe9b22b5f15356eadd5e Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 14:53:56 -0600 Subject: [PATCH 20/41] Minor adjsutment to llcontrolgroup. Demote non-critical failure from llerrs to llwarns. --- indra/llxml/llcontrol.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 18861cb23..2816918a8 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -99,8 +99,10 @@ typedef enum e_control_type class LLControlVariable : public LLRefCount { - friend class LLControlGroup; + LOG_CLASS(LLControlVariable); + friend class LLControlGroup; + public: typedef boost::signals2::signal validate_signal_t; typedef boost::signals2::signal commit_signal_t; @@ -208,6 +210,7 @@ T convert_from_llsd(const LLSD& sd, eControlType type, const std::string& contro //const U32 STRING_CACHE_SIZE = 10000; class LLControlGroup : public LLInstanceTracker { + LOG_CLASS(LLControlGroup); protected: typedef std::map ctrl_name_table_t; ctrl_name_table_t mNameTable; @@ -283,6 +286,7 @@ public: else { llwarns << "Control " << name << " not found." << llendl; + return T(); } return convert_from_llsd(value, type, name); } @@ -313,7 +317,7 @@ public: } else { - llerrs << "Invalid control " << name << llendl; + llwarns << "Invalid control " << name << llendl; } } From 074261067480e2ba285c63631316cf004c0b8cd0 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 14:59:02 -0600 Subject: [PATCH 21/41] Updated gObjectPreviewProgram shader (simple lighting) --- .../shaders/class1/objects/previewF.glsl | 41 +++++++++++++ .../shaders/class1/objects/previewV.glsl | 57 ++++++++++++++++--- indra/newview/llfloatermodelpreview.cpp | 24 ++++---- indra/newview/llviewershadermgr.cpp | 13 +++-- 4 files changed, 107 insertions(+), 28 deletions(-) create mode 100644 indra/newview/app_settings/shaders/class1/objects/previewF.glsl diff --git a/indra/newview/app_settings/shaders/class1/objects/previewF.glsl b/indra/newview/app_settings/shaders/class1/objects/previewF.glsl new file mode 100644 index 000000000..284da3d0a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/previewF.glsl @@ -0,0 +1,41 @@ +/** + * @file previewF.glsl + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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$ + */ + +#ifdef DEFINE_GL_FRAGCOLOR +out vec4 frag_color; +#else +#define frag_color gl_FragColor +#endif + +uniform sampler2D diffuseMap; + +VARYING vec4 vertex_color; +VARYING vec2 vary_texcoord0; + +void main() +{ + vec4 color = texture2D(diffuseMap,vary_texcoord0.xy) * vertex_color; + frag_color = color; +} diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl index 5dcfa8706..7f3f84398 100644 --- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl @@ -32,12 +32,51 @@ ATTRIBUTE vec3 position; ATTRIBUTE vec3 normal; ATTRIBUTE vec2 texcoord0; +uniform vec4 color; + VARYING vec4 vertex_color; VARYING vec2 vary_texcoord0; +uniform vec4 light_position[8]; +uniform vec3 light_direction[8]; +uniform vec3 light_attenuation[8]; +uniform vec3 light_diffuse[8]; + +//=================================================================================================== +//declare these here explicitly to separate them from atmospheric lighting elsewhere to work around +//drivers that are picky about functions being declared but not defined even if they aren't called +float calcDirectionalLight(vec3 n, vec3 l) +{ + float a = max(dot(n,l),0.0); + return a; +} + + +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight) +{ + //get light vector + vec3 lv = lp.xyz-v; + + //get distance + float d = length(lv); + + //normalize light vector + lv *= 1.0/d; + + //distance attenuation + float da = clamp(1.0/(la * d), 0.0, 1.0); + + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 + + //angular attenuation + da *= calcDirectionalLight(n, lv); + + return da; +} +//==================================================================================================== -vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); -void calcAtmospherics(vec3 inPositionEye); void main() { @@ -45,13 +84,15 @@ void main() vec4 pos = (modelview_matrix * vec4(position.xyz, 1.0)); gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; - + vec3 norm = normalize(normal_matrix * normal); - calcAtmospherics(pos.xyz); + vec4 col = vec4(0,0,0,1); - vec4 color = calcLighting(pos.xyz, norm, vec4(1,1,1,1), vec4(0.)); - vertex_color = color; - - + // Collect normal lights (need to be divided by two, as we later multiply by 2) + col.rgb += light_diffuse[1].rgb * calcDirectionalLight(norm, light_position[1].xyz); + col.rgb += light_diffuse[2].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z); + col.rgb += light_diffuse[3].rgb*calcPointLightOrSpotLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z); + + vertex_color = col*color; } diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 8ca18f6b0..8ff747187 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1641,7 +1641,6 @@ bool LLModelLoader::doLoadModel() //If no skeleton, do a breadth-first search to get at specific joints bool rootNode = false; - bool skeletonWithNoRootNode = false; //Need to test for a skeleton that does not have a root node //This occurs when your instance controller does not have an associated scene @@ -1652,10 +1651,6 @@ bool LLModelLoader::doLoadModel() { rootNode = true; } - else - { - skeletonWithNoRootNode = true; - } } if (!pSkeleton || !rootNode) @@ -5034,6 +5029,11 @@ BOOL LLModelPreview::render() refresh(); } + if (use_shaders) + { + gObjectPreviewProgram.bind(); + } + gGL.loadIdentity(); gPipeline.enableLightsPreview(); @@ -5043,8 +5043,9 @@ BOOL LLModelPreview::render() LLQuaternion av_rot = camera_rot; LLViewerCamera::getInstance()->setOriginAndLookAt( target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest + LLVector3::z_axis, // up + target_pos); // point of interest + z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); @@ -5058,11 +5059,6 @@ BOOL LLModelPreview::render() const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - if (use_shaders) - { - gObjectPreviewProgram.bind(); - } - LLGLEnable normalize(GL_NORMALIZE); if (!mBaseModel.empty() && mVertexBuffer[5].empty()) @@ -5248,10 +5244,10 @@ BOOL LLModelPreview::render() if (i + 1 >= hull_colors.size()) { - hull_colors.push_back(LLColor4U(rand()%128 + 127, rand()%128 + 127, rand()%128 + 127, 128)); + hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128)); } - glColor4ubv(hull_colors[i].mV); + gGL.diffuseColor4ubv(hull_colors[i].mV); LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); if (explode > 0.f) diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 313673721..76b3298ec 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -1887,18 +1887,19 @@ BOOL LLViewerShaderMgr::loadShadersObject() if (success) { gObjectPreviewProgram.mName = "Simple Shader"; - gObjectPreviewProgram.mFeatures.calculatesLighting = true; - gObjectPreviewProgram.mFeatures.calculatesAtmospherics = true; - gObjectPreviewProgram.mFeatures.hasGamma = true; - gObjectPreviewProgram.mFeatures.hasAtmospherics = true; - gObjectPreviewProgram.mFeatures.hasLighting = true; + gObjectPreviewProgram.mFeatures.calculatesLighting = false; + gObjectPreviewProgram.mFeatures.calculatesAtmospherics = false; + gObjectPreviewProgram.mFeatures.hasGamma = false; + gObjectPreviewProgram.mFeatures.hasAtmospherics = false; + gObjectPreviewProgram.mFeatures.hasLighting = false; gObjectPreviewProgram.mFeatures.mIndexedTextureChannels = 0; gObjectPreviewProgram.mFeatures.disableTextureIndex = true; gObjectPreviewProgram.mShaderFiles.clear(); gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewV.glsl", GL_VERTEX_SHADER_ARB)); - gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB)); + gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewF.glsl", GL_FRAGMENT_SHADER_ARB)); gObjectPreviewProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; success = gObjectPreviewProgram.createShader(NULL, NULL); + gObjectPreviewProgram.mFeatures.hasLighting = true; } if (success) From b901e57c55344291ca751251084ab3f8c5e6d017 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 15:00:50 -0600 Subject: [PATCH 22/41] Start culling of llmemtype (for now just #ifdef'd to nothingness. --- indra/llcommon/llallocator.cpp | 33 --------------------------------- indra/llcommon/llallocator.h | 6 ------ indra/llcommon/llmemtype.cpp | 5 ++++- indra/llcommon/llmemtype.h | 11 +++++++++++ 4 files changed, 15 insertions(+), 40 deletions(-) diff --git a/indra/llcommon/llallocator.cpp b/indra/llcommon/llallocator.cpp index 87654b5b9..34fc28d8c 100644 --- a/indra/llcommon/llallocator.cpp +++ b/indra/llcommon/llallocator.cpp @@ -35,28 +35,6 @@ DECLARE_bool(heap_profile_use_stack_trace); //DECLARE_double(tcmalloc_release_rate); -// static -void LLAllocator::pushMemType(S32 type) -{ - if(isProfiling()) - { - PushMemType(type); - } -} - -// static -S32 LLAllocator::popMemType() -{ - if (isProfiling()) - { - return PopMemType(); - } - else - { - return -1; - } -} - void LLAllocator::setProfilingEnabled(bool should_enable) { // NULL disables dumping to disk @@ -94,17 +72,6 @@ std::string LLAllocator::getRawProfile() // stub implementations for when tcmalloc is disabled // -// static -void LLAllocator::pushMemType(S32 type) -{ -} - -// static -S32 LLAllocator::popMemType() -{ - return -1; -} - void LLAllocator::setProfilingEnabled(bool should_enable) { } diff --git a/indra/llcommon/llallocator.h b/indra/llcommon/llallocator.h index a91dd57d1..d26ad73c5 100644 --- a/indra/llcommon/llallocator.h +++ b/indra/llcommon/llallocator.h @@ -29,16 +29,10 @@ #include -#include "llmemtype.h" #include "llallocator_heap_profile.h" class LL_COMMON_API LLAllocator { friend class LLMemoryView; - friend class LLMemType; - -private: - static void pushMemType(S32 type); - static S32 popMemType(); public: void setProfilingEnabled(bool should_enable); diff --git a/indra/llcommon/llmemtype.cpp b/indra/llcommon/llmemtype.cpp index 9dcdaeff1..614dee323 100644 --- a/indra/llcommon/llmemtype.cpp +++ b/indra/llcommon/llmemtype.cpp @@ -27,7 +27,9 @@ #include "llmemtype.h" #include "llallocator.h" +#if MEM_TRACK_TYPE std::vector LLMemType::DeclareMemType::mNameList; +#endif LLMemType::DeclareMemType LLMemType::MTYPE_INIT("Init"); LLMemType::DeclareMemType LLMemType::MTYPE_STARTUP("Startup"); @@ -194,7 +196,7 @@ LLMemType::DeclareMemType LLMemType::MTYPE_TEMP9("Temp9"); LLMemType::DeclareMemType LLMemType::MTYPE_OTHER("Other"); - +#if MEM_TRACK_TYPE LLMemType::DeclareMemType::DeclareMemType(char const * st) { mID = (S32)mNameList.size(); @@ -229,3 +231,4 @@ char const * LLMemType::getNameFromID(S32 id) } //-------------------------------------------------------------------------------------------------- +#endif //MEM_TRACK_TYPE \ No newline at end of file diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h index b51d7e0f6..2a411eb90 100644 --- a/indra/llcommon/llmemtype.h +++ b/indra/llcommon/llmemtype.h @@ -52,6 +52,10 @@ public: class LL_COMMON_API DeclareMemType { public: + +#if !MEM_TRACK_TYPE + DeclareMemType(char const * st) {}; //Do nothing +#else DeclareMemType(char const * st); ~DeclareMemType(); @@ -60,12 +64,17 @@ public: // array so we can map an index ID to Name static std::vector mNameList; +#endif }; +#if !MEM_TRACK_TYPE + LLMemType(DeclareMemType& dt){} //Do nothing +#else LLMemType(DeclareMemType& dt); ~LLMemType(); static char const * getNameFromID(S32 id); +#endif static DeclareMemType MTYPE_INIT; static DeclareMemType MTYPE_STARTUP; @@ -232,7 +241,9 @@ public: static DeclareMemType MTYPE_OTHER; // Special; used by display code +#if MEM_TRACK_TYPE S32 mTypeIndex; +#endif }; //---------------------------------------------------------------------------- From 1eb142535c1a5734c3c857e60aaf4c8a53902cac Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 15:02:17 -0600 Subject: [PATCH 23/41] Merge in changes to avoid a few potential problems with LLUUID when accessed from multiple threads. --- indra/llcommon/lluuid.cpp | 36 +++++++++++++++++++++++++++--------- indra/llcommon/lluuid.h | 3 +++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index 1cbd888e5..f6b9db6c1 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -41,10 +41,15 @@ #include "llmd5.h" #include "llstring.h" #include "lltimer.h" +#include "llthread.h" const LLUUID LLUUID::null; const LLTransactionID LLTransactionID::tnull; +// static +LLMutex * LLUUID::mMutex = NULL; + + /* NOT DONE YET!!! @@ -731,6 +736,7 @@ void LLUUID::getCurrentTime(uuid_time_t *timestamp) getSystemTime(&time_last); uuids_this_tick = uuids_per_tick; init = TRUE; + mMutex = new LLMutex; } uuid_time_t time_now = {0,0}; @@ -782,6 +788,7 @@ void LLUUID::generate() #endif if (!has_init) { + has_init = 1; if (getNodeID(node_id) <= 0) { get_random_bytes(node_id, 6); @@ -803,18 +810,24 @@ void LLUUID::generate() #else clock_seq = (U16)ll_rand(65536); #endif - has_init = 1; } // get current time getCurrentTime(×tamp); + U16 our_clock_seq = clock_seq; - // if clock went backward change clockseq - if (cmpTime(×tamp, &time_last) == -1) { + // if clock hasn't changed or went backward, change clockseq + if (cmpTime(×tamp, &time_last) != 1) + { + LLMutexLock lock(mMutex); clock_seq = (clock_seq + 1) & 0x3FFF; - if (clock_seq == 0) clock_seq++; + if (clock_seq == 0) + clock_seq++; + our_clock_seq = clock_seq; // Ensure we're using a different clock_seq value from previous time } + time_last = timestamp; + memcpy(mData+10, node_id, 6); /* Flawfinder: ignore */ U32 tmp; tmp = timestamp.low; @@ -836,7 +849,8 @@ void LLUUID::generate() tmp >>= 8; mData[6] = (unsigned char) tmp; - tmp = clock_seq; + tmp = our_clock_seq; + mData[9] = (unsigned char) tmp; tmp >>= 8; mData[8] = (unsigned char) tmp; @@ -846,8 +860,6 @@ void LLUUID::generate() md5_uuid.update(mData,16); md5_uuid.finalize(); md5_uuid.raw_digest(mData); - - time_last = timestamp; } void LLUUID::generate(const std::string& hash_string) @@ -861,8 +873,14 @@ U32 LLUUID::getRandomSeed() static unsigned char seed[16]; /* Flawfinder: ignore */ getNodeID(&seed[0]); - seed[6]='\0'; - seed[7]='\0'; + + // Incorporate the pid into the seed to prevent + // processes that start on the same host at the same + // time from generating the same seed. + pid_t pid = LLApp::getPid(); + + seed[6]=(unsigned char)(pid >> 8); + seed[7]=(unsigned char)(pid); getSystemTime((uuid_time_t *)(&seed[8])); LLMD5 md5_seed; diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index 726be4a82..4e5bed189 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -31,6 +31,8 @@ #include "stdtypes.h" #include "llpreprocessor.h" +class LLMutex; + const S32 UUID_BYTES = 16; const S32 UUID_WORDS = 4; const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below @@ -118,6 +120,7 @@ public: static BOOL validate(const std::string& in_string); // Validate that the UUID string is legal. static const LLUUID null; + static LLMutex * mMutex; static U32 getRandomSeed(); static S32 getNodeID(unsigned char * node_id); From c92c68dee2162fb11ea3dc9fbeca6db328811251 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 15:07:07 -0600 Subject: [PATCH 24/41] A few more preview-related changes. --- indra/newview/llfloaterimagepreview.cpp | 4 +++- indra/newview/pipeline.cpp | 25 +++++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 013c9aa49..72e46f09c 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -973,11 +973,13 @@ BOOL LLImagePreviewSculpted::render() { gObjectPreviewProgram.bind(); } + gPipeline.enableLightsPreview(); + gGL.pushMatrix(); const F32 SCALE = 1.25f; gGL.scalef(SCALE, SCALE, SCALE); const F32 BRIGHTNESS = 0.9f; - gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); + gGL.diffuseColor3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); mVertexBuffer->draw(LLRender::TRIANGLES, num_indices, 0); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 88b9f9c20..fff60e992 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -489,7 +489,10 @@ void LLPipeline::init() //gSavedSettings.getControl("RenderDelayVBUpdate")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); gSavedSettings.getControl("UseOcclusion")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); gSavedSettings.getControl("VertexShaderEnable")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + gSavedSettings.getControl("RenderDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); gSavedSettings.getControl("RenderFSAASamples")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + gSavedSettings.getControl("RenderAvatarVP")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + gSavedSettings.getControl("WindLightUseAtmosShaders")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); } LLPipeline::~LLPipeline() @@ -631,7 +634,16 @@ void LLPipeline::resizeScreenTexture() GLuint resX = gViewerWindow->getWorldViewWidthRaw(); GLuint resY = gViewerWindow->getWorldViewHeightRaw(); - allocateScreenBuffer(resX,resY); + if (!allocateScreenBuffer(resX,resY)) + { //FAILSAFE: screen buffer allocation failed, disable deferred rendering if it's enabled + //NOTE: if the session closes successfully after this call, deferred rendering will be + // disabled on future sessions + if (LLPipeline::sRenderDeferred) + { + gSavedSettings.setBOOL("RenderDeferred", FALSE); + LLPipeline::refreshCachedSettings(); + } + } } } @@ -902,6 +914,8 @@ void LLPipeline::refreshCachedSettings() && LLFeatureManager::getInstance()->isFeatureAvailable("UseOcclusion") && gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery) ? 2 : 0; + + updateRenderDeferred(); } void LLPipeline::releaseGLBuffers() @@ -5412,7 +5426,6 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) if (light->isLightSpotlight() // directional (spot-)light && (LLPipeline::sRenderDeferred || RenderSpotLightsInNondeferred)) // these are only rendered as GL spotlights if we're in deferred rendering mode *or* the setting forces them on { - LLVector3 spotparams = light->getSpotLightParams(); LLQuaternion quat = light->getRenderRotation(); LLVector3 at_axis(0,0,-1); // this matches deferred rendering's object light direction at_axis *= quat; @@ -5460,7 +5473,7 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) F32 light_radius = 16.f; - F32 x = 3.f; + F32 x = 3.f; float linatten = x / (light_radius); // % of brightness at radius mHWLightColors[2] = light_color; @@ -5624,7 +5637,7 @@ void LLPipeline::enableLightsPreview() LLVector4 light_pos(dir0, 0.0f); - LLLightState* light = gGL.getLight(0); + LLLightState* light = gGL.getLight(1); light->enable(); light->setPosition(light_pos); @@ -5636,7 +5649,7 @@ void LLPipeline::enableLightsPreview() light_pos = LLVector4(dir1, 0.f); - light = gGL.getLight(1); + light = gGL.getLight(2); light->enable(); light->setPosition(light_pos); light->setDiffuse(diffuse1); @@ -5646,7 +5659,7 @@ void LLPipeline::enableLightsPreview() light->setSpotCutoff(180.f); light_pos = LLVector4(dir2, 0.f); - light = gGL.getLight(2); + light = gGL.getLight(3); light->enable(); light->setPosition(light_pos); light->setDiffuse(diffuse2); From 24efffc68a61341d00242b414e3bfd185494e107 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 15:08:11 -0600 Subject: [PATCH 25/41] LLInitParam merge. --- indra/llcommon/llinitparam.h | 204 ++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 85 deletions(-) diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index 993f16d04..b79417f96 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -224,32 +225,81 @@ namespace LLInitParam typedef std::map parser_write_func_map_t; typedef std::map parser_inspect_func_map_t; + private: + template::value> + struct ReaderWriter + { + static bool read(T& param, Parser* parser) + { + parser_read_func_map_t::iterator found_it = parser->mParserReadFuncs->find(&typeid(T)); + if (found_it != parser->mParserReadFuncs->end()) + { + return found_it->second(*parser, (void*)¶m); + } + return false; + } + + static bool write(const T& param, Parser* parser, name_stack_t& name_stack) + { + parser_write_func_map_t::iterator found_it = parser->mParserWriteFuncs->find(&typeid(T)); + if (found_it != parser->mParserWriteFuncs->end()) + { + return found_it->second(*parser, (const void*)¶m, name_stack); + } + return false; + } + }; + + // read enums as ints + template + struct ReaderWriter + { + static bool read(T& param, Parser* parser) + { + // read all enums as ints + parser_read_func_map_t::iterator found_it = parser->mParserReadFuncs->find(&typeid(S32)); + if (found_it != parser->mParserReadFuncs->end()) + { + S32 value; + if (found_it->second(*parser, (void*)&value)) + { + param = (T)value; + return true; + } + } + return false; + } + + static bool write(const T& param, Parser* parser, name_stack_t& name_stack) + { + parser_write_func_map_t::iterator found_it = parser->mParserWriteFuncs->find(&typeid(S32)); + if (found_it != parser->mParserWriteFuncs->end()) + { + return found_it->second(*parser, (const void*)¶m, name_stack); + } + return false; + } + }; + + public: + Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map) : mParseSilently(false), mParserReadFuncs(&read_map), mParserWriteFuncs(&write_map), mParserInspectFuncs(&inspect_map) {} + virtual ~Parser(); template bool readValue(T& param) { - parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); - if (found_it != mParserReadFuncs->end()) - { - return found_it->second(*this, (void*)¶m); - } - return false; + return ReaderWriter::read(param, this); } template bool writeValue(const T& param, name_stack_t& name_stack) { - parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); - if (found_it != mParserWriteFuncs->end()) - { - return found_it->second(*this, (const void*)¶m, name_stack); - } - return false; + return ReaderWriter::write(param, this, name_stack); } // dispatch inspection to registered inspection functions, for each parameter in a param block @@ -841,31 +891,25 @@ namespace LLInitParam self_t& typed_param = static_cast(param); // no further names in stack, attempt to parse value now if (name_stack_range.first == name_stack_range.second) - { - if (parser.readValue(typed_param.getValue())) + { + std::string name; + + // try to parse a known named value + if(name_value_lookup_t::valueNamesExist() + && parser.readValue(name) + && name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + { + typed_param.setValueName(name); + typed_param.setProvided(); + return true; + } + // try to read value directly + else if (parser.readValue(typed_param.getValue())) { typed_param.clearValueName(); typed_param.setProvided(); return true; } - - // try to parse a known named value - if(name_value_lookup_t::valueNamesExist()) - { - // try to parse a known named value - std::string name; - if (parser.readValue(name)) - { - // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, typed_param.getValue())) - { - typed_param.setValueName(name); - typed_param.setProvided(); - return true; - } - - } - } } return false; } @@ -987,30 +1031,29 @@ namespace LLInitParam static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) { self_t& typed_param = static_cast(param); - // attempt to parse block... + + if (name_stack_range.first == name_stack_range.second) + { // try to parse a known named value + std::string name; + + if(name_value_lookup_t::valueNamesExist() + && parser.readValue(name) + && name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + { + typed_param.setValueName(name); + typed_param.setProvided(); + return true; + } + } + if(typed_param.deserializeBlock(parser, name_stack_range, new_name)) - { + { // attempt to parse block... typed_param.clearValueName(); typed_param.setProvided(); return true; } - if(name_value_lookup_t::valueNamesExist()) - { - // try to parse a known named value - std::string name; - if (parser.readValue(name)) - { - // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, typed_param.getValue())) - { - typed_param.setValueName(name); - typed_param.setProvided(); - return true; - } - } - } return false; } @@ -1160,31 +1203,23 @@ namespace LLInitParam value_t value; // no further names in stack, attempt to parse value now if (name_stack_range.first == name_stack_range.second) - { - // attempt to read value directly - if (parser.readValue(value)) + { + std::string name; + + // try to parse a known named value + if(name_value_lookup_t::valueNamesExist() + && parser.readValue(name) + && name_value_lookup_t::getValueFromName(name, value)) + { + typed_param.add(value); + typed_param.mValues.back().setValueName(name); + return true; + } + else if (parser.readValue(value)) // attempt to read value directly { typed_param.add(value); return true; } - - // try to parse a known named value - if(name_value_lookup_t::valueNamesExist()) - { - // try to parse a known named value - std::string name; - if (parser.readValue(name)) - { - // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value)) - { - typed_param.add(value); - typed_param.mValues.back().setValueName(name); - return true; - } - - } - } } return false; } @@ -1362,28 +1397,27 @@ namespace LLInitParam param_value_t& value = typed_param.mValues.back(); + if (name_stack_range.first == name_stack_range.second) + { // try to parse a known named value + std::string name; + + if(name_value_lookup_t::valueNamesExist() + && parser.readValue(name) + && name_value_lookup_t::getValueFromName(name, value.getValue())) + { + typed_param.mValues.back().setValueName(name); + typed_param.setProvided(); + return true; + } + } + // attempt to parse block... if(value.deserializeBlock(parser, name_stack_range, new_name)) { typed_param.setProvided(); return true; } - else if(name_value_lookup_t::valueNamesExist()) - { - // try to parse a known named value - std::string name; - if (parser.readValue(name)) - { - // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value.getValue())) - { - typed_param.mValues.back().setValueName(name); - typed_param.setProvided(); - return true; - } - } - } if (new_value) { // failed to parse new value, pop it off From 6725c17f0184d10ee71ac3c1c180d7cfa5686288 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 12 Feb 2013 15:10:15 -0600 Subject: [PATCH 26/41] LLSys merge and cleanup. --- indra/llcommon/llsys.cpp | 625 ++++++++++++++++++++++++++++++++++++--- indra/llcommon/llsys.h | 21 ++ 2 files changed, 603 insertions(+), 43 deletions(-) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 4056b3253..c96f2191f 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + #include "linden_common.h" #include "llsys.h" @@ -36,22 +40,45 @@ #endif #include "llprocessor.h" +#include "llerrorcontrol.h" +#include "llevents.h" +#include "lltimer.h" +#include "llsdserialize.h" +#include "llsdutil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llsd; #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN # include # include +# include // GetPerformanceInfo() et al. #elif LL_DARWIN # include # include # include # include # include +# include +# include +# include +# include +# include #elif LL_LINUX # include # include # include # include +# include const char MEMINFO_FILE[] = "/proc/meminfo"; #elif LL_SOLARIS # include @@ -70,6 +97,15 @@ extern int errno; static const S32 CPUINFO_BUFFER_SIZE = 16383; LLCPUInfo gSysCPU; +// Don't log memory info any more often than this. It also serves as our +// framerate sample size. +static const F32 MEM_INFO_THROTTLE = 20; +// Sliding window of samples. We intentionally limit the length of time we +// remember "the slowest" framerate because framerate is very slow at login. +// If we only triggered FrameWatcher logging when the session framerate +// dropped below the login framerate, we'd have very little additional data. +static const F32 MEM_INFO_WINDOW = 10*60; + #if LL_WINDOWS #ifndef DLLVERSIONINFO typedef struct _DllVersionInfo @@ -572,6 +608,7 @@ LLCPUInfo::LLCPUInfo() out << " (" << mCPUMHz << " MHz)"; } mCPUString = out.str(); + LLStringUtil::trim(mCPUString); } bool LLCPUInfo::hasAltivec() const @@ -613,8 +650,78 @@ void LLCPUInfo::stream(std::ostream& s) const s << "->mCPUString: " << mCPUString << std::endl; } +// Helper class for LLMemoryInfo: accumulate stats in the form we store for +// LLMemoryInfo::getStatsMap(). +class Stats +{ +public: + Stats(): + mStats(LLSD::emptyMap()) + {} + + // Store every integer type as LLSD::Integer. + template + void add(const LLSD::String& name, const T& value, + typename boost::enable_if >::type* = 0) + { + mStats[name] = LLSD::Integer(value); + } + + // Store every floating-point type as LLSD::Real. + template + void add(const LLSD::String& name, const T& value, + typename boost::enable_if >::type* = 0) + { + mStats[name] = LLSD::Real(value); + } + + // Hope that LLSD::Date values are sufficiently unambiguous. + void add(const LLSD::String& name, const LLSD::Date& value) + { + mStats[name] = value; + } + + LLSD get() const { return mStats; } + +private: + LLSD mStats; +}; + +// Wrap boost::regex_match() with a function that doesn't throw. +template +static bool regex_match_no_exc(const S& string, M& match, const R& regex) +{ + try + { + return boost::regex_match(string, match, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS("LLMemoryInfo") << "error matching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } +} + +// Wrap boost::regex_search() with a function that doesn't throw. +template +static bool regex_search_no_exc(const S& string, M& match, const R& regex) +{ + try + { + return boost::regex_search(string, match, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS("LLMemoryInfo") << "error searching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } +} + LLMemoryInfo::LLMemoryInfo() { + refresh(); } #if LL_WINDOWS @@ -638,11 +745,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB) U32 LLMemoryInfo::getPhysicalMemoryKB() const { #if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); - - return LLMemoryAdjustKBResult((U32)(state.ullTotalPhys >> 10)); + return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger()); #elif LL_DARWIN // This might work on Linux as well. Someone check... @@ -690,12 +793,82 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb) { #if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); + // Sigh, this shouldn't be a static method, then we wouldn't have to + // reload this data separately from refresh() + LLSD statsMap(loadStatsMap()); - avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ; - avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ; + avail_physical_mem_kb = statsMap["Avail Physical KB"].asInteger(); + avail_virtual_mem_kb = statsMap["Avail Virtual KB"].asInteger(); + +#elif LL_DARWIN + // mStatsMap is derived from vm_stat, look for (e.g.) "kb free": + // $ vm_stat + // Mach Virtual Memory Statistics: (page size of 4096 bytes) + // Pages free: 462078. + // Pages active: 142010. + // Pages inactive: 220007. + // Pages wired down: 159552. + // "Translation faults": 220825184. + // Pages copy-on-write: 2104153. + // Pages zero filled: 167034876. + // Pages reactivated: 65153. + // Pageins: 2097212. + // Pageouts: 41759. + // Object cache: 841598 hits of 7629869 lookups (11% hit rate) + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; + +#elif LL_LINUX + // mStatsMap is derived from MEMINFO_FILE: + // $ cat /proc/meminfo + // MemTotal: 4108424 kB + // MemFree: 1244064 kB + // Buffers: 85164 kB + // Cached: 1990264 kB + // SwapCached: 0 kB + // Active: 1176648 kB + // Inactive: 1427532 kB + // Active(anon): 529152 kB + // Inactive(anon): 15924 kB + // Active(file): 647496 kB + // Inactive(file): 1411608 kB + // Unevictable: 16 kB + // Mlocked: 16 kB + // HighTotal: 3266316 kB + // HighFree: 721308 kB + // LowTotal: 842108 kB + // LowFree: 522756 kB + // SwapTotal: 6384632 kB + // SwapFree: 6384632 kB + // Dirty: 28 kB + // Writeback: 0 kB + // AnonPages: 528820 kB + // Mapped: 89472 kB + // Shmem: 16324 kB + // Slab: 159624 kB + // SReclaimable: 145168 kB + // SUnreclaim: 14456 kB + // KernelStack: 2560 kB + // PageTables: 5560 kB + // NFS_Unstable: 0 kB + // Bounce: 0 kB + // WritebackTmp: 0 kB + // CommitLimit: 8438844 kB + // Committed_AS: 1271596 kB + // VmallocTotal: 122880 kB + // VmallocUsed: 65252 kB + // VmallocChunk: 52356 kB + // HardwareCorrupted: 0 kB + // HugePages_Total: 0 + // HugePages_Free: 0 + // HugePages_Rsvd: 0 + // HugePages_Surp: 0 + // Hugepagesize: 2048 kB + // DirectMap4k: 434168 kB + // DirectMap2M: 477184 kB + // (could also run 'free', but easier to read a file than run a program) + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; #else //do not know how to collect available memory info for other systems. @@ -708,56 +881,285 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v void LLMemoryInfo::stream(std::ostream& s) const { + // We want these memory stats to be easy to grep from the log, along with + // the timestamp. So preface each line with the timestamp and a + // distinctive marker. Without that, we'd have to search the log for the + // introducer line, then read subsequent lines, etc... + std::string pfx(LLError::utcTime() + " "); + + // Max key length + size_t key_width(0); + BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap)) + { + size_t len(pair.first.length()); + if (len > key_width) + { + key_width = len; + } + } + + // Now stream stats + BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap)) + { + s << pfx << std::setw(key_width+1) << (pair.first + ':') << ' '; + LLSD value(pair.second); + if (value.isInteger()) + s << std::setw(12) << value.asInteger(); + else if (value.isReal()) + s << std::fixed << std::setprecision(1) << value.asReal(); + else if (value.isDate()) + value.asDate().toStream(s); + else + s << value; // just use default LLSD formatting + s << std::endl; + } +} + +LLSD LLMemoryInfo::getStatsMap() const +{ + return mStatsMap; +} + +LLMemoryInfo& LLMemoryInfo::refresh() +{ + mStatsMap = loadStatsMap(); + + LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n"; + LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT); + LL_ENDL; + + return *this; +} + +LLSD LLMemoryInfo::loadStatsMap() +{ + // This implementation is derived from stream() code (as of 2011-06-29). + Stats stats; + + // associate timestamp for analysis over time + stats.add("timestamp", LLDate::now()); + #if LL_WINDOWS MEMORYSTATUSEX state; state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl; - s << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl; - s << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl; - s << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl; - s << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl; - s << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl; - s << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl; + DWORDLONG div = 1024; + + stats.add("Percent Memory use", state.dwMemoryLoad/div); + stats.add("Total Physical KB", state.ullTotalPhys/div); + stats.add("Avail Physical KB", state.ullAvailPhys/div); + stats.add("Total page KB", state.ullTotalPageFile/div); + stats.add("Avail page KB", state.ullAvailPageFile/div); + stats.add("Total Virtual KB", state.ullTotalVirtual/div); + stats.add("Avail Virtual KB", state.ullAvailVirtual/div); + + PERFORMANCE_INFORMATION perf; + perf.cb = sizeof(perf); + GetPerformanceInfo(&perf, sizeof(perf)); + + SIZE_T pagekb(perf.PageSize/1024); + stats.add("CommitTotal KB", perf.CommitTotal * pagekb); + stats.add("CommitLimit KB", perf.CommitLimit * pagekb); + stats.add("CommitPeak KB", perf.CommitPeak * pagekb); + stats.add("PhysicalTotal KB", perf.PhysicalTotal * pagekb); + stats.add("PhysicalAvail KB", perf.PhysicalAvailable * pagekb); + stats.add("SystemCache KB", perf.SystemCache * pagekb); + stats.add("KernelTotal KB", perf.KernelTotal * pagekb); + stats.add("KernelPaged KB", perf.KernelPaged * pagekb); + stats.add("KernelNonpaged KB", perf.KernelNonpaged * pagekb); + stats.add("PageSize KB", pagekb); + stats.add("HandleCount", perf.HandleCount); + stats.add("ProcessCount", perf.ProcessCount); + stats.add("ThreadCount", perf.ThreadCount); + + PROCESS_MEMORY_COUNTERS_EX pmem; + pmem.cb = sizeof(pmem); + // GetProcessMemoryInfo() is documented to accept either + // PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably + // using the redundant size info to distinguish. But its prototype + // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a + // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the + // pointer. + GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem)); + + stats.add("Page Fault Count", pmem.PageFaultCount); + stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div); + stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div); + stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div); + stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div); + stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div); + stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div); + stats.add("PagefileUsage KB", pmem.PagefileUsage/div); + stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div); + stats.add("PrivateUsage KB", pmem.PrivateUsage/div); + #elif LL_DARWIN - uint64_t phys = 0; - size_t len = sizeof(phys); + const vm_size_t pagekb(vm_page_size / 1024); + + // + // Collect the vm_stat's + // - if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) { - s << "Total Physical KB: " << phys/1024 << std::endl; - } - else - { - s << "Unable to collect memory information"; - } -#elif LL_SOLARIS - U64 phys = 0; + vm_statistics_data_t vmstat; + mach_msg_type_number_t vmstatCount = HOST_VM_INFO_COUNT; - phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); - - s << "Total Physical KB: " << phys << std::endl; -#else - // *NOTE: This works on linux. What will it do on other systems? - LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb"); - if(meminfo) + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) &vmstat, &vmstatCount) != KERN_SUCCESS) { - char line[MAX_STRING]; /* Flawfinder: ignore */ - memset(line, 0, MAX_STRING); - while(fgets(line, MAX_STRING, meminfo)) - { - line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/ - s << line; + LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; + } + else + { + stats.add("Pages free KB", pagekb * vmstat.free_count); + stats.add("Pages active KB", pagekb * vmstat.active_count); + stats.add("Pages inactive KB", pagekb * vmstat.inactive_count); + stats.add("Pages wired KB", pagekb * vmstat.wire_count); + + stats.add("Pages zero fill", vmstat.zero_fill_count); + stats.add("Page reactivations", vmstat.reactivations); + stats.add("Page-ins", vmstat.pageins); + stats.add("Page-outs", vmstat.pageouts); + + stats.add("Faults", vmstat.faults); + stats.add("Faults copy-on-write", vmstat.cow_faults); + + stats.add("Cache lookups", vmstat.lookups); + stats.add("Cache hits", vmstat.hits); + + stats.add("Page purgeable count", vmstat.purgeable_count); + stats.add("Page purges", vmstat.purges); + + stats.add("Page speculative reads", vmstat.speculative_count); + } + } + + // + // Collect the misc task info + // + + { + task_events_info_data_t taskinfo; + unsigned taskinfoSize = sizeof(taskinfo); + + if (task_info(mach_task_self(), TASK_EVENTS_INFO, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS) + { + LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; + } + else + { + stats.add("Task page-ins", taskinfo.pageins); + stats.add("Task copy-on-write faults", taskinfo.cow_faults); + stats.add("Task messages sent", taskinfo.messages_sent); + stats.add("Task messages received", taskinfo.messages_received); + stats.add("Task mach system call count", taskinfo.syscalls_mach); + stats.add("Task unix system call count", taskinfo.syscalls_unix); + stats.add("Task context switch count", taskinfo.csw); + } + } + + // + // Collect the basic task info + // + + { + task_basic_info_64_data_t taskinfo; + unsigned taskinfoSize = sizeof(taskinfo); + + if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS) + { + LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; + } + else + { + stats.add("Basic suspend count", taskinfo.suspend_count); + stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024); + stats.add("Basic resident memory KB", taskinfo.resident_size / 1024); + stats.add("Basic new thread policy", taskinfo.policy); + } + } + +#elif LL_SOLARIS + U64 phys = 0; + + phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); + + stats.add("Total Physical KB", phys); + +#elif LL_LINUX + std::ifstream meminfo(MEMINFO_FILE); + if (meminfo.is_open()) + { + // MemTotal: 4108424 kB + // MemFree: 1244064 kB + // Buffers: 85164 kB + // Cached: 1990264 kB + // SwapCached: 0 kB + // Active: 1176648 kB + // Inactive: 1427532 kB + // ... + // VmallocTotal: 122880 kB + // VmallocUsed: 65252 kB + // VmallocChunk: 52356 kB + // HardwareCorrupted: 0 kB + // HugePages_Total: 0 + // HugePages_Free: 0 + // HugePages_Rsvd: 0 + // HugePages_Surp: 0 + // Hugepagesize: 2048 kB + // DirectMap4k: 434168 kB + // DirectMap2M: 477184 kB + + // Intentionally don't pass the boost::no_except flag. This + // boost::regex object is constructed with a string literal, so it + // should be valid every time. If it becomes invalid, we WANT an + // exception, hopefully even before the dev checks in. + boost::regex stat_rx("(.+): +([0-9]+)( kB)?"); + boost::smatch matched; + + std::string line; + while (std::getline(meminfo, line)) + { + LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL; + if (regex_match_no_exc(line, matched, stat_rx)) + { + // e.g. "MemTotal: 4108424 kB" + LLSD::String key(matched[1].first, matched[1].second); + LLSD::String value_str(matched[2].first, matched[2].second); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast(value_str); + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in " << MEMINFO_FILE << " line: " + << line << LL_ENDL; + continue; + } + // Store this statistic. + stats.add(key, value); + } + else + { + LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: " + << line << LL_ENDL; + } } - fclose(meminfo); } else { - s << "Unable to collect memory information"; + LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; } + +#else + LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL; + #endif + + return stats.get(); } std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) @@ -778,6 +1180,143 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info) return s; } +class FrameWatcher +{ +public: + FrameWatcher(): + // Hooking onto the "mainloop" event pump gets us one call per frame. + mConnection(LLEventPumps::instance() + .obtain("mainloop") + .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))), + // Initializing mSampleStart to an invalid timestamp alerts us to skip + // trying to compute framerate on the first call. + mSampleStart(-1), + // Initializing mSampleEnd to 0 ensures that we treat the first call + // as the completion of a sample window. + mSampleEnd(0), + mFrames(0), + // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need + // the number of integer MEM_INFO_THROTTLE sample slots that will fit + // in MEM_INFO_WINDOW. Round up. + mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)), + // Initializing to F32_MAX means that the first real frame will become + // the slowest ever, which sounds like a good idea. + mSlowest(F32_MAX) + {} + + bool tick(const LLSD&) + { + F32 timestamp(mTimer.getElapsedTimeF32()); + + // Count this frame in the interval just completed. + ++mFrames; + + // Have we finished a sample window yet? + if (timestamp < mSampleEnd) + { + // no, just keep waiting + return false; + } + + // Set up for next sample window. Capture values for previous frame in + // local variables and reset data members. + U32 frames(mFrames); + F32 sampleStart(mSampleStart); + // No frames yet in next window + mFrames = 0; + // which starts right now + mSampleStart = timestamp; + // and ends MEM_INFO_THROTTLE seconds in the future + mSampleEnd = mSampleStart + MEM_INFO_THROTTLE; + + // On the very first call, that's all we can do, no framerate + // computation is possible. + if (sampleStart < 0) + { + return false; + } + + // How long did this actually take? As framerate slows, the duration + // of the frame we just finished could push us WELL beyond our desired + // sample window size. + F32 elapsed(timestamp - sampleStart); + F32 framerate(frames/elapsed); + + // Remember previous slowest framerate because we're just about to + // update it. + F32 slowest(mSlowest); + // Remember previous number of samples. + boost::circular_buffer::size_type prevSize(mSamples.size()); + + // Capture new framerate in our samples buffer. Once the buffer is + // full (after MEM_INFO_WINDOW seconds), this will displace the oldest + // sample. ("So they all rolled over, and one fell out...") + mSamples.push_back(framerate); + + // Calculate the new minimum framerate. I know of no way to update a + // rolling minimum without ever rescanning the buffer. But since there + // are only a few tens of items in this buffer, rescanning it is + // probably cheaper (and certainly easier to reason about) than + // attempting to optimize away some of the scans. + mSlowest = framerate; // pick an arbitrary entry to start + for (boost::circular_buffer::const_iterator si(mSamples.begin()), send(mSamples.end()); + si != send; ++si) + { + if (*si < mSlowest) + { + mSlowest = *si; + } + } + + // We're especially interested in memory as framerate drops. Only log + // when framerate drops below the slowest framerate we remember. + // (Should always be true for the end of the very first sample + // window.) + if (framerate >= slowest) + { + return false; + } + // Congratulations, we've hit a new low. :-P + + LL_INFOS("FrameWatcher") << ' '; + if (! prevSize) + { + LL_CONT << "initial framerate "; + } + else + { + LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE) + << " seconds "; + } + LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n' + << LLMemoryInfo() << LL_ENDL; + + return false; + } + +private: + // Storing the connection in an LLTempBoundListener ensures it will be + // disconnected when we're destroyed. + LLTempBoundListener mConnection; + // Track elapsed time + LLTimer mTimer; + // Some of what you see here is in fact redundant with functionality you + // can get from LLTimer. Unfortunately the LLTimer API is missing the + // feature we need: has at least the stated interval elapsed, and if so, + // exactly how long has passed? So we have to do it by hand, sigh. + // Time at start, end of sample window + F32 mSampleStart, mSampleEnd; + // Frames this sample window + U32 mFrames; + // Sliding window of framerate samples + boost::circular_buffer mSamples; + // Slowest framerate in mSamples + F32 mSlowest; +}; + +// Need an instance of FrameWatcher before it does any good +static FrameWatcher sFrameWatcher; + BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile) { std::string tmpfile; diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 41a4f2500..fb39ef4da 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -117,6 +117,27 @@ public: //get the available memory infomation in KiloBytes. static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb); + + // Retrieve a map of memory statistics. The keys of the map are platform- + // dependent. The values are in kilobytes to try to avoid integer overflow. + LLSD getStatsMap() const; + + // Re-fetch memory data (as reported by stream() and getStatsMap()) from the + // system. Normally this is fetched at construction time. Return (*this) + // to permit usage of the form: + // @code + // LLMemoryInfo info; + // ... + // info.refresh().getStatsMap(); + // @endcode + LLMemoryInfo& refresh(); + +private: + // set mStatsMap + static LLSD loadStatsMap(); + + // Memory stats for getStatsMap(). + LLSD mStatsMap; }; From b40e8fb508ea0f5350c8b66f1eae49f2406bc185 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 12 Feb 2013 22:21:39 +0100 Subject: [PATCH 27/41] Add checkbox "Keep aspect ratio" to snapshot floater. --- indra/newview/app_settings/settings.xml | 46 +- indra/newview/llfloatersnapshot.cpp | 462 ++++++++++-------- .../default/xui/en-us/floater_snapshot.xml | 28 +- 3 files changed, 312 insertions(+), 224 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 75eea9830..aef7f4e17 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8732,7 +8732,7 @@ This should be as low as possible, but too low may break functionality LastSnapshotType Comment - Select this as next type of snapshot to take (0 = postcard, 1 = texture, 2 = local image) + Select this as next type of snapshot to take (0 = feed, 1 = postcard, 2 = texture, 3 = local image) Persist 1 Type @@ -14185,6 +14185,50 @@ This should be as low as possible, but too low may break functionality Value 0 + SnapshotFeedKeepAspect + + Comment + When adjusting feed resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotPostcardKeepAspect + + Comment + When adjusting postcard resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotTextureKeepAspect + + Comment + When adjusting texture resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotLocalKeepAspect + + Comment + When adjusting local resolution keep its aspect ratio constant and equal to the target aspect. + Persist + 1 + Type + Boolean + Value + 0 + SnapshotOpenFreezeTime Comment diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index d3f4ed3f6..1ef53b269 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -91,8 +91,8 @@ ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- -S32 LLFloaterSnapshot::sUIWinHeightLong = 619 ; -S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 260 ; +S32 LLFloaterSnapshot::sUIWinHeightLong = 625 ; +S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 266 ; S32 LLFloaterSnapshot::sUIWinWidth = 219 ; S32 const THUMBHEIGHT = 159; @@ -111,6 +111,8 @@ S32 BORDER_WIDTH = 6; const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 +static std::string snapshotKeepAspectName(); + ///---------------------------------------------------------------------------- /// Class LLSnapshotLivePreview ///---------------------------------------------------------------------------- @@ -172,6 +174,7 @@ public: BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;} bool getShowFreezeFrameSnapshot() const { return mShowFreezeFrameSnapshot; } LLViewerTexture* getCurrentImage(); + char const* resolutionComboName() const; char const* aspectComboName() const; void setSnapshotType(ESnapshotType type) { mSnapshotType = type; } @@ -305,6 +308,7 @@ public: static void onClickUICheck(LLUICtrl *ctrl, void* data); static void onClickHUDCheck(LLUICtrl *ctrl, void* data); static void onClickKeepOpenCheck(LLUICtrl *ctrl, void* data); + static void onClickKeepAspect(LLUICtrl* ctrl, void* data); static void onCommitQuality(LLUICtrl* ctrl, void* data); static void onCommitFeedResolution(LLUICtrl* ctrl, void* data); static void onCommitPostcardResolution(LLUICtrl* ctrl, void* data); @@ -327,10 +331,14 @@ public: static LLSnapshotLivePreview* getPreviewView(void); static void setResolution(LLFloaterSnapshot* floater, const std::string& comboname, bool visible, bool update_controls = true); static void setAspect(LLFloaterSnapshot* floater, const std::string& comboname, bool update_controls = true); + static void storeAspectSetting(LLComboBox* combo, const std::string& comboname); + static void enforceAspect(LLFloaterSnapshot* floater, F32 new_aspect); + static void enforceResolution(LLFloaterSnapshot* floater, F32 new_aspect); static void updateControls(LLFloaterSnapshot* floater, bool delayed_formatted = false); static void resetFeedAndPostcardAspect(LLFloaterSnapshot* floater); static void updateLayout(LLFloaterSnapshot* floater); static void freezeTime(bool on); + static void keepAspect(LLFloaterSnapshot* view, bool on, bool force = false); static LLHandle sPreviewHandle; @@ -403,7 +411,7 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLRect& rect) : mNeedsFlash(TRUE), mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), mFormattedDataSize(0), - mSnapshotType(SNAPSHOT_FEED), + mSnapshotType((ESnapshotType)gSavedSettings.getS32("LastSnapshotType")), mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), mShowFreezeFrameSnapshot(FALSE), mCameraPos(LLViewerCamera::getInstance()->getOrigin()), @@ -1800,7 +1808,6 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) { - previewp->setSnapshotType(shot_type); LLViewerWindow::ESnapshotType layer_type = (shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL) ? (LLViewerWindow::ESnapshotType)gSavedSettings.getS32("SnapshotLayerType") : @@ -1841,6 +1848,7 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de floater->childSetVisible("more_btn", !is_advance); // the only item hidden in advanced mode floater->childSetVisible("less_btn", is_advance); floater->childSetVisible("type_label2", is_advance); + floater->childSetVisible("keep_aspect", is_advance); floater->childSetVisible("type_label3", is_advance); floater->childSetVisible("format_label", is_advance && is_local); floater->childSetVisible("local_format_combo", is_local); @@ -1896,6 +1904,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD && got_bytes && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLColor4::red : gColors.getColor( "LabelTextColor" )); + std::string target_size_str = gSavedSettings.getBOOL(snapshotKeepAspectName()) ? floater->getString("sourceAR") : floater->getString("targetAR"); + floater->childSetValue("type_label3", target_size_str); bool up_to_date = previewp && previewp->getSnapshotUpToDate(); bool can_upload = up_to_date && !previewp->isUsedBy(shot_type); @@ -1984,6 +1994,11 @@ void LLFloaterSnapshot::Impl::onCommitFeedAspect(LLUICtrl* ctrl, void* data) previewp->addManualOverride(LLSnapshotLivePreview::SNAPSHOT_FEED); } updateAspect(ctrl, data); + LLFloaterSnapshot* floater = (LLFloaterSnapshot*)data; + if (floater && previewp && gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(floater, previewp->getAspect()); + } } // static @@ -1997,6 +2012,11 @@ void LLFloaterSnapshot::Impl::onCommitPostcardAspect(LLUICtrl* ctrl, void* data) previewp->addManualOverride(LLSnapshotLivePreview::SNAPSHOT_POSTCARD); } updateAspect(ctrl, data); + LLFloaterSnapshot* floater = (LLFloaterSnapshot*)data; + if (floater && previewp && gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(floater, previewp->getAspect()); + } } // static @@ -2023,6 +2043,11 @@ void LLFloaterSnapshot::Impl::onCommitLocalAspect(LLUICtrl* ctrl, void* data) previewp->addManualOverride(LLSnapshotLivePreview::SNAPSHOT_LOCAL); } updateAspect(ctrl, data); + LLFloaterSnapshot* floater = (LLFloaterSnapshot*)data; + if (floater && previewp && gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(floater, previewp->getAspect()); + } } // static @@ -2249,6 +2274,18 @@ void LLFloaterSnapshot::Impl::onClickKeepOpenCheck(LLUICtrl* ctrl, void* data) gSavedSettings.setBOOL( "CloseSnapshotOnKeep", !check->get() ); } +// static +void LLFloaterSnapshot::Impl::onClickKeepAspect(LLUICtrl* ctrl, void* data) +{ + LLFloaterSnapshot* view = (LLFloaterSnapshot*)data; + if (view) + { + LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl; + keepAspect(view, check->get()); + updateControls(view); + } +} + // static void LLFloaterSnapshot::Impl::onCommitQuality(LLUICtrl* ctrl, void* data) { @@ -2318,6 +2355,46 @@ static std::string lastSnapshotAspectName() } } +static std::string snapshotKeepAspectName() +{ + switch(gSavedSettings.getS32("LastSnapshotType")) + { + case LLSnapshotLivePreview::SNAPSHOT_FEED: return "SnapshotFeedKeepAspect"; + case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "SnapshotPostcardKeepAspect"; + case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: return "SnapshotTextureKeepAspect"; + default: return "SnapshotLocalKeepAspect"; + } +} + +// static +void LLFloaterSnapshot::Impl::keepAspect(LLFloaterSnapshot* view, bool on, bool force) +{ + DoutEntering(dc::snapshot, "LLFloaterSnapshot::Impl::keepAspect(view, " << on << ", " << force << ")"); + bool cur_on = gSavedSettings.getBOOL(snapshotKeepAspectName()); + if ((!force && cur_on == on) || + gSavedSettings.getBOOL("RenderUIInSnapshot") || + gSavedSettings.getBOOL("RenderHUDInSnapshot")) + { + // No change. + return; + } + view->childSetValue("keep_aspect", on); + gSavedSettings.setBOOL(snapshotKeepAspectName(), on); + if (on) + { + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + S32 w = llround(view->childGetValue("snapshot_width").asReal(), 1.0); + S32 h = llround(view->childGetValue("snapshot_height").asReal(), 1.0); + gSavedSettings.setS32(lastSnapshotWidthName(), w); + gSavedSettings.setS32(lastSnapshotHeightName(), h); + comboSetCustom(view, previewp->resolutionComboName()); + enforceAspect(view, (F32)w / h); + } + } +} + // static void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool update_controls) { @@ -2329,192 +2406,12 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool return; } - S32 new_width = 0; - S32 new_height = 0; - F32 new_aspect = 0; LLSnapshotLivePreview* previewp = getPreviewView(); -#if 0 // Broken -- not doing this for now. - LLSnapshotLivePreview::ESnapshotType shot_type = (LLSnapshotLivePreview::ESnapshotType)gSavedSettings.getS32("LastSnapshotType"); - // Is the snapshot was already used (saved or uploaded) and no manual changes - // have been made since to the current destination type, and the raw snapshot - // is still up to date with regard to visibility of UI, HUD and BufferType etc? - if (previewp && previewp->isUsed() && !previewp->isOverriddenBy(shot_type) && previewp->getRawSnapshotUpToDate()) - { - previewp->getSize(new_width, new_height); - new_aspect = previewp->getAspect(); - S32 const old_width = new_width; - S32 const old_height = new_height; - F32 const old_aspect = new_aspect; - S32 raw_width; - S32 raw_height; - previewp->getRawSize(raw_width, raw_height); - F32 const raw_aspect = (F32)raw_width / raw_height; - bool fixed_crop = false; - bool fixed_size = false; - bool fixed_scale = false; - bool done = false; - bool fail = false; - while (!done) - { - // Attempt to change the size and aspect so the raw snapshot can also be used for this new destination. - S32 w, h, crop_offset; - bool crop_vertically; - previewp->setSize(new_width, new_height); - previewp->setAspect(new_aspect); - LLSnapshotLivePreview::EAspectSizeProblem ret = previewp->getAspectSizeProblem(w, h, crop_vertically, crop_offset); - switch(ret) - { - case LLSnapshotLivePreview::ASPECTSIZE_OK: - done = true; // Don't change anything (else) if the current settings are usable. - break; - case LLSnapshotLivePreview::CANNOT_CROP_HORIZONTALLY: - case LLSnapshotLivePreview::CANNOT_CROP_VERTICALLY: - if (fixed_crop) - { - done = fail = true; - } - else - { - // Set target aspect to aspect of the raw snapshot we have (no reason to crop anything). - new_aspect = raw_aspect; - fixed_crop = true; - } - break; - case LLSnapshotLivePreview::SIZE_TOO_LARGE: - if (fixed_size) - { - done = fail = true; - } - else - { - if (new_width > w) - { - new_width = w; - new_height = llround(new_width / new_aspect); - } - if (new_height > h) - { - new_width = llmin(w, llround(h * new_aspect)); - new_height = llmin(h, llround(new_width / new_aspect)); - } - fixed_size = true; - } - break; - case LLSnapshotLivePreview::CANNOT_RESIZE: - if (fixed_scale) - { - done = fail = true; - } - else - { - F32 ratio = llmin((F32)w / new_width, (F32)h / new_height); - new_width = llround(new_width * ratio); - new_height = llround(new_height * ratio); - fixed_scale = true; - } - break; - } - } - previewp->setAspect(old_aspect); - previewp->setSize(old_width, old_height); - if (fail) - { - new_aspect = 0; - new_width = new_height = 0; - } - else - { - LLComboBox* size_combo_box = NULL; - LLComboBox* aspect_combo_box = NULL; - switch(shot_type) - { - case LLSnapshotLivePreview::SNAPSHOT_FEED: - size_combo_box = view->getChild("feed_size_combo"); - aspect_combo_box = view->getChild("feed_aspect_combo"); - break; - case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: - size_combo_box = view->getChild("postcard_size_combo"); - aspect_combo_box = view->getChild("postcard_aspect_combo"); - break; - case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: - size_combo_box = view->getChild("texture_size_combo"); - aspect_combo_box = view->getChild("texture_aspect_combo"); - break; - case LLSnapshotLivePreview::SNAPSHOT_LOCAL: - size_combo_box = view->getChild("local_size_combo"); - aspect_combo_box = view->getChild("local_aspect_combo"); - break; - } - S32 index = 0; - S32 const size_custom = size_combo_box->getItemCount() - (shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE ? 0 : 1); // Texture does not end on 'Custom'. - while (index < size_custom) - { - size_combo_box->setCurrentByIndex(index); - std::string sdstring = size_combo_box->getSelectedValue(); - LLSD sdres; - std::stringstream sstream(sdstring); - LLSDSerialize::fromNotation(sdres, sstream, sdstring.size()); - S32 width = sdres[0]; - S32 height = sdres[1]; - if (width == 0 || height == 0) - { - width = gViewerWindow->getWindowDisplayWidth(); - height = gViewerWindow->getWindowDisplayHeight(); - } - if (width == new_width && height == new_height) - { - break; - } - ++index; - } - if (index == size_custom && shot_type != LLSnapshotLivePreview::SNAPSHOT_TEXTURE) - { - size_combo_box->setCurrentByIndex(index); - } - index = 0; - S32 const aspect_custom = aspect_combo_box->getItemCount() - 1; - while (index < aspect_custom) - { - aspect_combo_box->setCurrentByIndex(index); - std::string sdstring = aspect_combo_box->getSelectedValue(); - std::stringstream sstream; - sstream << sdstring; - F32 aspect; - sstream >> aspect; - if (aspect == -2) // Default - { - aspect = (F32)new_width / new_height; - } - if (aspect == 0) // Current window - { - aspect = (F32)gViewerWindow->getWindowDisplayWidth() / gViewerWindow->getWindowDisplayHeight(); - } - if (llabs(aspect - new_aspect) < 0.0001) - { - break; - } - ++index; - } - if (index == aspect_custom) - { - aspect_combo_box->setCurrentByIndex(index); - setAspect(view, previewp->aspectComboName(), update_controls); - } - } - } - else -#endif - { - view->getChild("feed_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFeedLastResolution")); - view->getChild("feed_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFeedLastAspect")); - view->getChild("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution")); - view->getChild("postcard_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastAspect")); - view->getChild("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution")); - view->getChild("texture_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastAspect")); - view->getChild("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution")); - view->getChild("local_aspect_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastAspect")); - } + view->getChild("feed_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFeedLastResolution")); + view->getChild("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution")); + view->getChild("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution")); + view->getChild("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution")); std::string sdstring = combobox->getSelectedValue(); LLSD sdres; @@ -2523,6 +2420,12 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool S32 width = sdres[0]; S32 height = sdres[1]; + + if (width != -1 && height != -1) + { + // Not "Custom". + keepAspect(view, false); + } if (previewp && combobox->getCurrentIndex() >= 0) { @@ -2535,15 +2438,8 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool } else if (width == -1 || height == -1) { - if (previewp->isUsed() && new_width != 0 && new_height != 0) - { - previewp->setSize(new_width, new_height); - } - else - { - // load last custom value - previewp->setSize(gSavedSettings.getS32(lastSnapshotWidthName()), gSavedSettings.getS32(lastSnapshotHeightName())); - } + // load last custom value + previewp->setSize(gSavedSettings.getS32(lastSnapshotWidthName()), gSavedSettings.getS32(lastSnapshotHeightName())); } else if (height == -2) { @@ -2594,17 +2490,8 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool if (previewp) { - if (new_aspect == 0) - { - // In case the aspect is 'Default', need to update aspect (which will call updateControls, if necessary). - setAspect(view, previewp->aspectComboName(), update_controls); - } - else - { - LLSpinCtrl* aspect_spinner = view->getChild("aspect_ratio"); - aspect_spinner->set(new_aspect); - previewp->setAspect(new_aspect); - } + // In case the aspect is 'Default', need to update aspect (which will call updateControls, if necessary). + setAspect(view, previewp->aspectComboName(), update_controls); } } @@ -2637,6 +2524,8 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda S32 width, height; previewp->getSize(width, height); aspect = (F32)width / height; + // Turn off "Keep aspect" when aspect is set to Default. + keepAspect(view, false); } else if (aspect == -1) // Custom { @@ -2648,6 +2537,7 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda } LLSpinCtrl* aspect_spinner = view->getChild("aspect_ratio"); + LLCheckBoxCtrl* keep_aspect = view->getChild("keep_aspect"); // Set whether or not the spinners can be changed. if (gSavedSettings.getBOOL("RenderUIInSnapshot") || @@ -2657,11 +2547,13 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda // Disable without making label gray. aspect_spinner->setAllowEdit(FALSE); aspect_spinner->setIncrement(0); + keep_aspect->setEnabled(FALSE); } else { aspect_spinner->setAllowEdit(TRUE); aspect_spinner->setIncrement(llmax(0.01f, lltrunc(aspect) / 100.0f)); + keep_aspect->setEnabled(TRUE); } // Sync the spinner and cache value. @@ -2682,6 +2574,77 @@ void LLFloaterSnapshot::Impl::updateAspect(LLUICtrl* ctrl, void* data, bool upda } } +// static +void LLFloaterSnapshot::Impl::enforceAspect(LLFloaterSnapshot* floater, F32 new_aspect) +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + LLComboBox* combo = floater->getChild(previewp->aspectComboName()); + S32 const aspect_custom = combo->getItemCount() - 1; + for (S32 index = 0; index <= aspect_custom; ++index) + { + combo->setCurrentByIndex(index); + if (index == aspect_custom) + { + gSavedSettings.setF32(lastSnapshotAspectName(), new_aspect); + break; + } + std::string sdstring = combo->getSelectedValue(); + std::stringstream sstream; + sstream << sdstring; + F32 aspect; + sstream >> aspect; + if (aspect == -2) // Default + { + continue; + } + if (aspect == 0) // Current window + { + aspect = (F32)gViewerWindow->getWindowDisplayWidth() / gViewerWindow->getWindowDisplayHeight(); + } + if (llabs(aspect - new_aspect) < 0.0001) + { + break; + } + } + storeAspectSetting(combo, previewp->aspectComboName()); + updateAspect(combo, floater, true); + } +} + +void LLFloaterSnapshot::Impl::enforceResolution(LLFloaterSnapshot* floater, F32 new_aspect) +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + // Get current size. + S32 w, h; + previewp->getSize(w, h); + // Do all calculation in floating point. + F32 cw = w; + F32 ch = h; + // Get the current raw size. + previewp->getRawSize(w, h); + F32 rw = w; + F32 rh = h; + // Fit rectangle with aspect new_aspect, around current rectangle, but cropped to raw size. + F32 nw = llmin(llmax(cw, ch * new_aspect), rw); + F32 nh = llmin(llmax(ch, cw / new_aspect), rh); + // Fit rectangle with aspect new_aspect inside that rectangle (in case it was cropped). + nw = llmin(nw, nh * new_aspect); + nh = llmin(nh, nw / new_aspect); + // Round off to nearest integer. + S32 new_width = llround(nw); + S32 new_height = llround(nh); + + gSavedSettings.setS32(lastSnapshotWidthName(), new_width); + gSavedSettings.setS32(lastSnapshotHeightName(), new_height); + comboSetCustom(floater, previewp->resolutionComboName()); + updateResolution(floater->getChild(previewp->resolutionComboName()), floater, false); + } +} + // static void LLFloaterSnapshot::Impl::onCommitLayerTypes(LLUICtrl* ctrl, void*data) { @@ -2700,13 +2663,20 @@ void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data) LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; if (view) { - gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view)); + LLSnapshotLivePreview::ESnapshotType snapshot_type = getTypeIndex(view); + gSavedSettings.setS32("LastSnapshotType", snapshot_type); + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + previewp->setSnapshotType(snapshot_type); + } + keepAspect(view, gSavedSettings.getBOOL(snapshotKeepAspectName()), true); updateControls(view); } } -//static +//static void LLFloaterSnapshot::Impl::onCommitSnapshotFormat(LLUICtrl* ctrl, void* data) { LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; @@ -2726,7 +2696,12 @@ void LLFloaterSnapshot::Impl::comboSetCustom(LLFloaterSnapshot* floater, const s LLComboBox* combo = floater->getChild(comboname); combo->setCurrentByIndex(combo->getItemCount() - 1); // "custom" is always the last index + storeAspectSetting(combo, comboname); +} +// static +void LLFloaterSnapshot::Impl::storeAspectSetting(LLComboBox* combo, const std::string& comboname) +{ if(comboname == "feed_size_combo") { gSavedSettings.setS32("SnapshotFeedLastResolution", combo->getCurrentIndex()); @@ -2763,8 +2738,11 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; if (view) { - S32 w = llround(view->childGetValue("snapshot_width").asReal(), 1.0); - S32 h = llround(view->childGetValue("snapshot_height").asReal(), 1.0); + LLSpinCtrl* width_spinner = view->getChild("snapshot_width"); + LLSpinCtrl* height_spinner = view->getChild("snapshot_height"); + + S32 w = llround((F32)width_spinner->getValue().asReal(), 1.0f); + S32 h = llround((F32)height_spinner->getValue().asReal(), 1.0f); LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) @@ -2774,6 +2752,33 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat if (w != curw || h != curh) { + // Enforce multiple of 32 if the step was 32. + Dout(dc::snapshot, "w = " << w << "; curw = " << curw); + if (llabs(w - curw) == 32) + { + w = (w + 16) & -32; + Dout(dc::snapshot, "w = (w + 16) & -32 = " << w); + } + if (llabs(h - curh) == 32) + { + h = (h + 16) & -32; + } + if (gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + F32 aspect = previewp->getAspect(); + if (h == curh) + { + // Width was changed. Change height to keep aspect constant. + h = llround(w / aspect); + } + else + { + // Height was changed. Change width to keep aspect constant. + w = llround(h * aspect); + } + } + width_spinner->forceSetValue(LLSD::Real(w)); + height_spinner->forceSetValue(LLSD::Real(h)); previewp->setMaxImageSize((S32)((LLSpinCtrl *)ctrl)->getMaxValue()) ; previewp->setSize(w,h); checkAutoSnapshot(previewp, FALSE); @@ -2791,6 +2796,30 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat } } +char const* LLSnapshotLivePreview::resolutionComboName() const +{ + char const* result; + switch(mSnapshotType) + { + case SNAPSHOT_FEED: + result = "feed_size_combo"; + break; + case SNAPSHOT_POSTCARD: + result = "postcard_size_combo"; + break; + case SNAPSHOT_TEXTURE: + result = "texture_size_combo"; + break; + case SNAPSHOT_LOCAL: + result = "local_size_combo"; + break; + default: + result= ""; + break; + } + return result; +} + char const* LLSnapshotLivePreview::aspectComboName() const { char const* result; @@ -2838,6 +2867,11 @@ void LLFloaterSnapshot::Impl::onCommitCustomAspect(LLUICtrl *ctrl, void* data) gSavedSettings.setF32(lastSnapshotAspectName(), a); + if (gSavedSettings.getBOOL(snapshotKeepAspectName())) + { + enforceResolution(view, a); + } + updateControls(view, true); } } @@ -2899,6 +2933,7 @@ BOOL LLFloaterSnapshot::postBuild() childSetCommitCallback("snapshot_height", Impl::onCommitCustomResolution, this); childSetCommitCallback("aspect_ratio", Impl::onCommitCustomAspect, this); + childSetCommitCallback("keep_aspect", Impl::onClickKeepAspect, this); childSetCommitCallback("ui_check", Impl::onClickUICheck, this); childSetValue("ui_check", gSavedSettings.getBOOL("RenderUIInSnapshot")); @@ -2949,8 +2984,9 @@ BOOL LLFloaterSnapshot::postBuild() Impl::sPreviewHandle = previewp->getHandle(); - impl.updateControls(this); + impl.keepAspect(sInstance, gSavedSettings.getBOOL(snapshotKeepAspectName()), true); impl.freezeTime(gSavedSettings.getBOOL("SnapshotOpenFreezeTime")); + impl.updateControls(this); return TRUE; } diff --git a/indra/newview/skins/default/xui/en-us/floater_snapshot.xml b/indra/newview/skins/default/xui/en-us/floater_snapshot.xml index 8142c0f2b..cb9d8cfda 100644 --- a/indra/newview/skins/default/xui/en-us/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en-us/floater_snapshot.xml @@ -53,16 +53,16 @@